blob: f8b8447235b2233caed3d81020719e57f9311ccb [file] [log] [blame]
diff -ruN binutils-2.24/bfd/archures.c binutils-2.24-riscv/bfd/archures.c
--- binutils-2.24/bfd/archures.c 2013-11-08 02:02:26.000000000 -0800
+++ binutils-2.24-riscv/bfd/archures.c 2014-12-02 16:05:44.901434894 -0800
@@ -583,6 +583,7 @@
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;
@@ -669,6 +670,7 @@
&bfd_or32_arch,
&bfd_pdp11_arch,
&bfd_powerpc_arch,
+ &bfd_riscv_arch,
&bfd_rs6000_arch,
&bfd_rl78_arch,
&bfd_rx_arch,
diff -ruN binutils-2.24/bfd/bfd-in2.h binutils-2.24-riscv/bfd/bfd-in2.h
--- binutils-2.24/bfd/bfd-in2.h 2013-11-18 00:40:15.000000000 -0800
+++ binutils-2.24-riscv/bfd/bfd-in2.h 2014-12-02 16:17:22.313790749 -0800
@@ -2003,6 +2003,9 @@
#define bfd_mach_ppc_e6500 5007
#define bfd_mach_ppc_titan 83
#define bfd_mach_ppc_vle 84
+ bfd_arch_riscv, /* RISC-V */
+#define bfd_mach_riscv_rocket32 132
+#define bfd_mach_riscv_rocket64 164
bfd_arch_rs6000, /* IBM RS/6000 */
#define bfd_mach_rs6k 6000
#define bfd_mach_rs6k_rs1 6001
@@ -5227,6 +5230,14 @@
value in a word. The relocation is relative offset from */
BFD_RELOC_MICROBLAZE_32_GOTOFF,
+ /* RISC-V relocations */
+ BFD_RELOC_RISCV_TLS_GOT_HI16,
+ BFD_RELOC_RISCV_TLS_GOT_LO16,
+ BFD_RELOC_RISCV_TLS_GD_HI16,
+ BFD_RELOC_RISCV_TLS_GD_LO16,
+ BFD_RELOC_RISCV_TLS_LDM_HI16,
+ BFD_RELOC_RISCV_TLS_LDM_LO16,
+
/* This is used to tell the dynamic linker to copy the value out of
the dynamic object into the runtime process image. */
BFD_RELOC_MICROBLAZE_COPY,
diff -ruN binutils-2.24/bfd/config.bfd binutils-2.24-riscv/bfd/config.bfd
--- binutils-2.24/bfd/config.bfd 2013-11-04 07:33:37.000000000 -0800
+++ binutils-2.24-riscv/bfd/config.bfd 2014-12-02 16:05:44.901434894 -0800
@@ -114,6 +114,7 @@
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 ;;
@@ -1295,6 +1296,14 @@
targ_defvec=bfd_elf32_rl78_vec
;;
+#ifdef BFD64
+ riscv*-*-*)
+ targ_defvec=bfd_elf64_littleriscv_vec
+ targ_selvecs="bfd_elf32_littleriscv_vec bfd_elf64_littleriscv_vec"
+ want64=true
+ ;;
+#endif
+
rx-*-elf)
targ_defvec=bfd_elf32_rx_le_vec
targ_selvecs="bfd_elf32_rx_be_vec bfd_elf32_rx_le_vec bfd_elf32_rx_be_ns_vec"
diff -ruN binutils-2.24/bfd/configure binutils-2.24-riscv/bfd/configure
--- binutils-2.24/bfd/configure 2013-12-02 01:30:30.000000000 -0800
+++ binutils-2.24-riscv/bfd/configure 2014-12-02 16:19:07.382447865 -0800
@@ -15280,6 +15280,7 @@
tb="$tb elf32-mips.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo" ;;
bfd_elf32_littlemoxie_vec) tb="$tb elf32-moxie.lo elf32.lo $elf" ;;
bfd_elf32_littlenios2_vec) tb="$tb elf32-nios2.lo elf32.lo $elf" ;;
+ bfd_elf32_littleriscv_vec) tb="$tb elf32-riscv.lo elfxx-riscv.lo elf32.lo $elf ecofflink.lo" ;;
bfd_elf32_m32c_vec) tb="$tb elf32-m32c.lo elf32.lo $elf" ;;
bfd_elf32_m32r_vec) tb="$tb elf32-m32r.lo elf32.lo $elf" ;;
bfd_elf32_m32rle_vec) tb="$tb elf32-m32r.lo elf32.lo $elf" ;;
@@ -15384,6 +15385,7 @@
bfd_elf32_littleaarch64_vec)tb="$tb elf32-aarch64.lo elfxx-aarch64.lo elf-ifunc.lo elf32.lo $elf"; target_size=64 ;;
bfd_elf64_little_generic_vec) tb="$tb elf64-gen.lo elf64.lo $elf"; target_size=64 ;;
bfd_elf64_littlemips_vec) tb="$tb elf64-mips.lo elf64.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;;
+ bfd_elf64_littleriscv_vec) tb="$tb elf64-riscv.lo elf64.lo elfxx-riscv.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;;
bfd_elf64_mmix_vec) tb="$tb elf64-mmix.lo elf64.lo $elf" target_size=64 ;;
bfd_elf64_powerpc_vec) tb="$tb elf64-ppc.lo elf64-gen.lo elf64.lo $elf"; target_size=64 ;;
bfd_elf64_powerpcle_vec) tb="$tb elf64-ppc.lo elf64-gen.lo elf64.lo $elf" target_size=64 ;;
diff -ruN binutils-2.24/bfd/configure.in binutils-2.24-riscv/bfd/configure.in
--- binutils-2.24/bfd/configure.in 2013-12-02 01:30:28.000000000 -0800
+++ binutils-2.24-riscv/bfd/configure.in 2014-12-02 16:18:24.430179236 -0800
@@ -769,6 +769,7 @@
tb="$tb elf32-mips.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo" ;;
bfd_elf32_littlemoxie_vec) tb="$tb elf32-moxie.lo elf32.lo $elf" ;;
bfd_elf32_littlenios2_vec) tb="$tb elf32-nios2.lo elf32.lo $elf" ;;
+ bfd_elf32_littleriscv_vec) tb="$tb elf32-riscv.lo elfxx-riscv.lo elf32.lo $elf ecofflink.lo" ;;
bfd_elf32_m32c_vec) tb="$tb elf32-m32c.lo elf32.lo $elf" ;;
bfd_elf32_m32r_vec) tb="$tb elf32-m32r.lo elf32.lo $elf" ;;
bfd_elf32_m32rle_vec) tb="$tb elf32-m32r.lo elf32.lo $elf" ;;
@@ -873,6 +874,7 @@
bfd_elf32_littleaarch64_vec)tb="$tb elf32-aarch64.lo elfxx-aarch64.lo elf-ifunc.lo elf32.lo $elf"; target_size=64 ;;
bfd_elf64_little_generic_vec) tb="$tb elf64-gen.lo elf64.lo $elf"; target_size=64 ;;
bfd_elf64_littlemips_vec) tb="$tb elf64-mips.lo elf64.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;;
+ bfd_elf64_littleriscv_vec) tb="$tb elf64-riscv.lo elf64.lo elfxx-riscv.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;;
bfd_elf64_mmix_vec) tb="$tb elf64-mmix.lo elf64.lo $elf" target_size=64 ;;
bfd_elf64_powerpc_vec) tb="$tb elf64-ppc.lo elf64-gen.lo elf64.lo $elf"; target_size=64 ;;
bfd_elf64_powerpcle_vec) tb="$tb elf64-ppc.lo elf64-gen.lo elf64.lo $elf" target_size=64 ;;
diff -ruN binutils-2.24/bfd/cpu-riscv.c binutils-2.24-riscv/bfd/cpu-riscv.c
--- binutils-2.24/bfd/cpu-riscv.c 1969-12-31 16:00:00.000000000 -0800
+++ binutils-2.24-riscv/bfd/cpu-riscv.c 2014-12-02 16:05:44.905434919 -0800
@@ -0,0 +1,78 @@
+/* bfd back-end for mips support
+ Copyright 1990, 1991, 1993, 1994, 1995, 1996, 1997, 1998, 2000, 2001,
+ 2002, 2003, 2004, 2005, 2007, 2008, 2009 Free Software Foundation, Inc.
+ Written by Steve Chamberlain of Cygnus Support.
+
+ 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; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#include "sysdep.h"
+#include "bfd.h"
+#include "libbfd.h"
+
+static const bfd_arch_info_type *mips_compatible
+ (const bfd_arch_info_type *, const bfd_arch_info_type *);
+
+/* The default routine tests bits_per_word, which is wrong on mips as
+ mips word size doesn't correlate with reloc size. */
+
+static const bfd_arch_info_type *
+mips_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_mips_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, \
+ mips_compatible, \
+ bfd_default_scan, \
+ NEXT, \
+ }
+
+enum
+{
+ I_riscv_rocket64,
+ I_riscv_rocket32
+};
+
+#define NN(index) (&arch_info_struct[(index) + 1])
+
+static const bfd_arch_info_type arch_info_struct[] =
+{
+ N (64, 64, bfd_mach_riscv_rocket64, "riscv:rocket64", FALSE, NN(I_riscv_rocket64)),
+ N (32, 32, bfd_mach_riscv_rocket32, "riscv:rocket32", FALSE, 0)
+};
+
+/* The default architecture is riscv:rocket64. */
+
+const bfd_arch_info_type bfd_riscv_arch =
+N (64, 64, 0, "riscv", TRUE, &arch_info_struct[0]);
diff -ruN binutils-2.24/bfd/elf32-riscv.c binutils-2.24-riscv/bfd/elf32-riscv.c
--- binutils-2.24/bfd/elf32-riscv.c 1969-12-31 16:00:00.000000000 -0800
+++ binutils-2.24-riscv/bfd/elf32-riscv.c 2014-12-02 16:05:44.905434919 -0800
@@ -0,0 +1,2170 @@
+/* MIPS-specific support for 32-bit ELF
+ Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+ 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+
+ Most of the information added by Ian Lance Taylor, Cygnus Support,
+ <ian@cygnus.com>.
+ N32/64 ABI support added by Mark Mitchell, CodeSourcery, LLC.
+ <mark@codesourcery.com>
+ Traditional MIPS targets support added by Koundinya.K, Dansk Data
+ Elektronik & Operations Research Group. <kk@ddeorg.soft.net>
+
+ 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; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+
+/* This file handles MIPS ELF targets. SGI Irix 5 uses a slightly
+ different MIPS ELF from other targets. This matters when linking.
+ This file supports both, switching at runtime. */
+
+#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"
+
+/* Get the ECOFF swapping routines. */
+#include "coff/sym.h"
+#include "coff/symconst.h"
+#include "coff/internal.h"
+#include "coff/ecoff.h"
+#include "coff/mips.h"
+#define ECOFF_SIGNED_32
+#include "ecoffswap.h"
+
+#include "opcode/riscv.h"
+
+static bfd_boolean mips_elf_assign_gp
+ (bfd *, bfd_vma *);
+static bfd_reloc_status_type mips_elf_final_gp
+ (bfd *, asymbol *, bfd_boolean, char **, bfd_vma *);
+static bfd_reloc_status_type mips_elf_gprel16_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type mips_elf_literal_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type mips_elf_gprel32_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type gprel32_with_gp
+ (bfd *, asymbol *, arelent *, asection *, bfd_boolean, void *, bfd_vma);
+static reloc_howto_type *bfd_elf32_bfd_reloc_type_lookup
+ (bfd *, bfd_reloc_code_real_type);
+static reloc_howto_type *mips_elf_n32_rtype_to_howto
+ (unsigned int, bfd_boolean);
+static void mips_info_to_howto_rel
+ (bfd *, arelent *, Elf_Internal_Rela *);
+static void mips_info_to_howto_rela
+ (bfd *, arelent *, Elf_Internal_Rela *);
+static bfd_boolean mips_elf_sym_is_global
+ (bfd *, asymbol *);
+static bfd_boolean mips_elf_n32_object_p
+ (bfd *);
+static bfd_boolean elf32_mips_grok_prstatus
+ (bfd *, Elf_Internal_Note *);
+static bfd_boolean elf32_mips_grok_psinfo
+ (bfd *, Elf_Internal_Note *);
+
+extern const bfd_target bfd_elf32_nbigmips_vec;
+extern const bfd_target bfd_elf32_nlittlemips_vec;
+
+/* The number of local .got entries we reserve. */
+#define MIPS_RESERVED_GOTNO (2)
+
+/* In case we're on a 32-bit machine, construct a 64-bit "-1" value
+ from smaller values. Start with zero, widen, *then* decrement. */
+#define MINUS_ONE (((bfd_vma)0) - 1)
+
+/* The relocation table used for SHT_REL sections. */
+
+static reloc_howto_type elf_mips_howto_table_rel[] =
+{
+ /* No relocation. */
+ HOWTO (R_RISCV_NONE, /* type */
+ 0, /* rightshift */
+ 0, /* size (0 = byte, 1 = short, 2 = long) */
+ 0, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_NONE", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (1),
+
+ /* 32 bit relocation. */
+ HOWTO (R_RISCV_32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit symbol relative relocation. */
+ HOWTO (R_RISCV_REL32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_REL32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 26 bit jump address. */
+ HOWTO (R_RISCV_26, /* type */
+ RISCV_JUMP_ALIGN_BITS, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_JUMP_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_TARGET, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ /* This needs complex overflow
+ detection, because the upper 36
+ bits must match the PC + 4. */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_26", /* name */
+ TRUE, /* partial_inplace */
+ ((1<<RISCV_JUMP_BITS)-1) << OP_SH_TARGET, /* src_mask */
+ ((1<<RISCV_JUMP_BITS)-1) << OP_SH_TARGET, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* R_RISCV_HI16 and R_RISCV_LO16 are unsupported for NewABI REL.
+ However, the native IRIX6 tools use them, so we try our best. */
+
+ /* High 16 bits of symbol value. */
+ HOWTO (R_RISCV_HI16, /* type */
+ RISCV_IMM_BITS, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_hi16_reloc, /* special_function */
+ "R_RISCV_HI16", /* name */
+ TRUE, /* partial_inplace */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of symbol value. */
+ HOWTO (R_RISCV_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_lo16_reloc, /* special_function */
+ "R_RISCV_LO16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* GP relative reference. */
+ HOWTO (R_RISCV_GPREL16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips_elf_gprel16_reloc, /* special_function */
+ "R_RISCV_GPREL16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Reference to literal section. */
+ HOWTO (R_RISCV_LITERAL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips_elf_literal_reloc, /* special_function */
+ "R_RISCV_LITERAL", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Reference to global offset table. */
+ HOWTO (R_RISCV_GOT16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_got16_reloc, /* special_function */
+ "R_RISCV_GOT16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 16 bit PC relative reference. Note that the ABI document has a typo
+ and claims R_RISCV_PC16 to be not rightshifted, rendering it useless.
+ We do the right thing here. */
+ HOWTO (R_RISCV_PC16, /* type */
+ RISCV_BRANCH_ALIGN_BITS, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_PC16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* 16 bit call through global offset table. */
+ HOWTO (R_RISCV_CALL16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_CALL16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit GP relative reference. */
+ HOWTO (R_RISCV_GPREL32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ mips_elf_gprel32_reloc, /* special_function */
+ "R_RISCV_GPREL32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (13),
+ EMPTY_HOWTO (14),
+ EMPTY_HOWTO (15),
+ EMPTY_HOWTO (16),
+ EMPTY_HOWTO (17),
+
+ /* 64 bit relocation. */
+ HOWTO (R_RISCV_64, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_64", /* name */
+ TRUE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Displacement in the global offset table. */
+ HOWTO (R_RISCV_GOT_DISP, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GOT_DISP", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (20),
+ EMPTY_HOWTO (21),
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_GOT_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GOT_HI16", /* name */
+ TRUE, /* partial_inplace */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_GOT_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GOT_LO16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 64 bit subtraction. */
+ HOWTO (R_RISCV_SUB, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_SUB", /* name */
+ TRUE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Insert the addend as an instruction. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_RISCV_INSERT_A, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_INSERT_A", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Insert the addend as an instruction, and change all relocations
+ to refer to the old instruction at the address. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_RISCV_INSERT_B, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_INSERT_B", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Delete a 32 bit instruction. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_RISCV_DELETE, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_DELETE", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (28),
+ EMPTY_HOWTO (29),
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_CALL_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_CALL_HI16", /* name */
+ TRUE, /* partial_inplace */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_CALL_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_CALL_LO16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Section displacement, used by an associated event location section. */
+ HOWTO (R_RISCV_SCN_DISP, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_SCN_DISP", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_RISCV_REL16, /* type */
+ 0, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_REL16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* These two are obsolete. */
+ EMPTY_HOWTO (R_RISCV_ADD_IMMEDIATE),
+ EMPTY_HOWTO (R_RISCV_PJUMP),
+
+ /* Similiar to R_RISCV_REL32, but used for relocations in a GOT section.
+ It must be used for multigot GOT's (and only there). */
+ HOWTO (R_RISCV_RELGOT, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_RELGOT", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO(37),
+
+ /* TLS relocations. */
+ HOWTO (R_RISCV_TLS_DTPMOD32, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_DTPMOD32", /* name */
+ TRUE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_RISCV_TLS_DTPREL32, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_DTPREL32", /* name */
+ TRUE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (R_RISCV_TLS_DTPMOD64),
+ EMPTY_HOWTO (R_RISCV_TLS_DTPREL64),
+
+ /* TLS general dynamic variable reference. */
+ HOWTO (R_RISCV_TLS_GD, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GD", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS local dynamic variable reference. */
+ HOWTO (R_RISCV_TLS_LDM, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_LDM", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS local dynamic offset. */
+ HOWTO (R_RISCV_TLS_DTPREL_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_DTPREL_HI16", /* name */
+ TRUE, /* partial_inplace */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS local dynamic offset. */
+ HOWTO (R_RISCV_TLS_DTPREL_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_DTPREL_LO16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS thread pointer offset. */
+ HOWTO (R_RISCV_TLS_GOTTPREL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GOTTPREL", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS IE dynamic relocations. */
+ HOWTO (R_RISCV_TLS_TPREL32, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_TPREL32", /* name */
+ TRUE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (R_RISCV_TLS_TPREL64),
+
+ /* TLS thread pointer offset. */
+ HOWTO (R_RISCV_TLS_TPREL_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_TPREL_HI16", /* name */
+ TRUE, /* partial_inplace */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS thread pointer offset. */
+ HOWTO (R_RISCV_TLS_TPREL_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_TPREL_LO16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_TLS_GOT_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GOT_HI16", /* name */
+ TRUE, /* partial_inplace */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS thread pointer offset. */
+ HOWTO (R_RISCV_TLS_GOT_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GOT_LO16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_TLS_GD_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GD_HI16", /* name */
+ TRUE, /* partial_inplace */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS thread pointer offset. */
+ HOWTO (R_RISCV_TLS_GD_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GD_LO16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_TLS_LDM_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_LDM_HI16", /* name */
+ TRUE, /* partial_inplace */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS thread pointer offset. */
+ HOWTO (R_RISCV_TLS_LDM_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_LDM_LO16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit relocation with no addend. */
+ HOWTO (R_RISCV_GLOB_DAT, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GLOB_DAT", /* name */
+ FALSE, /* partial_inplace */
+ 0x0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+};
+
+/* The relocation table used for SHT_RELA sections. */
+
+static reloc_howto_type elf_mips_howto_table_rela[] =
+{
+ /* No relocation. */
+ HOWTO (R_RISCV_NONE, /* type */
+ 0, /* rightshift */
+ 0, /* size (0 = byte, 1 = short, 2 = long) */
+ 0, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_NONE", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (1),
+
+ /* 32 bit relocation. */
+ HOWTO (R_RISCV_32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_32", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit symbol relative relocation. */
+ HOWTO (R_RISCV_REL32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_REL32", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 26 bit jump address. */
+ HOWTO (R_RISCV_26, /* type */
+ RISCV_JUMP_ALIGN_BITS, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_JUMP_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_TARGET, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ /* This needs complex overflow
+ detection, because the upper 36
+ bits must match the PC + 4. */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_26", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ ((1<<RISCV_JUMP_BITS)-1) << OP_SH_TARGET, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* High 16 bits of symbol value. */
+ HOWTO (R_RISCV_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_HI16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of symbol value. */
+ HOWTO (R_RISCV_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_LO16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* GP relative reference. */
+ HOWTO (R_RISCV_GPREL16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips_elf_gprel16_reloc, /* special_function */
+ "R_RISCV_GPREL16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Reference to literal section. */
+ HOWTO (R_RISCV_LITERAL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips_elf_literal_reloc, /* special_function */
+ "R_RISCV_LITERAL", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Reference to global offset table. */
+ HOWTO (R_RISCV_GOT16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GOT16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 16 bit PC relative reference. Note that the ABI document has a typo
+ and claims R_RISCV_PC16 to be not rightshifted, rendering it useless.
+ We do the right thing here. */
+ HOWTO (R_RISCV_PC16, /* type */
+ 2, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_PC16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* 16 bit call through global offset table. */
+ HOWTO (R_RISCV_CALL16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_CALL16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit GP relative reference. */
+ HOWTO (R_RISCV_GPREL32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ mips_elf_gprel32_reloc, /* special_function */
+ "R_RISCV_GPREL32", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (13),
+ EMPTY_HOWTO (14),
+ EMPTY_HOWTO (15),
+ EMPTY_HOWTO (16),
+ EMPTY_HOWTO (17),
+
+ /* 64 bit relocation. */
+ HOWTO (R_RISCV_64, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_64", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Displacement in the global offset table. */
+ HOWTO (R_RISCV_GOT_DISP, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GOT_DISP", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (20),
+ EMPTY_HOWTO (21),
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_GOT_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GOT_HI16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_GOT_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GOT_LO16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 64 bit subtraction. */
+ HOWTO (R_RISCV_SUB, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_SUB", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Insert the addend as an instruction. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_RISCV_INSERT_A, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_INSERT_A", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Insert the addend as an instruction, and change all relocations
+ to refer to the old instruction at the address. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_RISCV_INSERT_B, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_INSERT_B", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Delete a 32 bit instruction. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_RISCV_DELETE, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_DELETE", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (28),
+ EMPTY_HOWTO (29),
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_CALL_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_CALL_HI16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_CALL_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_CALL_LO16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Section displacement, used by an associated event location section. */
+ HOWTO (R_RISCV_SCN_DISP, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_SCN_DISP", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_RISCV_REL16, /* type */
+ 0, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_REL16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* These two are obsolete. */
+ EMPTY_HOWTO (R_RISCV_ADD_IMMEDIATE),
+ EMPTY_HOWTO (R_RISCV_PJUMP),
+
+ /* Similiar to R_RISCV_REL32, but used for relocations in a GOT section.
+ It must be used for multigot GOT's (and only there). */
+ HOWTO (R_RISCV_RELGOT, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_RELGOT", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO(37),
+
+ /* TLS relocations. */
+ HOWTO (R_RISCV_TLS_DTPMOD32, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_DTPMOD32", /* name */
+ FALSE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_RISCV_TLS_DTPREL32, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_DTPREL32", /* name */
+ TRUE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (R_RISCV_TLS_DTPMOD64),
+ EMPTY_HOWTO (R_RISCV_TLS_DTPREL64),
+
+ /* TLS general dynamic variable reference. */
+ HOWTO (R_RISCV_TLS_GD, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GD", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS local dynamic variable reference. */
+ HOWTO (R_RISCV_TLS_LDM, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_LDM", /* name */
+ TRUE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS local dynamic offset. */
+ HOWTO (R_RISCV_TLS_DTPREL_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_DTPREL_HI16", /* name */
+ TRUE, /* partial_inplace */
+ 0, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS local dynamic offset. */
+ HOWTO (R_RISCV_TLS_DTPREL_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_DTPREL_LO16", /* name */
+ TRUE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS thread pointer offset. */
+ HOWTO (R_RISCV_TLS_GOTTPREL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GOTTPREL", /* name */
+ TRUE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_RISCV_TLS_TPREL32, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_TPREL32", /* name */
+ FALSE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (R_RISCV_TLS_TPREL64),
+
+ /* TLS thread pointer offset. */
+ HOWTO (R_RISCV_TLS_TPREL_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_TPREL_HI16", /* name */
+ TRUE, /* partial_inplace */
+ 0, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS thread pointer offset. */
+ HOWTO (R_RISCV_TLS_TPREL_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_TPREL_LO16", /* name */
+ TRUE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_TLS_GOT_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GOT_HI16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_TLS_GOT_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GOT_LO16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_TLS_GD_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GD_HI16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_TLS_GD_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GD_LO16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_TLS_LDM_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_LDM_HI16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_TLS_LDM_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_LDM_LO16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit relocation with no addend. */
+ HOWTO (R_RISCV_GLOB_DAT, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GLOB_DAT", /* name */
+ FALSE, /* partial_inplace */
+ 0x0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+};
+
+/* GNU extension to record C++ vtable hierarchy */
+static reloc_howto_type elf_mips_gnu_vtinherit_howto =
+ HOWTO (R_RISCV_GNU_VTINHERIT, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 0, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ NULL, /* special_function */
+ "R_RISCV_GNU_VTINHERIT", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+/* GNU extension to record C++ vtable member usage */
+static reloc_howto_type elf_mips_gnu_vtentry_howto =
+ HOWTO (R_RISCV_GNU_VTENTRY, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 0, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_elf_rel_vtable_reloc_fn, /* special_function */
+ "R_RISCV_GNU_VTENTRY", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+/* 16 bit offset for pc-relative branches. */
+static reloc_howto_type elf_mips_gnu_rel16_s2 =
+ HOWTO (R_RISCV_GNU_REL16_S2, /* type */
+ 2, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GNU_REL16_S2", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ TRUE); /* pcrel_offset */
+
+/* 16 bit offset for pc-relative branches. */
+static reloc_howto_type elf_mips_gnu_rela16_s2 =
+ HOWTO (R_RISCV_GNU_REL16_S2, /* type */
+ 2, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GNU_REL16_S2", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ TRUE); /* pcrel_offset */
+
+/* Originally a VxWorks extension, but now used for other systems too. */
+static reloc_howto_type elf_mips_copy_howto =
+ HOWTO (R_RISCV_COPY, /* type */
+ 0, /* rightshift */
+ 0, /* this one is variable size */
+ 0, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_RISCV_COPY", /* name */
+ FALSE, /* partial_inplace */
+ 0x0, /* src_mask */
+ 0x0, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+/* Originally a VxWorks extension, but now used for other systems too. */
+static reloc_howto_type elf_mips_jump_slot_howto =
+ HOWTO (R_RISCV_JUMP_SLOT, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_RISCV_JUMP_SLOT", /* name */
+ FALSE, /* partial_inplace */
+ 0x0, /* src_mask */
+ 0x0, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+/* Set the GP value for OUTPUT_BFD. Returns FALSE if this is a
+ dangerous relocation. */
+
+static bfd_boolean
+mips_elf_assign_gp (bfd *output_bfd, bfd_vma *pgp)
+{
+ unsigned int count;
+ asymbol **sym;
+ unsigned int i;
+
+ /* If we've already figured out what GP will be, just return it. */
+ *pgp = _bfd_get_gp_value (output_bfd);
+ if (*pgp)
+ return TRUE;
+
+ count = bfd_get_symcount (output_bfd);
+ sym = bfd_get_outsymbols (output_bfd);
+
+ /* The linker script will have created a symbol named `_gp' with the
+ appropriate value. */
+ if (sym == NULL)
+ i = count;
+ else
+ {
+ for (i = 0; i < count; i++, sym++)
+ {
+ register const char *name;
+
+ name = bfd_asymbol_name (*sym);
+ if (*name == '_' && strcmp (name, "_gp") == 0)
+ {
+ *pgp = bfd_asymbol_value (*sym);
+ _bfd_set_gp_value (output_bfd, *pgp);
+ break;
+ }
+ }
+ }
+
+ if (i >= count)
+ {
+ /* Only get the error once. */
+ *pgp = 4;
+ _bfd_set_gp_value (output_bfd, *pgp);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* We have to figure out the gp value, so that we can adjust the
+ symbol value correctly. We look up the symbol _gp in the output
+ BFD. If we can't find it, we're stuck. We cache it in the ELF
+ target data. We don't need to adjust the symbol value for an
+ external symbol if we are producing relocatable output. */
+
+static bfd_reloc_status_type
+mips_elf_final_gp (bfd *output_bfd, asymbol *symbol, bfd_boolean relocatable,
+ char **error_message, bfd_vma *pgp)
+{
+ if (bfd_is_und_section (symbol->section)
+ && ! relocatable)
+ {
+ *pgp = 0;
+ return bfd_reloc_undefined;
+ }
+
+ *pgp = _bfd_get_gp_value (output_bfd);
+ if (*pgp == 0
+ && (! relocatable
+ || (symbol->flags & BSF_SECTION_SYM) != 0))
+ {
+ if (relocatable)
+ {
+ /* Make up a value. */
+ *pgp = symbol->section->output_section->vma /*+ 0x4000*/;
+ _bfd_set_gp_value (output_bfd, *pgp);
+ }
+ else if (!mips_elf_assign_gp (output_bfd, pgp))
+ {
+ *error_message =
+ (char *) _("GP relative relocation when _gp not defined");
+ return bfd_reloc_dangerous;
+ }
+ }
+
+ return bfd_reloc_ok;
+}
+
+/* Do a R_RISCV_GPREL16 relocation. This is a 16 bit value which must
+ become the offset from the gp register. */
+
+static bfd_reloc_status_type
+mips_elf_gprel16_reloc (bfd *abfd ATTRIBUTE_UNUSED, arelent *reloc_entry,
+ asymbol *symbol, void *data ATTRIBUTE_UNUSED,
+ asection *input_section, bfd *output_bfd,
+ char **error_message ATTRIBUTE_UNUSED)
+{
+ bfd_boolean relocatable;
+ bfd_reloc_status_type ret;
+ bfd_vma gp;
+
+ if (output_bfd != NULL)
+ relocatable = TRUE;
+ else
+ {
+ relocatable = FALSE;
+ output_bfd = symbol->section->output_section->owner;
+ }
+
+ ret = mips_elf_final_gp (output_bfd, symbol, relocatable, error_message,
+ &gp);
+ if (ret != bfd_reloc_ok)
+ return ret;
+
+ return _bfd_riscv_elf_gprel16_with_gp (abfd, symbol, reloc_entry,
+ input_section, relocatable,
+ data, gp);
+}
+
+/* Do a R_RISCV_LITERAL relocation. */
+
+static bfd_reloc_status_type
+mips_elf_literal_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+ void *data, asection *input_section, bfd *output_bfd,
+ char **error_message)
+{
+ bfd_boolean relocatable;
+ bfd_reloc_status_type ret;
+ bfd_vma gp;
+
+ /* R_RISCV_LITERAL relocations are defined for local symbols only. */
+ if (output_bfd != NULL
+ && (symbol->flags & BSF_SECTION_SYM) == 0
+ && (symbol->flags & BSF_LOCAL) != 0)
+ {
+ *error_message = (char *)
+ _("literal relocation occurs for an external symbol");
+ return bfd_reloc_outofrange;
+ }
+
+ /* FIXME: The entries in the .lit8 and .lit4 sections should be merged. */
+ if (output_bfd != NULL)
+ relocatable = TRUE;
+ else
+ {
+ relocatable = FALSE;
+ output_bfd = symbol->section->output_section->owner;
+ }
+
+ ret = mips_elf_final_gp (output_bfd, symbol, relocatable, error_message,
+ &gp);
+ if (ret != bfd_reloc_ok)
+ return ret;
+
+ return _bfd_riscv_elf_gprel16_with_gp (abfd, symbol, reloc_entry,
+ input_section, relocatable,
+ data, gp);
+}
+
+/* Do a R_RISCV_GPREL32 relocation. This is a 32 bit value which must
+ become the offset from the gp register. */
+
+static bfd_reloc_status_type
+mips_elf_gprel32_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+ void *data, asection *input_section, bfd *output_bfd,
+ char **error_message)
+{
+ bfd_boolean relocatable;
+ bfd_reloc_status_type ret;
+ bfd_vma gp;
+
+ /* R_RISCV_GPREL32 relocations are defined for local symbols only. */
+ if (output_bfd != NULL
+ && (symbol->flags & BSF_SECTION_SYM) == 0
+ && (symbol->flags & BSF_LOCAL) != 0)
+ {
+ *error_message = (char *)
+ _("32bits gp relative relocation occurs for an external symbol");
+ return bfd_reloc_outofrange;
+ }
+
+ if (output_bfd != NULL)
+ {
+ relocatable = TRUE;
+ gp = _bfd_get_gp_value (output_bfd);
+ }
+ else
+ {
+ relocatable = FALSE;
+ output_bfd = symbol->section->output_section->owner;
+
+ ret = mips_elf_final_gp (output_bfd, symbol, relocatable,
+ error_message, &gp);
+ if (ret != bfd_reloc_ok)
+ return ret;
+ }
+
+ return gprel32_with_gp (abfd, symbol, reloc_entry, input_section,
+ relocatable, data, gp);
+}
+
+static bfd_reloc_status_type
+gprel32_with_gp (bfd *abfd, asymbol *symbol, arelent *reloc_entry,
+ asection *input_section, bfd_boolean relocatable,
+ void *data, bfd_vma gp)
+{
+ bfd_vma relocation;
+ unsigned long val;
+
+ if (bfd_is_com_section (symbol->section))
+ relocation = 0;
+ else
+ relocation = symbol->value;
+
+ relocation += symbol->section->output_section->vma;
+ relocation += symbol->section->output_offset;
+
+ if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
+ return bfd_reloc_outofrange;
+
+ if (reloc_entry->howto->src_mask == 0)
+ val = 0;
+ else
+ val = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
+
+ /* Set val to the offset into the section or symbol. */
+ val += reloc_entry->addend;
+
+ /* Adjust val for the final section location and GP value. If we
+ are producing relocatable output, we don't want to do this for
+ an external symbol. */
+ if (! relocatable
+ || (symbol->flags & BSF_SECTION_SYM) != 0)
+ val += relocation - gp;
+
+ bfd_put_32 (abfd, val, (bfd_byte *) data + reloc_entry->address);
+
+ if (relocatable)
+ reloc_entry->address += input_section->output_offset;
+
+ return bfd_reloc_ok;
+}
+
+/* A mapping from BFD reloc types to MIPS ELF reloc types. */
+
+struct elf_reloc_map {
+ bfd_reloc_code_real_type bfd_val;
+ enum elf_riscv_reloc_type elf_val;
+};
+
+static const struct elf_reloc_map mips_reloc_map[] =
+{
+ { BFD_RELOC_NONE, R_RISCV_NONE },
+ { BFD_RELOC_32, R_RISCV_32 },
+ /* There is no BFD reloc for R_RISCV_REL32. */
+ { BFD_RELOC_CTOR, R_RISCV_32 },
+ { BFD_RELOC_64, R_RISCV_64 },
+ { BFD_RELOC_16_PCREL_S2, R_RISCV_PC16 },
+ { BFD_RELOC_HI16_S, R_RISCV_HI16 },
+ { BFD_RELOC_LO16, R_RISCV_LO16 },
+ { BFD_RELOC_GPREL16, R_RISCV_GPREL16 },
+ { BFD_RELOC_GPREL32, R_RISCV_GPREL32 },
+ { BFD_RELOC_MIPS_JMP, R_RISCV_26 },
+ { BFD_RELOC_MIPS_LITERAL, R_RISCV_LITERAL },
+ { BFD_RELOC_MIPS_GOT16, R_RISCV_GOT16 },
+ { BFD_RELOC_MIPS_CALL16, R_RISCV_CALL16 },
+ { BFD_RELOC_MIPS_GOT_DISP, R_RISCV_GOT_DISP },
+ { BFD_RELOC_MIPS_GOT_HI16, R_RISCV_GOT_HI16 },
+ { BFD_RELOC_MIPS_GOT_LO16, R_RISCV_GOT_LO16 },
+ { BFD_RELOC_MIPS_SUB, R_RISCV_SUB },
+ { BFD_RELOC_MIPS_INSERT_A, R_RISCV_INSERT_A },
+ { BFD_RELOC_MIPS_INSERT_B, R_RISCV_INSERT_B },
+ { BFD_RELOC_MIPS_DELETE, R_RISCV_DELETE },
+ { BFD_RELOC_MIPS_CALL_HI16, R_RISCV_CALL_HI16 },
+ { BFD_RELOC_MIPS_CALL_LO16, R_RISCV_CALL_LO16 },
+ { BFD_RELOC_MIPS_SCN_DISP, R_RISCV_SCN_DISP },
+ { BFD_RELOC_MIPS_REL16, R_RISCV_REL16 },
+ { BFD_RELOC_MIPS_RELGOT, R_RISCV_RELGOT },
+ { BFD_RELOC_MIPS_TLS_DTPMOD32, R_RISCV_TLS_DTPMOD32 },
+ { BFD_RELOC_MIPS_TLS_DTPREL32, R_RISCV_TLS_DTPREL32 },
+ { BFD_RELOC_MIPS_TLS_DTPMOD64, R_RISCV_TLS_DTPMOD64 },
+ { BFD_RELOC_MIPS_TLS_DTPREL64, R_RISCV_TLS_DTPREL64 },
+ { BFD_RELOC_MIPS_TLS_GD, R_RISCV_TLS_GD },
+ { BFD_RELOC_MIPS_TLS_LDM, R_RISCV_TLS_LDM },
+ { BFD_RELOC_MIPS_TLS_DTPREL_HI16, R_RISCV_TLS_DTPREL_HI16 },
+ { BFD_RELOC_MIPS_TLS_DTPREL_LO16, R_RISCV_TLS_DTPREL_LO16 },
+ { BFD_RELOC_MIPS_TLS_GOTTPREL, R_RISCV_TLS_GOTTPREL },
+ { BFD_RELOC_MIPS_TLS_TPREL32, R_RISCV_TLS_TPREL32 },
+ { BFD_RELOC_MIPS_TLS_TPREL64, R_RISCV_TLS_TPREL64 },
+ { BFD_RELOC_MIPS_TLS_TPREL_HI16, R_RISCV_TLS_TPREL_HI16 },
+ { BFD_RELOC_MIPS_TLS_TPREL_LO16, R_RISCV_TLS_TPREL_LO16 }
+};
+
+/* Given a BFD reloc type, return a howto structure. */
+
+static reloc_howto_type *
+bfd_elf32_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+ bfd_reloc_code_real_type code)
+{
+ unsigned int i;
+ /* FIXME: We default to RELA here instead of choosing the right
+ relocation variant. */
+ reloc_howto_type *howto_table = elf_mips_howto_table_rela;
+
+ for (i = 0; i < sizeof (mips_reloc_map) / sizeof (struct elf_reloc_map);
+ i++)
+ {
+ if (mips_reloc_map[i].bfd_val == code)
+ return &howto_table[(int) mips_reloc_map[i].elf_val];
+ }
+
+ switch (code)
+ {
+ case BFD_RELOC_VTABLE_INHERIT:
+ return &elf_mips_gnu_vtinherit_howto;
+ case BFD_RELOC_VTABLE_ENTRY:
+ return &elf_mips_gnu_vtentry_howto;
+ case BFD_RELOC_MIPS_COPY:
+ return &elf_mips_copy_howto;
+ case BFD_RELOC_MIPS_JUMP_SLOT:
+ return &elf_mips_jump_slot_howto;
+ default:
+ bfd_set_error (bfd_error_bad_value);
+ return NULL;
+ }
+}
+
+static reloc_howto_type *
+bfd_elf32_bfd_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+ const char *r_name)
+{
+ unsigned int i;
+
+ for (i = 0;
+ i < (sizeof (elf_mips_howto_table_rela)
+ / sizeof (elf_mips_howto_table_rela[0]));
+ i++)
+ if (elf_mips_howto_table_rela[i].name != NULL
+ && strcasecmp (elf_mips_howto_table_rela[i].name, r_name) == 0)
+ return &elf_mips_howto_table_rela[i];
+
+ if (strcasecmp (elf_mips_gnu_vtinherit_howto.name, r_name) == 0)
+ return &elf_mips_gnu_vtinherit_howto;
+ if (strcasecmp (elf_mips_gnu_vtentry_howto.name, r_name) == 0)
+ return &elf_mips_gnu_vtentry_howto;
+ if (strcasecmp (elf_mips_gnu_rel16_s2.name, r_name) == 0)
+ return &elf_mips_gnu_rel16_s2;
+ if (strcasecmp (elf_mips_gnu_rela16_s2.name, r_name) == 0)
+ return &elf_mips_gnu_rela16_s2;
+ if (strcasecmp (elf_mips_copy_howto.name, r_name) == 0)
+ return &elf_mips_copy_howto;
+ if (strcasecmp (elf_mips_jump_slot_howto.name, r_name) == 0)
+ return &elf_mips_jump_slot_howto;
+
+ return NULL;
+}
+
+/* Given a MIPS Elf_Internal_Rel, fill in an arelent structure. */
+
+static reloc_howto_type *
+mips_elf_n32_rtype_to_howto (unsigned int r_type, bfd_boolean rela_p)
+{
+ switch (r_type)
+ {
+ case R_RISCV_GNU_VTINHERIT:
+ return &elf_mips_gnu_vtinherit_howto;
+ case R_RISCV_GNU_VTENTRY:
+ return &elf_mips_gnu_vtentry_howto;
+ case R_RISCV_GNU_REL16_S2:
+ if (rela_p)
+ return &elf_mips_gnu_rela16_s2;
+ else
+ return &elf_mips_gnu_rel16_s2;
+ case R_RISCV_COPY:
+ return &elf_mips_copy_howto;
+ case R_RISCV_JUMP_SLOT:
+ return &elf_mips_jump_slot_howto;
+ default:
+ BFD_ASSERT (r_type < (unsigned int) R_RISCV_max);
+ if (rela_p)
+ return &elf_mips_howto_table_rela[r_type];
+ else
+ return &elf_mips_howto_table_rel[r_type];
+ break;
+ }
+}
+
+/* Given a MIPS Elf_Internal_Rel, fill in an arelent structure. */
+
+static void
+mips_info_to_howto_rel (bfd *abfd, arelent *cache_ptr, Elf_Internal_Rela *dst)
+{
+ unsigned int r_type;
+
+ r_type = ELF32_R_TYPE (dst->r_info);
+ cache_ptr->howto = mips_elf_n32_rtype_to_howto (r_type, FALSE);
+
+ /* The addend for a GPREL16 or LITERAL relocation comes from the GP
+ value for the object file. We get the addend now, rather than
+ when we do the relocation, because the symbol manipulations done
+ by the linker may cause us to lose track of the input BFD. */
+ if (((*cache_ptr->sym_ptr_ptr)->flags & BSF_SECTION_SYM) != 0
+ && (r_type == R_RISCV_GPREL16 || r_type == (unsigned int) R_RISCV_LITERAL))
+ cache_ptr->addend = elf_gp (abfd);
+}
+
+/* Given a MIPS Elf_Internal_Rela, fill in an arelent structure. */
+
+static void
+mips_info_to_howto_rela (bfd *abfd ATTRIBUTE_UNUSED,
+ arelent *cache_ptr, Elf_Internal_Rela *dst)
+{
+ unsigned int r_type;
+
+ r_type = ELF32_R_TYPE (dst->r_info);
+ cache_ptr->howto = mips_elf_n32_rtype_to_howto (r_type, TRUE);
+ cache_ptr->addend = dst->r_addend;
+}
+
+/* Determine whether a symbol is global for the purposes of splitting
+ the symbol table into global symbols and local symbols. At least
+ on Irix 5, this split must be between section symbols and all other
+ symbols. On most ELF targets the split is between static symbols
+ and externally visible symbols. */
+
+static bfd_boolean
+mips_elf_sym_is_global (bfd *abfd ATTRIBUTE_UNUSED, asymbol *sym)
+{
+ return ((sym->flags & (BSF_GLOBAL | BSF_WEAK | BSF_GNU_UNIQUE)) != 0
+ || bfd_is_und_section (bfd_get_section (sym))
+ || bfd_is_com_section (bfd_get_section (sym)));
+}
+
+/* Set the right machine number for a MIPS ELF file. */
+
+static bfd_boolean
+mips_elf_n32_object_p (bfd *abfd)
+{
+ unsigned long mach;
+
+ mach = _bfd_elf_riscv_mach (elf_elfheader (abfd)->e_flags);
+ bfd_default_set_arch_mach (abfd, bfd_arch_riscv, mach);
+ return TRUE;
+}
+
+/* Support for core dump NOTE sections. */
+static bfd_boolean
+elf32_mips_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
+{
+ int offset;
+ unsigned int size;
+
+ switch (note->descsz)
+ {
+ default:
+ return FALSE;
+
+ case 440: /* Linux/MIPS N32 */
+ /* pr_cursig */
+ elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12);
+
+ /* pr_pid */
+ elf_tdata (abfd)->core_lwpid = bfd_get_32 (abfd, note->descdata + 24);
+
+ /* pr_reg */
+ offset = 72;
+ size = 360;
+
+ break;
+ }
+
+ /* Make a ".reg/999" section. */
+ return _bfd_elfcore_make_pseudosection (abfd, ".reg", size,
+ note->descpos + offset);
+}
+
+static bfd_boolean
+elf32_mips_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
+{
+ switch (note->descsz)
+ {
+ default:
+ return FALSE;
+
+ case 128: /* Linux/MIPS elf_prpsinfo */
+ elf_tdata (abfd)->core_program
+ = _bfd_elfcore_strndup (abfd, note->descdata + 32, 16);
+ elf_tdata (abfd)->core_command
+ = _bfd_elfcore_strndup (abfd, note->descdata + 48, 80);
+ }
+
+ /* Note that for some reason, a spurious space is tacked
+ onto the end of the args in some (at least one anyway)
+ implementations, so strip it off if it exists. */
+
+ {
+ char *command = elf_tdata (abfd)->core_command;
+ int n = strlen (command);
+
+ if (0 < n && command[n - 1] == ' ')
+ command[n - 1] = '\0';
+ }
+
+ return TRUE;
+}
+
+/* ECOFF swapping routines. These are used when dealing with the
+ .mdebug section, which is in the ECOFF debugging format. */
+static const struct ecoff_debug_swap mips_elf32_ecoff_debug_swap = {
+ /* Symbol table magic number. */
+ magicSym,
+ /* Alignment of debugging information. E.g., 4. */
+ 4,
+ /* Sizes of external symbolic information. */
+ sizeof (struct hdr_ext),
+ sizeof (struct dnr_ext),
+ sizeof (struct pdr_ext),
+ sizeof (struct sym_ext),
+ sizeof (struct opt_ext),
+ sizeof (struct fdr_ext),
+ sizeof (struct rfd_ext),
+ sizeof (struct ext_ext),
+ /* Functions to swap in external symbolic data. */
+ ecoff_swap_hdr_in,
+ ecoff_swap_dnr_in,
+ ecoff_swap_pdr_in,
+ ecoff_swap_sym_in,
+ ecoff_swap_opt_in,
+ ecoff_swap_fdr_in,
+ ecoff_swap_rfd_in,
+ ecoff_swap_ext_in,
+ _bfd_ecoff_swap_tir_in,
+ _bfd_ecoff_swap_rndx_in,
+ /* Functions to swap out external symbolic data. */
+ ecoff_swap_hdr_out,
+ ecoff_swap_dnr_out,
+ ecoff_swap_pdr_out,
+ ecoff_swap_sym_out,
+ ecoff_swap_opt_out,
+ ecoff_swap_fdr_out,
+ ecoff_swap_rfd_out,
+ ecoff_swap_ext_out,
+ _bfd_ecoff_swap_tir_out,
+ _bfd_ecoff_swap_rndx_out,
+ /* Function to read in symbolic data. */
+ _bfd_riscv_elf_read_ecoff_info
+};
+
+#define ELF_ARCH bfd_arch_riscv
+#define ELF_TARGET_ID MIPS_ELF_DATA
+#define ELF_MACHINE_CODE EM_RISCV
+
+#define elf_backend_collect TRUE
+#define elf_backend_type_change_ok TRUE
+#define elf_backend_can_gc_sections TRUE
+#define elf_info_to_howto mips_info_to_howto_rela
+#define elf_info_to_howto_rel mips_info_to_howto_rel
+#define elf_backend_sym_is_global mips_elf_sym_is_global
+#define elf_backend_object_p mips_elf_n32_object_p
+#define elf_backend_symbol_processing _bfd_riscv_elf_symbol_processing
+#define elf_backend_section_processing _bfd_riscv_elf_section_processing
+#define elf_backend_section_from_shdr _bfd_riscv_elf_section_from_shdr
+#define elf_backend_fake_sections _bfd_riscv_elf_fake_sections
+#define elf_backend_section_from_bfd_section \
+ _bfd_riscv_elf_section_from_bfd_section
+#define elf_backend_add_symbol_hook _bfd_riscv_elf_add_symbol_hook
+#define elf_backend_link_output_symbol_hook \
+ _bfd_riscv_elf_link_output_symbol_hook
+#define elf_backend_create_dynamic_sections \
+ _bfd_riscv_elf_create_dynamic_sections
+#define elf_backend_check_relocs _bfd_riscv_elf_check_relocs
+#define elf_backend_merge_symbol_attribute \
+ _bfd_riscv_elf_merge_symbol_attribute
+#define elf_backend_get_target_dtag _bfd_riscv_elf_get_target_dtag
+#define elf_backend_adjust_dynamic_symbol \
+ _bfd_riscv_elf_adjust_dynamic_symbol
+#define elf_backend_always_size_sections \
+ _bfd_riscv_elf_always_size_sections
+#define elf_backend_size_dynamic_sections \
+ _bfd_riscv_elf_size_dynamic_sections
+#define elf_backend_init_index_section _bfd_elf_init_1_index_section
+#define elf_backend_relocate_section _bfd_riscv_elf_relocate_section
+#define elf_backend_finish_dynamic_symbol \
+ _bfd_riscv_elf_finish_dynamic_symbol
+#define elf_backend_finish_dynamic_sections \
+ _bfd_riscv_elf_finish_dynamic_sections
+#define elf_backend_final_write_processing \
+ _bfd_riscv_elf_final_write_processing
+#define elf_backend_additional_program_headers \
+ _bfd_riscv_elf_additional_program_headers
+#define elf_backend_modify_segment_map _bfd_riscv_elf_modify_segment_map
+#define elf_backend_gc_mark_hook _bfd_riscv_elf_gc_mark_hook
+#define elf_backend_gc_sweep_hook _bfd_riscv_elf_gc_sweep_hook
+#define elf_backend_copy_indirect_symbol \
+ _bfd_riscv_elf_copy_indirect_symbol
+#define elf_backend_grok_prstatus elf32_mips_grok_prstatus
+#define elf_backend_grok_psinfo elf32_mips_grok_psinfo
+#define elf_backend_ecoff_debug_swap &mips_elf32_ecoff_debug_swap
+
+#define elf_backend_got_header_size (4 * MIPS_RESERVED_GOTNO)
+
+/* MIPS n32 ELF can use a mixture of REL and RELA, but some Relocations
+ work better/work only in RELA, so we default to this. */
+#define elf_backend_may_use_rel_p 1
+#define elf_backend_may_use_rela_p 1
+#define elf_backend_default_use_rela_p 1
+#define elf_backend_rela_plts_and_copies_p 0
+#define elf_backend_sign_extend_vma TRUE
+#define elf_backend_plt_readonly 1
+#define elf_backend_plt_sym_val _bfd_riscv_elf_plt_sym_val
+
+#define elf_backend_discard_info _bfd_riscv_elf_discard_info
+#define elf_backend_ignore_discarded_relocs \
+ _bfd_riscv_elf_ignore_discarded_relocs
+#define elf_backend_write_section _bfd_riscv_elf_write_section
+#define elf_backend_mips_rtype_to_howto mips_elf_n32_rtype_to_howto
+#define bfd_elf32_find_nearest_line _bfd_riscv_elf_find_nearest_line
+#define bfd_elf32_find_inliner_info _bfd_riscv_elf_find_inliner_info
+#define bfd_elf32_new_section_hook _bfd_riscv_elf_new_section_hook
+#define bfd_elf32_set_section_contents _bfd_riscv_elf_set_section_contents
+#define bfd_elf32_bfd_get_relocated_section_contents \
+ _bfd_elf_riscv_get_relocated_section_contents
+#define bfd_elf32_bfd_link_hash_table_create \
+ _bfd_riscv_elf_link_hash_table_create
+#define bfd_elf32_bfd_final_link _bfd_riscv_elf_final_link
+#define bfd_elf32_bfd_merge_private_bfd_data \
+ _bfd_riscv_elf_merge_private_bfd_data
+#define bfd_elf32_bfd_set_private_flags _bfd_riscv_elf_set_private_flags
+#define bfd_elf32_bfd_print_private_bfd_data \
+ _bfd_riscv_elf_print_private_bfd_data
+#define bfd_elf32_bfd_relax_section _bfd_riscv_relax_section
+
+/* Support for SGI-ish mips targets using n32 ABI. */
+
+#define TARGET_LITTLE_SYM bfd_elf32_littleriscv_vec
+#define TARGET_LITTLE_NAME "elf32-littleriscv"
+#define TARGET_BIG_SYM bfd_elf32_bigriscv_vec
+#define TARGET_BIG_NAME "elf32-bigriscv"
+
+#define ELF_MAXPAGESIZE 0x10000
+#define ELF_COMMONPAGESIZE 0x1000
+
+#include "elf32-target.h"
diff -ruN binutils-2.24/bfd/elf64-riscv.c binutils-2.24-riscv/bfd/elf64-riscv.c
--- binutils-2.24/bfd/elf64-riscv.c 1969-12-31 16:00:00.000000000 -0800
+++ binutils-2.24-riscv/bfd/elf64-riscv.c 2014-12-02 16:05:44.905434919 -0800
@@ -0,0 +1,2999 @@
+/* MIPS-specific support for 64-bit ELF
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ 2006, 2007, 2008, 2009, 2010
+ Free Software Foundation, Inc.
+ Ian Lance Taylor, Cygnus Support
+ Linker support added by Mark Mitchell, CodeSourcery, LLC.
+ <mark@codesourcery.com>
+
+ 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; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+
+/* This file supports the 64-bit MIPS ELF ABI.
+
+ The MIPS 64-bit ELF ABI uses an unusual reloc format. This file
+ overrides the usual ELF reloc handling, and handles reading and
+ writing the relocations here. */
+
+/* TODO: Many things are unsupported, even if there is some code for it
+ . (which was mostly stolen from elf32-mips.c and slightly adapted).
+ .
+ . - Relocation handling for REL relocs is wrong in many cases and
+ . generally untested.
+ . - Relocation handling for RELA relocs related to GOT support are
+ . also likely to be wrong.
+ . - Support for MIPS16 is untested.
+ . - Combined relocs with RSS_* entries are unsupported.
+ . - The whole GOT handling for NewABI is missing, some parts of
+ . the OldABI version is still lying around and should be removed.
+ */
+
+#include "sysdep.h"
+#include "bfd.h"
+#include "libbfd.h"
+#include "aout/ar.h"
+#include "bfdlink.h"
+#include "genlink.h"
+#include "elf-bfd.h"
+#include "elfxx-riscv.h"
+#include "elf/riscv.h"
+#include "opcode/riscv.h"
+
+/* Get the ECOFF swapping routines. The 64-bit ABI is not supposed to
+ use ECOFF. However, we support it anyhow for an easier changeover. */
+#include "coff/sym.h"
+#include "coff/symconst.h"
+#include "coff/internal.h"
+#include "coff/ecoff.h"
+/* The 64 bit versions of the mdebug data structures are in alpha.h. */
+#include "coff/alpha.h"
+#define ECOFF_SIGNED_64
+#include "ecoffswap.h"
+
+#include "opcode/riscv.h"
+
+static void mips_elf64_swap_reloc_in
+ (bfd *, const Elf64_Mips_External_Rel *, Elf64_Mips_Internal_Rela *);
+static void mips_elf64_swap_reloca_in
+ (bfd *, const Elf64_Mips_External_Rela *, Elf64_Mips_Internal_Rela *);
+static void mips_elf64_swap_reloc_out
+ (bfd *, const Elf64_Mips_Internal_Rela *, Elf64_Mips_External_Rel *);
+static void mips_elf64_swap_reloca_out
+ (bfd *, const Elf64_Mips_Internal_Rela *, Elf64_Mips_External_Rela *);
+static void mips_elf64_be_swap_reloc_in
+ (bfd *, const bfd_byte *, Elf_Internal_Rela *);
+static void mips_elf64_be_swap_reloc_out
+ (bfd *, const Elf_Internal_Rela *, bfd_byte *);
+static void mips_elf64_be_swap_reloca_in
+ (bfd *, const bfd_byte *, Elf_Internal_Rela *);
+static void mips_elf64_be_swap_reloca_out
+ (bfd *, const Elf_Internal_Rela *, bfd_byte *);
+static reloc_howto_type *bfd_elf64_bfd_reloc_type_lookup
+ (bfd *, bfd_reloc_code_real_type);
+static reloc_howto_type *mips_elf64_rtype_to_howto
+ (unsigned int, bfd_boolean);
+static void mips_elf64_info_to_howto_rel
+ (bfd *, arelent *, Elf_Internal_Rela *);
+static void mips_elf64_info_to_howto_rela
+ (bfd *, arelent *, Elf_Internal_Rela *);
+static long mips_elf64_get_reloc_upper_bound
+ (bfd *, asection *);
+static long mips_elf64_canonicalize_reloc
+ (bfd *, asection *, arelent **, asymbol **);
+static long mips_elf64_get_dynamic_reloc_upper_bound
+ (bfd *);
+static long mips_elf64_canonicalize_dynamic_reloc
+ (bfd *, arelent **, asymbol **);
+static bfd_boolean mips_elf64_slurp_one_reloc_table
+ (bfd *, asection *, Elf_Internal_Shdr *, bfd_size_type, arelent *,
+ asymbol **, bfd_boolean);
+static bfd_boolean mips_elf64_slurp_reloc_table
+ (bfd *, asection *, asymbol **, bfd_boolean);
+static void mips_elf64_write_relocs
+ (bfd *, asection *, void *);
+static void mips_elf64_write_rel
+ (bfd *, asection *, Elf_Internal_Shdr *, int *, void *);
+static void mips_elf64_write_rela
+ (bfd *, asection *, Elf_Internal_Shdr *, int *, void *);
+static bfd_reloc_status_type mips_elf64_gprel16_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type mips_elf64_literal_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type mips_elf64_gprel32_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_boolean mips_elf64_assign_gp
+ (bfd *, bfd_vma *);
+static bfd_reloc_status_type mips_elf64_final_gp
+ (bfd *, asymbol *, bfd_boolean, char **, bfd_vma *);
+static bfd_boolean mips_elf64_object_p
+ (bfd *);
+static bfd_boolean elf64_mips_grok_prstatus
+ (bfd *, Elf_Internal_Note *);
+static bfd_boolean elf64_mips_grok_psinfo
+ (bfd *, Elf_Internal_Note *);
+
+/* In case we're on a 32-bit machine, construct a 64-bit "-1" value
+ from smaller values. Start with zero, widen, *then* decrement. */
+#define MINUS_ONE (((bfd_vma)0) - 1)
+
+/* The number of local .got entries we reserve. */
+#define MIPS_RESERVED_GOTNO (2)
+
+/* The relocation table used for SHT_REL sections. */
+
+static reloc_howto_type mips_elf64_howto_table_rel[] =
+{
+ /* No relocation. */
+ HOWTO (R_RISCV_NONE, /* type */
+ 0, /* rightshift */
+ 0, /* size (0 = byte, 1 = short, 2 = long) */
+ 0, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_NONE", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (1),
+
+ /* 32 bit relocation. */
+ HOWTO (R_RISCV_32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit symbol relative relocation. */
+ HOWTO (R_RISCV_REL32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_REL32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 26 bit jump address. */
+ HOWTO (R_RISCV_26, /* type */
+ RISCV_JUMP_ALIGN_BITS, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_JUMP_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_TARGET, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ /* This needs complex overflow
+ detection, because the upper 36
+ bits must match the PC + 4. */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_26", /* name */
+ TRUE, /* partial_inplace */
+ ((1<<RISCV_JUMP_BITS)-1) << OP_SH_TARGET, /* src_mask */
+ ((1<<RISCV_JUMP_BITS)-1) << OP_SH_TARGET, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* R_RISCV_HI16 and R_RISCV_LO16 are unsupported for NewABI REL.
+ However, the native IRIX6 tools use them, so we try our best. */
+
+ /* High 16 bits of symbol value. */
+ HOWTO (R_RISCV_HI16, /* type */
+ RISCV_IMM_BITS, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_hi16_reloc, /* special_function */
+ "R_RISCV_HI16", /* name */
+ TRUE, /* partial_inplace */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of symbol value. */
+ HOWTO (R_RISCV_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_lo16_reloc, /* special_function */
+ "R_RISCV_LO16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* GP relative reference. */
+ HOWTO (R_RISCV_GPREL16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips_elf64_gprel16_reloc, /* special_function */
+ "R_RISCV_GPREL16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Reference to literal section. */
+ HOWTO (R_RISCV_LITERAL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips_elf64_literal_reloc, /* special_function */
+ "R_RISCV_LITERAL", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Reference to global offset table. */
+ HOWTO (R_RISCV_GOT16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_got16_reloc, /* special_function */
+ "R_RISCV_GOT16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 16 bit PC relative reference. Note that the ABI document has a typo
+ and claims R_RISCV_PC16 to be not rightshifted, rendering it useless.
+ We do the right thing here. */
+ HOWTO (R_RISCV_PC16, /* type */
+ RISCV_BRANCH_ALIGN_BITS, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_PC16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* 16 bit call through global offset table. */
+ HOWTO (R_RISCV_CALL16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_CALL16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit GP relative reference. */
+ HOWTO (R_RISCV_GPREL32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ mips_elf64_gprel32_reloc, /* special_function */
+ "R_RISCV_GPREL32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (13),
+ EMPTY_HOWTO (14),
+ EMPTY_HOWTO (15),
+ EMPTY_HOWTO (16),
+ EMPTY_HOWTO (17),
+
+ /* 64 bit relocation. */
+ HOWTO (R_RISCV_64, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_64", /* name */
+ TRUE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Displacement in the global offset table. */
+ HOWTO (R_RISCV_GOT_DISP, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GOT_DISP", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (20),
+ EMPTY_HOWTO (21),
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_GOT_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GOT_HI16", /* name */
+ TRUE, /* partial_inplace */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_GOT_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GOT_LO16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 64 bit subtraction. */
+ HOWTO (R_RISCV_SUB, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_SUB", /* name */
+ TRUE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Insert the addend as an instruction. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_RISCV_INSERT_A, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_INSERT_A", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Insert the addend as an instruction, and change all relocations
+ to refer to the old instruction at the address. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_RISCV_INSERT_B, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_INSERT_B", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Delete a 32 bit instruction. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_RISCV_DELETE, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_DELETE", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (28),
+ EMPTY_HOWTO (29),
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_CALL_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_CALL_HI16", /* name */
+ TRUE, /* partial_inplace */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_CALL_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_CALL_LO16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Section displacement, used by an associated event location section. */
+ HOWTO (R_RISCV_SCN_DISP, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_SCN_DISP", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_RISCV_REL16, /* type */
+ 0, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_REL16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* These two are obsolete. */
+ EMPTY_HOWTO (R_RISCV_ADD_IMMEDIATE),
+ EMPTY_HOWTO (R_RISCV_PJUMP),
+
+ /* Similiar to R_RISCV_REL32, but used for relocations in a GOT section.
+ It must be used for multigot GOT's (and only there). */
+ HOWTO (R_RISCV_RELGOT, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_RELGOT", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO(37),
+
+ /* TLS relocations. */
+ EMPTY_HOWTO (R_RISCV_TLS_DTPMOD32),
+ EMPTY_HOWTO (R_RISCV_TLS_DTPREL32),
+
+ HOWTO (R_RISCV_TLS_DTPMOD64, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_DTPMOD64", /* name */
+ TRUE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_RISCV_TLS_DTPREL64, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_DTPREL64", /* name */
+ TRUE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS general dynamic variable reference. */
+ HOWTO (R_RISCV_TLS_GD, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GD", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS local dynamic variable reference. */
+ HOWTO (R_RISCV_TLS_LDM, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_LDM", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS local dynamic offset. */
+ HOWTO (R_RISCV_TLS_DTPREL_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_DTPREL_HI16", /* name */
+ TRUE, /* partial_inplace */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS local dynamic offset. */
+ HOWTO (R_RISCV_TLS_DTPREL_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_DTPREL_LO16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS thread pointer offset. */
+ HOWTO (R_RISCV_TLS_GOTTPREL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GOTTPREL", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS IE dynamic relocations. */
+ EMPTY_HOWTO (R_RISCV_TLS_TPREL32),
+
+ HOWTO (R_RISCV_TLS_TPREL64, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_TPREL64", /* name */
+ TRUE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS thread pointer offset. */
+ HOWTO (R_RISCV_TLS_TPREL_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_TPREL_HI16", /* name */
+ TRUE, /* partial_inplace */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS thread pointer offset. */
+ HOWTO (R_RISCV_TLS_TPREL_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_TPREL_LO16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_TLS_GOT_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GOT_HI16", /* name */
+ TRUE, /* partial_inplace */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS thread pointer offset. */
+ HOWTO (R_RISCV_TLS_GOT_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GOT_LO16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_TLS_GD_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GD_HI16", /* name */
+ TRUE, /* partial_inplace */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS thread pointer offset. */
+ HOWTO (R_RISCV_TLS_GD_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GD_LO16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_TLS_LDM_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_LDM_HI16", /* name */
+ TRUE, /* partial_inplace */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS thread pointer offset. */
+ HOWTO (R_RISCV_TLS_LDM_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_LDM_LO16", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit relocation with no addend. */
+ HOWTO (R_RISCV_GLOB_DAT, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GLOB_DAT", /* name */
+ FALSE, /* partial_inplace */
+ 0x0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+};
+
+/* The relocation table used for SHT_RELA sections. */
+
+static reloc_howto_type mips_elf64_howto_table_rela[] =
+{
+ /* No relocation. */
+ HOWTO (R_RISCV_NONE, /* type */
+ 0, /* rightshift */
+ 0, /* size (0 = byte, 1 = short, 2 = long) */
+ 0, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_NONE", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (1),
+
+ /* 32 bit relocation. */
+ HOWTO (R_RISCV_32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_32", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit symbol relative relocation. */
+ HOWTO (R_RISCV_REL32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_REL32", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 26 bit jump address. */
+ HOWTO (R_RISCV_26, /* type */
+ RISCV_JUMP_ALIGN_BITS, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_JUMP_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_TARGET, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ /* This needs complex overflow
+ detection, because the upper 36
+ bits must match the PC + 4. */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_26", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ ((1<<RISCV_JUMP_BITS)-1) << OP_SH_TARGET, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* High 16 bits of symbol value. */
+ HOWTO (R_RISCV_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_HI16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of symbol value. */
+ HOWTO (R_RISCV_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_LO16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* GP relative reference. */
+ HOWTO (R_RISCV_GPREL16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips_elf64_gprel16_reloc, /* special_function */
+ "R_RISCV_GPREL16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Reference to literal section. */
+ HOWTO (R_RISCV_LITERAL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips_elf64_literal_reloc, /* special_function */
+ "R_RISCV_LITERAL", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Reference to global offset table. */
+ HOWTO (R_RISCV_GOT16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GOT16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 16 bit PC relative reference. Note that the ABI document has a typo
+ and claims R_RISCV_PC16 to be not rightshifted, rendering it useless.
+ We do the right thing here. */
+ HOWTO (R_RISCV_PC16, /* type */
+ 2, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_PC16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* 16 bit call through global offset table. */
+ HOWTO (R_RISCV_CALL16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_CALL16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit GP relative reference. */
+ HOWTO (R_RISCV_GPREL32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ mips_elf64_gprel32_reloc, /* special_function */
+ "R_RISCV_GPREL32", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (13),
+ EMPTY_HOWTO (14),
+ EMPTY_HOWTO (15),
+ EMPTY_HOWTO (16),
+ EMPTY_HOWTO (17),
+
+ /* 64 bit relocation. */
+ HOWTO (R_RISCV_64, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_64", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Displacement in the global offset table. */
+ HOWTO (R_RISCV_GOT_DISP, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GOT_DISP", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (20),
+ EMPTY_HOWTO (21),
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_GOT_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GOT_HI16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_GOT_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GOT_LO16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 64 bit subtraction. */
+ HOWTO (R_RISCV_SUB, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_SUB", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Insert the addend as an instruction. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_RISCV_INSERT_A, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_INSERT_A", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Insert the addend as an instruction, and change all relocations
+ to refer to the old instruction at the address. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_RISCV_INSERT_B, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_INSERT_B", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Delete a 32 bit instruction. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_RISCV_DELETE, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_DELETE", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (28),
+ EMPTY_HOWTO (29),
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_CALL_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_CALL_HI16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_CALL_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_CALL_LO16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Section displacement, used by an associated event location section. */
+ HOWTO (R_RISCV_SCN_DISP, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_SCN_DISP", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_RISCV_REL16, /* type */
+ 0, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_REL16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* These two are obsolete. */
+ EMPTY_HOWTO (R_RISCV_ADD_IMMEDIATE),
+ EMPTY_HOWTO (R_RISCV_PJUMP),
+
+ /* Similiar to R_RISCV_REL32, but used for relocations in a GOT section.
+ It must be used for multigot GOT's (and only there). */
+ HOWTO (R_RISCV_RELGOT, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_RELGOT", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO(37),
+
+ /* TLS relocations. */
+ EMPTY_HOWTO (R_RISCV_TLS_DTPMOD32),
+ EMPTY_HOWTO (R_RISCV_TLS_DTPREL32),
+
+ HOWTO (R_RISCV_TLS_DTPMOD64, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_DTPMOD64", /* name */
+ FALSE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_RISCV_TLS_DTPREL64, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_DTPREL64", /* name */
+ TRUE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS general dynamic variable reference. */
+ HOWTO (R_RISCV_TLS_GD, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GD", /* name */
+ TRUE, /* partial_inplace */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS local dynamic variable reference. */
+ HOWTO (R_RISCV_TLS_LDM, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_LDM", /* name */
+ TRUE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS local dynamic offset. */
+ HOWTO (R_RISCV_TLS_DTPREL_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_DTPREL_HI16", /* name */
+ TRUE, /* partial_inplace */
+ 0, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS local dynamic offset. */
+ HOWTO (R_RISCV_TLS_DTPREL_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_DTPREL_LO16", /* name */
+ TRUE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS thread pointer offset. */
+ HOWTO (R_RISCV_TLS_GOTTPREL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GOTTPREL", /* name */
+ TRUE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (R_RISCV_TLS_TPREL32),
+
+ HOWTO (R_RISCV_TLS_TPREL64, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_TPREL64", /* name */
+ FALSE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS thread pointer offset. */
+ HOWTO (R_RISCV_TLS_TPREL_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_TPREL_HI16", /* name */
+ TRUE, /* partial_inplace */
+ 0, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* TLS thread pointer offset. */
+ HOWTO (R_RISCV_TLS_TPREL_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_TPREL_LO16", /* name */
+ TRUE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_TLS_GOT_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GOT_HI16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_TLS_GOT_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GOT_LO16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_TLS_GD_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GD_HI16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_TLS_GD_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_GD_LO16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_TLS_LDM_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BIGIMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_BIGIMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_LDM_HI16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ ((1<<RISCV_BIGIMM_BITS)-1) << OP_SH_BIGIMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_RISCV_TLS_LDM_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_IMM_BITS, /* bitsize */
+ FALSE, /* pc_relative */
+ OP_SH_IMMEDIATE, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_TLS_LDM_LO16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ (RISCV_IMM_REACH-1) << OP_SH_IMMEDIATE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit relocation with no addend. */
+ HOWTO (R_RISCV_GLOB_DAT, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GLOB_DAT", /* name */
+ FALSE, /* partial_inplace */
+ 0x0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+};
+
+/* GNU extension to record C++ vtable hierarchy */
+static reloc_howto_type elf_mips_gnu_vtinherit_howto =
+ HOWTO (R_RISCV_GNU_VTINHERIT, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 0, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ NULL, /* special_function */
+ "R_RISCV_GNU_VTINHERIT", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+/* GNU extension to record C++ vtable member usage */
+static reloc_howto_type elf_mips_gnu_vtentry_howto =
+ HOWTO (R_RISCV_GNU_VTENTRY, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 0, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_elf_rel_vtable_reloc_fn, /* special_function */
+ "R_RISCV_GNU_VTENTRY", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+/* 16 bit offset for pc-relative branches. */
+static reloc_howto_type elf_mips_gnu_rel16_s2 =
+ HOWTO (R_RISCV_GNU_REL16_S2, /* type */
+ RISCV_BRANCH_ALIGN_BITS, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BRANCH_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GNU_REL16_S2", /* name */
+ TRUE, /* partial_inplace */
+ RISCV_BRANCH_REACH-1, /* src_mask */
+ RISCV_BRANCH_REACH-1, /* dst_mask */
+ TRUE); /* pcrel_offset */
+
+/* 16 bit offset for pc-relative branches. */
+static reloc_howto_type elf_mips_gnu_rela16_s2 =
+ HOWTO (R_RISCV_GNU_REL16_S2, /* type */
+ RISCV_BRANCH_ALIGN_BITS, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ RISCV_BRANCH_BITS, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_riscv_elf_generic_reloc, /* special_function */
+ "R_RISCV_GNU_REL16_S2", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ RISCV_BRANCH_REACH-1, /* dst_mask */
+ TRUE); /* pcrel_offset */
+
+/* Originally a VxWorks extension, but now used for other systems too. */
+static reloc_howto_type elf_mips_copy_howto =
+ HOWTO (R_RISCV_COPY, /* type */
+ 0, /* rightshift */
+ 0, /* this one is variable size */
+ 0, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_RISCV_COPY", /* name */
+ FALSE, /* partial_inplace */
+ 0x0, /* src_mask */
+ 0x0, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+/* Originally a VxWorks extension, but now used for other systems too. */
+static reloc_howto_type elf_mips_jump_slot_howto =
+ HOWTO (R_RISCV_JUMP_SLOT, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_RISCV_JUMP_SLOT", /* name */
+ FALSE, /* partial_inplace */
+ 0x0, /* src_mask */
+ 0x0, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+/* Swap in a MIPS 64-bit Rel reloc. */
+
+static void
+mips_elf64_swap_reloc_in (bfd *abfd, const Elf64_Mips_External_Rel *src,
+ Elf64_Mips_Internal_Rela *dst)
+{
+ dst->r_offset = H_GET_64 (abfd, src->r_offset);
+ dst->r_sym = H_GET_32 (abfd, src->r_sym);
+ dst->r_ssym = H_GET_8 (abfd, src->r_ssym);
+ dst->r_type3 = H_GET_8 (abfd, src->r_type3);
+ dst->r_type2 = H_GET_8 (abfd, src->r_type2);
+ dst->r_type = H_GET_8 (abfd, src->r_type);
+ dst->r_addend = 0;
+}
+
+/* Swap in a MIPS 64-bit Rela reloc. */
+
+static void
+mips_elf64_swap_reloca_in (bfd *abfd, const Elf64_Mips_External_Rela *src,
+ Elf64_Mips_Internal_Rela *dst)
+{
+ dst->r_offset = H_GET_64 (abfd, src->r_offset);
+ dst->r_sym = H_GET_32 (abfd, src->r_sym);
+ dst->r_ssym = H_GET_8 (abfd, src->r_ssym);
+ dst->r_type3 = H_GET_8 (abfd, src->r_type3);
+ dst->r_type2 = H_GET_8 (abfd, src->r_type2);
+ dst->r_type = H_GET_8 (abfd, src->r_type);
+ dst->r_addend = H_GET_S64 (abfd, src->r_addend);
+}
+
+/* Swap out a MIPS 64-bit Rel reloc. */
+
+static void
+mips_elf64_swap_reloc_out (bfd *abfd, const Elf64_Mips_Internal_Rela *src,
+ Elf64_Mips_External_Rel *dst)
+{
+ H_PUT_64 (abfd, src->r_offset, dst->r_offset);
+ H_PUT_32 (abfd, src->r_sym, dst->r_sym);
+ H_PUT_8 (abfd, src->r_ssym, dst->r_ssym);
+ H_PUT_8 (abfd, src->r_type3, dst->r_type3);
+ H_PUT_8 (abfd, src->r_type2, dst->r_type2);
+ H_PUT_8 (abfd, src->r_type, dst->r_type);
+}
+
+/* Swap out a MIPS 64-bit Rela reloc. */
+
+static void
+mips_elf64_swap_reloca_out (bfd *abfd, const Elf64_Mips_Internal_Rela *src,
+ Elf64_Mips_External_Rela *dst)
+{
+ H_PUT_64 (abfd, src->r_offset, dst->r_offset);
+ H_PUT_32 (abfd, src->r_sym, dst->r_sym);
+ H_PUT_8 (abfd, src->r_ssym, dst->r_ssym);
+ H_PUT_8 (abfd, src->r_type3, dst->r_type3);
+ H_PUT_8 (abfd, src->r_type2, dst->r_type2);
+ H_PUT_8 (abfd, src->r_type, dst->r_type);
+ H_PUT_S64 (abfd, src->r_addend, dst->r_addend);
+}
+
+/* Swap in a MIPS 64-bit Rel reloc. */
+
+static void
+mips_elf64_be_swap_reloc_in (bfd *abfd, const bfd_byte *src,
+ Elf_Internal_Rela *dst)
+{
+ Elf64_Mips_Internal_Rela mirel;
+
+ mips_elf64_swap_reloc_in (abfd,
+ (const Elf64_Mips_External_Rel *) src,
+ &mirel);
+
+ dst[0].r_offset = mirel.r_offset;
+ dst[0].r_info = ELF64_R_INFO (mirel.r_sym, mirel.r_type);
+ dst[0].r_addend = 0;
+ dst[1].r_offset = mirel.r_offset;
+ dst[1].r_info = ELF64_R_INFO (mirel.r_ssym, mirel.r_type2);
+ dst[1].r_addend = 0;
+ dst[2].r_offset = mirel.r_offset;
+ dst[2].r_info = ELF64_R_INFO (STN_UNDEF, mirel.r_type3);
+ dst[2].r_addend = 0;
+}
+
+/* Swap in a MIPS 64-bit Rela reloc. */
+
+static void
+mips_elf64_be_swap_reloca_in (bfd *abfd, const bfd_byte *src,
+ Elf_Internal_Rela *dst)
+{
+ Elf64_Mips_Internal_Rela mirela;
+
+ mips_elf64_swap_reloca_in (abfd,
+ (const Elf64_Mips_External_Rela *) src,
+ &mirela);
+
+ dst[0].r_offset = mirela.r_offset;
+ dst[0].r_info = ELF64_R_INFO (mirela.r_sym, mirela.r_type);
+ dst[0].r_addend = mirela.r_addend;
+ dst[1].r_offset = mirela.r_offset;
+ dst[1].r_info = ELF64_R_INFO (mirela.r_ssym, mirela.r_type2);
+ dst[1].r_addend = 0;
+ dst[2].r_offset = mirela.r_offset;
+ dst[2].r_info = ELF64_R_INFO (STN_UNDEF, mirela.r_type3);
+ dst[2].r_addend = 0;
+}
+
+/* Swap out a MIPS 64-bit Rel reloc. */
+
+static void
+mips_elf64_be_swap_reloc_out (bfd *abfd, const Elf_Internal_Rela *src,
+ bfd_byte *dst)
+{
+ Elf64_Mips_Internal_Rela mirel;
+
+ mirel.r_offset = src[0].r_offset;
+ BFD_ASSERT(src[0].r_offset == src[1].r_offset);
+
+ mirel.r_type = ELF64_MIPS_R_TYPE (src[0].r_info);
+ mirel.r_sym = ELF64_R_SYM (src[0].r_info);
+ mirel.r_type2 = ELF64_MIPS_R_TYPE (src[1].r_info);
+ mirel.r_ssym = ELF64_MIPS_R_SSYM (src[1].r_info);
+ mirel.r_type3 = ELF64_MIPS_R_TYPE (src[2].r_info);
+
+ mips_elf64_swap_reloc_out (abfd, &mirel,
+ (Elf64_Mips_External_Rel *) dst);
+}
+
+/* Swap out a MIPS 64-bit Rela reloc. */
+
+static void
+mips_elf64_be_swap_reloca_out (bfd *abfd, const Elf_Internal_Rela *src,
+ bfd_byte *dst)
+{
+ Elf64_Mips_Internal_Rela mirela;
+
+ mirela.r_offset = src[0].r_offset;
+ BFD_ASSERT(src[0].r_offset == src[1].r_offset);
+ BFD_ASSERT(src[0].r_offset == src[2].r_offset);
+
+ mirela.r_type = ELF64_MIPS_R_TYPE (src[0].r_info);
+ mirela.r_sym = ELF64_R_SYM (src[0].r_info);
+ mirela.r_addend = src[0].r_addend;
+ BFD_ASSERT(src[1].r_addend == 0);
+ BFD_ASSERT(src[2].r_addend == 0);
+
+ mirela.r_type2 = ELF64_MIPS_R_TYPE (src[1].r_info);
+ mirela.r_ssym = ELF64_MIPS_R_SSYM (src[1].r_info);
+ mirela.r_type3 = ELF64_MIPS_R_TYPE (src[2].r_info);
+
+ mips_elf64_swap_reloca_out (abfd, &mirela,
+ (Elf64_Mips_External_Rela *) dst);
+}
+
+/* Set the GP value for OUTPUT_BFD. Returns FALSE if this is a
+ dangerous relocation. */
+
+static bfd_boolean
+mips_elf64_assign_gp (bfd *output_bfd, bfd_vma *pgp)
+{
+ unsigned int count;
+ asymbol **sym;
+ unsigned int i;
+
+ /* If we've already figured out what GP will be, just return it. */
+ *pgp = _bfd_get_gp_value (output_bfd);
+ if (*pgp)
+ return TRUE;
+
+ count = bfd_get_symcount (output_bfd);
+ sym = bfd_get_outsymbols (output_bfd);
+
+ /* The linker script will have created a symbol named `_gp' with the
+ appropriate value. */
+ if (sym == NULL)
+ i = count;
+ else
+ {
+ for (i = 0; i < count; i++, sym++)
+ {
+ register const char *name;
+
+ name = bfd_asymbol_name (*sym);
+ if (*name == '_' && strcmp (name, "_gp") == 0)
+ {
+ *pgp = bfd_asymbol_value (*sym);
+ _bfd_set_gp_value (output_bfd, *pgp);
+ break;
+ }
+ }
+ }
+
+ if (i >= count)
+ {
+ /* Only get the error once. */
+ *pgp = 4;
+ _bfd_set_gp_value (output_bfd, *pgp);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* We have to figure out the gp value, so that we can adjust the
+ symbol value correctly. We look up the symbol _gp in the output
+ BFD. If we can't find it, we're stuck. We cache it in the ELF
+ target data. We don't need to adjust the symbol value for an
+ external symbol if we are producing relocatable output. */
+
+static bfd_reloc_status_type
+mips_elf64_final_gp (bfd *output_bfd, asymbol *symbol, bfd_boolean relocatable,
+ char **error_message, bfd_vma *pgp)
+{
+ if (bfd_is_und_section (symbol->section)
+ && ! relocatable)
+ {
+ *pgp = 0;
+ return bfd_reloc_undefined;
+ }
+
+ *pgp = _bfd_get_gp_value (output_bfd);
+ if (*pgp == 0
+ && (! relocatable
+ || (symbol->flags & BSF_SECTION_SYM) != 0))
+ {
+ if (relocatable)
+ {
+ /* Make up a value. */
+ *pgp = symbol->section->output_section->vma /*+ 0x4000*/;
+ _bfd_set_gp_value (output_bfd, *pgp);
+ }
+ else if (!mips_elf64_assign_gp (output_bfd, pgp))
+ {
+ *error_message =
+ (char *) _("GP relative relocation when _gp not defined");
+ return bfd_reloc_dangerous;
+ }
+ }
+
+ return bfd_reloc_ok;
+}
+
+/* Do a R_RISCV_GPREL16 relocation. This is a 16 bit value which must
+ become the offset from the gp register. */
+
+static bfd_reloc_status_type
+mips_elf64_gprel16_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+ void *data, asection *input_section, bfd *output_bfd,
+ char **error_message)
+{
+ bfd_boolean relocatable;
+ bfd_reloc_status_type ret;
+ bfd_vma gp;
+
+ /* If we're relocating, and this is an external symbol, we don't want
+ to change anything. */
+ if (output_bfd != NULL
+ && (symbol->flags & BSF_SECTION_SYM) == 0
+ && (symbol->flags & BSF_LOCAL) != 0)
+ {
+ reloc_entry->address += input_section->output_offset;
+ return bfd_reloc_ok;
+ }
+
+ if (output_bfd != NULL)
+ relocatable = TRUE;
+ else
+ {
+ relocatable = FALSE;
+ output_bfd = symbol->section->output_section->owner;
+ }
+
+ ret = mips_elf64_final_gp (output_bfd, symbol, relocatable, error_message,
+ &gp);
+ if (ret != bfd_reloc_ok)
+ return ret;
+
+ return _bfd_riscv_elf_gprel16_with_gp (abfd, symbol, reloc_entry,
+ input_section, relocatable,
+ data, gp);
+}
+
+/* Do a R_RISCV_LITERAL relocation. */
+
+static bfd_reloc_status_type
+mips_elf64_literal_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+ void *data, asection *input_section, bfd *output_bfd,
+ char **error_message)
+{
+ bfd_boolean relocatable;
+ bfd_reloc_status_type ret;
+ bfd_vma gp;
+
+ /* R_RISCV_LITERAL relocations are defined for local symbols only. */
+ if (output_bfd != NULL
+ && (symbol->flags & BSF_SECTION_SYM) == 0
+ && (symbol->flags & BSF_LOCAL) != 0)
+ {
+ *error_message = (char *)
+ _("literal relocation occurs for an external symbol");
+ return bfd_reloc_outofrange;
+ }
+
+ /* FIXME: The entries in the .lit8 and .lit4 sections should be merged. */
+ if (output_bfd != NULL)
+ relocatable = TRUE;
+ else
+ {
+ relocatable = FALSE;
+ output_bfd = symbol->section->output_section->owner;
+ }
+
+ ret = mips_elf64_final_gp (output_bfd, symbol, relocatable, error_message,
+ &gp);
+ if (ret != bfd_reloc_ok)
+ return ret;
+
+ return _bfd_riscv_elf_gprel16_with_gp (abfd, symbol, reloc_entry,
+ input_section, relocatable,
+ data, gp);
+}
+
+/* Do a R_RISCV_GPREL32 relocation. This is a 32 bit value which must
+ become the offset from the gp register. */
+
+static bfd_reloc_status_type
+mips_elf64_gprel32_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+ void *data, asection *input_section, bfd *output_bfd,
+ char **error_message)
+{
+ bfd_boolean relocatable;
+ bfd_reloc_status_type ret;
+ bfd_vma gp;
+ bfd_vma relocation;
+ bfd_vma val;
+
+ /* R_RISCV_GPREL32 relocations are defined for local symbols only. */
+ if (output_bfd != NULL
+ && (symbol->flags & BSF_SECTION_SYM) == 0
+ && (symbol->flags & BSF_LOCAL) != 0)
+ {
+ *error_message = (char *)
+ _("32bits gp relative relocation occurs for an external symbol");
+ return bfd_reloc_outofrange;
+ }
+
+ if (output_bfd != NULL)
+ relocatable = TRUE;
+ else
+ {
+ relocatable = FALSE;
+ output_bfd = symbol->section->output_section->owner;
+ }
+
+ ret = mips_elf64_final_gp (output_bfd, symbol, relocatable,
+ error_message, &gp);
+ if (ret != bfd_reloc_ok)
+ return ret;
+
+ if (bfd_is_com_section (symbol->section))
+ relocation = 0;
+ else
+ relocation = symbol->value;
+
+ relocation += symbol->section->output_section->vma;
+ relocation += symbol->section->output_offset;
+
+ if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
+ return bfd_reloc_outofrange;
+
+ /* Set val to the offset into the section or symbol. */
+ val = reloc_entry->addend;
+
+ if (reloc_entry->howto->partial_inplace)
+ val += bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
+
+ /* Adjust val for the final section location and GP value. If we
+ are producing relocatable output, we don't want to do this for
+ an external symbol. */
+ if (! relocatable
+ || (symbol->flags & BSF_SECTION_SYM) != 0)
+ val += relocation - gp;
+
+ if (reloc_entry->howto->partial_inplace)
+ bfd_put_32 (abfd, val, (bfd_byte *) data + reloc_entry->address);
+ else
+ reloc_entry->addend = val;
+
+ if (relocatable)
+ reloc_entry->address += input_section->output_offset;
+
+ return bfd_reloc_ok;
+}
+
+/* A mapping from BFD reloc types to MIPS ELF reloc types. */
+
+struct elf_reloc_map {
+ bfd_reloc_code_real_type bfd_val;
+ enum elf_riscv_reloc_type elf_val;
+};
+
+static const struct elf_reloc_map mips_reloc_map[] =
+{
+ { BFD_RELOC_NONE, R_RISCV_NONE },
+ { BFD_RELOC_32, R_RISCV_32 },
+ /* There is no BFD reloc for R_RISCV_REL32. */
+ { BFD_RELOC_64, R_RISCV_64 },
+ { BFD_RELOC_CTOR, R_RISCV_64 },
+ { BFD_RELOC_16_PCREL_S2, R_RISCV_PC16 },
+ { BFD_RELOC_HI16_S, R_RISCV_HI16 },
+ { BFD_RELOC_LO16, R_RISCV_LO16 },
+ { BFD_RELOC_GPREL16, R_RISCV_GPREL16 },
+ { BFD_RELOC_GPREL32, R_RISCV_GPREL32 },
+ { BFD_RELOC_MIPS_JMP, R_RISCV_26 },
+ { BFD_RELOC_MIPS_LITERAL, R_RISCV_LITERAL },
+ { BFD_RELOC_MIPS_GOT16, R_RISCV_GOT16 },
+ { BFD_RELOC_MIPS_CALL16, R_RISCV_CALL16 },
+ { BFD_RELOC_MIPS_GOT_DISP, R_RISCV_GOT_DISP },
+ { BFD_RELOC_MIPS_GOT_HI16, R_RISCV_GOT_HI16 },
+ { BFD_RELOC_MIPS_GOT_LO16, R_RISCV_GOT_LO16 },
+ { BFD_RELOC_MIPS_SUB, R_RISCV_SUB },
+ { BFD_RELOC_MIPS_INSERT_A, R_RISCV_INSERT_A },
+ { BFD_RELOC_MIPS_INSERT_B, R_RISCV_INSERT_B },
+ { BFD_RELOC_MIPS_DELETE, R_RISCV_DELETE },
+ { BFD_RELOC_MIPS_CALL_HI16, R_RISCV_CALL_HI16 },
+ { BFD_RELOC_MIPS_CALL_LO16, R_RISCV_CALL_LO16 },
+ { BFD_RELOC_MIPS_SCN_DISP, R_RISCV_SCN_DISP },
+ { BFD_RELOC_MIPS_REL16, R_RISCV_REL16 },
+ /* Use of R_RISCV_ADD_IMMEDIATE and R_RISCV_PJUMP is deprecated. */
+ { BFD_RELOC_MIPS_RELGOT, R_RISCV_RELGOT },
+ { BFD_RELOC_MIPS_TLS_DTPMOD32, R_RISCV_TLS_DTPMOD32 },
+ { BFD_RELOC_MIPS_TLS_DTPREL32, R_RISCV_TLS_DTPREL32 },
+ { BFD_RELOC_MIPS_TLS_DTPMOD64, R_RISCV_TLS_DTPMOD64 },
+ { BFD_RELOC_MIPS_TLS_DTPREL64, R_RISCV_TLS_DTPREL64 },
+ { BFD_RELOC_MIPS_TLS_GD, R_RISCV_TLS_GD },
+ { BFD_RELOC_MIPS_TLS_LDM, R_RISCV_TLS_LDM },
+ { BFD_RELOC_MIPS_TLS_DTPREL_HI16, R_RISCV_TLS_DTPREL_HI16 },
+ { BFD_RELOC_MIPS_TLS_DTPREL_LO16, R_RISCV_TLS_DTPREL_LO16 },
+ { BFD_RELOC_MIPS_TLS_GOTTPREL, R_RISCV_TLS_GOTTPREL },
+ { BFD_RELOC_MIPS_TLS_TPREL32, R_RISCV_TLS_TPREL32 },
+ { BFD_RELOC_MIPS_TLS_TPREL64, R_RISCV_TLS_TPREL64 },
+ { BFD_RELOC_MIPS_TLS_TPREL_HI16, R_RISCV_TLS_TPREL_HI16 },
+ { BFD_RELOC_MIPS_TLS_TPREL_LO16, R_RISCV_TLS_TPREL_LO16 },
+ { BFD_RELOC_RISCV_TLS_GOT_HI16, R_RISCV_TLS_GOT_HI16 },
+ { BFD_RELOC_RISCV_TLS_GOT_LO16, R_RISCV_TLS_GOT_LO16 },
+ { BFD_RELOC_RISCV_TLS_GD_HI16, R_RISCV_TLS_GD_HI16 },
+ { BFD_RELOC_RISCV_TLS_GD_LO16, R_RISCV_TLS_GD_LO16 },
+ { BFD_RELOC_RISCV_TLS_LDM_HI16, R_RISCV_TLS_LDM_HI16 },
+ { BFD_RELOC_RISCV_TLS_LDM_LO16, R_RISCV_TLS_LDM_LO16 }
+};
+
+/* Given a BFD reloc type, return a howto structure. */
+
+static reloc_howto_type *
+bfd_elf64_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+ bfd_reloc_code_real_type code)
+{
+ unsigned int i;
+ /* FIXME: We default to RELA here instead of choosing the right
+ relocation variant. */
+ reloc_howto_type *howto_table = mips_elf64_howto_table_rela;
+
+ for (i = 0; i < sizeof (mips_reloc_map) / sizeof (struct elf_reloc_map);
+ i++)
+ {
+ if (mips_reloc_map[i].bfd_val == code)
+ return &howto_table[(int) mips_reloc_map[i].elf_val];
+ }
+
+ switch (code)
+ {
+ case BFD_RELOC_VTABLE_INHERIT:
+ return &elf_mips_gnu_vtinherit_howto;
+ case BFD_RELOC_VTABLE_ENTRY:
+ return &elf_mips_gnu_vtentry_howto;
+ case BFD_RELOC_MIPS_COPY:
+ return &elf_mips_copy_howto;
+ case BFD_RELOC_MIPS_JUMP_SLOT:
+ return &elf_mips_jump_slot_howto;
+ default:
+ bfd_set_error (bfd_error_bad_value);
+ return NULL;
+ }
+}
+
+static reloc_howto_type *
+bfd_elf64_bfd_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+ const char *r_name)
+{
+ unsigned int i;
+
+ for (i = 0;
+ i < (sizeof (mips_elf64_howto_table_rela)
+ / sizeof (mips_elf64_howto_table_rela[0])); i++)
+ if (mips_elf64_howto_table_rela[i].name != NULL
+ && strcasecmp (mips_elf64_howto_table_rela[i].name, r_name) == 0)
+ return &mips_elf64_howto_table_rela[i];
+
+ if (strcasecmp (elf_mips_gnu_vtinherit_howto.name, r_name) == 0)
+ return &elf_mips_gnu_vtinherit_howto;
+ if (strcasecmp (elf_mips_gnu_vtentry_howto.name, r_name) == 0)
+ return &elf_mips_gnu_vtentry_howto;
+ if (strcasecmp (elf_mips_gnu_rel16_s2.name, r_name) == 0)
+ return &elf_mips_gnu_rel16_s2;
+ if (strcasecmp (elf_mips_gnu_rela16_s2.name, r_name) == 0)
+ return &elf_mips_gnu_rela16_s2;
+ if (strcasecmp (elf_mips_copy_howto.name, r_name) == 0)
+ return &elf_mips_copy_howto;
+ if (strcasecmp (elf_mips_jump_slot_howto.name, r_name) == 0)
+ return &elf_mips_jump_slot_howto;
+
+ return NULL;
+}
+
+/* Given a MIPS Elf_Internal_Rel, fill in an arelent structure. */
+
+static reloc_howto_type *
+mips_elf64_rtype_to_howto (unsigned int r_type, bfd_boolean rela_p)
+{
+ switch (r_type)
+ {
+ case R_RISCV_GNU_VTINHERIT:
+ return &elf_mips_gnu_vtinherit_howto;
+ case R_RISCV_GNU_VTENTRY:
+ return &elf_mips_gnu_vtentry_howto;
+ case R_RISCV_GNU_REL16_S2:
+ if (rela_p)
+ return &elf_mips_gnu_rela16_s2;
+ else
+ return &elf_mips_gnu_rel16_s2;
+ case R_RISCV_COPY:
+ return &elf_mips_copy_howto;
+ case R_RISCV_JUMP_SLOT:
+ return &elf_mips_jump_slot_howto;
+ default:
+ BFD_ASSERT (r_type < (unsigned int) R_RISCV_max);
+ if (rela_p)
+ return &mips_elf64_howto_table_rela[r_type];
+ else
+ return &mips_elf64_howto_table_rel[r_type];
+ break;
+ }
+}
+
+/* Prevent relocation handling by bfd for MIPS ELF64. */
+
+static void
+mips_elf64_info_to_howto_rel (bfd *abfd ATTRIBUTE_UNUSED,
+ arelent *cache_ptr ATTRIBUTE_UNUSED,
+ Elf_Internal_Rela *dst ATTRIBUTE_UNUSED)
+{
+ BFD_ASSERT (0);
+}
+
+static void
+mips_elf64_info_to_howto_rela (bfd *abfd ATTRIBUTE_UNUSED,
+ arelent *cache_ptr ATTRIBUTE_UNUSED,
+ Elf_Internal_Rela *dst ATTRIBUTE_UNUSED)
+{
+ BFD_ASSERT (0);
+}
+
+/* Since each entry in an SHT_REL or SHT_RELA section can represent up
+ to three relocs, we must tell the user to allocate more space. */
+
+static long
+mips_elf64_get_reloc_upper_bound (bfd *abfd ATTRIBUTE_UNUSED, asection *sec)
+{
+ return (sec->reloc_count * 3 + 1) * sizeof (arelent *);
+}
+
+static long
+mips_elf64_get_dynamic_reloc_upper_bound (bfd *abfd)
+{
+ return _bfd_elf_get_dynamic_reloc_upper_bound (abfd) * 3;
+}
+
+/* We must also copy more relocations than the corresponding functions
+ in elf.c would, so the two following functions are slightly
+ modified from elf.c, that multiply the external relocation count by
+ 3 to obtain the internal relocation count. */
+
+static long
+mips_elf64_canonicalize_reloc (bfd *abfd, sec_ptr section,
+ arelent **relptr, asymbol **symbols)
+{
+ arelent *tblptr;
+ unsigned int i;
+ const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+
+ if (! bed->s->slurp_reloc_table (abfd, section, symbols, FALSE))
+ return -1;
+
+ tblptr = section->relocation;
+ for (i = 0; i < section->reloc_count * 3; i++)
+ *relptr++ = tblptr++;
+
+ *relptr = NULL;
+
+ return section->reloc_count * 3;
+}
+
+static long
+mips_elf64_canonicalize_dynamic_reloc (bfd *abfd, arelent **storage,
+ asymbol **syms)
+{
+ bfd_boolean (*slurp_relocs) (bfd *, asection *, asymbol **, bfd_boolean);
+ asection *s;
+ long ret;
+
+ if (elf_dynsymtab (abfd) == 0)
+ {
+ bfd_set_error (bfd_error_invalid_operation);
+ return -1;
+ }
+
+ slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table;
+ ret = 0;
+ for (s = abfd->sections; s != NULL; s = s->next)
+ {
+ if (elf_section_data (s)->this_hdr.sh_link == elf_dynsymtab (abfd)
+ && (elf_section_data (s)->this_hdr.sh_type == SHT_REL
+ || elf_section_data (s)->this_hdr.sh_type == SHT_RELA))
+ {
+ arelent *p;
+ long count, i;
+
+ if (! (*slurp_relocs) (abfd, s, syms, TRUE))
+ return -1;
+ count = s->size / elf_section_data (s)->this_hdr.sh_entsize * 3;
+ p = s->relocation;
+ for (i = 0; i < count; i++)
+ *storage++ = p++;
+ ret += count;
+ }
+ }
+
+ *storage = NULL;
+
+ return ret;
+}
+
+/* Read the relocations from one reloc section. This is mostly copied
+ from elfcode.h, except for the changes to expand one external
+ relocation to 3 internal ones. We must unfortunately set
+ reloc_count to the number of external relocations, because a lot of
+ generic code seems to depend on this. */
+
+static bfd_boolean
+mips_elf64_slurp_one_reloc_table (bfd *abfd, asection *asect,
+ Elf_Internal_Shdr *rel_hdr,
+ bfd_size_type reloc_count,
+ arelent *relents, asymbol **symbols,
+ bfd_boolean dynamic)
+{
+ void *allocated;
+ bfd_byte *native_relocs;
+ arelent *relent;
+ bfd_vma i;
+ int entsize;
+ bfd_boolean rela_p;
+
+ allocated = bfd_malloc (rel_hdr->sh_size);
+ if (allocated == NULL)
+ return FALSE;
+
+ if (bfd_seek (abfd, rel_hdr->sh_offset, SEEK_SET) != 0
+ || (bfd_bread (allocated, rel_hdr->sh_size, abfd)
+ != rel_hdr->sh_size))
+ goto error_return;
+
+ native_relocs = allocated;
+
+ entsize = rel_hdr->sh_entsize;
+ BFD_ASSERT (entsize == sizeof (Elf64_Mips_External_Rel)
+ || entsize == sizeof (Elf64_Mips_External_Rela));
+
+ if (entsize == sizeof (Elf64_Mips_External_Rel))
+ rela_p = FALSE;
+ else
+ rela_p = TRUE;
+
+ for (i = 0, relent = relents;
+ i < reloc_count;
+ i++, native_relocs += entsize)
+ {
+ Elf64_Mips_Internal_Rela rela;
+ bfd_boolean used_sym, used_ssym;
+ int ir;
+
+ if (entsize == sizeof (Elf64_Mips_External_Rela))
+ mips_elf64_swap_reloca_in (abfd,
+ (Elf64_Mips_External_Rela *) native_relocs,
+ &rela);
+ else
+ mips_elf64_swap_reloc_in (abfd,
+ (Elf64_Mips_External_Rel *) native_relocs,
+ &rela);
+
+ /* Each entry represents exactly three actual relocations. */
+
+ used_sym = FALSE;
+ used_ssym = FALSE;
+ for (ir = 0; ir < 3; ir++)
+ {
+ enum elf_riscv_reloc_type type;
+
+ switch (ir)
+ {
+ default:
+ abort ();
+ case 0:
+ type = (enum elf_riscv_reloc_type) rela.r_type;
+ break;
+ case 1:
+ type = (enum elf_riscv_reloc_type) rela.r_type2;
+ break;
+ case 2:
+ type = (enum elf_riscv_reloc_type) rela.r_type3;
+ break;
+ }
+
+ /* Some types require symbols, whereas some do not. */
+ switch (type)
+ {
+ case R_RISCV_NONE:
+ case R_RISCV_LITERAL:
+ case R_RISCV_INSERT_A:
+ case R_RISCV_INSERT_B:
+ case R_RISCV_DELETE:
+ relent->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
+ break;
+
+ default:
+ if (! used_sym)
+ {
+ if (rela.r_sym == STN_UNDEF)
+ relent->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
+ else
+ {
+ asymbol **ps, *s;
+
+ ps = symbols + rela.r_sym - 1;
+ s = *ps;
+ if ((s->flags & BSF_SECTION_SYM) == 0)
+ relent->sym_ptr_ptr = ps;
+ else
+ relent->sym_ptr_ptr = s->section->symbol_ptr_ptr;
+ }
+
+ used_sym = TRUE;
+ }
+ else if (! used_ssym)
+ {
+ switch (rela.r_ssym)
+ {
+ case RSS_UNDEF:
+ relent->sym_ptr_ptr =
+ bfd_abs_section_ptr->symbol_ptr_ptr;
+ break;
+
+ case RSS_GP:
+ case RSS_GP0:
+ case RSS_LOC:
+ /* FIXME: I think these need to be handled using
+ special howto structures. */
+ BFD_ASSERT (0);
+ break;
+
+ default:
+ BFD_ASSERT (0);
+ break;
+ }
+
+ used_ssym = TRUE;
+ }
+ else
+ relent->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
+
+ break;
+ }
+
+ /* The address of an ELF reloc is section relative for an
+ object file, and absolute for an executable file or
+ shared library. The address of a BFD reloc is always
+ section relative. */
+ if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0 || dynamic)
+ relent->address = rela.r_offset;
+ else
+ relent->address = rela.r_offset - asect->vma;
+
+ relent->addend = rela.r_addend;
+
+ relent->howto = mips_elf64_rtype_to_howto (type, rela_p);
+
+ ++relent;
+ }
+ }
+
+ asect->reloc_count += (relent - relents) / 3;
+
+ if (allocated != NULL)
+ free (allocated);
+
+ return TRUE;
+
+ error_return:
+ if (allocated != NULL)
+ free (allocated);
+ return FALSE;
+}
+
+/* Read the relocations. On Irix 6, there can be two reloc sections
+ associated with a single data section. This is copied from
+ elfcode.h as well, with changes as small as accounting for 3
+ internal relocs per external reloc and resetting reloc_count to
+ zero before processing the relocs of a section. */
+
+static bfd_boolean
+mips_elf64_slurp_reloc_table (bfd *abfd, asection *asect,
+ asymbol **symbols, bfd_boolean dynamic)
+{
+ struct bfd_elf_section_data * const d = elf_section_data (asect);
+ Elf_Internal_Shdr *rel_hdr;
+ Elf_Internal_Shdr *rel_hdr2;
+ bfd_size_type reloc_count;
+ bfd_size_type reloc_count2;
+ arelent *relents;
+ bfd_size_type amt;
+
+ if (asect->relocation != NULL)
+ return TRUE;
+
+ if (! dynamic)
+ {
+ if ((asect->flags & SEC_RELOC) == 0
+ || asect->reloc_count == 0)
+ return TRUE;
+
+ rel_hdr = d->rel.hdr;
+ reloc_count = rel_hdr ? NUM_SHDR_ENTRIES (rel_hdr) : 0;
+ rel_hdr2 = d->rela.hdr;
+ reloc_count2 = (rel_hdr2 ? NUM_SHDR_ENTRIES (rel_hdr2) : 0);
+
+ BFD_ASSERT (asect->reloc_count == reloc_count + reloc_count2);
+ BFD_ASSERT ((rel_hdr && asect->rel_filepos == rel_hdr->sh_offset)
+ || (rel_hdr2 && asect->rel_filepos == rel_hdr2->sh_offset));
+
+ }
+ else
+ {
+ /* Note that ASECT->RELOC_COUNT tends not to be accurate in this
+ case because relocations against this section may use the
+ dynamic symbol table, and in that case bfd_section_from_shdr
+ in elf.c does not update the RELOC_COUNT. */
+ if (asect->size == 0)
+ return TRUE;
+
+ rel_hdr = &d->this_hdr;
+ reloc_count = NUM_SHDR_ENTRIES (rel_hdr);
+ rel_hdr2 = NULL;
+ reloc_count2 = 0;
+ }
+
+ /* Allocate space for 3 arelent structures for each Rel structure. */
+ amt = (reloc_count + reloc_count2) * 3 * sizeof (arelent);
+ relents = bfd_alloc (abfd, amt);
+ if (relents == NULL)
+ return FALSE;
+
+ /* The slurp_one_reloc_table routine increments reloc_count. */
+ asect->reloc_count = 0;
+
+ if (rel_hdr != NULL
+ && ! mips_elf64_slurp_one_reloc_table (abfd, asect,
+ rel_hdr, reloc_count,
+ relents,
+ symbols, dynamic))
+ return FALSE;
+ if (rel_hdr2 != NULL
+ && ! mips_elf64_slurp_one_reloc_table (abfd, asect,
+ rel_hdr2, reloc_count2,
+ relents + reloc_count * 3,
+ symbols, dynamic))
+ return FALSE;
+
+ asect->relocation = relents;
+ return TRUE;
+}
+
+/* Write out the relocations. */
+
+static void
+mips_elf64_write_relocs (bfd *abfd, asection *sec, void *data)
+{
+ bfd_boolean *failedp = data;
+ int count;
+ Elf_Internal_Shdr *rel_hdr;
+ unsigned int idx;
+
+ /* If we have already failed, don't do anything. */
+ if (*failedp)
+ return;
+
+ if ((sec->flags & SEC_RELOC) == 0)
+ return;
+
+ /* The linker backend writes the relocs out itself, and sets the
+ reloc_count field to zero to inhibit writing them here. Also,
+ sometimes the SEC_RELOC flag gets set even when there aren't any
+ relocs. */
+ if (sec->reloc_count == 0)
+ return;
+
+ /* We can combine up to three relocs that refer to the same address
+ if the latter relocs have no associated symbol. */
+ count = 0;
+ for (idx = 0; idx < sec->reloc_count; idx++)
+ {
+ bfd_vma addr;
+ unsigned int i;
+
+ ++count;
+
+ addr = sec->orelocation[idx]->address;
+ for (i = 0; i < 2; i++)
+ {
+ arelent *r;
+
+ if (idx + 1 >= sec->reloc_count)
+ break;
+ r = sec->orelocation[idx + 1];
+ if (r->address != addr
+ || ! bfd_is_abs_section ((*r->sym_ptr_ptr)->section)
+ || (*r->sym_ptr_ptr)->value != 0)
+ break;
+
+ /* We can merge the reloc at IDX + 1 with the reloc at IDX. */
+
+ ++idx;
+ }
+ }
+
+ rel_hdr = _bfd_elf_single_rel_hdr (sec);
+
+ /* Do the actual relocation. */
+
+ if (rel_hdr->sh_entsize == sizeof(Elf64_Mips_External_Rel))
+ mips_elf64_write_rel (abfd, sec, rel_hdr, &count, data);
+ else if (rel_hdr->sh_entsize == sizeof(Elf64_Mips_External_Rela))
+ mips_elf64_write_rela (abfd, sec, rel_hdr, &count, data);
+ else
+ BFD_ASSERT (0);
+}
+
+static void
+mips_elf64_write_rel (bfd *abfd, asection *sec,
+ Elf_Internal_Shdr *rel_hdr,
+ int *count, void *data)
+{
+ bfd_boolean *failedp = data;
+ Elf64_Mips_External_Rel *ext_rel;
+ unsigned int idx;
+ asymbol *last_sym = 0;
+ int last_sym_idx = 0;
+
+ rel_hdr->sh_size = rel_hdr->sh_entsize * *count;
+ rel_hdr->contents = bfd_alloc (abfd, rel_hdr->sh_size);
+ if (rel_hdr->contents == NULL)
+ {
+ *failedp = TRUE;
+ return;
+ }
+
+ ext_rel = (Elf64_Mips_External_Rel *) rel_hdr->contents;
+ for (idx = 0; idx < sec->reloc_count; idx++, ext_rel++)
+ {
+ arelent *ptr;
+ Elf64_Mips_Internal_Rela int_rel;
+ asymbol *sym;
+ int n;
+ unsigned int i;
+
+ ptr = sec->orelocation[idx];
+
+ /* The address of an ELF reloc is section relative for an object
+ file, and absolute for an executable file or shared library.
+ The address of a BFD reloc is always section relative. */
+ if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0)
+ int_rel.r_offset = ptr->address;
+ else
+ int_rel.r_offset = ptr->address + sec->vma;
+
+ sym = *ptr->sym_ptr_ptr;
+ if (sym == last_sym)
+ n = last_sym_idx;
+ else if (bfd_is_abs_section (sym->section) && sym->value == 0)
+ n = STN_UNDEF;
+ else
+ {
+ last_sym = sym;
+ n = _bfd_elf_symbol_from_bfd_symbol (abfd, &sym);
+ if (n < 0)
+ {
+ *failedp = TRUE;
+ return;
+ }
+ last_sym_idx = n;
+ }
+
+ int_rel.r_sym = n;
+ int_rel.r_ssym = RSS_UNDEF;
+
+ if ((*ptr->sym_ptr_ptr)->the_bfd->xvec != abfd->xvec
+ && ! _bfd_elf_validate_reloc (abfd, ptr))
+ {
+ *failedp = TRUE;
+ return;
+ }
+
+ int_rel.r_type = ptr->howto->type;
+ int_rel.r_type2 = (int) R_RISCV_NONE;
+ int_rel.r_type3 = (int) R_RISCV_NONE;
+
+ for (i = 0; i < 2; i++)
+ {
+ arelent *r;
+
+ if (idx + 1 >= sec->reloc_count)
+ break;
+ r = sec->orelocation[idx + 1];
+ if (r->address != ptr->address
+ || ! bfd_is_abs_section ((*r->sym_ptr_ptr)->section)
+ || (*r->sym_ptr_ptr)->value != 0)
+ break;
+
+ /* We can merge the reloc at IDX + 1 with the reloc at IDX. */
+
+ if (i == 0)
+ int_rel.r_type2 = r->howto->type;
+ else
+ int_rel.r_type3 = r->howto->type;
+
+ ++idx;
+ }
+
+ mips_elf64_swap_reloc_out (abfd, &int_rel, ext_rel);
+ }
+
+ BFD_ASSERT (ext_rel - (Elf64_Mips_External_Rel *) rel_hdr->contents
+ == *count);
+}
+
+static void
+mips_elf64_write_rela (bfd *abfd, asection *sec,
+ Elf_Internal_Shdr *rela_hdr,
+ int *count, void *data)
+{
+ bfd_boolean *failedp = data;
+ Elf64_Mips_External_Rela *ext_rela;
+ unsigned int idx;
+ asymbol *last_sym = 0;
+ int last_sym_idx = 0;
+
+ rela_hdr->sh_size = rela_hdr->sh_entsize * *count;
+ rela_hdr->contents = bfd_alloc (abfd, rela_hdr->sh_size);
+ if (rela_hdr->contents == NULL)
+ {
+ *failedp = TRUE;
+ return;
+ }
+
+ ext_rela = (Elf64_Mips_External_Rela *) rela_hdr->contents;
+ for (idx = 0; idx < sec->reloc_count; idx++, ext_rela++)
+ {
+ arelent *ptr;
+ Elf64_Mips_Internal_Rela int_rela;
+ asymbol *sym;
+ int n;
+ unsigned int i;
+
+ ptr = sec->orelocation[idx];
+
+ /* The address of an ELF reloc is section relative for an object
+ file, and absolute for an executable file or shared library.
+ The address of a BFD reloc is always section relative. */
+ if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0)
+ int_rela.r_offset = ptr->address;
+ else
+ int_rela.r_offset = ptr->address + sec->vma;
+
+ sym = *ptr->sym_ptr_ptr;
+ if (sym == last_sym)
+ n = last_sym_idx;
+ else if (bfd_is_abs_section (sym->section) && sym->value == 0)
+ n = STN_UNDEF;
+ else
+ {
+ last_sym = sym;
+ n = _bfd_elf_symbol_from_bfd_symbol (abfd, &sym);
+ if (n < 0)
+ {
+ *failedp = TRUE;
+ return;
+ }
+ last_sym_idx = n;
+ }
+
+ int_rela.r_sym = n;
+ int_rela.r_addend = ptr->addend;
+ int_rela.r_ssym = RSS_UNDEF;
+
+ if ((*ptr->sym_ptr_ptr)->the_bfd->xvec != abfd->xvec
+ && ! _bfd_elf_validate_reloc (abfd, ptr))
+ {
+ *failedp = TRUE;
+ return;
+ }
+
+ int_rela.r_type = ptr->howto->type;
+ int_rela.r_type2 = (int) R_RISCV_NONE;
+ int_rela.r_type3 = (int) R_RISCV_NONE;
+
+ for (i = 0; i < 2; i++)
+ {
+ arelent *r;
+
+ if (idx + 1 >= sec->reloc_count)
+ break;
+ r = sec->orelocation[idx + 1];
+ if (r->address != ptr->address
+ || ! bfd_is_abs_section ((*r->sym_ptr_ptr)->section)
+ || (*r->sym_ptr_ptr)->value != 0)
+ break;
+
+ /* We can merge the reloc at IDX + 1 with the reloc at IDX. */
+
+ if (i == 0)
+ int_rela.r_type2 = r->howto->type;
+ else
+ int_rela.r_type3 = r->howto->type;
+
+ ++idx;
+ }
+
+ mips_elf64_swap_reloca_out (abfd, &int_rela, ext_rela);
+ }
+
+ BFD_ASSERT (ext_rela - (Elf64_Mips_External_Rela *) rela_hdr->contents
+ == *count);
+}
+
+/* Set the right machine number for a MIPS ELF file. */
+
+static bfd_boolean
+mips_elf64_object_p (bfd *abfd)
+{
+ unsigned long mach;
+
+ mach = _bfd_elf_riscv_mach (elf_elfheader (abfd)->e_flags);
+ bfd_default_set_arch_mach (abfd, bfd_arch_riscv, mach);
+ return TRUE;
+}
+
+/* Support for core dump NOTE sections. */
+static bfd_boolean
+elf64_mips_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
+{
+ int offset;
+ unsigned int size;
+
+ switch (note->descsz)
+ {
+ default:
+ return FALSE;
+
+ case 480: /* Linux/MIPS - N64 kernel */
+ /* pr_cursig */
+ elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12);
+
+ /* pr_pid */
+ elf_tdata (abfd)->core_lwpid = bfd_get_32 (abfd, note->descdata + 32);
+
+ /* pr_reg */
+ offset = 112;
+ size = 360;
+
+ break;
+ }
+
+ /* Make a ".reg/999" section. */
+ return _bfd_elfcore_make_pseudosection (abfd, ".reg",
+ size, note->descpos + offset);
+}
+
+static bfd_boolean
+elf64_mips_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
+{
+ switch (note->descsz)
+ {
+ default:
+ return FALSE;
+
+ case 136: /* Linux/MIPS - N64 kernel elf_prpsinfo */
+ elf_tdata (abfd)->core_program
+ = _bfd_elfcore_strndup (abfd, note->descdata + 40, 16);
+ elf_tdata (abfd)->core_command
+ = _bfd_elfcore_strndup (abfd, note->descdata + 56, 80);
+ }
+
+ /* Note that for some reason, a spurious space is tacked
+ onto the end of the args in some (at least one anyway)
+ implementations, so strip it off if it exists. */
+
+ {
+ char *command = elf_tdata (abfd)->core_command;
+ int n = strlen (command);
+
+ if (0 < n && command[n - 1] == ' ')
+ command[n - 1] = '\0';
+ }
+
+ return TRUE;
+}
+
+/* ECOFF swapping routines. These are used when dealing with the
+ .mdebug section, which is in the ECOFF debugging format. */
+static const struct ecoff_debug_swap mips_elf64_ecoff_debug_swap =
+{
+ /* Symbol table magic number. */
+ magicSym2,
+ /* Alignment of debugging information. E.g., 4. */
+ 8,
+ /* Sizes of external symbolic information. */
+ sizeof (struct hdr_ext),
+ sizeof (struct dnr_ext),
+ sizeof (struct pdr_ext),
+ sizeof (struct sym_ext),
+ sizeof (struct opt_ext),
+ sizeof (struct fdr_ext),
+ sizeof (struct rfd_ext),
+ sizeof (struct ext_ext),
+ /* Functions to swap in external symbolic data. */
+ ecoff_swap_hdr_in,
+ ecoff_swap_dnr_in,
+ ecoff_swap_pdr_in,
+ ecoff_swap_sym_in,
+ ecoff_swap_opt_in,
+ ecoff_swap_fdr_in,
+ ecoff_swap_rfd_in,
+ ecoff_swap_ext_in,
+ _bfd_ecoff_swap_tir_in,
+ _bfd_ecoff_swap_rndx_in,
+ /* Functions to swap out external symbolic data. */
+ ecoff_swap_hdr_out,
+ ecoff_swap_dnr_out,
+ ecoff_swap_pdr_out,
+ ecoff_swap_sym_out,
+ ecoff_swap_opt_out,
+ ecoff_swap_fdr_out,
+ ecoff_swap_rfd_out,
+ ecoff_swap_ext_out,
+ _bfd_ecoff_swap_tir_out,
+ _bfd_ecoff_swap_rndx_out,
+ /* Function to read in symbolic data. */
+ _bfd_riscv_elf_read_ecoff_info
+};
+
+/* Relocations in the 64 bit MIPS ELF ABI are more complex than in
+ standard ELF. This structure is used to redirect the relocation
+ handling routines. */
+
+const struct elf_size_info mips_elf64_size_info =
+{
+ sizeof (Elf64_External_Ehdr),
+ sizeof (Elf64_External_Phdr),
+ sizeof (Elf64_External_Shdr),
+ sizeof (Elf64_Mips_External_Rel),
+ sizeof (Elf64_Mips_External_Rela),
+ sizeof (Elf64_External_Sym),
+ sizeof (Elf64_External_Dyn),
+ sizeof (Elf_External_Note),
+ 4, /* hash-table entry size */
+ 3, /* internal relocations per external relocations */
+ 64, /* arch_size */
+ 3, /* log_file_align */
+ ELFCLASS64,
+ EV_CURRENT,
+ bfd_elf64_write_out_phdrs,
+ bfd_elf64_write_shdrs_and_ehdr,
+ bfd_elf64_checksum_contents,
+ mips_elf64_write_relocs,
+ bfd_elf64_swap_symbol_in,
+ bfd_elf64_swap_symbol_out,
+ mips_elf64_slurp_reloc_table,
+ bfd_elf64_slurp_symbol_table,
+ bfd_elf64_swap_dyn_in,
+ bfd_elf64_swap_dyn_out,
+ mips_elf64_be_swap_reloc_in,
+ mips_elf64_be_swap_reloc_out,
+ mips_elf64_be_swap_reloca_in,
+ mips_elf64_be_swap_reloca_out
+};
+
+#define ELF_ARCH bfd_arch_riscv
+#define ELF_TARGET_ID MIPS_ELF_DATA
+#define ELF_MACHINE_CODE EM_RISCV
+
+#define elf_backend_collect TRUE
+#define elf_backend_type_change_ok TRUE
+#define elf_backend_can_gc_sections TRUE
+#define elf_info_to_howto mips_elf64_info_to_howto_rela
+#define elf_info_to_howto_rel mips_elf64_info_to_howto_rel
+#define elf_backend_object_p mips_elf64_object_p
+#define elf_backend_symbol_processing _bfd_riscv_elf_symbol_processing
+#define elf_backend_section_processing _bfd_riscv_elf_section_processing
+#define elf_backend_section_from_shdr _bfd_riscv_elf_section_from_shdr
+#define elf_backend_fake_sections _bfd_riscv_elf_fake_sections
+#define elf_backend_section_from_bfd_section \
+ _bfd_riscv_elf_section_from_bfd_section
+#define elf_backend_add_symbol_hook _bfd_riscv_elf_add_symbol_hook
+#define elf_backend_link_output_symbol_hook \
+ _bfd_riscv_elf_link_output_symbol_hook
+#define elf_backend_create_dynamic_sections \
+ _bfd_riscv_elf_create_dynamic_sections
+#define elf_backend_check_relocs _bfd_riscv_elf_check_relocs
+#define elf_backend_merge_symbol_attribute \
+ _bfd_riscv_elf_merge_symbol_attribute
+#define elf_backend_get_target_dtag _bfd_riscv_elf_get_target_dtag
+#define elf_backend_adjust_dynamic_symbol \
+ _bfd_riscv_elf_adjust_dynamic_symbol
+#define elf_backend_always_size_sections \
+ _bfd_riscv_elf_always_size_sections
+#define elf_backend_size_dynamic_sections \
+ _bfd_riscv_elf_size_dynamic_sections
+#define elf_backend_init_index_section _bfd_elf_init_1_index_section
+#define elf_backend_relocate_section _bfd_riscv_elf_relocate_section
+#define elf_backend_finish_dynamic_symbol \
+ _bfd_riscv_elf_finish_dynamic_symbol
+#define elf_backend_finish_dynamic_sections \
+ _bfd_riscv_elf_finish_dynamic_sections
+#define elf_backend_final_write_processing \
+ _bfd_riscv_elf_final_write_processing
+#define elf_backend_additional_program_headers \
+ _bfd_riscv_elf_additional_program_headers
+#define elf_backend_modify_segment_map _bfd_riscv_elf_modify_segment_map
+#define elf_backend_gc_mark_hook _bfd_riscv_elf_gc_mark_hook
+#define elf_backend_gc_sweep_hook _bfd_riscv_elf_gc_sweep_hook
+#define elf_backend_copy_indirect_symbol \
+ _bfd_riscv_elf_copy_indirect_symbol
+#define elf_backend_ignore_discarded_relocs \
+ _bfd_riscv_elf_ignore_discarded_relocs
+#define elf_backend_mips_rtype_to_howto mips_elf64_rtype_to_howto
+#define elf_backend_ecoff_debug_swap &mips_elf64_ecoff_debug_swap
+#define elf_backend_size_info mips_elf64_size_info
+
+#define elf_backend_grok_prstatus elf64_mips_grok_prstatus
+#define elf_backend_grok_psinfo elf64_mips_grok_psinfo
+
+#define elf_backend_got_header_size (4 * MIPS_RESERVED_GOTNO)
+
+/* MIPS ELF64 can use a mixture of REL and RELA, but some Relocations
+ work better/work only in RELA, so we default to this. */
+#define elf_backend_may_use_rel_p 1
+#define elf_backend_may_use_rela_p 1
+#define elf_backend_default_use_rela_p 1
+#define elf_backend_rela_plts_and_copies_p 0
+#define elf_backend_plt_readonly 1
+#define elf_backend_plt_sym_val _bfd_riscv_elf_plt_sym_val
+
+#define elf_backend_sign_extend_vma TRUE
+
+#define elf_backend_write_section _bfd_riscv_elf_write_section
+
+/* We don't set bfd_elf64_bfd_is_local_label_name because the 32-bit
+ MIPS-specific function only applies to IRIX5, which had no 64-bit
+ ABI. */
+#define bfd_elf64_find_nearest_line _bfd_riscv_elf_find_nearest_line
+#define bfd_elf64_find_inliner_info _bfd_riscv_elf_find_inliner_info
+#define bfd_elf64_new_section_hook _bfd_riscv_elf_new_section_hook
+#define bfd_elf64_set_section_contents _bfd_riscv_elf_set_section_contents
+#define bfd_elf64_bfd_get_relocated_section_contents \
+ _bfd_elf_riscv_get_relocated_section_contents
+#define bfd_elf64_bfd_link_hash_table_create \
+ _bfd_riscv_elf_link_hash_table_create
+#define bfd_elf64_bfd_final_link _bfd_riscv_elf_final_link
+#define bfd_elf64_bfd_merge_private_bfd_data \
+ _bfd_riscv_elf_merge_private_bfd_data
+#define bfd_elf64_bfd_set_private_flags _bfd_riscv_elf_set_private_flags
+#define bfd_elf64_bfd_print_private_bfd_data \
+ _bfd_riscv_elf_print_private_bfd_data
+
+#define bfd_elf64_get_reloc_upper_bound mips_elf64_get_reloc_upper_bound
+#define bfd_elf64_canonicalize_reloc mips_elf64_canonicalize_reloc
+#define bfd_elf64_get_dynamic_reloc_upper_bound mips_elf64_get_dynamic_reloc_upper_bound
+#define bfd_elf64_canonicalize_dynamic_reloc mips_elf64_canonicalize_dynamic_reloc
+#define bfd_elf64_bfd_relax_section _bfd_riscv_relax_section
+
+/* MIPS ELF64 archive functions. */
+#define bfd_elf64_archive_functions
+extern bfd_boolean bfd_elf64_archive_slurp_armap
+ (bfd *);
+extern bfd_boolean bfd_elf64_archive_write_armap
+ (bfd *, unsigned int, struct orl *, unsigned int, int);
+#define bfd_elf64_archive_slurp_extended_name_table \
+ _bfd_archive_coff_slurp_extended_name_table
+#define bfd_elf64_archive_construct_extended_name_table \
+ _bfd_archive_coff_construct_extended_name_table
+#define bfd_elf64_archive_truncate_arname \
+ _bfd_archive_coff_truncate_arname
+#define bfd_elf64_archive_read_ar_hdr _bfd_archive_coff_read_ar_hdr
+#define bfd_elf64_archive_write_ar_hdr _bfd_archive_coff_write_ar_hdr
+#define bfd_elf64_archive_openr_next_archived_file \
+ _bfd_archive_coff_openr_next_archived_file
+#define bfd_elf64_archive_get_elt_at_index \
+ _bfd_archive_coff_get_elt_at_index
+#define bfd_elf64_archive_generic_stat_arch_elt \
+ _bfd_archive_coff_generic_stat_arch_elt
+#define bfd_elf64_archive_update_armap_timestamp \
+ _bfd_archive_coff_update_armap_timestamp
+
+/* The SGI style (n)64 NewABI. */
+#define TARGET_LITTLE_SYM bfd_elf64_littleriscv_vec
+#define TARGET_LITTLE_NAME "elf64-littleriscv"
+#define TARGET_BIG_SYM bfd_elf64_bigriscv_vec
+#define TARGET_BIG_NAME "elf64-bigriscv"
+
+#define ELF_MAXPAGESIZE 0x10000
+#define ELF_COMMONPAGESIZE 0x1000
+
+#include "elf64-target.h"
diff -ruN binutils-2.24/bfd/elfxx-riscv.c binutils-2.24-riscv/bfd/elfxx-riscv.c
--- binutils-2.24/bfd/elfxx-riscv.c 1969-12-31 16:00:00.000000000 -0800
+++ binutils-2.24-riscv/bfd/elfxx-riscv.c 2014-12-02 16:05:44.909434942 -0800
@@ -0,0 +1,7387 @@
+/* MIPS-specific support for ELF
+ Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+ 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ Most of the information added by Ian Lance Taylor, Cygnus Support,
+ <ian@cygnus.com>.
+ N32/64 ABI support added by Mark Mitchell, CodeSourcery, LLC.
+ <mark@codesourcery.com>
+ Traditional MIPS targets support added by Koundinya.K, Dansk Data
+ Elektronik & Operations Research Group. <kk@ddeorg.soft.net>
+
+ 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; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+
+/* This file handles functionality common to the different MIPS ABI's. */
+
+#include "sysdep.h"
+#include "bfd.h"
+#include "libbfd.h"
+#include "libiberty.h"
+#include "elf-bfd.h"
+#include "elfxx-riscv.h"
+#include "elf/riscv.h"
+#include "opcode/riscv.h"
+
+/* Get the ECOFF swapping routines. */
+#include "coff/sym.h"
+#include "coff/symconst.h"
+#include "coff/ecoff.h"
+#include "coff/mips.h"
+
+#include "hashtab.h"
+
+/* This structure is used to hold information about one GOT entry.
+ There are three types of entry:
+
+ (1) absolute addresses
+ (abfd == NULL)
+ (2) SYMBOL + OFFSET addresses, where SYMBOL is local to an input bfd
+ (abfd != NULL, symndx >= 0)
+ (3) SYMBOL addresses, where SYMBOL is not local to an input bfd
+ (abfd != NULL, symndx == -1)
+
+ Type (3) entries are treated differently for different types of GOT.
+ In the "master" GOT -- i.e. the one that describes every GOT
+ reference needed in the link -- the mips_got_entry is keyed on both
+ the symbol and the input bfd that references it. If it turns out
+ that we need multiple GOTs, we can then use this information to
+ create separate GOTs for each input bfd.
+
+ However, we want each of these separate GOTs to have at most one
+ entry for a given symbol, so their type (3) entries are keyed only
+ on the symbol. The input bfd given by the "abfd" field is somewhat
+ arbitrary in this case.
+
+ This means that when there are multiple GOTs, each GOT has a unique
+ mips_got_entry for every symbol within it. We can therefore use the
+ mips_got_entry fields (tls_type and gotidx) to track the symbol's
+ GOT index.
+
+ However, if it turns out that we need only a single GOT, we continue
+ to use the master GOT to describe it. There may therefore be several
+ mips_got_entries for the same symbol, each with a different input bfd.
+ We want to make sure that each symbol gets a unique GOT entry, so when
+ there's a single GOT, we use the symbol's hash entry, not the
+ mips_got_entry fields, to track a symbol's GOT index. */
+struct mips_got_entry
+{
+ /* The input bfd in which the symbol is defined. */
+ bfd *abfd;
+ /* The index of the symbol, as stored in the relocation r_info, if
+ we have a local symbol; -1 otherwise. */
+ long symndx;
+ union
+ {
+ /* If abfd == NULL, an address that must be stored in the got. */
+ bfd_vma address;
+ /* If abfd != NULL && symndx != -1, the addend of the relocation
+ that should be added to the symbol value. */
+ bfd_vma addend;
+ /* If abfd != NULL && symndx == -1, the hash table entry
+ corresponding to symbol in the GOT. The symbol's entry
+ is in the local area if h->global_got_area is GGA_NONE,
+ otherwise it is in the global area. */
+ struct mips_elf_link_hash_entry *h;
+ } d;
+
+ /* The TLS types included in this GOT entry (specifically, GD and
+ IE). The GD and IE flags can be added as we encounter new
+ relocations. LDM can also be set; it will always be alone, not
+ combined with any GD or IE flags. An LDM GOT entry will be
+ a local symbol entry with r_symndx == 0. */
+ unsigned char tls_type;
+
+ /* The offset from the beginning of the .got section to the entry
+ corresponding to this symbol+addend. If it's a global symbol
+ whose offset is yet to be decided, it's going to be -1. */
+ long gotidx;
+};
+
+/* This structure describes a range of addends: [MIN_ADDEND, MAX_ADDEND].
+ The structures form a non-overlapping list that is sorted by increasing
+ MIN_ADDEND. */
+struct mips_got_page_range
+{
+ struct mips_got_page_range *next;
+ bfd_signed_vma min_addend;
+ bfd_signed_vma max_addend;
+};
+
+/* This structure describes the range of addends that are applied to page
+ relocations against a given symbol. */
+struct mips_got_page_entry
+{
+ /* The input bfd in which the symbol is defined. */
+ bfd *abfd;
+ /* The index of the symbol, as stored in the relocation r_info. */
+ long symndx;
+ /* The ranges for this page entry. */
+ struct mips_got_page_range *ranges;
+ /* The maximum number of page entries needed for RANGES. */
+ bfd_vma num_pages;
+};
+
+/* This structure is used to hold .got information when linking. */
+
+struct mips_got_info
+{
+ /* The global symbol in the GOT with the lowest index in the dynamic
+ symbol table. */
+ struct elf_link_hash_entry *global_gotsym;
+ /* The number of global .got entries. */
+ unsigned int global_gotno;
+ /* The number of global .got entries that are in the GGA_RELOC_ONLY area. */
+ unsigned int reloc_only_gotno;
+ /* The number of .got slots used for TLS. */
+ unsigned int tls_gotno;
+ /* The first unused TLS .got entry. Used only during
+ mips_elf_initialize_tls_index. */
+ unsigned int tls_assigned_gotno;
+ /* The number of local .got entries, eventually including page entries. */
+ unsigned int local_gotno;
+ /* The maximum number of page entries needed. */
+ unsigned int page_gotno;
+ /* The number of local .got entries we have used. */
+ unsigned int assigned_gotno;
+ /* A hash table holding members of the got. */
+ struct htab *got_entries;
+ /* A hash table of mips_got_page_entry structures. */
+ struct htab *got_page_entries;
+ /* This is the GOT index of the TLS LDM entry for the GOT, MINUS_ONE
+ for none, or MINUS_TWO for not yet assigned. This is needed
+ because a single-GOT link may have multiple hash table entries
+ for the LDM. It does not get initialized in multi-GOT mode. */
+ bfd_vma tls_ldm_offset;
+};
+
+/* Another structure used to pass arguments for got entries traversal. */
+
+struct mips_elf_set_global_got_offset_arg
+{
+ struct mips_got_info *g;
+ int value;
+ unsigned int needed_relocs;
+ struct bfd_link_info *info;
+};
+
+/* A structure used to count TLS relocations or GOT entries, for GOT
+ entry or ELF symbol table traversal. */
+
+struct mips_elf_count_tls_arg
+{
+ struct bfd_link_info *info;
+ unsigned int needed;
+};
+
+struct _mips_elf_section_data
+{
+ struct bfd_elf_section_data elf;
+ union
+ {
+ bfd_byte *tdata;
+ } u;
+};
+
+#define mips_elf_section_data(sec) \
+ ((struct _mips_elf_section_data *) elf_section_data (sec))
+
+#define is_mips_elf(bfd) \
+ (bfd_get_flavour (bfd) == bfd_target_elf_flavour \
+ && elf_tdata (bfd) != NULL \
+ && elf_object_id (bfd) == MIPS_ELF_DATA)
+
+/* The ABI says that every symbol used by dynamic relocations must have
+ a global GOT entry. Among other things, this provides the dynamic
+ linker with a free, directly-indexed cache. The GOT can therefore
+ contain symbols that are not referenced by GOT relocations themselves
+ (in other words, it may have symbols that are not referenced by things
+ like R_RISCV_GOT16).
+
+ GOT relocations are less likely to overflow if we put the associated
+ GOT entries towards the beginning. We therefore divide the global
+ GOT entries into two areas: "normal" and "reloc-only". Entries in
+ the first area can be used for both dynamic relocations and GP-relative
+ accesses, while those in the "reloc-only" area are for dynamic
+ relocations only.
+
+ These GGA_* ("Global GOT Area") values are organised so that lower
+ values are more general than higher values. Also, non-GGA_NONE
+ values are ordered by the position of the area in the GOT. */
+#define GGA_NORMAL 0
+#define GGA_RELOC_ONLY 1
+#define GGA_NONE 2
+
+/* This structure is passed to mips_elf_sort_hash_table_f when sorting
+ the dynamic symbols. */
+
+struct mips_elf_hash_sort_data
+{
+ /* The symbol in the global GOT with the lowest dynamic symbol table
+ index. */
+ struct elf_link_hash_entry *low;
+ /* The least dynamic symbol table index corresponding to a non-TLS
+ symbol with a GOT entry. */
+ long min_got_dynindx;
+ /* The greatest dynamic symbol table index corresponding to a symbol
+ with a GOT entry that is not referenced (e.g., a dynamic symbol
+ with dynamic relocations pointing to it from non-primary GOTs). */
+ long max_unref_got_dynindx;
+ /* The greatest dynamic symbol table index not corresponding to a
+ symbol without a GOT entry. */
+ long max_non_got_dynindx;
+};
+
+/* The MIPS ELF linker needs additional information for each symbol in
+ the global hash table. */
+
+struct mips_elf_link_hash_entry
+{
+ struct elf_link_hash_entry root;
+
+ /* External symbol information. */
+ EXTR esym;
+
+ /* Number of R_RISCV_32, R_RISCV_REL32, or R_RISCV_64 relocs against
+ this symbol. */
+ unsigned int possibly_dynamic_relocs;
+
+#define GOT_NORMAL 0
+#define GOT_TLS_GD 1
+#define GOT_TLS_LDM 2
+#define GOT_TLS_IE 4
+#define GOT_TLS_OFFSET_DONE 0x40
+#define GOT_TLS_DONE 0x80
+ unsigned char tls_type;
+
+ /* This is only used in single-GOT mode; in multi-GOT mode there
+ is one mips_got_entry per GOT entry, so the offset is stored
+ there. In single-GOT mode there may be many mips_got_entry
+ structures all referring to the same GOT slot. It might be
+ possible to use root.got.offset instead, but that field is
+ overloaded already. */
+ bfd_vma tls_got_offset;
+
+ /* The highest GGA_* value that satisfies all references to this symbol. */
+ unsigned int global_got_area : 2;
+
+ /* True if one of the relocations described by possibly_dynamic_relocs
+ is against a readonly section. */
+ unsigned int readonly_reloc : 1;
+
+ /* True if there is a relocation against this symbol that must be
+ resolved by the static linker (in other words, if the relocation
+ cannot possibly be made dynamic). */
+ unsigned int has_static_relocs : 1;
+};
+
+/* MIPS ELF linker hash table. */
+
+struct mips_elf_link_hash_table
+{
+ struct elf_link_hash_table root;
+#if 0
+ /* We no longer use this. */
+ /* String section indices for the dynamic section symbols. */
+ bfd_size_type dynsym_sec_strindex[SIZEOF_MIPS_DYNSYM_SECNAMES];
+#endif
+
+ /* Shortcuts to some dynamic sections, or NULL if they are not
+ being used. */
+ asection *srelbss;
+ asection *sdynbss;
+ asection *srelplt;
+ asection *srelplt2;
+ asection *sgotplt;
+ asection *splt;
+ asection *sgot;
+
+ /* The master GOT information. */
+ struct mips_got_info *got_info;
+
+ /* The size of the PLT header in bytes. */
+ bfd_vma plt_header_size;
+
+ /* The size of a PLT entry in bytes. */
+ bfd_vma plt_entry_size;
+
+ /* The number of reserved entries at the beginning of the GOT. */
+ unsigned int reserved_gotno;
+};
+
+/* Get the MIPS ELF linker hash table from a link_info structure. */
+
+#define mips_elf_hash_table(p) \
+ (elf_hash_table_id ((struct elf_link_hash_table *) ((p)->hash)) \
+ == MIPS_ELF_DATA ? ((struct mips_elf_link_hash_table *) ((p)->hash)) : NULL)
+
+/* A structure used to communicate with htab_traverse callbacks. */
+struct mips_htab_traverse_info
+{
+ /* The usual link-wide information. */
+ struct bfd_link_info *info;
+ bfd *output_bfd;
+
+ /* Starts off FALSE and is set to TRUE if the link should be aborted. */
+ bfd_boolean error;
+};
+
+#define TLS_RELOC_P(r_type) \
+ (r_type == R_RISCV_TLS_DTPMOD32 \
+ || r_type == R_RISCV_TLS_DTPMOD64 \
+ || r_type == R_RISCV_TLS_DTPREL32 \
+ || r_type == R_RISCV_TLS_DTPREL64 \
+ || TLS_GD_RELOC_P(r_type) \
+ || TLS_LDM_RELOC_P(r_type) \
+ || r_type == R_RISCV_TLS_LDM \
+ || r_type == R_RISCV_TLS_DTPREL_HI16 \
+ || r_type == R_RISCV_TLS_DTPREL_LO16 \
+ || TLS_GOTTPREL_RELOC_P(r_type) \
+ || r_type == R_RISCV_TLS_TPREL32 \
+ || r_type == R_RISCV_TLS_TPREL64 \
+ || r_type == R_RISCV_TLS_TPREL_HI16 \
+ || r_type == R_RISCV_TLS_TPREL_LO16)
+
+#define TLS_GOTTPREL_RELOC_P(r_type) \
+ ((r_type) == R_RISCV_TLS_GOTTPREL \
+ || (r_type) == R_RISCV_TLS_GOT_HI16 \
+ || (r_type) == R_RISCV_TLS_GOT_LO16)
+
+#define TLS_GD_RELOC_P(r_type) \
+ ((r_type) == R_RISCV_TLS_GD \
+ || (r_type) == R_RISCV_TLS_GD_HI16 \
+ || (r_type) == R_RISCV_TLS_GD_LO16)
+
+#define TLS_LDM_RELOC_P(r_type) \
+ ((r_type) == R_RISCV_TLS_LDM \
+ || (r_type) == R_RISCV_TLS_LDM_HI16 \
+ || (r_type) == R_RISCV_TLS_LDM_LO16)
+
+/* Structure used to pass information to mips_elf_output_extsym. */
+
+struct extsym_info
+{
+ bfd *abfd;
+ struct bfd_link_info *info;
+ struct ecoff_debug_info *debug;
+ const struct ecoff_debug_swap *swap;
+ bfd_boolean failed;
+};
+
+/* The structure of the runtime procedure descriptor created by the
+ loader for use by the static exception system. */
+
+typedef struct runtime_pdr {
+ bfd_vma adr; /* Memory address of start of procedure. */
+ long regmask; /* Save register mask. */
+ long regoffset; /* Save register offset. */
+ long fregmask; /* Save floating point register mask. */
+ long fregoffset; /* Save floating point register offset. */
+ long frameoffset; /* Frame size. */
+ short framereg; /* Frame pointer register. */
+ short pcreg; /* Offset or reg of return pc. */
+ long irpss; /* Index into the runtime string table. */
+ long reserved;
+ struct exception_info *exception_info;/* Pointer to exception array. */
+} RPDR, *pRPDR;
+#define cbRPDR sizeof (RPDR)
+#define rpdNil ((pRPDR) 0)
+
+static struct mips_got_entry *mips_elf_create_local_got_entry
+ (bfd *, struct bfd_link_info *, bfd *, bfd_vma, unsigned long,
+ struct mips_elf_link_hash_entry *, int);
+static bfd_boolean mips_elf_sort_hash_table_f
+ (struct mips_elf_link_hash_entry *, void *);
+static bfd_boolean mips_elf_create_dynamic_relocation
+ (bfd *, struct bfd_link_info *, const Elf_Internal_Rela *,
+ struct mips_elf_link_hash_entry *, asection *, bfd_vma,
+ bfd_vma *, asection *);
+static hashval_t mips_elf_got_entry_hash
+ (const void *);
+
+/* This will be used when we sort the dynamic relocation records. */
+static bfd *reldyn_sorting_bfd;
+
+/* True if ABFD is a PIC object. */
+#define PIC_OBJECT_P(abfd) \
+ ((elf_elfheader (abfd)->e_flags & EF_MIPS_PIC) != 0)
+
+/* Nonzero if ABFD is using the RV64 ABI. */
+#define ABI_64_P(abfd) \
+ (get_elf_backend_data (abfd)->s->elfclass == ELFCLASS64)
+
+/* Nonzero if ABFD is using the RV32 ABI. */
+#define ABI_32_P(abfd) (!ABI_64_P(abfd))
+
+/* The name of the options section. */
+#define MIPS_ELF_OPTIONS_SECTION_NAME(abfd) ".MIPS.options"
+
+/* True if NAME is the recognized name of any SHT_MIPS_OPTIONS section.
+ Some IRIX system files do not use MIPS_ELF_OPTIONS_SECTION_NAME. */
+#define MIPS_ELF_OPTIONS_SECTION_NAME_P(NAME) \
+ (strcmp (NAME, ".MIPS.options") == 0)
+
+/* Whether the section is readonly. */
+#define MIPS_ELF_READONLY_SECTION(sec) \
+ ((sec->flags & (SEC_ALLOC | SEC_LOAD | SEC_READONLY)) \
+ == (SEC_ALLOC | SEC_LOAD | SEC_READONLY))
+
+/* The size of an external REL relocation. */
+#define MIPS_ELF_REL_SIZE(abfd) \
+ (get_elf_backend_data (abfd)->s->sizeof_rel)
+
+/* The size of an external dynamic table entry. */
+#define MIPS_ELF_DYN_SIZE(abfd) \
+ (get_elf_backend_data (abfd)->s->sizeof_dyn)
+
+/* The size of a GOT entry. */
+#define MIPS_ELF_GOT_SIZE(abfd) \
+ (get_elf_backend_data (abfd)->s->arch_size / 8)
+
+/* The size of a symbol-table entry. */
+#define MIPS_ELF_SYM_SIZE(abfd) \
+ (get_elf_backend_data (abfd)->s->sizeof_sym)
+
+/* The default alignment for sections, as a power of two. */
+#define MIPS_ELF_LOG_FILE_ALIGN(abfd) \
+ (get_elf_backend_data (abfd)->s->log_file_align)
+
+/* Get word-sized data. */
+#define MIPS_ELF_GET_WORD(abfd, ptr) \
+ (ABI_64_P (abfd) ? bfd_get_64 (abfd, ptr) : bfd_get_32 (abfd, ptr))
+
+/* Put out word-sized data. */
+#define MIPS_ELF_PUT_WORD(abfd, val, ptr) \
+ (ABI_64_P (abfd) \
+ ? bfd_put_64 (abfd, val, ptr) \
+ : bfd_put_32 (abfd, val, ptr))
+
+/* Add a dynamic symbol table-entry. */
+#define MIPS_ELF_ADD_DYNAMIC_ENTRY(info, tag, val) \
+ _bfd_elf_add_dynamic_entry (info, tag, val)
+
+#define MIPS_ELF_RTYPE_TO_HOWTO(abfd, rtype, rela) \
+ (get_elf_backend_data (abfd)->elf_backend_mips_rtype_to_howto (rtype, rela))
+
+/* The name of the dynamic relocation section. */
+#define MIPS_ELF_REL_DYN_NAME(INFO) ".rel.dyn"
+
+/* In case we're on a 32-bit machine, construct a 64-bit "-1" value
+ from smaller values. Start with zero, widen, *then* decrement. */
+#define MINUS_ONE (((bfd_vma)0) - 1)
+#define MINUS_TWO (((bfd_vma)0) - 2)
+
+/* The value to write into got[1] for SVR4 targets, to identify it is
+ a GNU object. The dynamic linker can then use got[1] to store the
+ module pointer. */
+#define MIPS_ELF_GNU_GOT1_MASK(abfd) \
+ ((bfd_vma) 1 << (ABI_64_P (abfd) ? 63 : 31))
+
+#define MIPS_FUNCTION_STUB_NORMAL_SIZE 16
+#define MIPS_FUNCTION_STUB_BIG_SIZE 20
+
+/* The name of the dynamic interpreter. This is put in the .interp
+ section. */
+
+#define ELF_DYNAMIC_INTERPRETER(abfd) \
+ (ABI_64_P (abfd) ? "/lib/ld.so.1" \
+ : "/lib32/ld.so.1")
+
+#ifdef BFD64
+#define MNAME(bfd,pre,pos) \
+ (ABI_64_P (bfd) ? CONCAT4 (pre,64,_,pos) : CONCAT4 (pre,32,_,pos))
+#define ELF_R_SYM(bfd, i) \
+ (ABI_64_P (bfd) ? ELF64_R_SYM (i) : ELF32_R_SYM (i))
+#define ELF_R_TYPE(bfd, i) \
+ (ABI_64_P (bfd) ? ELF64_MIPS_R_TYPE (i) : ELF32_R_TYPE (i))
+#define ELF_R_INFO(bfd, s, t) \
+ (ABI_64_P (bfd) ? ELF64_R_INFO (s, t) : ELF32_R_INFO (s, t))
+#else
+#define MNAME(bfd,pre,pos) CONCAT4 (pre,32,_,pos)
+#define ELF_R_SYM(bfd, i) \
+ (ELF32_R_SYM (i))
+#define ELF_R_TYPE(bfd, i) \
+ (ELF32_R_TYPE (i))
+#define ELF_R_INFO(bfd, s, t) \
+ (ELF32_R_INFO (s, t))
+#endif
+
+#define IS_STORE_RELOC(bfd, reloc, opcode) \
+ ((ELF_R_TYPE (bfd, reloc) == R_RISCV_LO16 \
+ || ELF_R_TYPE (bfd, reloc) == R_RISCV_TLS_TPREL_LO16 \
+ || ELF_R_TYPE (bfd, reloc) == R_RISCV_TLS_DTPREL_LO16) \
+ && OPCODE_IS_STORE(opcode))
+
+#define MATCH_LREG(abfd) (ABI_64_P(abfd) ? MATCH_LD : MATCH_LW)
+#define MATCH_SREG(abfd) (ABI_64_P(abfd) ? MATCH_SD : MATCH_SW)
+
+#define OPCODE_MATCHES(OPCODE, OP) \
+ (((OPCODE) & MASK_##OP) == MATCH_##OP)
+
+#define OPCODE_IS_STORE(OPCODE) \
+ (OPCODE_MATCHES(OPCODE, SD) || OPCODE_MATCHES(OPCODE, SW) || \
+ OPCODE_MATCHES(OPCODE, SH) || OPCODE_MATCHES(OPCODE, SB) || \
+ OPCODE_MATCHES(OPCODE, FSW) || OPCODE_MATCHES(OPCODE, FSD))
+
+/* The format of the first PLT entry. */
+
+#define RISCV_PLT0_ENTRY_INSNS 44
+static void
+riscv_make_plt0_entry(bfd* abfd, bfd_vma gotplt_value, bfd_vma addr,
+ bfd_vma entry[RISCV_PLT0_ENTRY_INSNS])
+{
+ /* save ra and arg registers to stack
+ auipc v0, %hi(GOTPLT)
+ addi v0, v0, %lo(GOTPLT)
+ sub a1, v1, v0
+ ld a0, PTRSIZE(v0)
+ ld v0, 0(v0)
+ slli a1, a1, 1
+ addi a1, a1, -4*PTRSIZE
+ jalr v0
+ restore ra and arg registers from stack
+ jr v0
+ */
+
+ int i = 0, j, regbytes = ABI_64_P(abfd) ? 8 : 4;
+ entry[i++] = RISCV_ITYPE(ADDI, 14, 14, -16*regbytes);
+ entry[i++] = RISCV_BTYPE(SREG(abfd), 14, LINK_REG, 0);
+ for (j = 0; j < 14; j++)
+ entry[i++] = RISCV_BTYPE(SREG(abfd), 14, 18+j, (j+1)*regbytes);
+ gotplt_value -= addr + i*4;
+ entry[i++] = RISCV_LTYPE(AUIPC, 16, RISCV_LUI_HIGH_PART(gotplt_value));
+ entry[i++] = RISCV_ITYPE(ADDI, 16, 16, RISCV_CONST_LOW_PART(gotplt_value));
+ entry[i++] = RISCV_RTYPE(SUB, 19, 17, 16);
+ entry[i++] = RISCV_ITYPE(LREG(abfd), 18, 16, regbytes);
+ entry[i++] = RISCV_ITYPE(LREG(abfd), 16, 16, 0);
+ entry[i++] = RISCV_ITYPE(SLLI, 19, 19, 1);
+ entry[i++] = RISCV_ITYPE(ADDI, 19, 19, -4*regbytes);
+ entry[i++] = RISCV_ITYPE(JALR_C, LINK_REG, 16, 0);
+ entry[i++] = RISCV_ITYPE(LREG(abfd), LINK_REG, 14, 0);
+ for (j = 0; j < 14; j++)
+ entry[i++] = RISCV_ITYPE(LREG(abfd), 18+j, 14, (j+1)*regbytes);
+ entry[i++] = RISCV_ITYPE(ADDI, 14, 14, 16*regbytes);
+ entry[i++] = RISCV_ITYPE(JALR_J, 0, 16, 0);
+
+ BFD_ASSERT(i <= RISCV_PLT0_ENTRY_INSNS);
+ while (i < RISCV_PLT0_ENTRY_INSNS)
+ entry[i++] = RISCV_ITYPE(ADDI, 0, 0, 0);
+}
+
+/* The format of subsequent PLT entries. */
+
+#define RISCV_PLT_ENTRY_INSNS 4
+static void
+riscv_make_plt_entry(bfd* abfd, bfd_vma got_address, bfd_vma addr,
+ bfd_vma entry[RISCV_PLT_ENTRY_INSNS])
+{
+ /* auipc v1, %hi(.got.plt entry)
+ l[w|d] v0, %lo(.got.plt entry)(t6)
+ addi v1, v1, %lo(.got.plt entry)
+ jr v0
+ */
+
+ got_address -= addr;
+ entry[0] = RISCV_LTYPE(AUIPC, 17, RISCV_LUI_HIGH_PART(got_address));
+ entry[1] = RISCV_ITYPE(LREG(abfd), 16, 17, RISCV_CONST_LOW_PART(got_address));
+ entry[2] = RISCV_ITYPE(ADDI, 17, 17, RISCV_CONST_LOW_PART(got_address));
+ entry[3] = RISCV_ITYPE(JALR_J, 0, 16, 0);
+}
+
+/* Look up an entry in a MIPS ELF linker hash table. */
+
+#define mips_elf_link_hash_lookup(table, string, create, copy, follow) \
+ ((struct mips_elf_link_hash_entry *) \
+ elf_link_hash_lookup (&(table)->root, (string), (create), \
+ (copy), (follow)))
+
+/* Traverse a MIPS ELF linker hash table. */
+
+#define mips_elf_link_hash_traverse(table, func, info) \
+ (elf_link_hash_traverse \
+ (&(table)->root, \
+ (bfd_boolean (*) (struct elf_link_hash_entry *, void *)) (func), \
+ (info)))
+
+/* Find the base offsets for thread-local storage in this object,
+ for GD/LD and IE/LE respectively. */
+
+#define TP_OFFSET 0x7000
+#define DTP_OFFSET 0x8000
+
+static bfd_vma
+dtprel_base (struct bfd_link_info *info)
+{
+ /* If tls_sec is NULL, we should have signalled an error already. */
+ if (elf_hash_table (info)->tls_sec == NULL)
+ return 0;
+ return elf_hash_table (info)->tls_sec->vma + DTP_OFFSET;
+}
+
+static bfd_vma
+tprel_base (struct bfd_link_info *info)
+{
+ /* If tls_sec is NULL, we should have signalled an error already. */
+ if (elf_hash_table (info)->tls_sec == NULL)
+ return 0;
+ return elf_hash_table (info)->tls_sec->vma + TP_OFFSET;
+}
+
+/* Create an entry in a MIPS ELF linker hash table. */
+
+static struct bfd_hash_entry *
+mips_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
+ struct bfd_hash_table *table, const char *string)
+{
+ struct mips_elf_link_hash_entry *ret =
+ (struct mips_elf_link_hash_entry *) entry;
+
+ /* Allocate the structure if it has not already been allocated by a
+ subclass. */
+ if (ret == NULL)
+ ret = bfd_hash_allocate (table, sizeof (struct mips_elf_link_hash_entry));
+ if (ret == NULL)
+ return (struct bfd_hash_entry *) ret;
+
+ /* Call the allocation method of the superclass. */
+ ret = ((struct mips_elf_link_hash_entry *)
+ _bfd_elf_link_hash_newfunc ((struct bfd_hash_entry *) ret,
+ table, string));
+ if (ret != NULL)
+ {
+ /* Set local fields. */
+ memset (&ret->esym, 0, sizeof (EXTR));
+ /* We use -2 as a marker to indicate that the information has
+ not been set. -1 means there is no associated ifd. */
+ ret->esym.ifd = -2;
+ ret->possibly_dynamic_relocs = 0;
+ ret->tls_type = GOT_NORMAL;
+ ret->global_got_area = GGA_NONE;
+ ret->readonly_reloc = FALSE;
+ ret->has_static_relocs = FALSE;
+ }
+
+ return (struct bfd_hash_entry *) ret;
+}
+
+bfd_boolean
+_bfd_riscv_elf_new_section_hook (bfd *abfd, asection *sec)
+{
+ if (!sec->used_by_bfd)
+ {
+ struct _mips_elf_section_data *sdata;
+ bfd_size_type amt = sizeof (*sdata);
+
+ sdata = bfd_zalloc (abfd, amt);
+ if (sdata == NULL)
+ return FALSE;
+ sec->used_by_bfd = sdata;
+ }
+
+ return _bfd_elf_new_section_hook (abfd, sec);
+}
+
+/* Read ECOFF debugging information from a .mdebug section into a
+ ecoff_debug_info structure. */
+
+bfd_boolean
+_bfd_riscv_elf_read_ecoff_info (bfd *abfd, asection *section,
+ struct ecoff_debug_info *debug)
+{
+ HDRR *symhdr;
+ const struct ecoff_debug_swap *swap;
+ char *ext_hdr;
+
+ swap = get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap;
+ memset (debug, 0, sizeof (*debug));
+
+ ext_hdr = bfd_malloc (swap->external_hdr_size);
+ if (ext_hdr == NULL && swap->external_hdr_size != 0)
+ goto error_return;
+
+ if (! bfd_get_section_contents (abfd, section, ext_hdr, 0,
+ swap->external_hdr_size))
+ goto error_return;
+
+ symhdr = &debug->symbolic_header;
+ (*swap->swap_hdr_in) (abfd, ext_hdr, symhdr);
+
+ /* The symbolic header contains absolute file offsets and sizes to
+ read. */
+#define READ(ptr, offset, count, size, type) \
+ if (symhdr->count == 0) \
+ debug->ptr = NULL; \
+ else \
+ { \
+ bfd_size_type amt = (bfd_size_type) size * symhdr->count; \
+ debug->ptr = bfd_malloc (amt); \
+ if (debug->ptr == NULL) \
+ goto error_return; \
+ if (bfd_seek (abfd, symhdr->offset, SEEK_SET) != 0 \
+ || bfd_bread (debug->ptr, amt, abfd) != amt) \
+ goto error_return; \
+ }
+
+ READ (line, cbLineOffset, cbLine, sizeof (unsigned char), unsigned char *);
+ READ (external_dnr, cbDnOffset, idnMax, swap->external_dnr_size, void *);
+ READ (external_pdr, cbPdOffset, ipdMax, swap->external_pdr_size, void *);
+ READ (external_sym, cbSymOffset, isymMax, swap->external_sym_size, void *);
+ READ (external_opt, cbOptOffset, ioptMax, swap->external_opt_size, void *);
+ READ (external_aux, cbAuxOffset, iauxMax, sizeof (union aux_ext),
+ union aux_ext *);
+ READ (ss, cbSsOffset, issMax, sizeof (char), char *);
+ READ (ssext, cbSsExtOffset, issExtMax, sizeof (char), char *);
+ READ (external_fdr, cbFdOffset, ifdMax, swap->external_fdr_size, void *);
+ READ (external_rfd, cbRfdOffset, crfd, swap->external_rfd_size, void *);
+ READ (external_ext, cbExtOffset, iextMax, swap->external_ext_size, void *);
+#undef READ
+
+ debug->fdr = NULL;
+
+ return TRUE;
+
+ error_return:
+ if (ext_hdr != NULL)
+ free (ext_hdr);
+ if (debug->line != NULL)
+ free (debug->line);
+ if (debug->external_dnr != NULL)
+ free (debug->external_dnr);
+ if (debug->external_pdr != NULL)
+ free (debug->external_pdr);
+ if (debug->external_sym != NULL)
+ free (debug->external_sym);
+ if (debug->external_opt != NULL)
+ free (debug->external_opt);
+ if (debug->external_aux != NULL)
+ free (debug->external_aux);
+ if (debug->ss != NULL)
+ free (debug->ss);
+ if (debug->ssext != NULL)
+ free (debug->ssext);
+ if (debug->external_fdr != NULL)
+ free (debug->external_fdr);
+ if (debug->external_rfd != NULL)
+ free (debug->external_rfd);
+ if (debug->external_ext != NULL)
+ free (debug->external_ext);
+ return FALSE;
+}
+
+static inline bfd_boolean
+got16_reloc_p (int r_type)
+{
+ return r_type == R_RISCV_GOT16;
+}
+
+static inline bfd_boolean
+call16_reloc_p (int r_type)
+{
+ return r_type == R_RISCV_CALL16;
+}
+
+static inline bfd_boolean
+hi16_reloc_p (int r_type)
+{
+ return r_type == R_RISCV_HI16;
+}
+
+static inline bfd_boolean
+lo16_reloc_p (int r_type)
+{
+ return r_type == R_RISCV_LO16;
+}
+
+static bfd_vma
+mips_elf_high (bfd_vma value)
+{
+ return RISCV_LUI_HIGH_PART (value) & ((1<<RISCV_BIGIMM_BITS)-1);
+}
+
+bfd_reloc_status_type
+_bfd_riscv_elf_gprel16_with_gp (bfd *abfd, asymbol *symbol,
+ arelent *reloc_entry, asection *input_section,
+ bfd_boolean relocatable, void *data, bfd_vma gp)
+{
+ bfd_vma relocation;
+ bfd_signed_vma val;
+ bfd_reloc_status_type status;
+
+ if (bfd_is_com_section (symbol->section))
+ relocation = 0;
+ else
+ relocation = symbol->value;
+
+ relocation += symbol->section->output_section->vma;
+ relocation += symbol->section->output_offset;
+
+ if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
+ return bfd_reloc_outofrange;
+
+ /* Set val to the offset into the section or symbol. */
+ val = reloc_entry->addend;
+
+ _bfd_riscv_elf_sign_extend (val, RISCV_IMM_BITS);
+
+ /* Adjust val for the final section location and GP value. If we
+ are producing relocatable output, we don't want to do this for
+ an external symbol. */
+ if (! relocatable
+ || (symbol->flags & BSF_SECTION_SYM) != 0)
+ val += relocation - gp;
+
+ if (reloc_entry->howto->partial_inplace)
+ {
+ status = _bfd_relocate_contents (reloc_entry->howto, abfd, val,
+ (bfd_byte *) data
+ + reloc_entry->address);
+ if (status != bfd_reloc_ok)
+ return status;
+ }
+ else
+ reloc_entry->addend = val;
+
+ if (relocatable)
+ reloc_entry->address += input_section->output_offset;
+
+ return bfd_reloc_ok;
+}
+
+/* Used to store a REL high-part relocation such as R_RISCV_HI16 or
+ R_RISCV_GOT16. REL is the relocation, INPUT_SECTION is the section
+ that contains the relocation field and DATA points to the start of
+ INPUT_SECTION. */
+
+struct mips_hi16
+{
+ struct mips_hi16 *next;
+ bfd_byte *data;
+ asection *input_section;
+ arelent rel;
+};
+
+/* FIXME: This should not be a static variable. */
+
+static struct mips_hi16 *mips_hi16_list;
+
+/* A howto special_function for REL *HI16 relocations. We can only
+ calculate the correct value once we've seen the partnering
+ *LO16 relocation, so just save the information for later.
+
+ The ABI requires that the *LO16 immediately follow the *HI16.
+ However, as a GNU extension, we permit an arbitrary number of
+ *HI16s to be associated with a single *LO16. This significantly
+ simplies the relocation handling in gcc. */
+
+bfd_reloc_status_type
+_bfd_riscv_elf_hi16_reloc (bfd *abfd ATTRIBUTE_UNUSED, arelent *reloc_entry,
+ asymbol *symbol ATTRIBUTE_UNUSED, void *data,
+ asection *input_section, bfd *output_bfd,
+ char **error_message ATTRIBUTE_UNUSED)
+{
+ struct mips_hi16 *n;
+
+ if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
+ return bfd_reloc_outofrange;
+
+ n = bfd_malloc (sizeof *n);
+ if (n == NULL)
+ return bfd_reloc_outofrange;
+
+ n->next = mips_hi16_list;
+ n->data = data;
+ n->input_section = input_section;
+ n->rel = *reloc_entry;
+ mips_hi16_list = n;
+
+ if (output_bfd != NULL)
+ reloc_entry->address += input_section->output_offset;
+
+ return bfd_reloc_ok;
+}
+
+/* A howto special_function for REL R_MIPS*_GOT16 relocations. This is just
+ like any other 16-bit relocation when applied to global symbols, but is
+ treated in the same as R_RISCV_HI16 when applied to local symbols. */
+
+bfd_reloc_status_type
+_bfd_riscv_elf_got16_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+ void *data, asection *input_section,
+ bfd *output_bfd, char **error_message)
+{
+ if ((symbol->flags & (BSF_GLOBAL | BSF_WEAK)) != 0
+ || bfd_is_und_section (bfd_get_section (symbol))
+ || bfd_is_com_section (bfd_get_section (symbol)))
+ /* The relocation is against a global symbol. */
+ return _bfd_riscv_elf_generic_reloc (abfd, reloc_entry, symbol, data,
+ input_section, output_bfd,
+ error_message);
+
+ return _bfd_riscv_elf_hi16_reloc (abfd, reloc_entry, symbol, data,
+ input_section, output_bfd, error_message);
+}
+
+/* A howto special_function for REL *LO16 relocations. The *LO16 itself
+ is a straightforward 16 bit inplace relocation, but we must deal with
+ any partnering high-part relocations as well. */
+
+bfd_reloc_status_type
+_bfd_riscv_elf_lo16_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+ void *data, asection *input_section,
+ bfd *output_bfd, char **error_message)
+{
+ bfd_vma vallo;
+ bfd_byte *location = (bfd_byte *) data + reloc_entry->address;
+
+ if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
+ return bfd_reloc_outofrange;
+
+ vallo = bfd_get_32 (abfd, location);
+
+ while (mips_hi16_list != NULL)
+ {
+ bfd_reloc_status_type ret;
+ struct mips_hi16 *hi;
+
+ hi = mips_hi16_list;
+
+ /* R_MIPS*_GOT16 relocations are something of a special case. We
+ want to install the addend in the same way as for a R_MIPS*_HI16
+ relocation (with a rightshift of 16). However, since GOT16
+ relocations can also be used with global symbols, their howto
+ has a rightshift of 0. */
+ if (hi->rel.howto->type == R_RISCV_GOT16)
+ hi->rel.howto = MIPS_ELF_RTYPE_TO_HOWTO (abfd, R_RISCV_HI16, FALSE);
+
+ /* VALLO is a signed 16-bit number. Bias it by 0x8000 so that any
+ carry or borrow will induce a change of +1 or -1 in the high part. */
+ hi->rel.addend += (vallo + RISCV_IMM_REACH/2) & (RISCV_IMM_REACH-1);
+
+ ret = _bfd_riscv_elf_generic_reloc (abfd, &hi->rel, symbol, hi->data,
+ hi->input_section, output_bfd,
+ error_message);
+ if (ret != bfd_reloc_ok)
+ return ret;
+
+ mips_hi16_list = hi->next;
+ free (hi);
+ }
+
+ return _bfd_riscv_elf_generic_reloc (abfd, reloc_entry, symbol, data,
+ input_section, output_bfd,
+ error_message);
+}
+
+/* A generic howto special_function. This calculates and installs the
+ relocation itself, thus avoiding the oft-discussed problems in
+ bfd_perform_relocation and bfd_install_relocation. */
+
+bfd_reloc_status_type
+_bfd_riscv_elf_generic_reloc (bfd *abfd ATTRIBUTE_UNUSED, arelent *reloc_entry,
+ asymbol *symbol, void *data ATTRIBUTE_UNUSED,
+ asection *input_section, bfd *output_bfd,
+ char **error_message ATTRIBUTE_UNUSED)
+{
+ bfd_signed_vma val;
+ bfd_reloc_status_type status;
+ bfd_boolean relocatable;
+
+ relocatable = (output_bfd != NULL);
+
+ if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
+ return bfd_reloc_outofrange;
+
+ /* Build up the field adjustment in VAL. */
+ val = 0;
+ if (!relocatable || (symbol->flags & BSF_SECTION_SYM) != 0)
+ {
+ /* Either we're calculating the final field value or we have a
+ relocation against a section symbol. Add in the section's
+ offset or address. */
+ val += symbol->section->output_section->vma;
+ val += symbol->section->output_offset;
+ }
+
+ if (!relocatable)
+ {
+ /* We're calculating the final field value. Add in the symbol's value
+ and, if pc-relative, subtract the address of the field itself. */
+ val += symbol->value;
+ if (reloc_entry->howto->pc_relative)
+ {
+ val -= input_section->output_section->vma;
+ val -= input_section->output_offset;
+ val -= reloc_entry->address;
+ }
+ }
+
+ /* VAL is now the final adjustment. If we're keeping this relocation
+ in the output file, and if the relocation uses a separate addend,
+ we just need to add VAL to that addend. Otherwise we need to add
+ VAL to the relocation field itself. */
+ if (relocatable && !reloc_entry->howto->partial_inplace)
+ reloc_entry->addend += val;
+ else
+ {
+ bfd_byte *loc = (bfd_byte *) data + reloc_entry->address;
+ struct reloc_howto_struct howto = *reloc_entry->howto;
+
+ /* Add in the separate addend, if any. */
+ val += reloc_entry->addend;
+
+ /* Fix up dst_mask and value for R_RISCV_LO16 relocs on stores. */
+ if (IS_STORE_RELOC (abfd, howto.type,
+ bfd_big_endian(abfd) ? bfd_getb32(loc) : bfd_getl32(loc)))
+ {
+ val >>= OP_SH_IMMEDIATE;
+ val = ((val >> RISCV_IMMLO_BITS) << OP_SH_IMMHI) |
+ ((val & ((1<<RISCV_IMMLO_BITS)-1)) << OP_SH_IMMLO);
+ howto.dst_mask = (OP_MASK_IMMHI << OP_SH_IMMHI) |
+ (OP_MASK_IMMLO << OP_SH_IMMLO);
+ }
+
+ /* Add VAL to the reloc field. */
+ status = _bfd_relocate_contents (&howto, abfd, val, loc);
+
+ if (status != bfd_reloc_ok)
+ return status;
+ }
+
+ if (relocatable)
+ reloc_entry->address += input_section->output_offset;
+
+ return bfd_reloc_ok;
+}
+
+/* A .reginfo section holds a single Elf32_RegInfo structure. These
+ routines swap this structure in and out. They are used outside of
+ BFD, so they are globally visible. */
+
+void
+bfd_riscv_elf32_swap_reginfo_in (bfd *abfd, const Elf32_External_RegInfo *ex,
+ Elf32_RegInfo *in)
+{
+ in->ri_gprmask = H_GET_32 (abfd, ex->ri_gprmask);
+ in->ri_cprmask[0] = H_GET_32 (abfd, ex->ri_cprmask[0]);
+ in->ri_cprmask[1] = H_GET_32 (abfd, ex->ri_cprmask[1]);
+ in->ri_cprmask[2] = H_GET_32 (abfd, ex->ri_cprmask[2]);
+ in->ri_cprmask[3] = H_GET_32 (abfd, ex->ri_cprmask[3]);
+ in->ri_gp_value = H_GET_32 (abfd, ex->ri_gp_value);
+}
+
+void
+bfd_riscv_elf32_swap_reginfo_out (bfd *abfd, const Elf32_RegInfo *in,
+ Elf32_External_RegInfo *ex)
+{
+ H_PUT_32 (abfd, in->ri_gprmask, ex->ri_gprmask);
+ H_PUT_32 (abfd, in->ri_cprmask[0], ex->ri_cprmask[0]);
+ H_PUT_32 (abfd, in->ri_cprmask[1], ex->ri_cprmask[1]);
+ H_PUT_32 (abfd, in->ri_cprmask[2], ex->ri_cprmask[2]);
+ H_PUT_32 (abfd, in->ri_cprmask[3], ex->ri_cprmask[3]);
+ H_PUT_32 (abfd, in->ri_gp_value, ex->ri_gp_value);
+}
+
+/* In the 64 bit ABI, the .MIPS.options section holds register
+ information in an Elf64_Reginfo structure. These routines swap
+ them in and out. They are globally visible because they are used
+ outside of BFD. These routines are here so that gas can call them
+ without worrying about whether the 64 bit ABI has been included. */
+
+void
+bfd_riscv_elf64_swap_reginfo_in (bfd *abfd, const Elf64_External_RegInfo *ex,
+ Elf64_Internal_RegInfo *in)
+{
+ in->ri_gprmask = H_GET_32 (abfd, ex->ri_gprmask);
+ in->ri_pad = H_GET_32 (abfd, ex->ri_pad);
+ in->ri_cprmask[0] = H_GET_32 (abfd, ex->ri_cprmask[0]);
+ in->ri_cprmask[1] = H_GET_32 (abfd, ex->ri_cprmask[1]);
+ in->ri_cprmask[2] = H_GET_32 (abfd, ex->ri_cprmask[2]);
+ in->ri_cprmask[3] = H_GET_32 (abfd, ex->ri_cprmask[3]);
+ in->ri_gp_value = H_GET_64 (abfd, ex->ri_gp_value);
+}
+
+void
+bfd_riscv_elf64_swap_reginfo_out (bfd *abfd, const Elf64_Internal_RegInfo *in,
+ Elf64_External_RegInfo *ex)
+{
+ H_PUT_32 (abfd, in->ri_gprmask, ex->ri_gprmask);
+ H_PUT_32 (abfd, in->ri_pad, ex->ri_pad);
+ H_PUT_32 (abfd, in->ri_cprmask[0], ex->ri_cprmask[0]);
+ H_PUT_32 (abfd, in->ri_cprmask[1], ex->ri_cprmask[1]);
+ H_PUT_32 (abfd, in->ri_cprmask[2], ex->ri_cprmask[2]);
+ H_PUT_32 (abfd, in->ri_cprmask[3], ex->ri_cprmask[3]);
+ H_PUT_64 (abfd, in->ri_gp_value, ex->ri_gp_value);
+}
+
+/* Swap in an options header. */
+
+void
+bfd_riscv_elf_swap_options_in (bfd *abfd, const Elf_External_Options *ex,
+ Elf_Internal_Options *in)
+{
+ in->kind = H_GET_8 (abfd, ex->kind);
+ in->size = H_GET_8 (abfd, ex->size);
+ in->section = H_GET_16 (abfd, ex->section);
+ in->info = H_GET_32 (abfd, ex->info);
+}
+
+/* Swap out an options header. */
+
+void
+bfd_riscv_elf_swap_options_out (bfd *abfd, const Elf_Internal_Options *in,
+ Elf_External_Options *ex)
+{
+ H_PUT_8 (abfd, in->kind, ex->kind);
+ H_PUT_8 (abfd, in->size, ex->size);
+ H_PUT_16 (abfd, in->section, ex->section);
+ H_PUT_32 (abfd, in->info, ex->info);
+}
+
+/* This function is called via qsort() to sort the dynamic relocation
+ entries by increasing r_symndx value. */
+
+static int
+sort_dynamic_relocs (const void *arg1, const void *arg2)
+{
+ Elf_Internal_Rela int_reloc1;
+ Elf_Internal_Rela int_reloc2;
+ int diff;
+
+ bfd_elf32_swap_reloc_in (reldyn_sorting_bfd, arg1, &int_reloc1);
+ bfd_elf32_swap_reloc_in (reldyn_sorting_bfd, arg2, &int_reloc2);
+
+ diff = ELF32_R_SYM (int_reloc1.r_info) - ELF32_R_SYM (int_reloc2.r_info);
+ if (diff != 0)
+ return diff;
+
+ if (int_reloc1.r_offset < int_reloc2.r_offset)
+ return -1;
+ if (int_reloc1.r_offset > int_reloc2.r_offset)
+ return 1;
+ return 0;
+}
+
+/* Like sort_dynamic_relocs, but used for elf64 relocations. */
+
+static int
+sort_dynamic_relocs_64 (const void *arg1 ATTRIBUTE_UNUSED,
+ const void *arg2 ATTRIBUTE_UNUSED)
+{
+#ifdef BFD64
+ Elf_Internal_Rela int_reloc1[3];
+ Elf_Internal_Rela int_reloc2[3];
+
+ (*get_elf_backend_data (reldyn_sorting_bfd)->s->swap_reloc_in)
+ (reldyn_sorting_bfd, arg1, int_reloc1);
+ (*get_elf_backend_data (reldyn_sorting_bfd)->s->swap_reloc_in)
+ (reldyn_sorting_bfd, arg2, int_reloc2);
+
+ if (ELF64_R_SYM (int_reloc1[0].r_info) < ELF64_R_SYM (int_reloc2[0].r_info))
+ return -1;
+ if (ELF64_R_SYM (int_reloc1[0].r_info) > ELF64_R_SYM (int_reloc2[0].r_info))
+ return 1;
+
+ if (int_reloc1[0].r_offset < int_reloc2[0].r_offset)
+ return -1;
+ if (int_reloc1[0].r_offset > int_reloc2[0].r_offset)
+ return 1;
+ return 0;
+#else
+ abort ();
+#endif
+}
+
+
+/* This routine is used to write out ECOFF debugging external symbol
+ information. It is called via mips_elf_link_hash_traverse. The
+ ECOFF external symbol information must match the ELF external
+ symbol information. Unfortunately, at this point we don't know
+ whether a symbol is required by reloc information, so the two
+ tables may wind up being different. We must sort out the external
+ symbol information before we can set the final size of the .mdebug
+ section, and we must set the size of the .mdebug section before we
+ can relocate any sections, and we can't know which symbols are
+ required by relocation until we relocate the sections.
+ Fortunately, it is relatively unlikely that any symbol will be
+ stripped but required by a reloc. In particular, it can not happen
+ when generating a final executable. */
+
+static bfd_boolean
+mips_elf_output_extsym (struct mips_elf_link_hash_entry *h, void *data)
+{
+ struct extsym_info *einfo = data;
+ bfd_boolean strip;
+ asection *sec, *output_section;
+
+ if (h->root.root.type == bfd_link_hash_warning)
+ h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
+
+ if (h->root.indx == -2)
+ strip = FALSE;
+ else if ((h->root.def_dynamic
+ || h->root.ref_dynamic
+ || h->root.type == bfd_link_hash_new)
+ && !h->root.def_regular
+ && !h->root.ref_regular)
+ strip = TRUE;
+ else if (einfo->info->strip == strip_all
+ || (einfo->info->strip == strip_some
+ && bfd_hash_lookup (einfo->info->keep_hash,
+ h->root.root.root.string,
+ FALSE, FALSE) == NULL))
+ strip = TRUE;
+ else
+ strip = FALSE;
+
+ if (strip)
+ return TRUE;
+
+ if (h->esym.ifd == -2)
+ {
+ h->esym.jmptbl = 0;
+ h->esym.cobol_main = 0;
+ h->esym.weakext = 0;
+ h->esym.reserved = 0;
+ h->esym.ifd = ifdNil;
+ h->esym.asym.value = 0;
+ h->esym.asym.st = stGlobal;
+
+ if (h->root.root.type == bfd_link_hash_undefined
+ || h->root.root.type == bfd_link_hash_undefweak)
+ h->esym.asym.sc = scUndefined;
+ else if (h->root.root.type != bfd_link_hash_defined
+ && h->root.root.type != bfd_link_hash_defweak)
+ h->esym.asym.sc = scAbs;
+ else
+ {
+ const char *name;
+
+ sec = h->root.root.u.def.section;
+ output_section = sec->output_section;
+
+ /* When making a shared library and symbol h is the one from
+ the another shared library, OUTPUT_SECTION may be null. */
+ if (output_section == NULL)
+ h->esym.asym.sc = scUndefined;
+ else
+ {
+ name = bfd_section_name (output_section->owner, output_section);
+
+ if (strcmp (name, ".text") == 0)
+ h->esym.asym.sc = scText;
+ else if (strcmp (name, ".data") == 0)
+ h->esym.asym.sc = scData;
+ else if (strcmp (name, ".rodata") == 0
+ || strcmp (name, ".rdata") == 0)
+ h->esym.asym.sc = scRData;
+ else if (strcmp (name, ".bss") == 0)
+ h->esym.asym.sc = scBss;
+ else if (strcmp (name, ".init") == 0)
+ h->esym.asym.sc = scInit;
+ else if (strcmp (name, ".fini") == 0)
+ h->esym.asym.sc = scFini;
+ else
+ h->esym.asym.sc = scAbs;
+ }
+ }
+
+ h->esym.asym.reserved = 0;
+ h->esym.asym.index = indexNil;
+ }
+
+ if (h->root.root.type == bfd_link_hash_common)
+ h->esym.asym.value = h->root.root.u.c.size;
+ else if (h->root.root.type == bfd_link_hash_defined
+ || h->root.root.type == bfd_link_hash_defweak)
+ {
+ if (h->esym.asym.sc == scCommon || h->esym.asym.sc == scSCommon)
+ h->esym.asym.sc = scBss;
+
+ sec = h->root.root.u.def.section;
+ output_section = sec->output_section;
+ if (output_section != NULL)
+ h->esym.asym.value = (h->root.root.u.def.value
+ + sec->output_offset
+ + output_section->vma);
+ else
+ h->esym.asym.value = 0;
+ }
+
+ if (! bfd_ecoff_debug_one_external (einfo->abfd, einfo->debug, einfo->swap,
+ h->root.root.root.string,
+ &h->esym))
+ {
+ einfo->failed = TRUE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Functions to manage the got entry hash table. */
+
+/* Use all 64 bits of a bfd_vma for the computation of a 32-bit
+ hash number. */
+
+static INLINE hashval_t
+mips_elf_hash_bfd_vma (bfd_vma addr)
+{
+#ifdef BFD64
+ return addr + (addr >> 32);
+#else
+ return addr;
+#endif
+}
+
+/* got_entries only match if they're identical, except for gotidx, so
+ use all fields to compute the hash, and compare the appropriate
+ union members. */
+
+static hashval_t
+mips_elf_got_entry_hash (const void *entry_)
+{
+ const struct mips_got_entry *entry = (struct mips_got_entry *)entry_;
+
+ return entry->symndx
+ + ((entry->tls_type & GOT_TLS_LDM) << 17)
+ + (! entry->abfd ? mips_elf_hash_bfd_vma (entry->d.address)
+ : entry->abfd->id
+ + (entry->symndx >= 0 ? mips_elf_hash_bfd_vma (entry->d.addend)
+ : entry->d.h->root.root.root.hash));
+}
+
+static int
+mips_elf_got_entry_eq (const void *entry1, const void *entry2)
+{
+ const struct mips_got_entry *e1 = (struct mips_got_entry *)entry1;
+ const struct mips_got_entry *e2 = (struct mips_got_entry *)entry2;
+
+ /* An LDM entry can only match another LDM entry. */
+ if ((e1->tls_type ^ e2->tls_type) & GOT_TLS_LDM)
+ return 0;
+
+ return e1->abfd == e2->abfd && e1->symndx == e2->symndx
+ && (! e1->abfd ? e1->d.address == e2->d.address
+ : e1->symndx >= 0 ? e1->d.addend == e2->d.addend
+ : e1->d.h == e2->d.h);
+}
+
+static hashval_t
+mips_got_page_entry_hash (const void *entry_)
+{
+ const struct mips_got_page_entry *entry;
+
+ entry = (const struct mips_got_page_entry *) entry_;
+ return entry->abfd->id + entry->symndx;
+}
+
+static int
+mips_got_page_entry_eq (const void *entry1_, const void *entry2_)
+{
+ const struct mips_got_page_entry *entry1, *entry2;
+
+ entry1 = (const struct mips_got_page_entry *) entry1_;
+ entry2 = (const struct mips_got_page_entry *) entry2_;
+ return entry1->abfd == entry2->abfd && entry1->symndx == entry2->symndx;
+}
+
+/* Return the dynamic relocation section. If it doesn't exist, try to
+ create a new it if CREATE_P, otherwise return NULL. Also return NULL
+ if creation fails. */
+
+static asection *
+mips_elf_rel_dyn_section (struct bfd_link_info *info, bfd_boolean create_p)
+{
+ const char *dname;
+ asection *sreloc;
+ bfd *dynobj;
+
+ dname = MIPS_ELF_REL_DYN_NAME (info);
+ dynobj = elf_hash_table (info)->dynobj;
+ sreloc = bfd_get_section_by_name (dynobj, dname);
+ if (sreloc == NULL && create_p)
+ {
+ sreloc = bfd_make_section_with_flags (dynobj, dname,
+ (SEC_ALLOC
+ | SEC_LOAD
+ | SEC_HAS_CONTENTS
+ | SEC_IN_MEMORY
+ | SEC_LINKER_CREATED
+ | SEC_READONLY));
+ if (sreloc == NULL
+ || ! bfd_set_section_alignment (dynobj, sreloc,
+ MIPS_ELF_LOG_FILE_ALIGN (dynobj)))
+ return NULL;
+ }
+ return sreloc;
+}
+
+/* Count the number of relocations needed for a TLS GOT entry, with
+ access types from TLS_TYPE, and symbol H (or a local symbol if H
+ is NULL). */
+
+static int
+mips_tls_got_relocs (struct bfd_link_info *info, unsigned char tls_type,
+ struct elf_link_hash_entry *h)
+{
+ int indx = 0;
+ int ret = 0;
+ bfd_boolean need_relocs = FALSE;
+ bfd_boolean dyn = elf_hash_table (info)->dynamic_sections_created;
+
+ if (h && WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
+ && (!info->shared || !SYMBOL_REFERENCES_LOCAL (info, h)))
+ indx = h->dynindx;
+
+ if ((info->shared || indx != 0)
+ && (h == NULL
+ || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+ || h->root.type != bfd_link_hash_undefweak))
+ need_relocs = TRUE;
+
+ if (!need_relocs)
+ return FALSE;
+
+ if (tls_type & GOT_TLS_GD)
+ {
+ ret++;
+ if (indx != 0)
+ ret++;
+ }
+
+ if (tls_type & GOT_TLS_IE)
+ ret++;
+
+ if ((tls_type & GOT_TLS_LDM) && info->shared)
+ ret++;
+
+ return ret;
+}
+
+/* Count the number of TLS relocations required for the GOT entry in
+ ARG1, if it describes a local symbol. */
+
+static int
+mips_elf_count_local_tls_relocs (void **arg1, void *arg2)
+{
+ struct mips_got_entry *entry = * (struct mips_got_entry **) arg1;
+ struct mips_elf_count_tls_arg *arg = arg2;
+
+ if (entry->abfd != NULL && entry->symndx != -1)
+ arg->needed += mips_tls_got_relocs (arg->info, entry->tls_type, NULL);
+
+ return 1;
+}
+
+/* Count the number of TLS GOT entries required for the global (or
+ forced-local) symbol in ARG1. */
+
+static int
+mips_elf_count_global_tls_entries (void *arg1, void *arg2)
+{
+ struct mips_elf_link_hash_entry *hm
+ = (struct mips_elf_link_hash_entry *) arg1;
+ struct mips_elf_count_tls_arg *arg = arg2;
+
+ if (hm->tls_type & GOT_TLS_GD)
+ arg->needed += 2;
+ if (hm->tls_type & GOT_TLS_IE)
+ arg->needed += 1;
+
+ return 1;
+}
+
+/* Count the number of TLS relocations required for the global (or
+ forced-local) symbol in ARG1. */
+
+static int
+mips_elf_count_global_tls_relocs (void *arg1, void *arg2)
+{
+ struct mips_elf_link_hash_entry *hm
+ = (struct mips_elf_link_hash_entry *) arg1;
+ struct mips_elf_count_tls_arg *arg = arg2;
+
+ arg->needed += mips_tls_got_relocs (arg->info, hm->tls_type, &hm->root);
+
+ return 1;
+}
+
+/* Output a simple dynamic relocation into SRELOC. */
+
+static void
+mips_elf_output_dynamic_relocation (bfd *output_bfd,
+ asection *sreloc,
+ unsigned long reloc_index,
+ unsigned long indx,
+ int r_type,
+ bfd_vma offset)
+{
+ Elf_Internal_Rela rel[3];
+
+ memset (rel, 0, sizeof (rel));
+
+ rel[0].r_info = ELF_R_INFO (output_bfd, indx, r_type);
+ rel[0].r_offset = rel[1].r_offset = rel[2].r_offset = offset;
+
+ if (ABI_64_P (output_bfd))
+ {
+ (*get_elf_backend_data (output_bfd)->s->swap_reloc_out)
+ (output_bfd, &rel[0],
+ (sreloc->contents
+ + reloc_index * sizeof (Elf64_RISCV_External_Rel)));
+ }
+ else
+ bfd_elf32_swap_reloc_out
+ (output_bfd, &rel[0],
+ (sreloc->contents
+ + reloc_index * sizeof (Elf32_External_Rel)));
+}
+
+/* Initialize a set of TLS GOT entries for one symbol. */
+
+static void
+mips_elf_initialize_tls_slots (bfd *abfd, bfd_vma got_offset,
+ unsigned char *tls_type_p,
+ struct bfd_link_info *info,
+ struct mips_elf_link_hash_entry *h,
+ bfd_vma value)
+{
+ struct mips_elf_link_hash_table *htab;
+ int indx;
+ asection *sreloc, *sgot;
+ bfd_vma offset, offset2;
+ bfd_boolean need_relocs = FALSE;
+
+ htab = mips_elf_hash_table (info);
+ if (htab == NULL)
+ return;
+
+ sgot = htab->sgot;
+
+ indx = 0;
+ if (h != NULL)
+ {
+ bfd_boolean dyn = elf_hash_table (info)->dynamic_sections_created;
+
+ if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, &h->root)
+ && (!info->shared || !SYMBOL_REFERENCES_LOCAL (info, &h->root)))
+ indx = h->root.dynindx;
+ }
+
+ if (*tls_type_p & GOT_TLS_DONE)
+ return;
+
+ if ((info->shared || indx != 0)
+ && (h == NULL
+ || ELF_ST_VISIBILITY (h->root.other) == STV_DEFAULT
+ || h->root.type != bfd_link_hash_undefweak))
+ need_relocs = TRUE;
+
+ /* MINUS_ONE means the symbol is not defined in this object. It may not
+ be defined at all; assume that the value doesn't matter in that
+ case. Otherwise complain if we would use the value. */
+ BFD_ASSERT (value != MINUS_ONE || (indx != 0 && need_relocs)
+ || h->root.root.type == bfd_link_hash_undefweak);
+
+ /* Emit necessary relocations. */
+ sreloc = mips_elf_rel_dyn_section (info, FALSE);
+
+ /* General Dynamic. */
+ if (*tls_type_p & GOT_TLS_GD)
+ {
+ offset = got_offset;
+ offset2 = offset + MIPS_ELF_GOT_SIZE (abfd);
+
+ if (need_relocs)
+ {
+ mips_elf_output_dynamic_relocation
+ (abfd, sreloc, sreloc->reloc_count++, indx,
+ ABI_64_P (abfd) ? R_RISCV_TLS_DTPMOD64 : R_RISCV_TLS_DTPMOD32,
+ sgot->output_offset + sgot->output_section->vma + offset);
+
+ if (indx)
+ mips_elf_output_dynamic_relocation
+ (abfd, sreloc, sreloc->reloc_count++, indx,
+ ABI_64_P (abfd) ? R_RISCV_TLS_DTPREL64 : R_RISCV_TLS_DTPREL32,
+ sgot->output_offset + sgot->output_section->vma + offset2);
+ else
+ MIPS_ELF_PUT_WORD (abfd, value - dtprel_base (info),
+ sgot->contents + offset2);
+ }
+ else
+ {
+ MIPS_ELF_PUT_WORD (abfd, 1,
+ sgot->contents + offset);
+ MIPS_ELF_PUT_WORD (abfd, value - dtprel_base (info),
+ sgot->contents + offset2);
+ }
+
+ got_offset += 2 * MIPS_ELF_GOT_SIZE (abfd);
+ }
+
+ /* Initial Exec model. */
+ if (*tls_type_p & GOT_TLS_IE)
+ {
+ offset = got_offset;
+
+ if (need_relocs)
+ {
+ if (indx == 0)
+ MIPS_ELF_PUT_WORD (abfd, value - elf_hash_table (info)->tls_sec->vma,
+ sgot->contents + offset);
+ else
+ MIPS_ELF_PUT_WORD (abfd, 0,
+ sgot->contents + offset);
+
+ mips_elf_output_dynamic_relocation
+ (abfd, sreloc, sreloc->reloc_count++, indx,
+ ABI_64_P (abfd) ? R_RISCV_TLS_TPREL64 : R_RISCV_TLS_TPREL32,
+ sgot->output_offset + sgot->output_section->vma + offset);
+ }
+ else
+ MIPS_ELF_PUT_WORD (abfd, value - tprel_base (info),
+ sgot->contents + offset);
+ }
+
+ if (*tls_type_p & GOT_TLS_LDM)
+ {
+ /* The initial offset is zero, and the LD offsets will include the
+ bias by DTP_OFFSET. */
+ MIPS_ELF_PUT_WORD (abfd, 0,
+ sgot->contents + got_offset
+ + MIPS_ELF_GOT_SIZE (abfd));
+
+ if (!info->shared)
+ MIPS_ELF_PUT_WORD (abfd, 1,
+ sgot->contents + got_offset);
+ else
+ mips_elf_output_dynamic_relocation
+ (abfd, sreloc, sreloc->reloc_count++, indx,
+ ABI_64_P (abfd) ? R_RISCV_TLS_DTPMOD64 : R_RISCV_TLS_DTPMOD32,
+ sgot->output_offset + sgot->output_section->vma + got_offset);
+ }
+
+ *tls_type_p |= GOT_TLS_DONE;
+}
+
+/* Return the GOT index to use for a relocation of type R_TYPE against
+ a symbol accessed using TLS_TYPE models. The GOT entries for this
+ symbol in this GOT start at GOT_INDEX. This function initializes the
+ GOT entries and corresponding relocations. */
+
+static bfd_vma
+mips_tls_got_index (bfd *abfd, bfd_vma got_index, unsigned char *tls_type,
+ int r_type, struct bfd_link_info *info,
+ struct mips_elf_link_hash_entry *h, bfd_vma symbol)
+{
+ BFD_ASSERT (TLS_GOTTPREL_RELOC_P(r_type) || TLS_GD_RELOC_P(r_type)
+ || TLS_LDM_RELOC_P(r_type));
+
+ mips_elf_initialize_tls_slots (abfd, got_index, tls_type, info, h, symbol);
+
+ if (TLS_GOTTPREL_RELOC_P(r_type))
+ {
+ BFD_ASSERT (*tls_type & GOT_TLS_IE);
+ if (*tls_type & GOT_TLS_GD)
+ return got_index + 2 * MIPS_ELF_GOT_SIZE (abfd);
+ else
+ return got_index;
+ }
+
+ if (TLS_GD_RELOC_P(r_type))
+ {
+ BFD_ASSERT (*tls_type & GOT_TLS_GD);
+ return got_index;
+ }
+
+ if (TLS_LDM_RELOC_P(r_type))
+ {
+ BFD_ASSERT (*tls_type & GOT_TLS_LDM);
+ return got_index;
+ }
+
+ return got_index;
+}
+
+/* Return the GOT offset for address VALUE. If there is not yet a GOT
+ entry for this value, create one. If R_SYMNDX refers to a TLS symbol,
+ create a TLS GOT entry instead. Return -1 if no satisfactory GOT
+ offset can be found. */
+
+static bfd_vma
+mips_elf_local_got_index (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
+ bfd_vma value, unsigned long r_symndx,
+ struct mips_elf_link_hash_entry *h, int r_type)
+{
+ struct mips_elf_link_hash_table *htab;
+ struct mips_got_entry *entry;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ entry = mips_elf_create_local_got_entry (abfd, info, ibfd, value,
+ r_symndx, h, r_type);
+ if (!entry)
+ return MINUS_ONE;
+
+ if (TLS_RELOC_P (r_type))
+ {
+ if (entry->symndx == -1)
+ /* A type (3) entry in the single-GOT case. We use the symbol's
+ hash table entry to track the index. */
+ return mips_tls_got_index (abfd, h->tls_got_offset, &h->tls_type,
+ r_type, info, h, value);
+ else
+ return mips_tls_got_index (abfd, entry->gotidx, &entry->tls_type,
+ r_type, info, h, value);
+ }
+ else
+ return entry->gotidx;
+}
+
+/* Returns the GOT index for the global symbol indicated by H. */
+
+static bfd_vma
+mips_elf_global_got_index (bfd *abfd, struct elf_link_hash_entry *h,
+ int r_type, struct bfd_link_info *info)
+{
+ struct mips_elf_link_hash_table *htab;
+ bfd_vma got_index;
+ struct mips_got_info *g;
+ long global_got_dynindx = 0;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ g = htab->got_info;
+
+ if (g->global_gotsym != NULL)
+ global_got_dynindx = g->global_gotsym->dynindx;
+
+ if (TLS_RELOC_P (r_type))
+ {
+ struct mips_elf_link_hash_entry *hm
+ = (struct mips_elf_link_hash_entry *) h;
+ bfd_vma value = MINUS_ONE;
+
+ if ((h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ && h->root.u.def.section->output_section)
+ value = (h->root.u.def.value
+ + h->root.u.def.section->output_offset
+ + h->root.u.def.section->output_section->vma);
+
+ got_index = mips_tls_got_index (abfd, hm->tls_got_offset, &hm->tls_type,
+ r_type, info, hm, value);
+ }
+ else
+ {
+ /* Once we determine the global GOT entry with the lowest dynamic
+ symbol table index, we must put all dynamic symbols with greater
+ indices into the GOT. That makes it easy to calculate the GOT
+ offset. */
+ BFD_ASSERT (h->dynindx >= global_got_dynindx);
+ got_index = ((h->dynindx - global_got_dynindx + g->local_gotno)
+ * MIPS_ELF_GOT_SIZE (abfd));
+ }
+ BFD_ASSERT (got_index < htab->sgot->size);
+
+ return got_index;
+}
+
+/* Find a local GOT entry for an R_MIPS*_GOT16 relocation against VALUE.
+ EXTERNAL is true if the relocation was originally against a global
+ symbol that binds locally. */
+
+static bfd_vma
+mips_elf_got16_entry (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
+ bfd_vma value, bfd_boolean external)
+{
+ struct mips_got_entry *entry;
+
+ /* GOT16 relocations against local symbols are followed by a LO16
+ relocation; those against global symbols are not. Thus if the
+ symbol was originally local, the GOT16 relocation should load the
+ equivalent of %hi(VALUE), otherwise it should load VALUE itself. */
+ if (! external)
+ value = mips_elf_high (value) << RISCV_IMM_BITS;
+
+ /* It doesn't matter whether the original relocation was R_RISCV_GOT16,
+ R_MIPS16_GOT16, R_RISCV_CALL16, etc. The format of the entry is the
+ same in all cases. */
+ entry = mips_elf_create_local_got_entry (abfd, info, ibfd, value, 0,
+ NULL, R_RISCV_GOT16);
+ if (entry)
+ return entry->gotidx;
+ else
+ return MINUS_ONE;
+}
+
+/* Returns the offset for the entry at the INDEXth position
+ in the GOT. */
+
+static bfd_vma
+mips_elf_got_offset_from_index (struct bfd_link_info *info, bfd *output_bfd,
+ bfd_vma got_index)
+{
+ struct mips_elf_link_hash_table *htab;
+ asection *sgot;
+ bfd_vma gp;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ sgot = htab->sgot;
+ gp = _bfd_get_gp_value (output_bfd);
+
+ return sgot->output_section->vma + sgot->output_offset + got_index - gp;
+}
+
+/* Create and return a local GOT entry for VALUE, which was calculated
+ from a symbol belonging to INPUT_SECTON. Return NULL if it could not
+ be created. If R_SYMNDX refers to a TLS symbol, create a TLS entry
+ instead. */
+
+static struct mips_got_entry *
+mips_elf_create_local_got_entry (bfd *abfd, struct bfd_link_info *info,
+ bfd *ibfd, bfd_vma value,
+ unsigned long r_symndx,
+ struct mips_elf_link_hash_entry *h,
+ int r_type)
+{
+ struct mips_got_entry entry, **loc;
+ struct mips_got_info *g;
+ struct mips_elf_link_hash_table *htab;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ entry.abfd = NULL;
+ entry.symndx = -1;
+ entry.d.address = value;
+ entry.tls_type = 0;
+
+ g = htab->got_info;
+
+ /* This function shouldn't be called for symbols that live in the global
+ area of the GOT. */
+ BFD_ASSERT (h == NULL || h->global_got_area == GGA_NONE);
+ if (TLS_RELOC_P (r_type))
+ {
+ struct mips_got_entry *p;
+
+ entry.abfd = ibfd;
+ if (TLS_LDM_RELOC_P(r_type))
+ {
+ entry.tls_type = GOT_TLS_LDM;
+ entry.symndx = 0;
+ entry.d.addend = 0;
+ }
+ else if (h == NULL)
+ {
+ entry.symndx = r_symndx;
+ entry.d.addend = 0;
+ }
+ else
+ entry.d.h = h;
+
+ p = (struct mips_got_entry *)
+ htab_find (g->got_entries, &entry);
+
+ BFD_ASSERT (p);
+ return p;
+ }
+
+ loc = (struct mips_got_entry **) htab_find_slot (g->got_entries, &entry,
+ INSERT);
+ if (*loc)
+ return *loc;
+
+ entry.gotidx = MIPS_ELF_GOT_SIZE (abfd) * g->assigned_gotno++;
+ entry.tls_type = 0;
+
+ *loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
+
+ if (! *loc)
+ return NULL;
+
+ memcpy (*loc, &entry, sizeof entry);
+
+ if (g->assigned_gotno > g->local_gotno)
+ {
+ (*loc)->gotidx = -1;
+ /* We didn't allocate enough space in the GOT. */
+ (*_bfd_error_handler)
+ (_("not enough GOT space for local GOT entries"));
+ bfd_set_error (bfd_error_bad_value);
+ return NULL;
+ }
+
+ MIPS_ELF_PUT_WORD (abfd, value,
+ (htab->sgot->contents + entry.gotidx));
+
+ return *loc;
+}
+
+/* Return the number of dynamic section symbols required by OUTPUT_BFD.
+ The number might be exact or a worst-case estimate, depending on how
+ much information is available to elf_backend_omit_section_dynsym at
+ the current linking stage. */
+
+static bfd_size_type
+count_section_dynsyms (bfd *output_bfd, struct bfd_link_info *info)
+{
+ bfd_size_type count;
+
+ count = 0;
+ if (info->shared || elf_hash_table (info)->is_relocatable_executable)
+ {
+ asection *p;
+ const struct elf_backend_data *bed;
+
+ bed = get_elf_backend_data (output_bfd);
+ for (p = output_bfd->sections; p ; p = p->next)
+ if ((p->flags & SEC_EXCLUDE) == 0
+ && (p->flags & SEC_ALLOC) != 0
+ && !(*bed->elf_backend_omit_section_dynsym) (output_bfd, info, p))
+ ++count;
+ }
+ return count;
+}
+
+/* Sort the dynamic symbol table so that symbols that need GOT entries
+ appear towards the end. */
+
+static bfd_boolean
+mips_elf_sort_hash_table (bfd *abfd, struct bfd_link_info *info)
+{
+ struct mips_elf_link_hash_table *htab;
+ struct mips_elf_hash_sort_data hsd;
+ struct mips_got_info *g;
+
+ if (elf_hash_table (info)->dynsymcount == 0)
+ return TRUE;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ g = htab->got_info;
+ if (g == NULL)
+ return TRUE;
+
+ hsd.low = NULL;
+ hsd.max_unref_got_dynindx
+ = hsd.min_got_dynindx
+ = (elf_hash_table (info)->dynsymcount - g->reloc_only_gotno);
+ hsd.max_non_got_dynindx = count_section_dynsyms (abfd, info) + 1;
+ mips_elf_link_hash_traverse (((struct mips_elf_link_hash_table *)
+ elf_hash_table (info)),
+ mips_elf_sort_hash_table_f,
+ &hsd);
+
+ /* There should have been enough room in the symbol table to
+ accommodate both the GOT and non-GOT symbols. */
+ BFD_ASSERT (hsd.max_non_got_dynindx <= hsd.min_got_dynindx);
+ BFD_ASSERT ((unsigned long) hsd.max_unref_got_dynindx
+ == elf_hash_table (info)->dynsymcount);
+ BFD_ASSERT (elf_hash_table (info)->dynsymcount - hsd.min_got_dynindx
+ == g->global_gotno);
+
+ /* Now we know which dynamic symbol has the lowest dynamic symbol
+ table index in the GOT. */
+ g->global_gotsym = hsd.low;
+
+ return TRUE;
+}
+
+/* If H needs a GOT entry, assign it the highest available dynamic
+ index. Otherwise, assign it the lowest available dynamic
+ index. */
+
+static bfd_boolean
+mips_elf_sort_hash_table_f (struct mips_elf_link_hash_entry *h, void *data)
+{
+ struct mips_elf_hash_sort_data *hsd = data;
+
+ if (h->root.root.type == bfd_link_hash_warning)
+ h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
+
+ /* Symbols without dynamic symbol table entries aren't interesting
+ at all. */
+ if (h->root.dynindx == -1)
+ return TRUE;
+
+ switch (h->global_got_area)
+ {
+ case GGA_NONE:
+ h->root.dynindx = hsd->max_non_got_dynindx++;
+ break;
+
+ case GGA_NORMAL:
+ BFD_ASSERT (h->tls_type == GOT_NORMAL);
+
+ h->root.dynindx = --hsd->min_got_dynindx;
+ hsd->low = (struct elf_link_hash_entry *) h;
+ break;
+
+ case GGA_RELOC_ONLY:
+ BFD_ASSERT (h->tls_type == GOT_NORMAL);
+
+ if (hsd->max_unref_got_dynindx == hsd->min_got_dynindx)
+ hsd->low = (struct elf_link_hash_entry *) h;
+ h->root.dynindx = hsd->max_unref_got_dynindx++;
+ break;
+ }
+
+ return TRUE;
+}
+
+/* If H is a symbol that needs a global GOT entry, but has a dynamic
+ symbol table index lower than any we've seen to date, record it for
+ posterity. FOR_CALL is true if the caller is only interested in
+ using the GOT entry for calls. */
+
+static bfd_boolean
+mips_elf_record_global_got_symbol (struct elf_link_hash_entry *h,
+ bfd *abfd, struct bfd_link_info *info,
+ unsigned char tls_flag)
+{
+ struct mips_elf_link_hash_table *htab;
+ struct mips_elf_link_hash_entry *hmips;
+ struct mips_got_entry entry, **loc;
+ struct mips_got_info *g;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ hmips = (struct mips_elf_link_hash_entry *) h;
+
+ /* A global symbol in the GOT must also be in the dynamic symbol
+ table. */
+ if (h->dynindx == -1)
+ {
+ switch (ELF_ST_VISIBILITY (h->other))
+ {
+ case STV_INTERNAL:
+ case STV_HIDDEN:
+ _bfd_elf_link_hash_hide_symbol (info, h, TRUE);
+ break;
+ }
+ if (!bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+ }
+
+ /* Make sure we have a GOT to put this entry into. */
+ g = htab->got_info;
+ BFD_ASSERT (g != NULL);
+
+ entry.abfd = abfd;
+ entry.symndx = -1;
+ entry.d.h = (struct mips_elf_link_hash_entry *) h;
+ entry.tls_type = 0;
+
+ loc = (struct mips_got_entry **) htab_find_slot (g->got_entries, &entry,
+ INSERT);
+
+ /* If we've already marked this entry as needing GOT space, we don't
+ need to do it again. */
+ if (*loc)
+ {
+ (*loc)->tls_type |= tls_flag;
+ return TRUE;
+ }
+
+ *loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
+
+ if (! *loc)
+ return FALSE;
+
+ entry.gotidx = -1;
+ entry.tls_type = tls_flag;
+
+ memcpy (*loc, &entry, sizeof entry);
+
+ if (tls_flag == 0)
+ hmips->global_got_area = GGA_NORMAL;
+
+ return TRUE;
+}
+
+/* Reserve space in G for a GOT entry containing the value of symbol
+ SYMNDX in input bfd ABDF, plus ADDEND. */
+
+static bfd_boolean
+mips_elf_record_local_got_symbol (bfd *abfd, long symndx, bfd_vma addend,
+ struct bfd_link_info *info,
+ unsigned char tls_flag)
+{
+ struct mips_elf_link_hash_table *htab;
+ struct mips_got_info *g;
+ struct mips_got_entry entry, **loc;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ g = htab->got_info;
+ BFD_ASSERT (g != NULL);
+
+ entry.abfd = abfd;
+ entry.symndx = symndx;
+ entry.d.addend = addend;
+ entry.tls_type = tls_flag;
+ loc = (struct mips_got_entry **)
+ htab_find_slot (g->got_entries, &entry, INSERT);
+
+ if (*loc)
+ {
+ if (tls_flag == GOT_TLS_GD && !((*loc)->tls_type & GOT_TLS_GD))
+ {
+ g->tls_gotno += 2;
+ (*loc)->tls_type |= tls_flag;
+ }
+ else if (tls_flag == GOT_TLS_IE && !((*loc)->tls_type & GOT_TLS_IE))
+ {
+ g->tls_gotno += 1;
+ (*loc)->tls_type |= tls_flag;
+ }
+ return TRUE;
+ }
+
+ if (tls_flag != 0)
+ {
+ entry.gotidx = -1;
+ entry.tls_type = tls_flag;
+ if (tls_flag == GOT_TLS_IE)
+ g->tls_gotno += 1;
+ else if (tls_flag == GOT_TLS_GD)
+ g->tls_gotno += 2;
+ else if (g->tls_ldm_offset == MINUS_ONE)
+ {
+ g->tls_ldm_offset = MINUS_TWO;
+ g->tls_gotno += 2;
+ }
+ }
+ else
+ {
+ entry.gotidx = g->local_gotno++;
+ entry.tls_type = 0;
+ }
+
+ *loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
+
+ if (! *loc)
+ return FALSE;
+
+ memcpy (*loc, &entry, sizeof entry);
+
+ return TRUE;
+}
+
+/* Return the maximum number of GOT page entries required for RANGE. */
+
+static bfd_vma
+mips_elf_pages_for_range (const struct mips_got_page_range *range)
+{
+ return (range->max_addend - range->min_addend + RISCV_IMM_REACH-1) >> RISCV_IMM_BITS;
+}
+
+/* Record that ABFD has a page relocation against symbol SYMNDX and
+ that ADDEND is the addend for that relocation.
+
+ This function creates an upper bound on the number of GOT slots
+ required; no attempt is made to combine references to non-overridable
+ global symbols across multiple input files. */
+
+static bfd_boolean
+mips_elf_record_got_page_entry (struct bfd_link_info *info, bfd *abfd,
+ long symndx, bfd_signed_vma addend)
+{
+ struct mips_elf_link_hash_table *htab;
+ struct mips_got_info *g;
+ struct mips_got_page_entry lookup, *entry;
+ struct mips_got_page_range **range_ptr, *range;
+ bfd_vma old_pages, new_pages;
+ void **loc;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ g = htab->got_info;
+ BFD_ASSERT (g != NULL);
+
+ /* Find the mips_got_page_entry hash table entry for this symbol. */
+ lookup.abfd = abfd;
+ lookup.symndx = symndx;
+ loc = htab_find_slot (g->got_page_entries, &lookup, INSERT);
+ if (loc == NULL)
+ return FALSE;
+
+ /* Create a mips_got_page_entry if this is the first time we've
+ seen the symbol. */
+ entry = (struct mips_got_page_entry *) *loc;
+ if (!entry)
+ {
+ entry = bfd_alloc (abfd, sizeof (*entry));
+ if (!entry)
+ return FALSE;
+
+ entry->abfd = abfd;
+ entry->symndx = symndx;
+ entry->ranges = NULL;
+ entry->num_pages = 0;
+ *loc = entry;
+ }
+
+ /* Skip over ranges whose maximum extent cannot share a page entry
+ with ADDEND. */
+ range_ptr = &entry->ranges;
+ while (*range_ptr && addend > (*range_ptr)->max_addend + RISCV_IMM_REACH/2-1)
+ range_ptr = &(*range_ptr)->next;
+
+ /* If we scanned to the end of the list, or found a range whose
+ minimum extent cannot share a page entry with ADDEND, create
+ a new singleton range. */
+ range = *range_ptr;
+ if (!range || addend < range->min_addend - (RISCV_IMM_REACH/2-1))
+ {
+ range = bfd_alloc (abfd, sizeof (*range));
+ if (!range)
+ return FALSE;
+
+ range->next = *range_ptr;
+ range->min_addend = addend;
+ range->max_addend = addend;
+
+ *range_ptr = range;
+ entry->num_pages++;
+ g->page_gotno++;
+ return TRUE;
+ }
+
+ /* Remember how many pages the old range contributed. */
+ old_pages = mips_elf_pages_for_range (range);
+
+ /* Update the ranges. */
+ if (addend < range->min_addend)
+ range->min_addend = addend;
+ else if (addend > range->max_addend)
+ {
+ if (range->next && addend >= range->next->min_addend - (RISCV_IMM_REACH/2-1))
+ {
+ old_pages += mips_elf_pages_for_range (range->next);
+ range->max_addend = range->next->max_addend;
+ range->next = range->next->next;
+ }
+ else
+ range->max_addend = addend;
+ }
+
+ /* Record any change in the total estimate. */
+ new_pages = mips_elf_pages_for_range (range);
+ if (old_pages != new_pages)
+ {
+ entry->num_pages += new_pages - old_pages;
+ g->page_gotno += new_pages - old_pages;
+ }
+
+ return TRUE;
+}
+
+/* Add room for N relocations to the .rel(a).dyn section in ABFD. */
+
+static void
+mips_elf_allocate_dynamic_relocations (bfd *abfd, struct bfd_link_info *info,
+ unsigned int n)
+{
+ asection *s;
+ struct mips_elf_link_hash_table *htab;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ s = mips_elf_rel_dyn_section (info, FALSE);
+ BFD_ASSERT (s != NULL);
+
+ if (s->size == 0)
+ {
+ /* Make room for a null element. */
+ s->size += MIPS_ELF_REL_SIZE (abfd);
+ ++s->reloc_count;
+ }
+ s->size += n * MIPS_ELF_REL_SIZE (abfd);
+}
+
+/* A htab_traverse callback for GOT entries. Set boolean *DATA to true
+ if the GOT entry is for an indirect or warning symbol. */
+
+static int
+mips_elf_check_recreate_got (void **entryp, void *data)
+{
+ struct mips_got_entry *entry;
+ bfd_boolean *must_recreate;
+
+ entry = (struct mips_got_entry *) *entryp;
+ must_recreate = (bfd_boolean *) data;
+ if (entry->abfd != NULL && entry->symndx == -1)
+ {
+ struct mips_elf_link_hash_entry *h;
+
+ h = entry->d.h;
+ if (h->root.root.type == bfd_link_hash_indirect
+ || h->root.root.type == bfd_link_hash_warning)
+ {
+ *must_recreate = TRUE;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* A htab_traverse callback for GOT entries. Add all entries to
+ hash table *DATA, converting entries for indirect and warning
+ symbols into entries for the target symbol. Set *DATA to null
+ on error. */
+
+static int
+mips_elf_recreate_got (void **entryp, void *data)
+{
+ htab_t *new_got;
+ struct mips_got_entry *entry;
+ void **slot;
+
+ new_got = (htab_t *) data;
+ entry = (struct mips_got_entry *) *entryp;
+ if (entry->abfd != NULL && entry->symndx == -1)
+ {
+ struct mips_elf_link_hash_entry *h;
+
+ h = entry->d.h;
+ while (h->root.root.type == bfd_link_hash_indirect
+ || h->root.root.type == bfd_link_hash_warning)
+ {
+ BFD_ASSERT (h->global_got_area == GGA_NONE);
+ h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
+ }
+ entry->d.h = h;
+ }
+ slot = htab_find_slot (*new_got, entry, INSERT);
+ if (slot == NULL)
+ {
+ *new_got = NULL;
+ return 0;
+ }
+ if (*slot == NULL)
+ *slot = entry;
+ else
+ free (entry);
+ return 1;
+}
+
+/* If any entries in G->got_entries are for indirect or warning symbols,
+ replace them with entries for the target symbol. */
+
+static bfd_boolean
+mips_elf_resolve_final_got_entries (struct mips_got_info *g)
+{
+ bfd_boolean must_recreate;
+ htab_t new_got;
+
+ must_recreate = FALSE;
+ htab_traverse (g->got_entries, mips_elf_check_recreate_got, &must_recreate);
+ if (must_recreate)
+ {
+ new_got = htab_create (htab_size (g->got_entries),
+ mips_elf_got_entry_hash,
+ mips_elf_got_entry_eq, NULL);
+ htab_traverse (g->got_entries, mips_elf_recreate_got, &new_got);
+ if (new_got == NULL)
+ return FALSE;
+
+ /* Each entry in g->got_entries has either been copied to new_got
+ or freed. Now delete the hash table itself. */
+ htab_delete (g->got_entries);
+ g->got_entries = new_got;
+ }
+ return TRUE;
+}
+
+/* A mips_elf_link_hash_traverse callback for which DATA points
+ to the link_info structure. Count the number of type (3) entries
+ in the master GOT. */
+
+static int
+mips_elf_count_got_symbols (struct mips_elf_link_hash_entry *h, void *data)
+{
+ struct bfd_link_info *info;
+ struct mips_elf_link_hash_table *htab;
+ struct mips_got_info *g;
+
+ info = (struct bfd_link_info *) data;
+ htab = mips_elf_hash_table (info);
+ g = htab->got_info;
+ if (h->global_got_area != GGA_NONE)
+ {
+ /* Make a final decision about whether the symbol belongs in the
+ local or global GOT. Symbols that bind locally can (and in the
+ case of forced-local symbols, must) live in the local GOT.
+ Those that are aren't in the dynamic symbol table must also
+ live in the local GOT.
+
+ Note that the former condition does not always imply the
+ latter: symbols do not bind locally if they are completely
+ undefined. We'll report undefined symbols later if appropriate. */
+ if (h->root.dynindx == -1 || SYMBOL_REFERENCES_LOCAL (info, &h->root))
+ {
+ /* The symbol belongs in the local GOT. We no longer need this
+ entry if it was only used for relocations; those relocations
+ will be against the null or section symbol instead of H. */
+ if (h->global_got_area != GGA_RELOC_ONLY)
+ g->local_gotno++;
+ h->global_got_area = GGA_NONE;
+ }
+ else
+ {
+ g->global_gotno++;
+ if (h->global_got_area == GGA_RELOC_ONLY)
+ g->reloc_only_gotno++;
+ }
+ }
+ return 1;
+}
+
+/* Set the TLS GOT index for the GOT entry in ENTRYP. ENTRYP's NEXT field
+ is null iff there is just a single GOT. */
+
+static int
+mips_elf_initialize_tls_index (void **entryp, void *p)
+{
+ struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
+ struct mips_got_info *g = p;
+ bfd_vma next_index;
+ unsigned char tls_type;
+
+ /* We're only interested in TLS symbols. */
+ if (entry->tls_type == 0)
+ return 1;
+
+ next_index = MIPS_ELF_GOT_SIZE (entry->abfd) * (long) g->tls_assigned_gotno;
+
+ if (entry->symndx == -1)
+ {
+ /* A type (3) got entry in the single-GOT case. We use the symbol's
+ hash table entry to track its index. */
+ if (entry->d.h->tls_type & GOT_TLS_OFFSET_DONE)
+ return 1;
+ entry->d.h->tls_type |= GOT_TLS_OFFSET_DONE;
+ entry->d.h->tls_got_offset = next_index;
+ tls_type = entry->d.h->tls_type;
+ }
+ else
+ {
+ if (entry->tls_type & GOT_TLS_LDM)
+ {
+ /* There are separate mips_got_entry objects for each input bfd
+ that requires an LDM entry. Make sure that all LDM entries in
+ a GOT resolve to the same index. */
+ if (g->tls_ldm_offset != MINUS_TWO && g->tls_ldm_offset != MINUS_ONE)
+ {
+ entry->gotidx = g->tls_ldm_offset;
+ return 1;
+ }
+ g->tls_ldm_offset = next_index;
+ }
+ entry->gotidx = next_index;
+ tls_type = entry->tls_type;
+ }
+
+ /* Account for the entries we've just allocated. */
+ if (tls_type & (GOT_TLS_GD | GOT_TLS_LDM))
+ g->tls_assigned_gotno += 2;
+ if (tls_type & GOT_TLS_IE)
+ g->tls_assigned_gotno += 1;
+
+ return 1;
+}
+
+/* Returns the first relocation of type r_type found, beginning with
+ RELOCATION. RELEND is one-past-the-end of the relocation table. */
+
+static const Elf_Internal_Rela *
+mips_elf_next_relocation (bfd *abfd ATTRIBUTE_UNUSED, unsigned int r_type,
+ const Elf_Internal_Rela *relocation,
+ const Elf_Internal_Rela *relend)
+{
+ unsigned long r_symndx = ELF_R_SYM (abfd, relocation->r_info);
+
+ while (relocation < relend)
+ {
+ if (ELF_R_TYPE (abfd, relocation->r_info) == r_type
+ && ELF_R_SYM (abfd, relocation->r_info) == r_symndx)
+ return relocation;
+
+ ++relocation;
+ }
+
+ /* We didn't find it. */
+ return NULL;
+}
+
+/* Return whether an input relocation is against a local symbol. */
+
+static bfd_boolean
+mips_elf_local_relocation_p (bfd *input_bfd,
+ const Elf_Internal_Rela *relocation,
+ asection **local_sections)
+{
+ unsigned long r_symndx;
+ Elf_Internal_Shdr *symtab_hdr;
+ size_t extsymoff;
+
+ r_symndx = ELF_R_SYM (input_bfd, relocation->r_info);
+ symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+ extsymoff = (elf_bad_symtab (input_bfd)) ? 0 : symtab_hdr->sh_info;
+
+ if (r_symndx < extsymoff)
+ return TRUE;
+ if (elf_bad_symtab (input_bfd) && local_sections[r_symndx] != NULL)
+ return TRUE;
+
+ return FALSE;
+}
+
+/* Sign-extend VALUE, which has the indicated number of BITS. */
+
+bfd_vma
+_bfd_riscv_elf_sign_extend (bfd_vma value, int bits)
+{
+ if (value & ((bfd_vma) 1 << (bits - 1)))
+ /* VALUE is negative. */
+ value |= ((bfd_vma) - 1) << bits;
+
+ return value;
+}
+
+/* Return non-zero if the indicated VALUE has overflowed the maximum
+ range expressible by a signed number with the indicated number of
+ BITS. */
+
+static bfd_boolean
+mips_elf_overflow_p (bfd_vma value, int bits)
+{
+ bfd_signed_vma svalue = (bfd_signed_vma) value;
+
+ if (svalue > (1 << (bits - 1)) - 1)
+ /* The value is too big. */
+ return TRUE;
+ else if (svalue < -(1 << (bits - 1)))
+ /* The value is too small. */
+ return TRUE;
+
+ /* All is well. */
+ return FALSE;
+}
+
+/* Create the .got section to hold the global offset table. */
+
+static bfd_boolean
+mips_elf_create_got_section (bfd *abfd, struct bfd_link_info *info)
+{
+ flagword flags;
+ register asection *s;
+ struct elf_link_hash_entry *h;
+ struct bfd_link_hash_entry *bh;
+ struct mips_got_info *g;
+ bfd_size_type amt;
+ struct mips_elf_link_hash_table *htab;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ /* This function may be called more than once. */
+ if (htab->sgot)
+ return TRUE;
+
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+ | SEC_LINKER_CREATED);
+
+ /* We have to use an alignment of 2**4 here because this is hardcoded
+ in the function stub generation and in the linker script. */
+ s = bfd_make_section_with_flags (abfd, ".got", flags);
+ if (s == NULL
+ || ! bfd_set_section_alignment (abfd, s, 4))
+ return FALSE;
+ htab->sgot = s;
+
+ /* Define the symbol _GLOBAL_OFFSET_TABLE_. 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. */
+ bh = NULL;
+ if (! (_bfd_generic_link_add_one_symbol
+ (info, abfd, "_GLOBAL_OFFSET_TABLE_", BSF_GLOBAL, s,
+ 0, NULL, FALSE, get_elf_backend_data (abfd)->collect, &bh)))
+ return FALSE;
+
+ h = (struct elf_link_hash_entry *) bh;
+ h->non_elf = 0;
+ h->def_regular = 1;
+ h->type = STT_OBJECT;
+ elf_hash_table (info)->hgot = h;
+
+ if (info->shared
+ && ! bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+
+ amt = sizeof (struct mips_got_info);
+ g = bfd_alloc (abfd, amt);
+ if (g == NULL)
+ return FALSE;
+ g->global_gotsym = NULL;
+ g->global_gotno = 0;
+ g->reloc_only_gotno = 0;
+ g->tls_gotno = 0;
+ g->local_gotno = 0;
+ g->page_gotno = 0;
+ g->assigned_gotno = 0;
+ g->tls_ldm_offset = MINUS_ONE;
+ g->got_entries = htab_try_create (1, mips_elf_got_entry_hash,
+ mips_elf_got_entry_eq, NULL);
+ if (g->got_entries == NULL)
+ return FALSE;
+ g->got_page_entries = htab_try_create (1, mips_got_page_entry_hash,
+ mips_got_page_entry_eq, NULL);
+ if (g->got_page_entries == NULL)
+ return FALSE;
+ htab->got_info = g;
+ mips_elf_section_data (s)->elf.this_hdr.sh_flags |= SHF_ALLOC | SHF_WRITE;
+
+ /* We also need a .got.plt section when generating PLTs. */
+ s = bfd_make_section_with_flags (abfd, ".got.plt",
+ SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
+ | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+ if (s == NULL)
+ return FALSE;
+ htab->sgotplt = s;
+
+ return TRUE;
+}
+
+/* Calculate the value produced by the RELOCATION (which comes from
+ the INPUT_BFD). The ADDEND is the addend to use for this
+ RELOCATION; RELOCATION->R_ADDEND is ignored.
+
+ The result of the relocation calculation is stored in VALUEP.
+ On exit, set *CROSS_MODE_JUMP_P to true if the relocation field
+ is a MIPS16 jump to non-MIPS16 code, or vice versa.
+
+ This function returns bfd_reloc_continue if the caller need take no
+ further action regarding this relocation, bfd_reloc_notsupported if
+ something goes dramatically wrong, bfd_reloc_overflow if an
+ overflow occurs, and bfd_reloc_ok to indicate success. */
+
+static bfd_reloc_status_type
+mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
+ asection *input_section,
+ struct bfd_link_info *info,
+ const Elf_Internal_Rela *relocation,
+ bfd_vma addend, reloc_howto_type *howto,
+ Elf_Internal_Sym *local_syms,
+ asection **local_sections, bfd_vma *valuep,
+ const char **namep,
+ bfd_boolean save_addend)
+{
+ /* The eventual value we will return. */
+ bfd_vma value;
+ /* The address of the symbol against which the relocation is
+ occurring. */
+ bfd_vma symbol = 0;
+ /* The final GP value to be used for the relocatable, executable, or
+ shared object file being produced. */
+ bfd_vma gp;
+ /* The place (section offset or address) of the storage unit being
+ relocated. */
+ bfd_vma p;
+ /* The value of GP used to create the relocatable object. */
+ bfd_vma gp0;
+ /* The offset into the global offset table at which the address of
+ the relocation entry symbol, adjusted by the addend, resides
+ during execution. */
+ bfd_vma g = MINUS_ONE;
+ /* The section in which the symbol referenced by the relocation is
+ located. */
+ asection *sec = NULL;
+ struct mips_elf_link_hash_entry *h = NULL;
+ /* TRUE if the symbol referred to by this relocation is a local
+ symbol. */
+ bfd_boolean local_p, was_local_p;
+ Elf_Internal_Shdr *symtab_hdr;
+ size_t extsymoff;
+ unsigned long r_symndx;
+ int r_type;
+ /* TRUE if overflow occurred during the calculation of the
+ relocation value. */
+ bfd_boolean overflowed_p;
+ /* TRUE if this relocation refers to a MIPS16 function. */
+ struct mips_elf_link_hash_table *htab;
+ bfd *dynobj;
+
+ dynobj = elf_hash_table (info)->dynobj;
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ /* Parse the relocation. */
+ r_symndx = ELF_R_SYM (input_bfd, relocation->r_info);
+ r_type = ELF_R_TYPE (input_bfd, relocation->r_info);
+ p = (input_section->output_section->vma
+ + input_section->output_offset
+ + relocation->r_offset);
+
+ /* Assume that there will be no overflow. */
+ overflowed_p = FALSE;
+
+ /* Figure out whether or not the symbol is local, and get the offset
+ used in the array of hash table entries. */
+ symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+ local_p = mips_elf_local_relocation_p (input_bfd, relocation,
+ local_sections);
+ was_local_p = local_p;
+ if (! elf_bad_symtab (input_bfd))
+ extsymoff = symtab_hdr->sh_info;
+ else
+ {
+ /* The symbol table does not follow the rule that local symbols
+ must come before globals. */
+ extsymoff = 0;
+ }
+
+ /* Figure out the value of the symbol. */
+ if (local_p)
+ {
+ Elf_Internal_Sym *sym;
+
+ sym = local_syms + r_symndx;
+ sec = local_sections[r_symndx];
+
+ symbol = sec->output_section->vma + sec->output_offset;
+ if (ELF_ST_TYPE (sym->st_info) != STT_SECTION
+ || (sec->flags & SEC_MERGE))
+ symbol += sym->st_value;
+ if ((sec->flags & SEC_MERGE)
+ && ELF_ST_TYPE (sym->st_info) == STT_SECTION)
+ {
+ addend = _bfd_elf_rel_local_sym (abfd, sym, &sec, addend);
+ addend -= symbol;
+ addend += sec->output_section->vma + sec->output_offset;
+ }
+
+ /* Record the name of this symbol, for our caller. */
+ *namep = bfd_elf_string_from_elf_section (input_bfd,
+ symtab_hdr->sh_link,
+ sym->st_name);
+ if (*namep == '\0')
+ *namep = bfd_section_name (input_bfd, sec);
+ }
+ else
+ {
+ /* ??? Could we use RELOC_FOR_GLOBAL_SYMBOL here ? */
+
+ /* For global symbols we look up the symbol in the hash-table. */
+ h = ((struct mips_elf_link_hash_entry *)
+ elf_sym_hashes (input_bfd) [r_symndx - extsymoff]);
+ /* Find the real hash-table entry for this symbol. */
+ while (h->root.root.type == bfd_link_hash_indirect
+ || h->root.root.type == bfd_link_hash_warning)
+ h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
+
+ /* Record the name of this symbol, for our caller. */
+ *namep = h->root.root.root.string;
+
+ /* If this symbol is defined, calculate its address. Note that
+ _gp_disp is a magic symbol, always implicitly defined by the
+ linker, so it's inappropriate to check to see whether or not
+ its defined. */
+ if ((h->root.root.type == bfd_link_hash_defined
+ || h->root.root.type == bfd_link_hash_defweak)
+ && h->root.root.u.def.section)
+ {
+ sec = h->root.root.u.def.section;
+ if (sec->output_section)
+ symbol = (h->root.root.u.def.value
+ + sec->output_section->vma
+ + sec->output_offset);
+ else
+ symbol = h->root.root.u.def.value;
+ }
+ else if (h->root.root.type == bfd_link_hash_undefweak)
+ /* We allow relocations against undefined weak symbols, giving
+ it the value zero, so that you can undefined weak functions
+ and check to see if they exist by looking at their
+ addresses. */
+ symbol = 0;
+ else if (info->unresolved_syms_in_objects == RM_IGNORE
+ && ELF_ST_VISIBILITY (h->root.other) == STV_DEFAULT)
+ symbol = 0;
+ else if (strcmp (*namep, "_DYNAMIC_LINKING") == 0)
+ {
+ /* If this is a dynamic link, we should have created a
+ _DYNAMIC_LINKING symbol
+ in in _bfd_riscv_elf_create_dynamic_sections.
+ Otherwise, we should define the symbol with a value of 0.
+ FIXME: It should probably get into the symbol table
+ somehow as well. */
+ BFD_ASSERT (! info->shared);
+ BFD_ASSERT (bfd_get_section_by_name (abfd, ".dynamic") == NULL);
+ symbol = 0;
+ }
+ else if ((*info->callbacks->undefined_symbol)
+ (info, h->root.root.root.string, input_bfd,
+ input_section, relocation->r_offset,
+ (info->unresolved_syms_in_objects == RM_GENERATE_ERROR)
+ || ELF_ST_VISIBILITY (h->root.other)))
+ {
+ return bfd_reloc_undefined;
+ }
+ else
+ {
+ return bfd_reloc_notsupported;
+ }
+ }
+
+ local_p = h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->root);
+
+ gp0 = _bfd_get_gp_value (input_bfd);
+ gp = _bfd_get_gp_value (abfd);
+
+ /* If we haven't already determined the GOT offset, and we're going
+ to need it, get it now. */
+ switch (r_type)
+ {
+ case R_MIPS16_CALL16:
+ case R_MIPS16_GOT16:
+ case R_RISCV_CALL16:
+ case R_RISCV_GOT16:
+ case R_RISCV_GOT_DISP:
+ case R_RISCV_GOT_HI16:
+ case R_RISCV_CALL_HI16:
+ case R_RISCV_GOT_LO16:
+ case R_RISCV_CALL_LO16:
+ case R_RISCV_TLS_GD:
+ case R_RISCV_TLS_GD_HI16:
+ case R_RISCV_TLS_GD_LO16:
+ case R_RISCV_TLS_GOTTPREL:
+ case R_RISCV_TLS_GOT_HI16:
+ case R_RISCV_TLS_GOT_LO16:
+ case R_RISCV_TLS_LDM:
+ case R_RISCV_TLS_LDM_HI16:
+ case R_RISCV_TLS_LDM_LO16:
+ /* Find the index into the GOT where this value is located. */
+ if (TLS_LDM_RELOC_P(r_type))
+ {
+ g = mips_elf_local_got_index (abfd, input_bfd, info,
+ 0, 0, NULL, r_type);
+ if (g == MINUS_ONE)
+ return bfd_reloc_outofrange;
+ }
+ else if (!local_p)
+ {
+ BFD_ASSERT (addend == 0);
+ g = mips_elf_global_got_index (dynobj, &h->root, r_type, info);
+ if (h->tls_type == GOT_NORMAL
+ && !elf_hash_table (info)->dynamic_sections_created)
+ /* This is a static link. We must initialize the GOT entry. */
+ MIPS_ELF_PUT_WORD (dynobj, symbol, htab->sgot->contents + g);
+ }
+ else if (call16_reloc_p (r_type) || got16_reloc_p (r_type))
+ /* The calculation below does not involve "g". */
+ break;
+ else
+ {
+ g = mips_elf_local_got_index (abfd, input_bfd, info,
+ symbol + addend, r_symndx, h, r_type);
+ if (g == MINUS_ONE)
+ return bfd_reloc_outofrange;
+ }
+
+ /* Convert GOT indices to actual offsets. */
+ g = mips_elf_got_offset_from_index (info, abfd, g);
+ break;
+ }
+
+ /* Figure out what kind of relocation is being performed. */
+ switch (r_type)
+ {
+ case R_RISCV_NONE:
+ return bfd_reloc_continue;
+
+ case R_RISCV_32:
+ case R_RISCV_REL32:
+ case R_RISCV_64:
+ if ((info->shared
+ || (htab->root.dynamic_sections_created
+ && h != NULL
+ && h->root.def_dynamic
+ && !h->root.def_regular
+ && !h->has_static_relocs))
+ && r_symndx != STN_UNDEF
+ && (h == NULL
+ || h->root.root.type != bfd_link_hash_undefweak
+ || ELF_ST_VISIBILITY (h->root.other) == STV_DEFAULT)
+ && (input_section->flags & SEC_ALLOC) != 0)
+ {
+ /* If we're creating a shared library, then we can't know
+ where the symbol will end up. So, we create a relocation
+ record in the output, and leave the job up to the dynamic
+ linker. We must do the same for executable references to
+ shared library symbols, unless we've decided to use copy
+ relocs or PLTs instead. */
+ value = addend;
+ if (!mips_elf_create_dynamic_relocation (abfd,
+ info,
+ relocation,
+ h,
+ sec,
+ symbol,
+ &value,
+ input_section))
+ return bfd_reloc_undefined;
+ }
+ else
+ {
+ if (r_type != R_RISCV_REL32)
+ value = symbol + addend;
+ else
+ value = addend;
+ }
+ value &= howto->dst_mask;
+ break;
+
+ case R_RISCV_PC32:
+ value = symbol + addend - p;
+ value &= howto->dst_mask;
+ break;
+
+ case R_RISCV_26:
+ if (symbol == 0)
+ {
+ /* Need to support JAL to address 0 to statically link against libc.
+ For now, implement as an infinite loop, "1: JAL 1b", but this is
+ incorrect and we should relax it to JALR ra, x0. */
+ value = 0;
+ }
+ else
+ {
+ value = symbol + _bfd_riscv_elf_sign_extend (addend, RISCV_JUMP_BITS+RISCV_JUMP_ALIGN_BITS) - p;
+ overflowed_p = mips_elf_overflow_p (value, RISCV_JUMP_BITS+RISCV_JUMP_ALIGN_BITS);
+ }
+ value >>= howto->rightshift;
+ value <<= OP_SH_TARGET;
+ value &= howto->dst_mask;
+ break;
+
+ case R_RISCV_TLS_DTPREL_HI16:
+ value = ((mips_elf_high (addend + symbol - dtprel_base (info))
+ << OP_SH_BIGIMMEDIATE) & howto->dst_mask);
+ break;
+
+ case R_RISCV_TLS_DTPREL_LO16:
+ case R_RISCV_TLS_DTPREL32:
+ case R_RISCV_TLS_DTPREL64:
+ value = ((addend + symbol - dtprel_base (info))
+ << OP_SH_IMMEDIATE) & howto->dst_mask;
+ break;
+
+ case R_RISCV_TLS_TPREL_HI16:
+ value = (mips_elf_high (addend + symbol - tprel_base (info))
+ << OP_SH_BIGIMMEDIATE) & howto->dst_mask;
+ break;
+
+ case R_RISCV_TLS_TPREL_LO16:
+ value = ((symbol + addend - tprel_base (info))
+ << OP_SH_IMMEDIATE) & howto->dst_mask;
+ break;
+
+ case R_RISCV_HI16:
+ case R_MIPS16_HI16:
+ value = mips_elf_high (addend + symbol) << OP_SH_BIGIMMEDIATE;
+ value &= howto->dst_mask;
+ break;
+
+ case R_RISCV_LO16:
+ case R_MIPS16_LO16:
+ value = ((symbol + addend) << OP_SH_IMMEDIATE) & howto->dst_mask;
+ break;
+
+ case R_RISCV_LITERAL:
+ /* Because we don't merge literal sections, we can handle this
+ just like R_RISCV_GPREL16. In the long run, we should merge
+ shared literals, and then we will need to additional work
+ here. */
+
+ /* Fall through. */
+
+ case R_MIPS16_GPREL:
+ /* The R_MIPS16_GPREL performs the same calculation as
+ R_RISCV_GPREL16, but stores the relocated bits in a different
+ order. We don't need to do anything special here; the
+ differences are handled in mips_elf_perform_relocation. */
+ case R_RISCV_GPREL16:
+ /* Only sign-extend the addend if it was extracted from the
+ instruction. If the addend was separate, leave it alone,
+ otherwise we may lose significant bits. */
+ if (howto->partial_inplace)
+ addend = _bfd_riscv_elf_sign_extend (addend, RISCV_IMM_BITS);
+ value = symbol + addend - gp;
+ /* If the symbol was local, any earlier relocatable links will
+ have adjusted its addend with the gp offset, so compensate
+ for that now. Don't do it for symbols forced local in this
+ link, though, since they won't have had the gp offset applied
+ to them before. */
+ if (was_local_p)
+ value += gp0;
+ overflowed_p = mips_elf_overflow_p (value, RISCV_IMM_BITS);
+ break;
+
+ case R_MIPS16_GOT16:
+ case R_MIPS16_CALL16:
+ case R_RISCV_GOT16:
+ case R_RISCV_CALL16:
+ if (local_p)
+ {
+ value = mips_elf_got16_entry (abfd, input_bfd, info,
+ symbol + addend, !was_local_p);
+ if (value == MINUS_ONE)
+ return bfd_reloc_outofrange;
+ value
+ = mips_elf_got_offset_from_index (info, abfd, value);
+ overflowed_p = mips_elf_overflow_p (value, RISCV_IMM_BITS);
+ value = (value << OP_SH_IMMEDIATE) & howto->dst_mask;
+ break;
+ }
+
+ /* Fall through. */
+
+ case R_RISCV_TLS_GD:
+ case R_RISCV_TLS_GOTTPREL:
+ case R_RISCV_TLS_LDM:
+ case R_RISCV_GOT_DISP:
+ value = g;
+ overflowed_p = mips_elf_overflow_p (value, RISCV_IMM_BITS);
+ value = (value << OP_SH_IMMEDIATE) & howto->dst_mask;
+ break;
+
+ case R_RISCV_GPREL32:
+ value = (addend + symbol + gp0 - gp);
+ if (!save_addend)
+ value &= howto->dst_mask;
+ break;
+
+ case R_RISCV_TLS_GOT_HI16:
+ case R_RISCV_TLS_GD_HI16:
+ case R_RISCV_TLS_LDM_HI16:
+ case R_RISCV_GOT_HI16:
+ case R_RISCV_CALL_HI16:
+ /* We're allowed to handle these two relocations identically.
+ The dynamic linker is allowed to handle the CALL relocations
+ differently by creating a lazy evaluation stub. */
+ value = (mips_elf_high (g - p + gp) << OP_SH_BIGIMMEDIATE);
+ break;
+
+ case R_RISCV_TLS_GOT_LO16:
+ case R_RISCV_TLS_GD_LO16:
+ case R_RISCV_TLS_LDM_LO16:
+ case R_RISCV_GOT_LO16:
+ case R_RISCV_CALL_LO16:
+ value = ((g - p + gp) << OP_SH_IMMEDIATE) & howto->dst_mask;
+ break;
+
+ case R_RISCV_SUB:
+ value = symbol - addend;
+ value &= howto->dst_mask;
+ break;
+
+ case R_RISCV_SCN_DISP:
+ value = symbol + addend - sec->output_offset;
+ value &= howto->dst_mask;
+ break;
+
+ case R_RISCV_PJUMP:
+ case R_RISCV_GNU_VTINHERIT:
+ case R_RISCV_GNU_VTENTRY:
+ /* We don't do anything with these at present. */
+ return bfd_reloc_continue;
+
+ default:
+ /* An unrecognized relocation type. */
+ return bfd_reloc_notsupported;
+ }
+
+ /* Store the VALUE for our caller. */
+ *valuep = value;
+ return overflowed_p ? bfd_reloc_overflow : bfd_reloc_ok;
+}
+
+/* Obtain the field relocated by RELOCATION. */
+
+static bfd_vma
+mips_elf_obtain_contents (reloc_howto_type *howto,
+ const Elf_Internal_Rela *relocation,
+ bfd *input_bfd, bfd_byte *contents)
+{
+ bfd_vma x;
+ bfd_byte *location = contents + relocation->r_offset;
+
+ /* Obtain the bytes. */
+ x = bfd_get ((8 * bfd_get_reloc_size (howto)), input_bfd, location);
+
+ return x;
+}
+
+/* It has been determined that the result of the RELOCATION is the
+ VALUE. Use HOWTO to place VALUE into the output file at the
+ appropriate position. The SECTION is the section to which the
+ relocation applies.
+ CROSS_MODE_JUMP_P is true if the relocation field
+ is a MIPS16 jump to non-MIPS16 code, or vice versa.
+
+ Returns FALSE if anything goes wrong. */
+
+static bfd_boolean
+mips_elf_perform_relocation (struct bfd_link_info *info ATTRIBUTE_UNUSED,
+ reloc_howto_type *howto,
+ const Elf_Internal_Rela *relocation,
+ bfd_vma value, bfd *input_bfd,
+ asection *input_section ATTRIBUTE_UNUSED, bfd_byte *contents)
+{
+ bfd_vma x;
+ bfd_byte *location;
+ bfd_vma dst_mask = howto->dst_mask;
+
+ /* Figure out where the relocation is occurring. */
+ location = contents + relocation->r_offset;
+
+ /* Obtain the current value. */
+ x = mips_elf_obtain_contents (howto, relocation, input_bfd, contents);
+
+ /* Fix up dst_mask and value for R_RISCV_LO16 relocs on stores. */
+ if (IS_STORE_RELOC (input_bfd, relocation->r_info, x))
+ {
+ value >>= OP_SH_IMMEDIATE;
+ value = ((value >> RISCV_IMMLO_BITS) << OP_SH_IMMHI) |
+ ((value & ((1<<RISCV_IMMLO_BITS)-1)) << OP_SH_IMMLO);
+ dst_mask = (OP_MASK_IMMHI << OP_SH_IMMHI) | (OP_MASK_IMMLO << OP_SH_IMMLO);
+ }
+
+ /* Update the field, adding in any nonzero bits in the original. */
+ x = (x &~ dst_mask) | (((x & dst_mask) + value) & dst_mask);
+
+ /* Put the value into the output. */
+ bfd_put (8 * bfd_get_reloc_size (howto), input_bfd, x, location);
+
+ return TRUE;
+}
+
+/* Create a rel.dyn relocation for the dynamic linker to resolve. REL
+ is the original relocation, which is now being transformed into a
+ dynamic relocation. The ADDENDP is adjusted if necessary; the
+ caller should store the result in place of the original addend. */
+
+static bfd_boolean
+mips_elf_create_dynamic_relocation (bfd *output_bfd,
+ struct bfd_link_info *info,
+ const Elf_Internal_Rela *rel,
+ struct mips_elf_link_hash_entry *h,
+ asection *sec, bfd_vma symbol,
+ bfd_vma *addendp, asection *input_section)
+{
+ Elf_Internal_Rela outrel[3];
+ asection *sreloc;
+ int r_type;
+ long indx;
+ bfd_boolean defined_p;
+ struct mips_elf_link_hash_table *htab;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ r_type = ELF_R_TYPE (output_bfd, rel->r_info);
+ sreloc = mips_elf_rel_dyn_section (info, FALSE);
+ BFD_ASSERT (sreloc != NULL);
+ BFD_ASSERT (sreloc->contents != NULL);
+ BFD_ASSERT (sreloc->reloc_count * MIPS_ELF_REL_SIZE (output_bfd)
+ < sreloc->size);
+
+ outrel[0].r_offset =
+ _bfd_elf_section_offset (output_bfd, info, input_section, rel[0].r_offset);
+ if (ABI_64_P (output_bfd))
+ {
+ outrel[1].r_offset =
+ _bfd_elf_section_offset (output_bfd, info, input_section, rel[1].r_offset);
+ outrel[2].r_offset =
+ _bfd_elf_section_offset (output_bfd, info, input_section, rel[2].r_offset);
+ }
+
+ if (outrel[0].r_offset == MINUS_ONE)
+ /* The relocation field has been deleted. */
+ return TRUE;
+
+ if (outrel[0].r_offset == MINUS_TWO)
+ {
+ /* The relocation field has been converted into a relative value of
+ some sort. Functions like _bfd_elf_write_section_eh_frame expect
+ the field to be fully relocated, so add in the symbol's value. */
+ *addendp += symbol;
+ return TRUE;
+ }
+
+ /* We must now calculate the dynamic symbol table index to use
+ in the relocation. */
+ if (h != NULL && ! SYMBOL_REFERENCES_LOCAL (info, &h->root))
+ {
+ BFD_ASSERT (h->global_got_area != GGA_NONE);
+ indx = h->root.dynindx;
+ /* ??? glibc's ld.so just adds the final GOT entry to the
+ relocation field. It therefore treats relocs against
+ defined symbols in the same way as relocs against
+ undefined symbols. */
+ defined_p = FALSE;
+ }
+ else
+ {
+ if (sec != NULL && bfd_is_abs_section (sec))
+ indx = 0;
+ else if (sec == NULL || sec->owner == NULL)
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ else
+ {
+ indx = elf_section_data (sec->output_section)->dynindx;
+ if (indx == 0)
+ {
+ asection *osec = htab->root.text_index_section;
+ indx = elf_section_data (osec)->dynindx;
+ }
+ if (indx == 0)
+ abort ();
+ }
+
+ /* Instead of generating a relocation using the section
+ symbol, we may as well make it a fully relative
+ relocation. We want to avoid generating relocations to
+ local symbols because we used to generate them
+ incorrectly, without adding the original symbol value,
+ which is mandated by the ABI for section symbols. In
+ order to give dynamic loaders and applications time to
+ phase out the incorrect use, we refrain from emitting
+ section-relative relocations. It's not like they're
+ useful, after all. This should be a bit more efficient
+ as well. */
+ /* ??? Although this behavior is compatible with glibc's ld.so,
+ the ABI says that relocations against STN_UNDEF should have
+ a symbol value of 0. Irix rld honors this, so relocations
+ against STN_UNDEF have no effect. */
+ indx = 0;
+ defined_p = TRUE;
+ }
+
+ /* If the relocation was previously an absolute relocation and
+ this symbol will not be referred to by the relocation, we must
+ adjust it by the value we give it in the dynamic symbol table.
+ Otherwise leave the job up to the dynamic linker. */
+ if (defined_p && r_type != R_RISCV_REL32)
+ *addendp += symbol;
+
+ /* The relocation is always an REL32 relocation because we don't
+ know where the shared library will wind up at load-time. */
+ outrel[0].r_info = ELF_R_INFO (output_bfd, (unsigned long) indx,
+ R_RISCV_REL32);
+
+ /* For strict adherence to the ABI specification, we should
+ generate a R_RISCV_64 relocation record by itself before the
+ _REL32/_64 record as well, such that the addend is read in as
+ a 64-bit value (REL32 is a 32-bit relocation, after all).
+ However, since none of the existing ELF64 MIPS dynamic
+ loaders seems to care, we don't waste space with these
+ artificial relocations. If this turns out to not be true,
+ mips_elf_allocate_dynamic_relocation() should be tweaked so
+ as to make room for a pair of dynamic relocations per
+ invocation if ABI_64_P, and here we should generate an
+ additional relocation record with R_RISCV_64 by itself for a
+ NULL symbol before this relocation record. */
+ outrel[1].r_info = ELF_R_INFO (output_bfd, 0,
+ ABI_64_P (output_bfd)
+ ? R_RISCV_64
+ : R_RISCV_NONE);
+ outrel[2].r_info = ELF_R_INFO (output_bfd, 0, R_RISCV_NONE);
+
+ /* Adjust the output offset of the relocation to reference the
+ correct location in the output file. */
+ outrel[0].r_offset += (input_section->output_section->vma
+ + input_section->output_offset);
+ outrel[1].r_offset += (input_section->output_section->vma
+ + input_section->output_offset);
+ outrel[2].r_offset += (input_section->output_section->vma
+ + input_section->output_offset);
+
+ /* Put the relocation back out. We have to use the special
+ relocation outputter in the 64-bit case since the 64-bit
+ relocation format is non-standard. */
+ if (ABI_64_P (output_bfd))
+ {
+ (*get_elf_backend_data (output_bfd)->s->swap_reloc_out)
+ (output_bfd, &outrel[0],
+ (sreloc->contents
+ + sreloc->reloc_count * sizeof (Elf64_RISCV_External_Rel)));
+ }
+ else
+ bfd_elf32_swap_reloc_out
+ (output_bfd, &outrel[0],
+ (sreloc->contents + sreloc->reloc_count * sizeof (Elf32_External_Rel)));
+
+ /* We've now added another relocation. */
+ ++sreloc->reloc_count;
+
+ /* Make sure the output section is writable. The dynamic linker
+ will be writing to it. */
+ elf_section_data (input_section->output_section)->this_hdr.sh_flags
+ |= SHF_WRITE;
+
+ /* If we've written this relocation for a readonly section,
+ we need to set DF_TEXTREL again, so that we do not delete the
+ DT_TEXTREL tag. */
+ if (MIPS_ELF_READONLY_SECTION (input_section))
+ info->flags |= DF_TEXTREL;
+
+ return TRUE;
+}
+
+/* Return the MACH for a MIPS e_flags value. */
+
+unsigned long
+_bfd_elf_riscv_mach (flagword flags)
+{
+ switch (flags & EF_MIPS_MACH)
+ {
+ case E_RISCV_MACH_ROCKET32:
+ return bfd_mach_riscv_rocket32;
+
+ case E_RISCV_MACH_ROCKET64:
+ return bfd_mach_riscv_rocket64;
+
+ default:
+ switch (flags & EF_MIPS_ARCH)
+ {
+ case E_RISCV_ARCH_RV32:
+ return bfd_mach_riscv_rocket32;
+
+ default:
+ case E_RISCV_ARCH_RV64:
+ return bfd_mach_riscv_rocket64;
+ }
+ }
+
+ return 0;
+}
+
+/* Return printable name for ABI. */
+
+static INLINE char *
+elf_mips_abi_name (bfd *abfd)
+{
+ flagword flags;
+
+ flags = elf_elfheader (abfd)->e_flags;
+ switch (flags & EF_MIPS_ABI)
+ {
+ case 0:
+ if (ABI_32_P (abfd))
+ return "32";
+ else if (ABI_64_P (abfd))
+ return "64";
+ else
+ return "none";
+ default:
+ return "unknown abi";
+ }
+}
+
+/* MIPS ELF uses two common sections. One is the usual one, and the
+ other is for small objects. All the small objects are kept
+ together, and then referenced via the gp pointer, which yields
+ faster assembler code. This is what we use for the small common
+ section. This approach is copied from ecoff.c. */
+static asection mips_elf_scom_section;
+static asymbol mips_elf_scom_symbol;
+static asymbol *mips_elf_scom_symbol_ptr;
+
+/* MIPS ELF also uses an acommon section, which represents an
+ allocated common symbol which may be overridden by a
+ definition in a shared library. */
+static asection mips_elf_acom_section;
+static asymbol mips_elf_acom_symbol;
+static asymbol *mips_elf_acom_symbol_ptr;
+
+/* This is used for both the 32-bit and the 64-bit ABI. */
+
+void
+_bfd_riscv_elf_symbol_processing (bfd *abfd ATTRIBUTE_UNUSED, asymbol *asym)
+{
+ elf_symbol_type *elfsym;
+
+ /* Handle the special MIPS section numbers that a symbol may use. */
+ elfsym = (elf_symbol_type *) asym;
+ switch (elfsym->internal_elf_sym.st_shndx)
+ {
+ case SHN_MIPS_ACOMMON:
+ /* This section is used in a dynamically linked executable file.
+ It is an allocated common section. The dynamic linker can
+ either resolve these symbols to something in a shared
+ library, or it can just leave them here. For our purposes,
+ we can consider these symbols to be in a new section. */
+ if (mips_elf_acom_section.name == NULL)
+ {
+ /* Initialize the acommon section. */
+ mips_elf_acom_section.name = ".acommon";
+ mips_elf_acom_section.flags = SEC_ALLOC;
+ mips_elf_acom_section.output_section = &mips_elf_acom_section;
+ mips_elf_acom_section.symbol = &mips_elf_acom_symbol;
+ mips_elf_acom_section.symbol_ptr_ptr = &mips_elf_acom_symbol_ptr;
+ mips_elf_acom_symbol.name = ".acommon";
+ mips_elf_acom_symbol.flags = BSF_SECTION_SYM;
+ mips_elf_acom_symbol.section = &mips_elf_acom_section;
+ mips_elf_acom_symbol_ptr = &mips_elf_acom_symbol;
+ }
+ asym->section = &mips_elf_acom_section;
+ break;
+
+ case SHN_COMMON:
+ break;
+
+ case SHN_MIPS_SCOMMON:
+ if (mips_elf_scom_section.name == NULL)
+ {
+ /* Initialize the small common section. */
+ mips_elf_scom_section.name = ".scommon";
+ mips_elf_scom_section.flags = SEC_IS_COMMON;
+ mips_elf_scom_section.output_section = &mips_elf_scom_section;
+ mips_elf_scom_section.symbol = &mips_elf_scom_symbol;
+ mips_elf_scom_section.symbol_ptr_ptr = &mips_elf_scom_symbol_ptr;
+ mips_elf_scom_symbol.name = ".scommon";
+ mips_elf_scom_symbol.flags = BSF_SECTION_SYM;
+ mips_elf_scom_symbol.section = &mips_elf_scom_section;
+ mips_elf_scom_symbol_ptr = &mips_elf_scom_symbol;
+ }
+ asym->section = &mips_elf_scom_section;
+ asym->value = elfsym->internal_elf_sym.st_size;
+ break;
+
+ case SHN_MIPS_SUNDEFINED:
+ asym->section = bfd_und_section_ptr;
+ break;
+ }
+
+ /* If this is an odd-valued function symbol, assume it's a MIPS16 one. */
+ if (ELF_ST_TYPE (elfsym->internal_elf_sym.st_info) == STT_FUNC
+ && (asym->value & 1) != 0)
+ {
+ asym->value--;
+ elfsym->internal_elf_sym.st_other
+ = ELF_ST_SET_MIPS16 (elfsym->internal_elf_sym.st_other);
+ }
+}
+
+/* Implement elf_backend_eh_frame_address_size. */
+
+unsigned int
+_bfd_riscv_elf_eh_frame_address_size (bfd *abfd, asection *sec ATTRIBUTE_UNUSED)
+{
+ if (elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64)
+ return 8;
+ return 4;
+}
+
+/* There appears to be a bug in the MIPSpro linker that causes GOT_DISP
+ relocations against two unnamed section symbols to resolve to the
+ same address. For example, if we have code like:
+
+ lw $4,%got_disp(.data)($gp)
+ lw $25,%got_disp(.text)($gp)
+ jalr $25
+
+ then the linker will resolve both relocations to .data and the program
+ will jump there rather than to .text.
+
+ We can work around this problem by giving names to local section symbols.
+ This is also what the MIPSpro tools do. */
+
+bfd_boolean
+_bfd_riscv_elf_name_local_section_symbols (bfd *abfd ATTRIBUTE_UNUSED)
+{
+ return FALSE;
+}
+
+/* Work over a section just before writing it out. This routine is
+ used by both the 32-bit and the 64-bit ABI. FIXME: We recognize
+ sections that need the SHF_MIPS_GPREL flag by name; there has to be
+ a better way. */
+
+bfd_boolean
+_bfd_riscv_elf_section_processing (bfd *abfd, Elf_Internal_Shdr *hdr)
+{
+ if (hdr->sh_type == SHT_MIPS_REGINFO
+ && hdr->sh_size > 0)
+ {
+ bfd_byte buf[4];
+
+ BFD_ASSERT (hdr->sh_size == sizeof (Elf32_External_RegInfo));
+ BFD_ASSERT (hdr->contents == NULL);
+
+ if (bfd_seek (abfd,
+ hdr->sh_offset + sizeof (Elf32_External_RegInfo) - 4,
+ SEEK_SET) != 0)
+ return FALSE;
+ H_PUT_32 (abfd, elf_gp (abfd), buf);
+ if (bfd_bwrite (buf, 4, abfd) != 4)
+ return FALSE;
+ }
+
+ if (hdr->sh_type == SHT_MIPS_OPTIONS
+ && hdr->bfd_section != NULL
+ && mips_elf_section_data (hdr->bfd_section) != NULL
+ && mips_elf_section_data (hdr->bfd_section)->u.tdata != NULL)
+ {
+ bfd_byte *contents, *l, *lend;
+
+ /* We stored the section contents in the tdata field in the
+ set_section_contents routine. We save the section contents
+ so that we don't have to read them again.
+ At this point we know that elf_gp is set, so we can look
+ through the section contents to see if there is an
+ ODK_REGINFO structure. */
+
+ contents = mips_elf_section_data (hdr->bfd_section)->u.tdata;
+ l = contents;
+ lend = contents + hdr->sh_size;
+ while (l + sizeof (Elf_External_Options) <= lend)
+ {
+ Elf_Internal_Options intopt;
+
+ bfd_riscv_elf_swap_options_in (abfd, (Elf_External_Options *) l,
+ &intopt);
+ if (intopt.size < sizeof (Elf_External_Options))
+ {
+ (*_bfd_error_handler)
+ (_("%B: Warning: bad `%s' option size %u smaller than its header"),
+ abfd, MIPS_ELF_OPTIONS_SECTION_NAME (abfd), intopt.size);
+ break;
+ }
+ if (ABI_64_P (abfd) && intopt.kind == ODK_REGINFO)
+ {
+ bfd_byte buf[8];
+
+ if (bfd_seek (abfd,
+ (hdr->sh_offset
+ + (l - contents)
+ + sizeof (Elf_External_Options)
+ + (sizeof (Elf64_External_RegInfo) - 8)),
+ SEEK_SET) != 0)
+ return FALSE;
+ H_PUT_64 (abfd, elf_gp (abfd), buf);
+ if (bfd_bwrite (buf, 8, abfd) != 8)
+ return FALSE;
+ }
+ else if (intopt.kind == ODK_REGINFO)
+ {
+ bfd_byte buf[4];
+
+ if (bfd_seek (abfd,
+ (hdr->sh_offset
+ + (l - contents)
+ + sizeof (Elf_External_Options)
+ + (sizeof (Elf32_External_RegInfo) - 4)),
+ SEEK_SET) != 0)
+ return FALSE;
+ H_PUT_32 (abfd, elf_gp (abfd), buf);
+ if (bfd_bwrite (buf, 4, abfd) != 4)
+ return FALSE;
+ }
+ l += intopt.size;
+ }
+ }
+
+ if (hdr->bfd_section != NULL)
+ {
+ const char *name = bfd_get_section_name (abfd, hdr->bfd_section);
+
+ if (strcmp (name, ".rtproc") == 0)
+ {
+ if (hdr->sh_addralign != 0 && hdr->sh_entsize == 0)
+ {
+ unsigned int adjust;
+
+ adjust = hdr->sh_size % hdr->sh_addralign;
+ if (adjust != 0)
+ hdr->sh_size += hdr->sh_addralign - adjust;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+/* Handle a MIPS specific section when reading an object file. This
+ is called when elfcode.h finds a section with an unknown type.
+ This routine supports both the 32-bit and 64-bit ELF ABI.
+
+ FIXME: We need to handle the SHF_MIPS_GPREL flag, but I'm not sure
+ how to. */
+
+bfd_boolean
+_bfd_riscv_elf_section_from_shdr (bfd *abfd,
+ Elf_Internal_Shdr *hdr,
+ const char *name,
+ int shindex)
+{
+ flagword flags = 0;
+
+ /* There ought to be a place to keep ELF backend specific flags, but
+ at the moment there isn't one. We just keep track of the
+ sections by their name, instead. Fortunately, the ABI gives
+ suggested names for all the MIPS specific sections, so we will
+ probably get away with this. */
+ switch (hdr->sh_type)
+ {
+ case SHT_MIPS_LIBLIST:
+ if (strcmp (name, ".liblist") != 0)
+ return FALSE;
+ break;
+ case SHT_MIPS_MSYM:
+ if (strcmp (name, ".msym") != 0)
+ return FALSE;
+ break;
+ case SHT_MIPS_CONFLICT:
+ if (strcmp (name, ".conflict") != 0)
+ return FALSE;
+ break;
+ case SHT_MIPS_GPTAB:
+ BFD_ASSERT (FALSE);
+ case SHT_MIPS_UCODE:
+ if (strcmp (name, ".ucode") != 0)
+ return FALSE;
+ break;
+ case SHT_MIPS_DEBUG:
+ if (strcmp (name, ".mdebug") != 0)
+ return FALSE;
+ flags = SEC_DEBUGGING;
+ break;
+ case SHT_MIPS_REGINFO:
+ if (strcmp (name, ".reginfo") != 0
+ || hdr->sh_size != sizeof (Elf32_External_RegInfo))
+ return FALSE;
+ flags = (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_SAME_SIZE);
+ break;
+ case SHT_MIPS_IFACE:
+ if (strcmp (name, ".MIPS.interfaces") != 0)
+ return FALSE;
+ break;
+ case SHT_MIPS_CONTENT:
+ if (! CONST_STRNEQ (name, ".MIPS.content"))
+ return FALSE;
+ break;
+ case SHT_MIPS_OPTIONS:
+ if (!MIPS_ELF_OPTIONS_SECTION_NAME_P (name))
+ return FALSE;
+ break;
+ case SHT_MIPS_DWARF:
+ if (! CONST_STRNEQ (name, ".debug_")
+ && ! CONST_STRNEQ (name, ".zdebug_"))
+ return FALSE;
+ break;
+ case SHT_MIPS_SYMBOL_LIB:
+ if (strcmp (name, ".MIPS.symlib") != 0)
+ return FALSE;
+ break;
+ case SHT_MIPS_EVENTS:
+ if (! CONST_STRNEQ (name, ".MIPS.events")
+ && ! CONST_STRNEQ (name, ".MIPS.post_rel"))
+ return FALSE;
+ break;
+ default:
+ break;
+ }
+
+ if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex))
+ return FALSE;
+
+ if (flags)
+ {
+ if (! bfd_set_section_flags (abfd, hdr->bfd_section,
+ (bfd_get_section_flags (abfd,
+ hdr->bfd_section)
+ | flags)))
+ return FALSE;
+ }
+
+ /* FIXME: We should record sh_info for a .gptab section. */
+
+ /* For a .reginfo section, set the gp value in the tdata information
+ from the contents of this section. We need the gp value while
+ processing relocs, so we just get it now. The .reginfo section
+ is not used in the 64-bit MIPS ELF ABI. */
+ if (hdr->sh_type == SHT_MIPS_REGINFO)
+ {
+ Elf32_External_RegInfo ext;
+ Elf32_RegInfo s;
+
+ if (! bfd_get_section_contents (abfd, hdr->bfd_section,
+ &ext, 0, sizeof ext))
+ return FALSE;
+ bfd_riscv_elf32_swap_reginfo_in (abfd, &ext, &s);
+ elf_gp (abfd) = s.ri_gp_value;
+ }
+
+ /* For a SHT_MIPS_OPTIONS section, look for a ODK_REGINFO entry, and
+ set the gp value based on what we find. We may see both
+ SHT_MIPS_REGINFO and SHT_MIPS_OPTIONS/ODK_REGINFO; in that case,
+ they should agree. */
+ if (hdr->sh_type == SHT_MIPS_OPTIONS)
+ {
+ bfd_byte *contents, *l, *lend;
+
+ contents = bfd_malloc (hdr->sh_size);
+ if (contents == NULL)
+ return FALSE;
+ if (! bfd_get_section_contents (abfd, hdr->bfd_section, contents,
+ 0, hdr->sh_size))
+ {
+ free (contents);
+ return FALSE;
+ }
+ l = contents;
+ lend = contents + hdr->sh_size;
+ while (l + sizeof (Elf_External_Options) <= lend)
+ {
+ Elf_Internal_Options intopt;
+
+ bfd_riscv_elf_swap_options_in (abfd, (Elf_External_Options *) l,
+ &intopt);
+ if (intopt.size < sizeof (Elf_External_Options))
+ {
+ (*_bfd_error_handler)
+ (_("%B: Warning: bad `%s' option size %u smaller than its header"),
+ abfd, MIPS_ELF_OPTIONS_SECTION_NAME (abfd), intopt.size);
+ break;
+ }
+ if (ABI_64_P (abfd) && intopt.kind == ODK_REGINFO)
+ {
+ Elf64_Internal_RegInfo intreg;
+
+ bfd_riscv_elf64_swap_reginfo_in
+ (abfd,
+ ((Elf64_External_RegInfo *)
+ (l + sizeof (Elf_External_Options))),
+ &intreg);
+ elf_gp (abfd) = intreg.ri_gp_value;
+ }
+ else if (intopt.kind == ODK_REGINFO)
+ {
+ Elf32_RegInfo intreg;
+
+ bfd_riscv_elf32_swap_reginfo_in
+ (abfd,
+ ((Elf32_External_RegInfo *)
+ (l + sizeof (Elf_External_Options))),
+ &intreg);
+ elf_gp (abfd) = intreg.ri_gp_value;
+ }
+ l += intopt.size;
+ }
+ free (contents);
+ }
+
+ return TRUE;
+}
+
+/* Set the correct type for a MIPS ELF section. We do this by the
+ section name, which is a hack, but ought to work. This routine is
+ used by both the 32-bit and the 64-bit ABI. */
+
+bfd_boolean
+_bfd_riscv_elf_fake_sections (bfd *abfd ATTRIBUTE_UNUSED,
+ Elf_Internal_Shdr *hdr, asection *sec)
+{
+ const char *name = bfd_get_section_name (abfd, sec);
+
+ if (strcmp (name, ".liblist") == 0)
+ {
+ hdr->sh_type = SHT_MIPS_LIBLIST;
+ hdr->sh_info = sec->size / sizeof (Elf32_Lib);
+ /* The sh_link field is set in final_write_processing. */
+ }
+ else if (strcmp (name, ".conflict") == 0)
+ hdr->sh_type = SHT_MIPS_CONFLICT;
+ else if (CONST_STRNEQ (name, ".gptab."))
+ BFD_ASSERT (FALSE);
+ else if (strcmp (name, ".ucode") == 0)
+ hdr->sh_type = SHT_MIPS_UCODE;
+ else if (strcmp (name, ".mdebug") == 0)
+ {
+ hdr->sh_type = SHT_MIPS_DEBUG;
+ hdr->sh_entsize = 1;
+ }
+ else if (strcmp (name, ".reginfo") == 0)
+ {
+ hdr->sh_type = SHT_MIPS_REGINFO;
+ /* In a shared object on IRIX 5.3, the .reginfo section has an
+ entsize of 0x18. FIXME: Does this matter? */
+ hdr->sh_entsize = sizeof (Elf32_External_RegInfo);
+ }
+ else if (strcmp (name, ".got") == 0
+ || strcmp (name, ".srdata") == 0
+ || strcmp (name, ".sdata") == 0
+ || strcmp (name, ".sbss") == 0
+ || strcmp (name, ".lit4") == 0
+ || strcmp (name, ".lit8") == 0)
+ hdr->sh_flags |= SHF_MIPS_GPREL;
+ else if (strcmp (name, ".MIPS.interfaces") == 0)
+ {
+ hdr->sh_type = SHT_MIPS_IFACE;
+ hdr->sh_flags |= SHF_MIPS_NOSTRIP;
+ }
+ else if (CONST_STRNEQ (name, ".MIPS.content"))
+ {
+ hdr->sh_type = SHT_MIPS_CONTENT;
+ hdr->sh_flags |= SHF_MIPS_NOSTRIP;
+ /* The sh_info field is set in final_write_processing. */
+ }
+ else if (MIPS_ELF_OPTIONS_SECTION_NAME_P (name))
+ {
+ hdr->sh_type = SHT_MIPS_OPTIONS;
+ hdr->sh_entsize = 1;
+ hdr->sh_flags |= SHF_MIPS_NOSTRIP;
+ }
+ else if (CONST_STRNEQ (name, ".debug_")
+ || CONST_STRNEQ (name, ".zdebug_"))
+ {
+ hdr->sh_type = SHT_MIPS_DWARF;
+ }
+ else if (strcmp (name, ".MIPS.symlib") == 0)
+ {
+ hdr->sh_type = SHT_MIPS_SYMBOL_LIB;
+ /* The sh_link and sh_info fields are set in
+ final_write_processing. */
+ }
+ else if (CONST_STRNEQ (name, ".MIPS.events")
+ || CONST_STRNEQ (name, ".MIPS.post_rel"))
+ {
+ hdr->sh_type = SHT_MIPS_EVENTS;
+ hdr->sh_flags |= SHF_MIPS_NOSTRIP;
+ /* The sh_link field is set in final_write_processing. */
+ }
+ else if (strcmp (name, ".msym") == 0)
+ {
+ hdr->sh_type = SHT_MIPS_MSYM;
+ hdr->sh_flags |= SHF_ALLOC;
+ hdr->sh_entsize = 8;
+ }
+
+ /* The generic elf_fake_sections will set up REL_HDR using the default
+ kind of relocations. We used to set up a second header for the
+ non-default kind of relocations here, but only NewABI would use
+ these, and the IRIX ld doesn't like resulting empty RELA sections.
+ Thus we create those header only on demand now. */
+
+ return TRUE;
+}
+
+/* Given a BFD section, try to locate the corresponding ELF section
+ index. This is used by both the 32-bit and the 64-bit ABI.
+ Actually, it's not clear to me that the 64-bit ABI supports these,
+ but for non-PIC objects we will certainly want support for at least
+ the .scommon section. */
+
+bfd_boolean
+_bfd_riscv_elf_section_from_bfd_section (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec, int *retval)
+{
+ if (strcmp (bfd_get_section_name (abfd, sec), ".scommon") == 0)
+ {
+ *retval = SHN_MIPS_SCOMMON;
+ return TRUE;
+ }
+ if (strcmp (bfd_get_section_name (abfd, sec), ".acommon") == 0)
+ {
+ *retval = SHN_MIPS_ACOMMON;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Hook called by the linker routine which adds symbols from an object
+ file. We must handle the special MIPS section numbers here. */
+
+bfd_boolean
+_bfd_riscv_elf_add_symbol_hook (bfd *abfd ATTRIBUTE_UNUSED,
+ struct bfd_link_info *info ATTRIBUTE_UNUSED,
+ Elf_Internal_Sym *sym, const char **namep ATTRIBUTE_UNUSED,
+ flagword *flagsp ATTRIBUTE_UNUSED,
+ asection **secp, bfd_vma *valp)
+{
+ switch (sym->st_shndx)
+ {
+ case SHN_COMMON:
+ break;
+
+ case SHN_MIPS_SCOMMON:
+ *secp = bfd_make_section_old_way (abfd, ".scommon");
+ (*secp)->flags |= SEC_IS_COMMON;
+ *valp = sym->st_size;
+ break;
+
+ case SHN_MIPS_TEXT:
+ /* This section is used in a shared object. */
+ if (elf_tdata (abfd)->elf_text_section == NULL)
+ {
+ asymbol *elf_text_symbol;
+ asection *elf_text_section;
+ bfd_size_type amt = sizeof (asection);
+
+ elf_text_section = bfd_zalloc (abfd, amt);
+ if (elf_text_section == NULL)
+ return FALSE;
+
+ amt = sizeof (asymbol);
+ elf_text_symbol = bfd_zalloc (abfd, amt);
+ if (elf_text_symbol == NULL)
+ return FALSE;
+
+ /* Initialize the section. */
+
+ elf_tdata (abfd)->elf_text_section = elf_text_section;
+ elf_tdata (abfd)->elf_text_symbol = elf_text_symbol;
+
+ elf_text_section->symbol = elf_text_symbol;
+ elf_text_section->symbol_ptr_ptr = &elf_tdata (abfd)->elf_text_symbol;
+
+ elf_text_section->name = ".text";
+ elf_text_section->flags = SEC_NO_FLAGS;
+ elf_text_section->output_section = NULL;
+ elf_text_section->owner = abfd;
+ elf_text_symbol->name = ".text";
+ elf_text_symbol->flags = BSF_SECTION_SYM | BSF_DYNAMIC;
+ elf_text_symbol->section = elf_text_section;
+ }
+ /* This code used to do *secp = bfd_und_section_ptr if
+ info->shared. I don't know why, and that doesn't make sense,
+ so I took it out. */
+ *secp = elf_tdata (abfd)->elf_text_section;
+ break;
+
+ case SHN_MIPS_ACOMMON:
+ /* Fall through. XXX Can we treat this as allocated data? */
+ case SHN_MIPS_DATA:
+ /* This section is used in a shared object. */
+ if (elf_tdata (abfd)->elf_data_section == NULL)
+ {
+ asymbol *elf_data_symbol;
+ asection *elf_data_section;
+ bfd_size_type amt = sizeof (asection);
+
+ elf_data_section = bfd_zalloc (abfd, amt);
+ if (elf_data_section == NULL)
+ return FALSE;
+
+ amt = sizeof (asymbol);
+ elf_data_symbol = bfd_zalloc (abfd, amt);
+ if (elf_data_symbol == NULL)
+ return FALSE;
+
+ /* Initialize the section. */
+
+ elf_tdata (abfd)->elf_data_section = elf_data_section;
+ elf_tdata (abfd)->elf_data_symbol = elf_data_symbol;
+
+ elf_data_section->symbol = elf_data_symbol;
+ elf_data_section->symbol_ptr_ptr = &elf_tdata (abfd)->elf_data_symbol;
+
+ elf_data_section->name = ".data";
+ elf_data_section->flags = SEC_NO_FLAGS;
+ elf_data_section->output_section = NULL;
+ elf_data_section->owner = abfd;
+ elf_data_symbol->name = ".data";
+ elf_data_symbol->flags = BSF_SECTION_SYM | BSF_DYNAMIC;
+ elf_data_symbol->section = elf_data_section;
+ }
+ /* This code used to do *secp = bfd_und_section_ptr if
+ info->shared. I don't know why, and that doesn't make sense,
+ so I took it out. */
+ *secp = elf_tdata (abfd)->elf_data_section;
+ break;
+
+ case SHN_MIPS_SUNDEFINED:
+ *secp = bfd_und_section_ptr;
+ break;
+ }
+
+ return TRUE;
+}
+
+/* This hook function is called before the linker writes out a global
+ symbol. We mark symbols as small common if appropriate. This is
+ also where we undo the increment of the value for a mips16 symbol. */
+
+int
+_bfd_riscv_elf_link_output_symbol_hook
+ (struct bfd_link_info *info ATTRIBUTE_UNUSED,
+ const char *name ATTRIBUTE_UNUSED, Elf_Internal_Sym *sym,
+ asection *input_sec, struct elf_link_hash_entry *h ATTRIBUTE_UNUSED)
+{
+ /* If we see a common symbol, which implies a relocatable link, then
+ if a symbol was small common in an input file, mark it as small
+ common in the output file. */
+ if (sym->st_shndx == SHN_COMMON
+ && strcmp (input_sec->name, ".scommon") == 0)
+ sym->st_shndx = SHN_MIPS_SCOMMON;
+
+ return 1;
+}
+
+/* Functions for the dynamic linker. */
+
+/* Create dynamic sections when linking against a dynamic object. */
+
+bfd_boolean
+_bfd_riscv_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
+{
+ struct elf_link_hash_entry *h;
+ struct bfd_link_hash_entry *bh;
+ flagword flags;
+ register asection *s;
+ struct mips_elf_link_hash_table *htab;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+ | SEC_LINKER_CREATED | SEC_READONLY);
+
+ /* The psABI requires a read-only .dynamic section. */
+ s = bfd_get_section_by_name (abfd, ".dynamic");
+ if (s != NULL)
+ {
+ if (! bfd_set_section_flags (abfd, s, flags))
+ return FALSE;
+ }
+
+ /* We need to create .got section. */
+ if (!mips_elf_create_got_section (abfd, info))
+ return FALSE;
+
+ if (! mips_elf_rel_dyn_section (info, TRUE))
+ return FALSE;
+
+ if (!info->shared)
+ {
+ const char *name;
+
+ name = "_DYNAMIC_LINKING";
+ bh = NULL;
+ if (!(_bfd_generic_link_add_one_symbol
+ (info, abfd, name, BSF_GLOBAL, bfd_abs_section_ptr, 0,
+ NULL, FALSE, get_elf_backend_data (abfd)->collect, &bh)))
+ return FALSE;
+
+ h = (struct elf_link_hash_entry *) bh;
+ h->non_elf = 0;
+ h->def_regular = 1;
+ h->type = STT_SECTION;
+
+ if (! bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+ }
+
+ /* Create the .plt, .rel(a).plt, .dynbss and .rel(a).bss sections.
+ Also create the _PROCEDURE_LINKAGE_TABLE symbol. */
+ if (!_bfd_elf_create_dynamic_sections (abfd, info))
+ return FALSE;
+
+ /* Cache the sections created above. */
+ htab->splt = bfd_get_section_by_name (abfd, ".plt");
+ htab->sdynbss = bfd_get_section_by_name (abfd, ".dynbss");
+ htab->srelplt = bfd_get_section_by_name (abfd, ".rel.plt");
+ if (!htab->sdynbss
+ || !htab->srelplt
+ || !htab->splt)
+ abort ();
+
+ htab->plt_header_size = RISCV_PLT0_ENTRY_INSNS * 4;
+ htab->plt_entry_size = RISCV_PLT_ENTRY_INSNS * 4;
+
+ return TRUE;
+}
+
+/* Return true if relocation REL against section SEC is a REL rather than
+ RELA relocation. RELOCS is the first relocation in the section and
+ ABFD is the bfd that contains SEC. */
+
+static bfd_boolean
+mips_elf_rel_relocation_p (bfd *abfd, asection *sec,
+ const Elf_Internal_Rela *relocs,
+ const Elf_Internal_Rela *rel)
+{
+ Elf_Internal_Shdr *rel_hdr;
+ const struct elf_backend_data *bed;
+
+ /* To determine which flavor of relocation this is, we depend on the
+ fact that the INPUT_SECTION's REL_HDR is read before RELA_HDR. */
+ rel_hdr = elf_section_data (sec)->rel.hdr;
+ if (rel_hdr == NULL)
+ return FALSE;
+ bed = get_elf_backend_data (abfd);
+ return ((size_t) (rel - relocs)
+ < NUM_SHDR_ENTRIES (rel_hdr) * bed->s->int_rels_per_ext_rel);
+}
+
+/* Read the addend for REL relocation REL, which belongs to bfd ABFD.
+ HOWTO is the relocation's howto and CONTENTS points to the contents
+ of the section that REL is against. */
+
+static bfd_vma
+mips_elf_read_rel_addend (bfd *abfd, const Elf_Internal_Rela *rel,
+ reloc_howto_type *howto, bfd_byte *contents)
+{
+ bfd_vma addend;
+
+ /* Get the addend, which is stored in the input file. */
+ addend = mips_elf_obtain_contents (howto, rel, abfd, contents);
+
+ return addend & howto->src_mask;
+}
+
+/* REL is a relocation in ABFD that needs a partnering LO16 relocation
+ and *ADDEND is the addend for REL itself. Look for the LO16 relocation
+ and update *ADDEND with the final addend. Return true on success
+ or false if the LO16 could not be found. RELEND is the exclusive
+ upper bound on the relocations for REL's section. */
+
+static bfd_boolean
+mips_elf_add_lo16_rel_addend (bfd *abfd,
+ const Elf_Internal_Rela *rel,
+ const Elf_Internal_Rela *relend,
+ bfd_byte *contents, bfd_vma *addend)
+{
+ unsigned int lo16_type;
+ const Elf_Internal_Rela *lo16_relocation;
+ reloc_howto_type *lo16_howto;
+ bfd_vma l;
+
+ lo16_type = R_RISCV_LO16;
+
+ /* The combined value is the sum of the HI16 addend, left-shifted by
+ sixteen bits, and the LO16 addend, sign extended. (Usually, the
+ code does a `lui' of the HI16 value, and then an `addiu' of the
+ LO16 value.)
+
+ Scan ahead to find a matching LO16 relocation.
+
+ According to the MIPS ELF ABI, the R_RISCV_LO16 relocation must
+ be immediately following. However, for the IRIX6 ABI, the next
+ relocation may be a composed relocation consisting of several
+ relocations for the same address. In that case, the R_RISCV_LO16
+ relocation may occur as one of these. We permit a similar
+ extension in general, as that is useful for GCC.
+
+ In some cases GCC dead code elimination removes the LO16 but keeps
+ the corresponding HI16. This is strictly speaking a violation of
+ the ABI but not immediately harmful. */
+ lo16_relocation = mips_elf_next_relocation (abfd, lo16_type, rel, relend);
+ if (lo16_relocation == NULL)
+ return FALSE;
+
+ /* Obtain the addend kept there. */
+ lo16_howto = MIPS_ELF_RTYPE_TO_HOWTO (abfd, lo16_type, FALSE);
+ l = mips_elf_read_rel_addend (abfd, lo16_relocation, lo16_howto, contents);
+
+ l <<= lo16_howto->rightshift;
+ l = _bfd_riscv_elf_sign_extend (l, RISCV_IMM_BITS);
+
+ *addend <<= RISCV_IMM_BITS;
+ *addend += l;
+ return TRUE;
+}
+
+/* Try to read the contents of section SEC in bfd ABFD. Return true and
+ store the contents in *CONTENTS on success. Assume that *CONTENTS
+ already holds the contents if it is nonull on entry. */
+
+static bfd_boolean
+mips_elf_get_section_contents (bfd *abfd, asection *sec, bfd_byte **contents)
+{
+ if (*contents)
+ return TRUE;
+
+ /* Get cached copy if it exists. */
+ if (elf_section_data (sec)->this_hdr.contents != NULL)
+ {
+ *contents = elf_section_data (sec)->this_hdr.contents;
+ return TRUE;
+ }
+
+ return bfd_malloc_and_get_section (abfd, sec, contents);
+}
+
+/* Look through the relocs for a section during the first phase, and
+ allocate space in the global offset table. */
+
+bfd_boolean
+_bfd_riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
+ asection *sec, const Elf_Internal_Rela *relocs)
+{
+ const char *name;
+ bfd *dynobj;
+ Elf_Internal_Shdr *symtab_hdr;
+ struct elf_link_hash_entry **sym_hashes;
+ size_t extsymoff;
+ const Elf_Internal_Rela *rel;
+ const Elf_Internal_Rela *rel_end;
+ asection *sreloc;
+ const struct elf_backend_data *bed;
+ struct mips_elf_link_hash_table *htab;
+ bfd_byte *contents;
+ bfd_vma addend;
+ reloc_howto_type *howto;
+
+ if (info->relocatable)
+ return TRUE;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ dynobj = elf_hash_table (info)->dynobj;
+ symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+ sym_hashes = elf_sym_hashes (abfd);
+ extsymoff = (elf_bad_symtab (abfd)) ? 0 : symtab_hdr->sh_info;
+
+ bed = get_elf_backend_data (abfd);
+ rel_end = relocs + sec->reloc_count * bed->s->int_rels_per_ext_rel;
+
+ name = bfd_get_section_name (abfd, sec);
+
+ sreloc = NULL;
+ contents = NULL;
+ for (rel = relocs; rel < rel_end; ++rel)
+ {
+ unsigned long r_symndx;
+ unsigned int r_type;
+ struct elf_link_hash_entry *h;
+ bfd_boolean can_make_dynamic_p;
+
+ r_symndx = ELF_R_SYM (abfd, rel->r_info);
+ r_type = ELF_R_TYPE (abfd, rel->r_info);
+
+ if (r_symndx < extsymoff)
+ h = NULL;
+ else if (r_symndx >= extsymoff + NUM_SHDR_ENTRIES (symtab_hdr))
+ {
+ (*_bfd_error_handler)
+ (_("%B: Malformed reloc detected for section %s"),
+ abfd, name);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ else
+ {
+ h = sym_hashes[r_symndx - extsymoff];
+ while (h != NULL
+ && (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;
+ }
+
+ /* Set CAN_MAKE_DYNAMIC_P to true if we can convert this
+ relocation into a dynamic one. */
+ can_make_dynamic_p = FALSE;
+ switch (r_type)
+ {
+ case R_MIPS16_GOT16:
+ case R_MIPS16_CALL16:
+ case R_RISCV_GOT16:
+ case R_RISCV_CALL16:
+ case R_RISCV_CALL_HI16:
+ case R_RISCV_CALL_LO16:
+ case R_RISCV_GOT_HI16:
+ case R_RISCV_GOT_LO16:
+ case R_RISCV_GOT_DISP:
+ case R_RISCV_TLS_GOTTPREL:
+ case R_RISCV_TLS_GOT_HI16:
+ case R_RISCV_TLS_GOT_LO16:
+ case R_RISCV_TLS_GD:
+ case R_RISCV_TLS_GD_HI16:
+ case R_RISCV_TLS_GD_LO16:
+ case R_RISCV_TLS_LDM:
+ case R_RISCV_TLS_LDM_HI16:
+ case R_RISCV_TLS_LDM_LO16:
+ if (dynobj == NULL)
+ elf_hash_table (info)->dynobj = dynobj = abfd;
+ if (!mips_elf_create_got_section (dynobj, info))
+ return FALSE;
+ break;
+
+ case R_RISCV_32:
+ case R_RISCV_REL32:
+ case R_RISCV_64:
+ /* For executables that use PLTs and copy-relocs, we have a
+ choice between converting the relocation into a dynamic
+ one or using copy relocations or PLT entries. It is
+ usually better to do the former, unless the relocation is
+ against a read-only section. */
+ if ((info->shared
+ || (h != NULL
+ && !(!info->nocopyreloc
+ && !PIC_OBJECT_P (abfd)
+ && MIPS_ELF_READONLY_SECTION (sec))))
+ && (sec->flags & SEC_ALLOC) != 0)
+ {
+ can_make_dynamic_p = TRUE;
+ if (dynobj == NULL)
+ elf_hash_table (info)->dynobj = dynobj = abfd;
+ break;
+ }
+ /* For sections that are not SEC_ALLOC a copy reloc would be
+ output if possible (implying questionable semantics for
+ read-only data objects) or otherwise the final link would
+ fail as ld.so will not process them and could not therefore
+ handle any outstanding dynamic relocations.
+
+ For such sections that are also SEC_DEBUGGING, we can avoid
+ these problems by simply ignoring any relocs as these
+ sections have a predefined use and we know it is safe to do
+ so.
+
+ This is needed in cases such as a global symbol definition
+ in a shared library causing a common symbol from an object
+ file to be converted to an undefined reference. If that
+ happens, then all the relocations against this symbol from
+ SEC_DEBUGGING sections in the object file will resolve to
+ nil. */
+ if ((sec->flags & SEC_DEBUGGING) != 0)
+ break;
+ /* Fall through. */
+
+ default:
+ /* Most static relocations require pointer equality, except
+ for branches. */
+ if (h)
+ h->pointer_equality_needed = TRUE;
+ /* Fall through. */
+
+ case R_RISCV_26:
+ case R_RISCV_PC16:
+ case R_MIPS16_26:
+ if (h)
+ ((struct mips_elf_link_hash_entry *) h)->has_static_relocs = TRUE;
+ break;
+ }
+
+ if ((h == NULL && r_type == R_RISCV_CALL_LO16)
+ || r_type == R_RISCV_GOT_LO16
+ || r_type == R_RISCV_GOT_DISP)
+ {
+ /* We may need a local GOT entry for this relocation. We
+ don't count R_RISCV_GOT_PAGE because we can estimate the
+ maximum number of pages needed by looking at the size of
+ the segment. Similar comments apply to R_MIPS*_GOT16 and
+ R_MIPS*_CALL16, except on VxWorks, where GOT relocations
+ always evaluate to "G". We don't count R_RISCV_GOT_HI16, or
+ R_RISCV_CALL_HI16 because these are always followed by an
+ R_RISCV_GOT_LO16 or R_RISCV_CALL_LO16. */
+ if (!mips_elf_record_local_got_symbol (abfd, r_symndx,
+ rel->r_addend, info, 0))
+ return FALSE;
+ }
+
+ switch (r_type)
+ {
+ case R_RISCV_CALL16:
+ case R_MIPS16_CALL16:
+ if (h == NULL)
+ {
+ (*_bfd_error_handler)
+ (_("%B: CALL16 reloc at 0x%lx not against global symbol"),
+ abfd, (unsigned long) rel->r_offset);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ /* Fall through. */
+
+ case R_MIPS16_GOT16:
+ case R_RISCV_GOT16:
+ case R_RISCV_GOT_HI16:
+ case R_RISCV_GOT_LO16:
+ if (!h)
+ {
+ /* This relocation needs a page entry in the GOT. */
+ if (mips_elf_rel_relocation_p (abfd, sec, relocs, rel))
+ {
+ if (!mips_elf_get_section_contents (abfd, sec, &contents))
+ return FALSE;
+ howto = MIPS_ELF_RTYPE_TO_HOWTO (abfd, r_type, FALSE);
+ addend = mips_elf_read_rel_addend (abfd, rel,
+ howto, contents);
+ if (got16_reloc_p (r_type))
+ mips_elf_add_lo16_rel_addend (abfd, rel, rel_end,
+ contents, &addend);
+ else
+ addend <<= howto->rightshift;
+ }
+ else
+ addend = rel->r_addend;
+ if (!mips_elf_record_got_page_entry (info, abfd, r_symndx,
+ addend))
+ return FALSE;
+ }
+ /* Fall through. */
+
+ case R_RISCV_GOT_DISP:
+ if (h && !mips_elf_record_global_got_symbol (h, abfd, info, 0))
+ return FALSE;
+ break;
+
+ case R_RISCV_TLS_GOTTPREL:
+ case R_RISCV_TLS_GOT_HI16:
+ case R_RISCV_TLS_GOT_LO16:
+ if (info->shared)
+ info->flags |= DF_STATIC_TLS;
+ /* Fall through */
+
+ case R_RISCV_TLS_LDM:
+ case R_RISCV_TLS_LDM_HI16:
+ case R_RISCV_TLS_LDM_LO16:
+ if (TLS_LDM_RELOC_P(r_type))
+ {
+ r_symndx = STN_UNDEF;
+ h = NULL;
+ }
+ /* Fall through */
+
+ case R_RISCV_TLS_GD:
+ case R_RISCV_TLS_GD_HI16:
+ case R_RISCV_TLS_GD_LO16:
+ /* This symbol requires a global offset table entry, or two
+ for TLS GD relocations. */
+ {
+ unsigned char flag = (TLS_GD_RELOC_P(r_type)
+ ? GOT_TLS_GD
+ : TLS_LDM_RELOC_P(r_type)
+ ? GOT_TLS_LDM
+ : GOT_TLS_IE);
+ if (h != NULL)
+ {
+ struct mips_elf_link_hash_entry *hmips =
+ (struct mips_elf_link_hash_entry *) h;
+ hmips->tls_type |= flag;
+
+ if (h && !mips_elf_record_global_got_symbol (h, abfd, info, flag))
+ return FALSE;
+ }
+ else
+ {
+ BFD_ASSERT (flag == GOT_TLS_LDM || r_symndx != STN_UNDEF);
+
+ if (!mips_elf_record_local_got_symbol (abfd, r_symndx,
+ rel->r_addend,
+ info, flag))
+ return FALSE;
+ }
+ }
+ break;
+
+ case R_RISCV_32:
+ case R_RISCV_REL32:
+ case R_RISCV_64:
+ /* In VxWorks executables, references to external symbols
+ are handled using copy relocs or PLT stubs, so there's
+ no need to add a .rela.dyn entry for this relocation. */
+ if (can_make_dynamic_p)
+ {
+ if (sreloc == NULL)
+ {
+ sreloc = mips_elf_rel_dyn_section (info, TRUE);
+ if (sreloc == NULL)
+ return FALSE;
+ }
+ if (info->shared && h == NULL)
+ {
+ /* When creating a shared object, we must copy these
+ reloc types into the output file as R_RISCV_REL32
+ relocs. Make room for this reloc in .rel(a).dyn. */
+ mips_elf_allocate_dynamic_relocations (dynobj, info, 1);
+ if (MIPS_ELF_READONLY_SECTION (sec))
+ /* We tell the dynamic linker that there are
+ relocations against the text segment. */
+ info->flags |= DF_TEXTREL;
+ }
+ else
+ {
+ struct mips_elf_link_hash_entry *hmips;
+
+ /* For a shared object, we must copy this relocation
+ unless the symbol turns out to be undefined and
+ weak with non-default visibility, in which case
+ it will be left as zero.
+
+ We could elide R_RISCV_REL32 for locally binding symbols
+ in shared libraries, but do not yet do so.
+
+ For an executable, we only need to copy this
+ reloc if the symbol is defined in a dynamic
+ object. */
+ hmips = (struct mips_elf_link_hash_entry *) h;
+ ++hmips->possibly_dynamic_relocs;
+ if (MIPS_ELF_READONLY_SECTION (sec))
+ /* We need it to tell the dynamic linker if there
+ are relocations against the text segment. */
+ hmips->readonly_reloc = TRUE;
+ }
+ }
+
+ break;
+
+ case R_RISCV_26:
+ case R_RISCV_GPREL16:
+ case R_RISCV_LITERAL:
+ case R_RISCV_GPREL32:
+ break;
+
+ /* This relocation describes the C++ object vtable hierarchy.
+ Reconstruct it for later use during GC. */
+ case R_RISCV_GNU_VTINHERIT:
+ if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
+ return FALSE;
+ break;
+
+ /* This relocation describes which C++ vtable entries are actually
+ used. Record for later use during GC. */
+ case R_RISCV_GNU_VTENTRY:
+ BFD_ASSERT (h != NULL);
+ if (h != NULL
+ && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_offset))
+ return FALSE;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Refuse some position-dependent relocations when creating a
+ shared library. Do not refuse R_RISCV_32 / R_RISCV_64; they're
+ not PIC, but we can create dynamic relocations and the result
+ will be fine. Also do not refuse R_RISCV_LO16, which can be
+ combined with R_RISCV_GOT16. */
+ if (info->shared)
+ {
+ switch (r_type)
+ {
+ case R_MIPS16_HI16:
+ case R_RISCV_HI16:
+ /* Don't refuse a high part relocation if it's against
+ no symbol (e.g. part of a compound relocation). */
+ if (r_symndx == STN_UNDEF)
+ break;
+
+ howto = MIPS_ELF_RTYPE_TO_HOWTO (abfd, r_type, FALSE);
+ (*_bfd_error_handler)
+ (_("%B: relocation %s against `%s' can not be used when making a shared object; recompile with -fPIC"),
+ abfd, howto->name,
+ (h) ? h->root.root.string : "a local symbol");
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ default:
+ break;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+bfd_boolean
+_bfd_riscv_relax_section (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec ATTRIBUTE_UNUSED,
+ struct bfd_link_info *link_info ATTRIBUTE_UNUSED,
+ bfd_boolean *again)
+{
+ *again = FALSE;
+ return TRUE;
+}
+
+/* Allocate space for global sym dynamic relocs. */
+
+static bfd_boolean
+allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
+{
+ struct bfd_link_info *info = inf;
+ bfd *dynobj;
+ struct mips_elf_link_hash_entry *hmips;
+ struct mips_elf_link_hash_table *htab;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ dynobj = elf_hash_table (info)->dynobj;
+ hmips = (struct mips_elf_link_hash_entry *) h;
+
+ /* Ignore indirect and warning symbols. All relocations against
+ such symbols will be redirected to the target symbol. */
+ if (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ return TRUE;
+
+ /* If this symbol is defined in a dynamic object, or we are creating
+ a shared library, we will need to copy any R_RISCV_32 or
+ R_RISCV_REL32 relocs against it into the output file. */
+ if (! info->relocatable
+ && hmips->possibly_dynamic_relocs != 0
+ && (h->root.type == bfd_link_hash_defweak
+ || !h->def_regular
+ || info->shared))
+ {
+ bfd_boolean do_copy = TRUE;
+
+ if (h->root.type == bfd_link_hash_undefweak)
+ {
+ /* Do not copy relocations for undefined weak symbols with
+ non-default visibility. */
+ if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+ do_copy = FALSE;
+
+ /* 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;
+ }
+ }
+
+ if (do_copy)
+ {
+ /* Even though we don't directly need a GOT entry for this symbol,
+ the SVR4 psABI requires it to have a dynamic symbol table
+ index greater that DT_MIPS_GOTSYM if there are dynamic
+ relocations against it. */
+ if (hmips->global_got_area > GGA_RELOC_ONLY)
+ hmips->global_got_area = GGA_RELOC_ONLY;
+
+ mips_elf_allocate_dynamic_relocations
+ (dynobj, info, hmips->possibly_dynamic_relocs);
+ if (hmips->readonly_reloc)
+ /* We tell the dynamic linker that there are relocations
+ against the text segment. */
+ info->flags |= DF_TEXTREL;
+ }
+ }
+
+ 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. */
+
+bfd_boolean
+_bfd_riscv_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
+ struct elf_link_hash_entry *h)
+{
+ bfd *dynobj;
+ struct mips_elf_link_hash_entry *hmips;
+ struct mips_elf_link_hash_table *htab;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ dynobj = elf_hash_table (info)->dynobj;
+ hmips = (struct mips_elf_link_hash_entry *) h;
+
+ /* Make sure we know what is going on here. */
+ BFD_ASSERT (dynobj != NULL
+ && (h->needs_plt
+ || h->u.weakdef != NULL
+ || (h->def_dynamic
+ && h->ref_regular
+ && !h->def_regular)));
+
+ hmips = (struct mips_elf_link_hash_entry *) h;
+
+ /* As above, VxWorks requires PLT entries for externally-defined
+ functions that are only accessed through call relocations.
+
+ Both VxWorks and non-VxWorks targets also need PLT entries if there
+ are static-only relocations against an externally-defined function.
+ This can technically occur for shared libraries if there are
+ branches to the symbol, although it is unlikely that this will be
+ used in practice due to the short ranges involved. It can occur
+ for any relative or absolute relocation in executables; in that
+ case, the PLT entry becomes the function's canonical address. */
+ if (h->type == STT_FUNC && hmips->has_static_relocs
+ && !SYMBOL_CALLS_LOCAL (info, h)
+ && !(ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+ && h->root.type == bfd_link_hash_undefweak))
+ {
+ /* If this is the first symbol to need a PLT entry, allocate room
+ for the header. */
+ if (htab->splt->size == 0)
+ {
+ BFD_ASSERT (htab->sgotplt->size == 0);
+
+ /* If we're using the PLT additions to the psABI, each PLT
+ entry is 16 bytes and the PLT0 entry is 32 bytes.
+ Encourage better cache usage by aligning. We do this
+ lazily to avoid pessimizing traditional objects. */
+ if (!bfd_set_section_alignment (dynobj, htab->splt, 4))
+ return FALSE;
+
+ /* Make sure that .got.plt is word-aligned. We do this lazily
+ for the same reason as above. */
+ if (!bfd_set_section_alignment (dynobj, htab->sgotplt,
+ MIPS_ELF_LOG_FILE_ALIGN (dynobj)))
+ return FALSE;
+
+ htab->splt->size += htab->plt_header_size;
+
+ /* The last and first two entries in .got.plt are reserved. */
+ htab->sgotplt->size += 3 * MIPS_ELF_GOT_SIZE (dynobj);
+ }
+
+ /* Assign the next .plt entry to this symbol. */
+ h->plt.offset = htab->splt->size;
+ htab->splt->size += htab->plt_entry_size;
+
+ /* If the output file has no definition of the symbol, set the
+ symbol's value to the address of the stub. */
+ if (!h->def_regular)
+ {
+ h->root.u.def.section = htab->splt;
+ h->root.u.def.value = h->plt.offset;
+ }
+
+ /* Make room for the .got.plt entry and the R_RISCV_JUMP_SLOT
+ relocation. */
+ htab->sgotplt->size += MIPS_ELF_GOT_SIZE (dynobj);
+ htab->srelplt->size += MIPS_ELF_REL_SIZE (dynobj);
+
+ /* All relocations against this symbol that could have been made
+ dynamic will now refer to the PLT entry instead. */
+ hmips->possibly_dynamic_relocs = 0;
+
+ return TRUE;
+ }
+
+ /* If this is a weak symbol, and there is a real definition, the
+ processor independent code will have arranged for us to see the
+ real definition first, and we can just use the same value. */
+ if (h->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;
+ }
+
+ /* Otherwise, there is nothing further to do for symbols defined
+ in regular objects. */
+ if (h->def_regular)
+ return TRUE;
+
+ /* There's also nothing more to do if we'll convert all relocations
+ against this symbol into dynamic relocations. */
+ if (!hmips->has_static_relocs)
+ 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. */
+
+ if ((h->root.u.def.section->flags & SEC_ALLOC) != 0)
+ {
+ mips_elf_allocate_dynamic_relocations (dynobj, info, 1);
+ h->needs_copy = 1;
+ }
+
+ /* All relocations against this symbol that could have been made
+ dynamic will now refer to the local copy instead. */
+ hmips->possibly_dynamic_relocs = 0;
+
+ return _bfd_elf_adjust_dynamic_copy (h, htab->sdynbss);
+}
+
+/* This function is called after all the input files have been read,
+ and the input sections have been assigned to output sections. We
+ check for any mips16 stub sections that we can discard. */
+
+bfd_boolean
+_bfd_riscv_elf_always_size_sections (bfd *output_bfd,
+ struct bfd_link_info *info ATTRIBUTE_UNUSED)
+{
+ /* The .reginfo section has a fixed size. */
+ asection *ri = bfd_get_section_by_name (output_bfd, ".reginfo");
+ if (ri != NULL)
+ bfd_set_section_size (output_bfd, ri, sizeof (Elf32_External_RegInfo));
+ return TRUE;
+}
+
+/* If the link uses a GOT, lay it out and work out its size. */
+
+static bfd_boolean
+mips_elf_lay_out_got (bfd *output_bfd, struct bfd_link_info *info)
+{
+ bfd *dynobj;
+ asection *s;
+ struct mips_got_info *g;
+ bfd_size_type loadable_size = 0;
+ bfd_size_type page_gotno;
+ bfd *sub;
+ struct mips_elf_count_tls_arg count_tls_arg;
+ struct mips_elf_link_hash_table *htab;
+ struct mips_elf_count_tls_arg arg;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ s = htab->sgot;
+ if (s == NULL)
+ return TRUE;
+
+ dynobj = elf_hash_table (info)->dynobj;
+ g = htab->got_info;
+
+ /* Allocate room for the reserved entries. */
+ BFD_ASSERT (g->assigned_gotno == 0);
+ htab->reserved_gotno = 2;
+ g->local_gotno += htab->reserved_gotno;
+ g->assigned_gotno = htab->reserved_gotno;
+
+ /* Replace entries for indirect and warning symbols with entries for
+ the target symbol. */
+ if (!mips_elf_resolve_final_got_entries (g))
+ return FALSE;
+
+ /* Count the number of GOT symbols. */
+ mips_elf_link_hash_traverse (htab, mips_elf_count_got_symbols, info);
+
+ /* Calculate the total loadable size of the output. That
+ will give us the maximum number of GOT_PAGE entries
+ required. */
+ for (sub = info->input_bfds; sub; sub = sub->link_next)
+ {
+ asection *subsection;
+
+ for (subsection = sub->sections;
+ subsection;
+ subsection = subsection->next)
+ {
+ if ((subsection->flags & SEC_ALLOC) == 0)
+ continue;
+ loadable_size += ((subsection->size + 0xf)
+ &~ (bfd_size_type) 0xf);
+ }
+ }
+
+ /* Assume there are two loadable segments consisting of contiguous
+ sections. Is 5 enough? */
+ page_gotno = (loadable_size >> 16) + 5;
+
+ /* Choose the smaller of the two estimates; both are intended to be
+ conservative. */
+ if (page_gotno > g->page_gotno)
+ page_gotno = g->page_gotno;
+
+ g->local_gotno += page_gotno;
+ s->size += g->local_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
+ s->size += g->global_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
+
+ /* We need to calculate tls_gotno for global symbols at this point
+ instead of building it up earlier, to avoid doublecounting
+ entries for one global symbol from multiple input files. */
+ count_tls_arg.info = info;
+ count_tls_arg.needed = 0;
+ elf_link_hash_traverse (elf_hash_table (info),
+ mips_elf_count_global_tls_entries,
+ &count_tls_arg);
+ g->tls_gotno += count_tls_arg.needed;
+ s->size += g->tls_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
+
+ /* Set up TLS entries. */
+ g->tls_assigned_gotno = g->global_gotno + g->local_gotno;
+ htab_traverse (g->got_entries, mips_elf_initialize_tls_index, g);
+
+ /* Allocate room for the TLS relocations. */
+ arg.info = info;
+ arg.needed = 0;
+ htab_traverse (g->got_entries, mips_elf_count_local_tls_relocs, &arg);
+ elf_link_hash_traverse (elf_hash_table (info),
+ mips_elf_count_global_tls_relocs, &arg);
+ if (arg.needed)
+ mips_elf_allocate_dynamic_relocations (dynobj, info, arg.needed);
+
+ return TRUE;
+}
+
+/* Set the sizes of the dynamic sections. */
+
+bfd_boolean
+_bfd_riscv_elf_size_dynamic_sections (bfd *output_bfd,
+ struct bfd_link_info *info)
+{
+ bfd *dynobj;
+ asection *s, *sreldyn;
+ struct mips_elf_link_hash_table *htab;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+ dynobj = elf_hash_table (info)->dynobj;
+ BFD_ASSERT (dynobj != NULL);
+
+ if (elf_hash_table (info)->dynamic_sections_created)
+ {
+ /* Set the contents of the .interp section to the interpreter. */
+ if (info->executable)
+ {
+ s = bfd_get_section_by_name (dynobj, ".interp");
+ BFD_ASSERT (s != NULL);
+ s->size
+ = strlen (ELF_DYNAMIC_INTERPRETER (output_bfd)) + 1;
+ s->contents
+ = (bfd_byte *) ELF_DYNAMIC_INTERPRETER (output_bfd);
+ }
+
+ /* Create a symbol for the PLT, if we know that we are using it. */
+ if (htab->splt && htab->splt->size > 0 && htab->root.hplt == NULL)
+ {
+ struct elf_link_hash_entry *h;
+
+ h = _bfd_elf_define_linkage_sym (dynobj, info, htab->splt,
+ "_PROCEDURE_LINKAGE_TABLE_");
+ htab->root.hplt = h;
+ if (h == NULL)
+ return FALSE;
+ h->type = STT_FUNC;
+ }
+ }
+
+ /* Allocate space for global sym dynamic relocs. */
+ elf_link_hash_traverse (&htab->root, allocate_dynrelocs, (PTR) info);
+
+ if (!mips_elf_lay_out_got (output_bfd, info))
+ return FALSE;
+
+ /* 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)
+ {
+ const char *name;
+
+ /* It's OK to base decisions on the section name, because none
+ of the dynobj section names depend upon the input files. */
+ name = bfd_get_section_name (dynobj, s);
+
+ if ((s->flags & SEC_LINKER_CREATED) == 0)
+ continue;
+
+ if (CONST_STRNEQ (name, ".rel"))
+ {
+ if (s->size != 0)
+ {
+ /* We use the reloc_count field as a counter if we need
+ to copy relocs into the output file. */
+ if (strcmp (name, MIPS_ELF_REL_DYN_NAME (info)) != 0)
+ s->reloc_count = 0;
+
+ /* If combreloc is enabled, elf_link_sort_relocs() will
+ sort relocations, but in a different way than we do,
+ and before we're done creating relocations. Also, it
+ will move them around between input sections'
+ relocation's contents, so our sorting would be
+ broken, so don't let it run. */
+ info->combreloc = 0;
+ }
+ }
+ else if (s == htab->splt)
+ {
+ }
+ else if (! CONST_STRNEQ (name, ".init")
+ && s != htab->sgot
+ && s != htab->sgotplt
+ && s != htab->sdynbss)
+ {
+ /* It's not one of our sections, so don't allocate space. */
+ continue;
+ }
+
+ if (s->size == 0)
+ {
+ s->flags |= SEC_EXCLUDE;
+ continue;
+ }
+
+ if ((s->flags & SEC_HAS_CONTENTS) == 0)
+ continue;
+
+ /* Allocate memory for the section contents. */
+ s->contents = bfd_zalloc (dynobj, s->size);
+ if (s->contents == NULL)
+ {
+ bfd_set_error (bfd_error_no_memory);
+ return FALSE;
+ }
+ }
+
+ if (elf_hash_table (info)->dynamic_sections_created)
+ {
+ /* Add some entries to the .dynamic section. We fill in the
+ values later, in _bfd_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 may be filled in by the dynamic linker and
+ used by the debugger. */
+ if (info->executable
+ && !MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_DEBUG, 0))
+ return FALSE;
+
+ if ((info->flags & DF_TEXTREL) != 0)
+ {
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_TEXTREL, 0))
+ return FALSE;
+
+ /* Clear the DF_TEXTREL flag. It will be set again if we
+ write out an actual text relocation; we may not, because
+ at this point we do not know whether e.g. any .eh_frame
+ absolute relocations have been converted to PC-relative. */
+ info->flags &= ~DF_TEXTREL;
+ }
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_PLTGOT, 0))
+ return FALSE;
+
+ sreldyn = mips_elf_rel_dyn_section (info, FALSE);
+ {
+ if (sreldyn && sreldyn->size > 0)
+ {
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_REL, 0))
+ return FALSE;
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_RELSZ, 0))
+ return FALSE;
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_RELENT, 0))
+ return FALSE;
+ }
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_RLD_VERSION, 0))
+ return FALSE;
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_FLAGS, 0))
+ return FALSE;
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_BASE_ADDRESS, 0))
+ return FALSE;
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_LOCAL_GOTNO, 0))
+ return FALSE;
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_SYMTABNO, 0))
+ return FALSE;
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_UNREFEXTNO, 0))
+ return FALSE;
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_GOTSYM, 0))
+ return FALSE;
+ }
+ if (htab->splt->size > 0)
+ {
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_PLTREL, 0))
+ return FALSE;
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_JMPREL, 0))
+ return FALSE;
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_PLTRELSZ, 0))
+ return FALSE;
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_PLTGOT, 0))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/* REL is a relocation in INPUT_BFD that is being copied to OUTPUT_BFD.
+ Adjust its R_ADDEND field so that it is correct for the output file.
+ LOCAL_SYMS and LOCAL_SECTIONS are arrays of INPUT_BFD's local symbols
+ and sections respectively; both use symbol indexes. */
+
+static void
+mips_elf_adjust_addend (bfd *output_bfd, struct bfd_link_info *info,
+ bfd *input_bfd, Elf_Internal_Sym *local_syms,
+ asection **local_sections, Elf_Internal_Rela *rel)
+{
+ unsigned int r_type, r_symndx;
+ Elf_Internal_Sym *sym;
+ asection *sec;
+
+ if (mips_elf_local_relocation_p (input_bfd, rel, local_sections))
+ {
+ r_type = ELF_R_TYPE (output_bfd, rel->r_info);
+ if (r_type == R_MIPS16_GPREL
+ || r_type == R_RISCV_GPREL16
+ || r_type == R_RISCV_GPREL32
+ || r_type == R_RISCV_LITERAL)
+ {
+ rel->r_addend += _bfd_get_gp_value (input_bfd);
+ rel->r_addend -= _bfd_get_gp_value (output_bfd);
+ }
+
+ r_symndx = ELF_R_SYM (output_bfd, rel->r_info);
+ sym = local_syms + r_symndx;
+
+ /* Adjust REL's addend to account for section merging. */
+ if (!info->relocatable)
+ {
+ sec = local_sections[r_symndx];
+ _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
+ }
+
+ /* This would normally be done by the rela_normal code in elflink.c. */
+ if (ELF_ST_TYPE (sym->st_info) == STT_SECTION)
+ rel->r_addend += local_sections[r_symndx]->output_offset;
+ }
+}
+
+/* Relocate a MIPS ELF section. */
+
+bfd_boolean
+_bfd_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;
+ const Elf_Internal_Rela *relend;
+ bfd_vma addend = 0;
+ bfd_boolean use_saved_addend_p = FALSE;
+ const struct elf_backend_data *bed;
+
+ bed = get_elf_backend_data (output_bfd);
+ relend = relocs + input_section->reloc_count * bed->s->int_rels_per_ext_rel;
+ for (rel = relocs; rel < relend; ++rel)
+ {
+ const char *name;
+ bfd_vma value = 0;
+ reloc_howto_type *howto;
+ /* TRUE if the relocation is a RELA relocation, rather than a
+ REL relocation. */
+ bfd_boolean rela_relocation_p = TRUE;
+ unsigned int r_type = ELF_R_TYPE (output_bfd, rel->r_info);
+ const char *msg;
+ unsigned long r_symndx;
+ asection *sec;
+ Elf_Internal_Shdr *symtab_hdr;
+ struct elf_link_hash_entry *h;
+ bfd_boolean rel_reloc;
+
+ rel_reloc = mips_elf_rel_relocation_p (input_bfd, input_section,
+ relocs, rel);
+ /* Find the relocation howto for this relocation. */
+ howto = MIPS_ELF_RTYPE_TO_HOWTO (input_bfd, r_type, !rel_reloc);
+
+ r_symndx = ELF_R_SYM (input_bfd, rel->r_info);
+ symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+ if (mips_elf_local_relocation_p (input_bfd, rel, local_sections))
+ {
+ sec = local_sections[r_symndx];
+ h = NULL;
+ }
+ else
+ {
+ unsigned long extsymoff;
+
+ extsymoff = 0;
+ if (!elf_bad_symtab (input_bfd))
+ extsymoff = symtab_hdr->sh_info;
+ h = elf_sym_hashes (input_bfd) [r_symndx - extsymoff];
+ 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;
+
+ sec = NULL;
+ if (h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ sec = h->root.u.def.section;
+ }
+
+ if (sec != NULL && elf_discarded_section (sec))
+ RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
+ rel, relend, howto, contents);
+
+ if (!use_saved_addend_p)
+ {
+ /* If these relocations were originally of the REL variety,
+ we must pull the addend out of the field that will be
+ relocated. Otherwise, we simply use the contents of the
+ RELA relocation. */
+ if (mips_elf_rel_relocation_p (input_bfd, input_section,
+ relocs, rel))
+ {
+ rela_relocation_p = FALSE;
+ addend = mips_elf_read_rel_addend (input_bfd, rel,
+ howto, contents);
+ if (hi16_reloc_p (r_type)
+ || (got16_reloc_p (r_type)
+ && mips_elf_local_relocation_p (input_bfd, rel,
+ local_sections)))
+ {
+ if (!mips_elf_add_lo16_rel_addend (input_bfd, rel, relend,
+ contents, &addend))
+ {
+ if (h)
+ name = h->root.root.string;
+ else
+ name = bfd_elf_sym_name (input_bfd, symtab_hdr,
+ local_syms + r_symndx,
+ sec);
+ (*_bfd_error_handler)
+ (_("%B: Can't find matching LO16 reloc against `%s' for %s at 0x%lx in section `%A'"),
+ input_bfd, input_section, name, howto->name,
+ rel->r_offset);
+ }
+ }
+ else
+ addend <<= howto->rightshift;
+ }
+ else
+ addend = rel->r_addend;
+ mips_elf_adjust_addend (output_bfd, info, input_bfd,
+ local_syms, local_sections, rel);
+ }
+
+ if (info->relocatable)
+ {
+ if (!rela_relocation_p && rel->r_addend)
+ {
+ addend += rel->r_addend;
+ if (hi16_reloc_p (r_type) || got16_reloc_p (r_type))
+ addend = mips_elf_high (addend);
+ else
+ addend >>= howto->rightshift;
+
+ /* We use the source mask, rather than the destination
+ mask because the place to which we are writing will be
+ source of the addend in the final link. */
+ addend &= howto->src_mask;
+
+ if (! mips_elf_perform_relocation (info, howto, rel, addend,
+ input_bfd, input_section,
+ contents))
+ return FALSE;
+ }
+
+ /* Go on to the next relocation. */
+ continue;
+ }
+
+ /* In the N32 and 64-bit ABIs there may be multiple consecutive
+ relocations for the same offset. In that case we are
+ supposed to treat the output of each relocation as the addend
+ for the next. */
+ if (rel + 1 < relend
+ && rel->r_offset == rel[1].r_offset
+ && ELF_R_TYPE (input_bfd, rel[1].r_info) != R_RISCV_NONE)
+ use_saved_addend_p = TRUE;
+ else
+ use_saved_addend_p = FALSE;
+
+ /* Figure out what value we are supposed to relocate. */
+ switch (mips_elf_calculate_relocation (output_bfd, input_bfd,
+ input_section, info, rel,
+ addend, howto, local_syms,
+ local_sections, &value,
+ &name, use_saved_addend_p))
+ {
+ case bfd_reloc_continue:
+ /* There's nothing to do. */
+ continue;
+
+ case bfd_reloc_undefined:
+ /* mips_elf_calculate_relocation already called the
+ undefined_symbol callback. There's no real point in
+ trying to perform the relocation at this point, so we
+ just skip ahead to the next relocation. */
+ continue;
+
+ case bfd_reloc_notsupported:
+ msg = _("internal error: unsupported relocation error");
+ info->callbacks->warning
+ (info, msg, name, input_bfd, input_section, rel->r_offset);
+ return FALSE;
+
+ case bfd_reloc_overflow:
+ if (use_saved_addend_p)
+ /* Ignore overflow until we reach the last relocation for
+ a given location. */
+ ;
+ else
+ {
+ struct mips_elf_link_hash_table *htab;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+ BFD_ASSERT (name != NULL);
+ if (! ((*info->callbacks->reloc_overflow)
+ (info, NULL, name, howto->name, (bfd_vma) 0,
+ input_bfd, input_section, rel->r_offset)))
+ return FALSE;
+ }
+ break;
+
+ case bfd_reloc_ok:
+ break;
+
+ default:
+ abort ();
+ break;
+ }
+
+ /* If we've got another relocation for the address, keep going
+ until we reach the last one. */
+ if (use_saved_addend_p)
+ {
+ addend = value;
+ continue;
+ }
+
+ /* Actually perform the relocation. */
+ if (! mips_elf_perform_relocation (info, howto, rel, value,
+ input_bfd, input_section,
+ contents))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Finish up dynamic symbol handling. We set the contents of various
+ dynamic sections here. */
+
+bfd_boolean
+_bfd_riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
+ struct bfd_link_info *info,
+ struct elf_link_hash_entry *h,
+ Elf_Internal_Sym *sym)
+{
+ bfd *dynobj;
+ asection *sgot;
+ struct mips_got_info *g;
+ const char *name;
+ struct mips_elf_link_hash_table *htab;
+ struct mips_elf_link_hash_entry *hmips;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+ dynobj = elf_hash_table (info)->dynobj;
+ hmips = (struct mips_elf_link_hash_entry *) h;
+
+ if (h->plt.offset != MINUS_ONE)
+ {
+ /* We've decided to create a PLT entry for this symbol. */
+ bfd_byte *loc;
+ bfd_vma header_address, plt_index, got_address;
+ bfd_vma plt_entry[RISCV_PLT_ENTRY_INSNS];
+ int i;
+
+ BFD_ASSERT (h->dynindx != -1);
+ BFD_ASSERT (htab->splt != NULL);
+ BFD_ASSERT (h->plt.offset <= htab->splt->size);
+ BFD_ASSERT (!h->def_regular);
+
+ /* Calculate the address of the PLT header. */
+ header_address = (htab->splt->output_section->vma
+ + htab->splt->output_offset);
+
+ /* Calculate the index of the entry. */
+ plt_index = ((h->plt.offset - htab->plt_header_size)
+ / htab->plt_entry_size);
+
+ /* Calculate the address of the .got.plt entry. */
+ got_address = (htab->sgotplt->output_section->vma
+ + htab->sgotplt->output_offset
+ + (2 + plt_index) * MIPS_ELF_GOT_SIZE (dynobj));
+
+ /* Initially point the .got.plt entry at the PLT header. */
+ loc = (htab->sgotplt->contents
+ + (2 + plt_index) * MIPS_ELF_GOT_SIZE (dynobj));
+ if (ABI_64_P (output_bfd))
+ bfd_put_64 (output_bfd, header_address, loc);
+ else
+ bfd_put_32 (output_bfd, header_address, loc);
+
+ /* Find out where the .plt entry should go. */
+ loc = htab->splt->contents + h->plt.offset;
+
+ /* Fill in the PLT entry itself. */
+ riscv_make_plt_entry (output_bfd, got_address,
+ header_address + h->plt.offset, plt_entry);
+ for (i = 0; i < RISCV_PLT_ENTRY_INSNS; i++)
+ bfd_put_32 (output_bfd, plt_entry[i], loc + 4*i);
+
+ /* Emit an R_RISCV_JUMP_SLOT relocation against the .got.plt entry. */
+ mips_elf_output_dynamic_relocation (output_bfd, htab->srelplt,
+ plt_index, h->dynindx,
+ R_RISCV_JUMP_SLOT, got_address);
+
+ /* We distinguish between PLT entries and lazy-binding stubs by
+ giving the former an st_other value of STO_MIPS_PLT. Set the
+ flag and leave the value if there are any relocations in the
+ binary where pointer equality matters. */
+ sym->st_shndx = SHN_UNDEF;
+ if (h->pointer_equality_needed)
+ sym->st_other = STO_MIPS_PLT;
+ else
+ sym->st_value = 0;
+ }
+
+ BFD_ASSERT (h->dynindx != -1
+ || h->forced_local);
+
+ sgot = htab->sgot;
+ g = htab->got_info;
+ BFD_ASSERT (g != NULL);
+
+ /* Run through the global symbol table, creating GOT entries for all
+ the symbols that need them. */
+ if (hmips->global_got_area != GGA_NONE)
+ {
+ bfd_vma offset;
+ bfd_vma value;
+
+ value = sym->st_value;
+ offset = mips_elf_global_got_index (dynobj, h, R_RISCV_GOT16, info);
+ MIPS_ELF_PUT_WORD (output_bfd, value, sgot->contents + offset);
+ }
+
+ /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. */
+ name = h->root.root.string;
+ if (strcmp (name, "_DYNAMIC") == 0
+ || h == elf_hash_table (info)->hgot)
+ sym->st_shndx = SHN_ABS;
+ else if (strcmp (name, "_DYNAMIC_LINKING") == 0)
+ {
+ sym->st_shndx = SHN_ABS;
+ sym->st_info = ELF_ST_INFO (STB_GLOBAL, STT_SECTION);
+ sym->st_value = 1;
+ }
+
+ /* Emit a copy reloc, if needed. */
+ if (h->needs_copy)
+ {
+ asection *s;
+ bfd_vma symval;
+
+ BFD_ASSERT (h->dynindx != -1);
+
+ s = mips_elf_rel_dyn_section (info, FALSE);
+ symval = (h->root.u.def.section->output_section->vma
+ + h->root.u.def.section->output_offset
+ + h->root.u.def.value);
+ mips_elf_output_dynamic_relocation (output_bfd, s, s->reloc_count++,
+ h->dynindx, R_RISCV_COPY, symval);
+ }
+
+ return TRUE;
+}
+
+/* Write out a plt0 entry to the beginning of .plt. */
+
+static void
+mips_finish_exec_plt (bfd *output_bfd, struct bfd_link_info *info)
+{
+ bfd_byte *loc;
+ bfd_vma gotplt_value, plt_address;
+ bfd_vma plt_entry[RISCV_PLT0_ENTRY_INSNS];
+ struct mips_elf_link_hash_table *htab;
+ int i;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ plt_address = htab->splt->output_section->vma + htab->splt->output_offset;
+ /* Calculate the value of .got.plt. */
+ gotplt_value = (htab->sgotplt->output_section->vma
+ + htab->sgotplt->output_offset);
+
+ /* Install the PLT header. */
+ loc = htab->splt->contents;
+ riscv_make_plt0_entry (output_bfd, gotplt_value, plt_address, plt_entry);
+ for (i = 0; i < RISCV_PLT0_ENTRY_INSNS; i++)
+ bfd_put_32 (output_bfd, plt_entry[i], loc + 4*i);
+}
+
+/* Finish up the dynamic sections. */
+
+bfd_boolean
+_bfd_riscv_elf_finish_dynamic_sections (bfd *output_bfd,
+ struct bfd_link_info *info)
+{
+ bfd *dynobj;
+ asection *sdyn;
+ asection *sgot;
+ struct mips_got_info *gg, *g;
+ struct mips_elf_link_hash_table *htab;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ dynobj = elf_hash_table (info)->dynobj;
+
+ sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
+
+ sgot = htab->sgot;
+ g = gg = htab->got_info;
+
+ if (elf_hash_table (info)->dynamic_sections_created)
+ {
+ bfd_byte *b;
+ int dyn_to_skip = 0, dyn_skipped = 0;
+
+ BFD_ASSERT (sdyn != NULL);
+ BFD_ASSERT (gg != NULL);
+
+ for (b = sdyn->contents;
+ b < sdyn->contents + sdyn->size;
+ b += MIPS_ELF_DYN_SIZE (dynobj))
+ {
+ Elf_Internal_Dyn dyn;
+ const char *name;
+ size_t elemsize;
+ asection *s;
+ bfd_boolean swap_out_p;
+
+ /* Read in the current dynamic entry. */
+ (*get_elf_backend_data (dynobj)->s->swap_dyn_in) (dynobj, b, &dyn);
+
+ /* Assume that we're going to modify it and write it out. */
+ swap_out_p = TRUE;
+
+ switch (dyn.d_tag)
+ {
+ case DT_RELENT:
+ dyn.d_un.d_val = MIPS_ELF_REL_SIZE (dynobj);
+ break;
+
+ case DT_STRSZ:
+ /* Rewrite DT_STRSZ. */
+ dyn.d_un.d_val =
+ _bfd_elf_strtab_size (elf_hash_table (info)->dynstr);
+ break;
+
+ case DT_PLTGOT:
+ s = htab->sgot;
+ dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
+ break;
+
+ case DT_MIPS_PLTGOT:
+ s = htab->sgotplt;
+ dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
+ break;
+
+ case DT_MIPS_RLD_VERSION:
+ dyn.d_un.d_val = 1; /* XXX */
+ break;
+
+ case DT_MIPS_FLAGS:
+ dyn.d_un.d_val = RHF_NOTPOT; /* XXX */
+ break;
+
+ case DT_MIPS_TIME_STAMP:
+ {
+ time_t t;
+ time (&t);
+ dyn.d_un.d_val = t;
+ }
+ break;
+
+ case DT_MIPS_ICHECKSUM:
+ /* XXX FIXME: */
+ swap_out_p = FALSE;
+ break;
+
+ case DT_MIPS_IVERSION:
+ /* XXX FIXME: */
+ swap_out_p = FALSE;
+ break;
+
+ case DT_MIPS_BASE_ADDRESS:
+ s = output_bfd->sections;
+ BFD_ASSERT (s != NULL);
+ dyn.d_un.d_ptr = s->vma & ~(bfd_vma) (RISCV_IMM_REACH-1);
+ break;
+
+ case DT_MIPS_LOCAL_GOTNO:
+ dyn.d_un.d_val = g->local_gotno;
+ break;
+
+ case DT_MIPS_UNREFEXTNO:
+ /* The index into the dynamic symbol table which is the
+ entry of the first external symbol that is not
+ referenced within the same object. */
+ dyn.d_un.d_val = bfd_count_sections (output_bfd) + 1;
+ break;
+
+ case DT_MIPS_GOTSYM:
+ if (gg->global_gotsym)
+ {
+ dyn.d_un.d_val = gg->global_gotsym->dynindx;
+ break;
+ }
+ /* In case if we don't have global got symbols we default
+ to setting DT_MIPS_GOTSYM to the same value as
+ DT_MIPS_SYMTABNO, so we just fall through. */
+
+ case DT_MIPS_SYMTABNO:
+ name = ".dynsym";
+ elemsize = MIPS_ELF_SYM_SIZE (output_bfd);
+ s = bfd_get_section_by_name (output_bfd, name);
+ BFD_ASSERT (s != NULL);
+
+ dyn.d_un.d_val = s->size / elemsize;
+ break;
+
+ case DT_MIPS_HIPAGENO:
+ dyn.d_un.d_val = g->local_gotno - htab->reserved_gotno;
+ break;
+
+ case DT_MIPS_OPTIONS:
+ s = (bfd_get_section_by_name
+ (output_bfd, MIPS_ELF_OPTIONS_SECTION_NAME (output_bfd)));
+ dyn.d_un.d_ptr = s->vma;
+ break;
+
+ case DT_PLTREL:
+ dyn.d_un.d_val = DT_REL;
+ break;
+
+ case DT_PLTRELSZ:
+ dyn.d_un.d_val = htab->srelplt->size;
+ break;
+
+ case DT_JMPREL:
+ dyn.d_un.d_ptr = (htab->srelplt->output_section->vma
+ + htab->srelplt->output_offset);
+ break;
+
+ case DT_TEXTREL:
+ /* If we didn't need any text relocations after all, delete
+ the dynamic tag. */
+ if (!(info->flags & DF_TEXTREL))
+ {
+ dyn_to_skip = MIPS_ELF_DYN_SIZE (dynobj);
+ swap_out_p = FALSE;
+ }
+ break;
+
+ case DT_FLAGS:
+ /* If we didn't need any text relocations after all, clear
+ DF_TEXTREL from DT_FLAGS. */
+ if (!(info->flags & DF_TEXTREL))
+ dyn.d_un.d_val &= ~DF_TEXTREL;
+ else
+ swap_out_p = FALSE;
+ break;
+
+ default:
+ swap_out_p = FALSE;
+ break;
+ }
+
+ if (swap_out_p || dyn_skipped)
+ (*get_elf_backend_data (dynobj)->s->swap_dyn_out)
+ (dynobj, &dyn, b - dyn_skipped);
+
+ if (dyn_to_skip)
+ {
+ dyn_skipped += dyn_to_skip;
+ dyn_to_skip = 0;
+ }
+ }
+
+ /* Wipe out any trailing entries if we shifted down a dynamic tag. */
+ if (dyn_skipped > 0)
+ memset (b - dyn_skipped, 0, dyn_skipped);
+ }
+
+ if (sgot != NULL && sgot->size > 0
+ && !bfd_is_abs_section (sgot->output_section))
+ {
+ /* The first entry of the global offset table will be filled at
+ runtime. The second entry will be used by some runtime loaders.
+ This isn't the case of IRIX rld. */
+ MIPS_ELF_PUT_WORD (output_bfd, (bfd_vma) 0, sgot->contents);
+ MIPS_ELF_PUT_WORD (output_bfd, MIPS_ELF_GNU_GOT1_MASK (output_bfd),
+ sgot->contents + MIPS_ELF_GOT_SIZE (output_bfd));
+
+ elf_section_data (sgot->output_section)->this_hdr.sh_entsize
+ = MIPS_ELF_GOT_SIZE (output_bfd);
+ }
+
+ /* The generation of dynamic relocations for the non-primary gots
+ adds more dynamic relocations. We cannot count them until
+ here. */
+
+ if (elf_hash_table (info)->dynamic_sections_created)
+ {
+ bfd_byte *b;
+ bfd_boolean swap_out_p;
+
+ BFD_ASSERT (sdyn != NULL);
+
+ for (b = sdyn->contents;
+ b < sdyn->contents + sdyn->size;
+ b += MIPS_ELF_DYN_SIZE (dynobj))
+ {
+ Elf_Internal_Dyn dyn;
+ asection *s;
+
+ /* Read in the current dynamic entry. */
+ (*get_elf_backend_data (dynobj)->s->swap_dyn_in) (dynobj, b, &dyn);
+
+ /* Assume that we're going to modify it and write it out. */
+ swap_out_p = TRUE;
+
+ switch (dyn.d_tag)
+ {
+ case DT_RELSZ:
+ /* Reduce DT_RELSZ to account for any relocations we
+ decided not to make. This is for the n64 irix rld,
+ which doesn't seem to apply any relocations if there
+ are trailing null entries. */
+ s = mips_elf_rel_dyn_section (info, FALSE);
+ dyn.d_un.d_val = (s->reloc_count
+ * (ABI_64_P (output_bfd)
+ ? sizeof (Elf64_RISCV_External_Rel)
+ : sizeof (Elf32_External_Rel)));
+ /* Adjust the section size too. Tools like the prelinker
+ can reasonably expect the values to the same. */
+ elf_section_data (s->output_section)->this_hdr.sh_size
+ = dyn.d_un.d_val;
+ break;
+
+ default:
+ swap_out_p = FALSE;
+ break;
+ }
+
+ if (swap_out_p)
+ (*get_elf_backend_data (dynobj)->s->swap_dyn_out)
+ (dynobj, &dyn, b);
+ }
+ }
+
+ {
+ asection *s;
+
+ /* The psABI says that the dynamic relocations must be sorted in
+ increasing order of r_symndx. */
+ s = mips_elf_rel_dyn_section (info, FALSE);
+ if (s != NULL && s->size > (bfd_vma)2 * MIPS_ELF_REL_SIZE (output_bfd))
+ {
+ reldyn_sorting_bfd = output_bfd;
+
+ if (ABI_64_P (output_bfd))
+ qsort ((Elf64_External_Rel *) s->contents + 1,
+ s->reloc_count - 1, sizeof (Elf64_RISCV_External_Rel),
+ sort_dynamic_relocs_64);
+ else
+ qsort ((Elf32_External_Rel *) s->contents + 1,
+ s->reloc_count - 1, sizeof (Elf32_External_Rel),
+ sort_dynamic_relocs);
+ }
+ }
+
+ if (htab->splt && htab->splt->size > 0)
+ mips_finish_exec_plt (output_bfd, info);
+ return TRUE;
+}
+
+
+/* Set ABFD's EF_MIPS_ARCH and EF_MIPS_MACH flags. */
+
+static void
+mips_set_isa_flags (bfd *abfd)
+{
+ flagword val;
+
+ switch (bfd_get_mach (abfd))
+ {
+ default:
+ case bfd_mach_riscv_rocket64:
+ val = E_RISCV_ARCH_RV64 | E_RISCV_MACH_ROCKET64;
+ break;
+
+ case bfd_mach_riscv_rocket32:
+ val = E_RISCV_ARCH_RV32 | E_RISCV_MACH_ROCKET32;
+ break;
+ }
+
+ elf_elfheader (abfd)->e_flags &= ~(EF_MIPS_ARCH | EF_MIPS_MACH);
+ elf_elfheader (abfd)->e_flags |= val;
+}
+
+
+/* The final processing done just before writing out a MIPS ELF object
+ file. This gets the MIPS architecture right based on the machine
+ number. This is used by both the 32-bit and the 64-bit ABI. */
+
+void
+_bfd_riscv_elf_final_write_processing (bfd *abfd,
+ bfd_boolean linker ATTRIBUTE_UNUSED)
+{
+ unsigned int i;
+ Elf_Internal_Shdr **hdrpp;
+ const char *name;
+ asection *sec;
+
+ /* Keep the existing EF_MIPS_MACH and EF_MIPS_ARCH flags if the former
+ is nonzero. This is for compatibility with old objects, which used
+ a combination of a 32-bit EF_MIPS_ARCH and a 64-bit EF_MIPS_MACH. */
+ if ((elf_elfheader (abfd)->e_flags & EF_MIPS_MACH) == 0)
+ mips_set_isa_flags (abfd);
+
+ /* Set the sh_info field for .gptab sections and other appropriate
+ info for each special section. */
+ for (i = 1, hdrpp = elf_elfsections (abfd) + 1;
+ i < elf_numsections (abfd);
+ i++, hdrpp++)
+ {
+ switch ((*hdrpp)->sh_type)
+ {
+ case SHT_MIPS_MSYM:
+ case SHT_MIPS_LIBLIST:
+ sec = bfd_get_section_by_name (abfd, ".dynstr");
+ if (sec != NULL)
+ (*hdrpp)->sh_link = elf_section_data (sec)->this_idx;
+ break;
+
+ case SHT_MIPS_GPTAB:
+ BFD_ASSERT (FALSE);
+
+ case SHT_MIPS_CONTENT:
+ BFD_ASSERT ((*hdrpp)->bfd_section != NULL);
+ name = bfd_get_section_name (abfd, (*hdrpp)->bfd_section);
+ BFD_ASSERT (name != NULL
+ && CONST_STRNEQ (name, ".MIPS.content"));
+ sec = bfd_get_section_by_name (abfd,
+ name + sizeof ".MIPS.content" - 1);
+ BFD_ASSERT (sec != NULL);
+ (*hdrpp)->sh_link = elf_section_data (sec)->this_idx;
+ break;
+
+ case SHT_MIPS_SYMBOL_LIB:
+ sec = bfd_get_section_by_name (abfd, ".dynsym");
+ if (sec != NULL)
+ (*hdrpp)->sh_link = elf_section_data (sec)->this_idx;
+ sec = bfd_get_section_by_name (abfd, ".liblist");
+ if (sec != NULL)
+ (*hdrpp)->sh_info = elf_section_data (sec)->this_idx;
+ break;
+
+ case SHT_MIPS_EVENTS:
+ BFD_ASSERT ((*hdrpp)->bfd_section != NULL);
+ name = bfd_get_section_name (abfd, (*hdrpp)->bfd_section);
+ BFD_ASSERT (name != NULL);
+ if (CONST_STRNEQ (name, ".MIPS.events"))
+ sec = bfd_get_section_by_name (abfd,
+ name + sizeof ".MIPS.events" - 1);
+ else
+ {
+ BFD_ASSERT (CONST_STRNEQ (name, ".MIPS.post_rel"));
+ sec = bfd_get_section_by_name (abfd,
+ (name
+ + sizeof ".MIPS.post_rel" - 1));
+ }
+ BFD_ASSERT (sec != NULL);
+ (*hdrpp)->sh_link = elf_section_data (sec)->this_idx;
+ break;
+
+ }
+ }
+}
+
+/* When creating an IRIX5 executable, we need REGINFO and RTPROC
+ segments. */
+
+int
+_bfd_riscv_elf_additional_program_headers (bfd *abfd,
+ struct bfd_link_info *info ATTRIBUTE_UNUSED)
+{
+ asection *s;
+ int ret = 0;
+
+ /* See if we need a PT_MIPS_REGINFO segment. */
+ s = bfd_get_section_by_name (abfd, ".reginfo");
+ if (s && (s->flags & SEC_LOAD))
+ ++ret;
+
+ /* Allocate a PT_NULL header in dynamic objects. See
+ _bfd_riscv_elf_modify_segment_map for details. */
+ if (bfd_get_section_by_name (abfd, ".dynamic"))
+ ++ret;
+
+ return ret;
+}
+
+/* Modify the segment map for an IRIX5 executable. */
+
+bfd_boolean
+_bfd_riscv_elf_modify_segment_map (bfd *abfd,
+ struct bfd_link_info *info)
+{
+ asection *s;
+ struct elf_segment_map *m, **pm;
+ bfd_size_type amt;
+
+ /* If there is a .reginfo section, we need a PT_MIPS_REGINFO
+ segment. */
+ s = bfd_get_section_by_name (abfd, ".reginfo");
+ if (s != NULL && (s->flags & SEC_LOAD) != 0)
+ {
+ for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
+ if (m->p_type == PT_MIPS_REGINFO)
+ break;
+ if (m == NULL)
+ {
+ amt = sizeof *m;
+ m = bfd_zalloc (abfd, amt);
+ if (m == NULL)
+ return FALSE;
+
+ m->p_type = PT_MIPS_REGINFO;
+ m->count = 1;
+ m->sections[0] = s;
+
+ /* We want to put it after the PHDR and INTERP segments. */
+ pm = &elf_tdata (abfd)->segment_map;
+ while (*pm != NULL
+ && ((*pm)->p_type == PT_PHDR
+ || (*pm)->p_type == PT_INTERP))
+ pm = &(*pm)->next;
+
+ m->next = *pm;
+ *pm = m;
+ }
+ }
+
+ {
+ /* On IRIX5, the PT_DYNAMIC segment includes the .dynamic,
+ .dynstr, .dynsym, and .hash sections, and everything in
+ between. */
+ for (pm = &elf_tdata (abfd)->segment_map; *pm != NULL;
+ pm = &(*pm)->next)
+ if ((*pm)->p_type == PT_DYNAMIC)
+ break;
+ m = *pm;
+ if (m != NULL)
+ {
+ /* For a normal mips executable the permissions for the PT_DYNAMIC
+ segment are read, write and execute. We do that here since
+ the code in elf.c sets only the read permission. This matters
+ sometimes for the dynamic linker. */
+ if (bfd_get_section_by_name (abfd, ".dynamic") != NULL)
+ {
+ m->p_flags = PF_R | PF_W | PF_X;
+ m->p_flags_valid = 1;
+ }
+ }
+ }
+
+ /* Allocate a spare program header in dynamic objects so that tools
+ like the prelinker can add an extra PT_LOAD entry.
+
+ If the prelinker needs to make room for a new PT_LOAD entry, its
+ standard procedure is to move the first (read-only) sections into
+ the new (writable) segment. However, the MIPS ABI requires
+ .dynamic to be in a read-only segment, and the section will often
+ start within sizeof (ElfNN_Phdr) bytes of the last program header.
+
+ Although the prelinker could in principle move .dynamic to a
+ writable segment, it seems better to allocate a spare program
+ header instead, and avoid the need to move any sections.
+ There is a long tradition of allocating spare dynamic tags,
+ so allocating a spare program header seems like a natural
+ extension.
+
+ If INFO is NULL, we may be copying an already prelinked binary
+ with objcopy or strip, so do not add this header. */
+ if (info != NULL
+ && bfd_get_section_by_name (abfd, ".dynamic"))
+ {
+ for (pm = &elf_tdata (abfd)->segment_map; *pm != NULL; pm = &(*pm)->next)
+ if ((*pm)->p_type == PT_NULL)
+ break;
+ if (*pm == NULL)
+ {
+ m = bfd_zalloc (abfd, sizeof (*m));
+ if (m == NULL)
+ return FALSE;
+
+ m->p_type = PT_NULL;
+ *pm = m;
+ }
+ }
+
+ return TRUE;
+}
+
+/* Return the section that should be marked against GC for a given
+ relocation. */
+
+asection *
+_bfd_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)
+{
+ /* ??? Do mips16 stub sections need to be handled special? */
+
+ if (h != NULL)
+ switch (ELF_R_TYPE (sec->owner, 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. */
+
+bfd_boolean
+_bfd_riscv_elf_gc_sweep_hook (bfd *abfd ATTRIBUTE_UNUSED,
+ struct bfd_link_info *info ATTRIBUTE_UNUSED,
+ asection *sec ATTRIBUTE_UNUSED,
+ const Elf_Internal_Rela *relocs ATTRIBUTE_UNUSED)
+{
+ return TRUE;
+}
+
+/* Copy data from a MIPS ELF indirect symbol to its direct symbol,
+ hiding the old indirect symbol. Process additional relocation
+ information. Also called for weakdefs, in which case we just let
+ _bfd_elf_link_hash_copy_indirect copy the flags for us. */
+
+void
+_bfd_riscv_elf_copy_indirect_symbol (struct bfd_link_info *info,
+ struct elf_link_hash_entry *dir,
+ struct elf_link_hash_entry *ind)
+{
+ struct mips_elf_link_hash_entry *dirmips, *indmips;
+
+ _bfd_elf_link_hash_copy_indirect (info, dir, ind);
+
+ dirmips = (struct mips_elf_link_hash_entry *) dir;
+ indmips = (struct mips_elf_link_hash_entry *) ind;
+ /* Any absolute non-dynamic relocations against an indirect or weak
+ definition will be against the target symbol. */
+ if (indmips->has_static_relocs)
+ dirmips->has_static_relocs = TRUE;
+
+ if (ind->root.type != bfd_link_hash_indirect)
+ return;
+
+ dirmips->possibly_dynamic_relocs += indmips->possibly_dynamic_relocs;
+ if (indmips->readonly_reloc)
+ dirmips->readonly_reloc = TRUE;
+ if (indmips->global_got_area < dirmips->global_got_area)
+ dirmips->global_got_area = indmips->global_got_area;
+ if (indmips->global_got_area < GGA_NONE)
+ indmips->global_got_area = GGA_NONE;
+
+ if (dirmips->tls_type == 0)
+ dirmips->tls_type = indmips->tls_type;
+}
+
+#define PDR_SIZE 32
+
+bfd_boolean
+_bfd_riscv_elf_discard_info (bfd *abfd, struct elf_reloc_cookie *cookie,
+ struct bfd_link_info *info)
+{
+ asection *o;
+ bfd_boolean ret = FALSE;
+ unsigned char *tdata;
+ size_t i, skip;
+
+ o = bfd_get_section_by_name (abfd, ".pdr");
+ if (! o)
+ return FALSE;
+ if (o->size == 0)
+ return FALSE;
+ if (o->size % PDR_SIZE != 0)
+ return FALSE;
+ if (o->output_section != NULL
+ && bfd_is_abs_section (o->output_section))
+ return FALSE;
+
+ tdata = bfd_zmalloc (o->size / PDR_SIZE);
+ if (! tdata)
+ return FALSE;
+
+ cookie->rels = _bfd_elf_link_read_relocs (abfd, o, NULL, NULL,
+ info->keep_memory);
+ if (!cookie->rels)
+ {
+ free (tdata);
+ return FALSE;
+ }
+
+ cookie->rel = cookie->rels;
+ cookie->relend = cookie->rels + o->reloc_count;
+
+ for (i = 0, skip = 0; i < o->size / PDR_SIZE; i ++)
+ {
+ if (bfd_elf_reloc_symbol_deleted_p (i * PDR_SIZE, cookie))
+ {
+ tdata[i] = 1;
+ skip ++;
+ }
+ }
+
+ if (skip != 0)
+ {
+ mips_elf_section_data (o)->u.tdata = tdata;
+ o->size -= skip * PDR_SIZE;
+ ret = TRUE;
+ }
+ else
+ free (tdata);
+
+ if (! info->keep_memory)
+ free (cookie->rels);
+
+ return ret;
+}
+
+bfd_boolean
+_bfd_riscv_elf_ignore_discarded_relocs (asection *sec)
+{
+ if (strcmp (sec->name, ".pdr") == 0)
+ return TRUE;
+ return FALSE;
+}
+
+bfd_boolean
+_bfd_riscv_elf_write_section (bfd *output_bfd,
+ struct bfd_link_info *link_info ATTRIBUTE_UNUSED,
+ asection *sec, bfd_byte *contents)
+{
+ bfd_byte *to, *from, *end;
+ int i;
+
+ if (strcmp (sec->name, ".pdr") != 0)
+ return FALSE;
+
+ if (mips_elf_section_data (sec)->u.tdata == NULL)
+ return FALSE;
+
+ to = contents;
+ end = contents + sec->size;
+ for (from = contents, i = 0;
+ from < end;
+ from += PDR_SIZE, i++)
+ {
+ if ((mips_elf_section_data (sec)->u.tdata)[i] == 1)
+ continue;
+ if (to != from)
+ memcpy (to, from, PDR_SIZE);
+ to += PDR_SIZE;
+ }
+ bfd_set_section_contents (output_bfd, sec->output_section, contents,
+ sec->output_offset, sec->size);
+ return TRUE;
+}
+
+/* MIPS ELF uses a special find_nearest_line routine in order the
+ handle the ECOFF debugging information. */
+
+struct mips_elf_find_line
+{
+ struct ecoff_debug_info d;
+ struct ecoff_find_line i;
+};
+
+bfd_boolean
+_bfd_riscv_elf_find_nearest_line (bfd *abfd, asection *section,
+ asymbol **symbols, bfd_vma offset,
+ const char **filename_ptr,
+ const char **functionname_ptr,
+ unsigned int *line_ptr)
+{
+ asection *msec;
+
+ if (_bfd_dwarf1_find_nearest_line (abfd, section, symbols, offset,
+ filename_ptr, functionname_ptr,
+ line_ptr))
+ return TRUE;
+
+ if (_bfd_dwarf2_find_nearest_line (abfd, section, symbols, offset,
+ filename_ptr, functionname_ptr,
+ line_ptr, ABI_64_P (abfd) ? 8 : 0,
+ &elf_tdata (abfd)->dwarf2_find_line_info))
+ return TRUE;
+
+ msec = bfd_get_section_by_name (abfd, ".mdebug");
+ if (msec != NULL)
+ {
+ flagword origflags;
+ struct mips_elf_find_line *fi;
+ const struct ecoff_debug_swap * const swap =
+ get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap;
+
+ /* If we are called during a link, mips_elf_final_link may have
+ cleared the SEC_HAS_CONTENTS field. We force it back on here
+ if appropriate (which it normally will be). */
+ origflags = msec->flags;
+ if (elf_section_data (msec)->this_hdr.sh_type != SHT_NOBITS)
+ msec->flags |= SEC_HAS_CONTENTS;
+
+ fi = elf_tdata (abfd)->find_line_info;
+ if (fi == NULL)
+ {
+ bfd_size_type external_fdr_size;
+ char *fraw_src;
+ char *fraw_end;
+ struct fdr *fdr_ptr;
+ bfd_size_type amt = sizeof (struct mips_elf_find_line);
+
+ fi = bfd_zalloc (abfd, amt);
+ if (fi == NULL)
+ {
+ msec->flags = origflags;
+ return FALSE;
+ }
+
+ if (! _bfd_riscv_elf_read_ecoff_info (abfd, msec, &fi->d))
+ {
+ msec->flags = origflags;
+ return FALSE;
+ }
+
+ /* Swap in the FDR information. */
+ amt = fi->d.symbolic_header.ifdMax * sizeof (struct fdr);
+ fi->d.fdr = bfd_alloc (abfd, amt);
+ if (fi->d.fdr == NULL)
+ {
+ msec->flags = origflags;
+ return FALSE;
+ }
+ external_fdr_size = swap->external_fdr_size;
+ fdr_ptr = fi->d.fdr;
+ fraw_src = (char *) fi->d.external_fdr;
+ fraw_end = (fraw_src
+ + fi->d.symbolic_header.ifdMax * external_fdr_size);
+ for (; fraw_src < fraw_end; fraw_src += external_fdr_size, fdr_ptr++)
+ (*swap->swap_fdr_in) (abfd, fraw_src, fdr_ptr);
+
+ elf_tdata (abfd)->find_line_info = fi;
+
+ /* Note that we don't bother to ever free this information.
+ find_nearest_line is either called all the time, as in
+ objdump -l, so the information should be saved, or it is
+ rarely called, as in ld error messages, so the memory
+ wasted is unimportant. Still, it would probably be a
+ good idea for free_cached_info to throw it away. */
+ }
+
+ if (_bfd_ecoff_locate_line (abfd, section, offset, &fi->d, swap,
+ &fi->i, filename_ptr, functionname_ptr,
+ line_ptr))
+ {
+ msec->flags = origflags;
+ return TRUE;
+ }
+
+ msec->flags = origflags;
+ }
+
+ /* Fall back on the generic ELF find_nearest_line routine. */
+
+ return _bfd_elf_find_nearest_line (abfd, section, symbols, offset,
+ filename_ptr, functionname_ptr,
+ line_ptr);
+}
+
+bfd_boolean
+_bfd_riscv_elf_find_inliner_info (bfd *abfd,
+ const char **filename_ptr,
+ const char **functionname_ptr,
+ unsigned int *line_ptr)
+{
+ bfd_boolean found;
+ found = _bfd_dwarf2_find_inliner_info (abfd, filename_ptr,
+ functionname_ptr, line_ptr,
+ & elf_tdata (abfd)->dwarf2_find_line_info);
+ return found;
+}
+
+
+/* When are writing out the .options or .MIPS.options section,
+ remember the bytes we are writing out, so that we can install the
+ GP value in the section_processing routine. */
+
+bfd_boolean
+_bfd_riscv_elf_set_section_contents (bfd *abfd, sec_ptr section,
+ const void *location,
+ file_ptr offset, bfd_size_type count)
+{
+ if (MIPS_ELF_OPTIONS_SECTION_NAME_P (section->name))
+ {
+ bfd_byte *c;
+
+ if (elf_section_data (section) == NULL)
+ {
+ bfd_size_type amt = sizeof (struct bfd_elf_section_data);
+ section->used_by_bfd = bfd_zalloc (abfd, amt);
+ if (elf_section_data (section) == NULL)
+ return FALSE;
+ }
+ c = mips_elf_section_data (section)->u.tdata;
+ if (c == NULL)
+ {
+ c = bfd_zalloc (abfd, section->size);
+ if (c == NULL)
+ return FALSE;
+ mips_elf_section_data (section)->u.tdata = c;
+ }
+
+ memcpy (c + offset, location, count);
+ }
+
+ return _bfd_elf_set_section_contents (abfd, section, location, offset,
+ count);
+}
+
+/* This is almost identical to bfd_generic_get_... except that some
+ MIPS relocations need to be handled specially. Sigh. */
+
+bfd_byte *
+_bfd_elf_riscv_get_relocated_section_contents
+ (bfd *abfd,
+ struct bfd_link_info *link_info,
+ struct bfd_link_order *link_order,
+ bfd_byte *data,
+ bfd_boolean relocatable,
+ asymbol **symbols)
+{
+ /* Get enough memory to hold the stuff */
+ bfd *input_bfd = link_order->u.indirect.section->owner;
+ asection *input_section = link_order->u.indirect.section;
+ bfd_size_type sz;
+
+ long reloc_size = bfd_get_reloc_upper_bound (input_bfd, input_section);
+ arelent **reloc_vector = NULL;
+ long reloc_count;
+
+ if (reloc_size < 0)
+ goto error_return;
+
+ reloc_vector = bfd_malloc (reloc_size);
+ if (reloc_vector == NULL && reloc_size != 0)
+ goto error_return;
+
+ /* read in the section */
+ sz = input_section->rawsize ? input_section->rawsize : input_section->size;
+ if (!bfd_get_section_contents (input_bfd, input_section, data, 0, sz))
+ goto error_return;
+
+ reloc_count = bfd_canonicalize_reloc (input_bfd,
+ input_section,
+ reloc_vector,
+ symbols);
+ if (reloc_count < 0)
+ goto error_return;
+
+ if (reloc_count > 0)
+ {
+ arelent **parent;
+
+ {
+ struct bfd_hash_entry *h;
+ struct bfd_link_hash_entry *lh;
+ /* Skip all this stuff if we aren't mixing formats. */
+ if (abfd && input_bfd
+ && abfd->xvec == input_bfd->xvec)
+ lh = 0;
+ else
+ {
+ h = bfd_hash_lookup (&link_info->hash->table, "_gp", FALSE, FALSE);
+ lh = (struct bfd_link_hash_entry *) h;
+ }
+ lookup:
+ if (lh)
+ {
+ switch (lh->type)
+ {
+ case bfd_link_hash_undefined:
+ case bfd_link_hash_undefweak:
+ case bfd_link_hash_common:
+ case bfd_link_hash_defined:
+ case bfd_link_hash_defweak:
+ break;
+ case bfd_link_hash_indirect:
+ case bfd_link_hash_warning:
+ lh = lh->u.i.link;
+ /* @@FIXME ignoring warning for now */
+ goto lookup;
+ case bfd_link_hash_new:
+ default:
+ abort ();
+ }
+ }
+ }
+ /* end mips */
+ for (parent = reloc_vector; *parent != NULL; parent++)
+ {
+ char *error_message = NULL;
+ bfd_reloc_status_type r;
+
+ r = bfd_perform_relocation (input_bfd, *parent, data,
+ input_section,
+ relocatable ? abfd : NULL,
+ &error_message);
+
+ if (relocatable)
+ {
+ asection *os = input_section->output_section;
+
+ /* A partial link, so keep the relocs */
+ os->orelocation[os->reloc_count] = *parent;
+ os->reloc_count++;
+ }
+
+ if (r != bfd_reloc_ok)
+ {
+ switch (r)
+ {
+ case bfd_reloc_undefined:
+ if (!((*link_info->callbacks->undefined_symbol)
+ (link_info, bfd_asymbol_name (*(*parent)->sym_ptr_ptr),
+ input_bfd, input_section, (*parent)->address, TRUE)))
+ goto error_return;
+ break;
+ case bfd_reloc_dangerous:
+ BFD_ASSERT (error_message != NULL);
+ if (!((*link_info->callbacks->reloc_dangerous)
+ (link_info, error_message, input_bfd, input_section,
+ (*parent)->address)))
+ goto error_return;
+ break;
+ case bfd_reloc_overflow:
+ if (!((*link_info->callbacks->reloc_overflow)
+ (link_info, NULL,
+ bfd_asymbol_name (*(*parent)->sym_ptr_ptr),
+ (*parent)->howto->name, (*parent)->addend,
+ input_bfd, input_section, (*parent)->address)))
+ goto error_return;
+ break;
+ case bfd_reloc_outofrange:
+ default:
+ abort ();
+ break;
+ }
+
+ }
+ }
+ }
+ if (reloc_vector != NULL)
+ free (reloc_vector);
+ return data;
+
+error_return:
+ if (reloc_vector != NULL)
+ free (reloc_vector);
+ return NULL;
+}
+
+/* Create a MIPS ELF linker hash table. */
+
+struct bfd_link_hash_table *
+_bfd_riscv_elf_link_hash_table_create (bfd *abfd)
+{
+ struct mips_elf_link_hash_table *ret;
+ bfd_size_type amt = sizeof (struct mips_elf_link_hash_table);
+
+ ret = bfd_malloc (amt);
+ if (ret == NULL)
+ return NULL;
+
+ if (!_bfd_elf_link_hash_table_init (&ret->root, abfd,
+ mips_elf_link_hash_newfunc,
+ sizeof (struct mips_elf_link_hash_entry),
+ MIPS_ELF_DATA))
+ {
+ free (ret);
+ return NULL;
+ }
+
+#if 0
+ /* We no longer use this. */
+ for (i = 0; i < SIZEOF_MIPS_DYNSYM_SECNAMES; i++)
+ ret->dynsym_sec_strindex[i] = (bfd_size_type) -1;
+#endif
+ ret->srelbss = NULL;
+ ret->sdynbss = NULL;
+ ret->srelplt = NULL;
+ ret->srelplt2 = NULL;
+ ret->sgotplt = NULL;
+ ret->splt = NULL;
+ ret->sgot = NULL;
+ ret->got_info = NULL;
+ ret->plt_header_size = 0;
+ ret->plt_entry_size = 0;
+
+ return &ret->root.root;
+}
+
+/* We need to use a special link routine to handle the .reginfo and
+ the .mdebug sections. We need to merge all instances of these
+ sections together, not write them all out sequentially. */
+
+bfd_boolean
+_bfd_riscv_elf_final_link (bfd *abfd, struct bfd_link_info *info)
+{
+ asection *o;
+ struct bfd_link_order *p;
+ asection *reginfo_sec, *mdebug_sec;
+ Elf32_RegInfo reginfo;
+ struct ecoff_debug_info debug;
+ const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+ const struct ecoff_debug_swap *swap = bed->elf_backend_ecoff_debug_swap;
+ HDRR *symhdr = &debug.symbolic_header;
+ void *mdebug_handle = NULL;
+ asection *s;
+ EXTR esym;
+ unsigned int i;
+ struct mips_elf_link_hash_table *htab;
+
+ static const char * const secname[] =
+ {
+ ".text", ".init", ".fini", ".data",
+ ".rodata", ".bss"
+ };
+ static const int sc[] =
+ {
+ scText, scInit, scFini, scData,
+ scRData, scBss
+ };
+
+ /* Sort the dynamic symbols so that those with GOT entries come after
+ those without. */
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ if (!mips_elf_sort_hash_table (abfd, info))
+ return FALSE;
+
+ /* Get a value for the GP register. */
+ if (elf_gp (abfd) == 0)
+ {
+ 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)
+ elf_gp (abfd) = (h->u.def.value
+ + h->u.def.section->output_section->vma
+ + h->u.def.section->output_offset);
+ else if (info->relocatable)
+ {
+ bfd_vma lo = MINUS_ONE;
+
+ /* Find the GP-relative section with the lowest offset. */
+ for (o = abfd->sections; o != NULL; o = o->next)
+ if (o->vma < lo
+ && (elf_section_data (o)->this_hdr.sh_flags & SHF_MIPS_GPREL))
+ lo = o->vma;
+
+ /* And calculate GP relative to that. */
+ elf_gp (abfd) = lo;
+ }
+ else
+ {
+ /* If the relocate_section function needs to do a reloc
+ involving the GP value, it should make a reloc_dangerous
+ callback to warn that GP is not defined. */
+ }
+ }
+
+ /* Go through the sections and collect the .reginfo and .mdebug
+ information. */
+ reginfo_sec = NULL;
+ mdebug_sec = NULL;
+ for (o = abfd->sections; o != NULL; o = o->next)
+ {
+ if (strcmp (o->name, ".reginfo") == 0)
+ {
+ memset (&reginfo, 0, sizeof reginfo);
+
+ /* We have found the .reginfo section in the output file.
+ Look through all the link_orders comprising it and merge
+ the information together. */
+ for (p = o->map_head.link_order; p != NULL; p = p->next)
+ {
+ asection *input_section;
+ bfd *input_bfd;
+ Elf32_External_RegInfo ext;
+ Elf32_RegInfo sub;
+
+ if (p->type != bfd_indirect_link_order)
+ {
+ if (p->type == bfd_data_link_order)
+ continue;
+ abort ();
+ }
+
+ input_section = p->u.indirect.section;
+ input_bfd = input_section->owner;
+
+ if (! bfd_get_section_contents (input_bfd, input_section,
+ &ext, 0, sizeof ext))
+ return FALSE;
+
+ bfd_riscv_elf32_swap_reginfo_in (input_bfd, &ext, &sub);
+
+ reginfo.ri_gprmask |= sub.ri_gprmask;
+ reginfo.ri_cprmask[0] |= sub.ri_cprmask[0];
+ reginfo.ri_cprmask[1] |= sub.ri_cprmask[1];
+ reginfo.ri_cprmask[2] |= sub.ri_cprmask[2];
+ reginfo.ri_cprmask[3] |= sub.ri_cprmask[3];
+
+ /* ri_gp_value is set by the function
+ mips_elf32_section_processing when the section is
+ finally written out. */
+
+ /* Hack: reset the SEC_HAS_CONTENTS flag so that
+ elf_link_input_bfd ignores this section. */
+ input_section->flags &= ~SEC_HAS_CONTENTS;
+ }
+
+ /* Size has been set in _bfd_riscv_elf_always_size_sections. */
+ BFD_ASSERT(o->size == sizeof (Elf32_External_RegInfo));
+
+ /* Skip this section later on (I don't think this currently
+ matters, but someday it might). */
+ o->map_head.link_order = NULL;
+
+ reginfo_sec = o;
+ }
+
+ if (strcmp (o->name, ".mdebug") == 0)
+ {
+ struct extsym_info einfo;
+ bfd_vma last;
+
+ /* We have found the .mdebug section in the output file.
+ Look through all the link_orders comprising it and merge
+ the information together. */
+ symhdr->magic = swap->sym_magic;
+ /* FIXME: What should the version stamp be? */
+ symhdr->vstamp = 0;
+ symhdr->ilineMax = 0;
+ symhdr->cbLine = 0;
+ symhdr->idnMax = 0;
+ symhdr->ipdMax = 0;
+ symhdr->isymMax = 0;
+ symhdr->ioptMax = 0;
+ symhdr->iauxMax = 0;
+ symhdr->issMax = 0;
+ symhdr->issExtMax = 0;
+ symhdr->ifdMax = 0;
+ symhdr->crfd = 0;
+ symhdr->iextMax = 0;
+
+ /* We accumulate the debugging information itself in the
+ debug_info structure. */
+ debug.line = NULL;
+ debug.external_dnr = NULL;
+ debug.external_pdr = NULL;
+ debug.external_sym = NULL;
+ debug.external_opt = NULL;
+ debug.external_aux = NULL;
+ debug.ss = NULL;
+ debug.ssext = debug.ssext_end = NULL;
+ debug.external_fdr = NULL;
+ debug.external_rfd = NULL;
+ debug.external_ext = debug.external_ext_end = NULL;
+
+ mdebug_handle = bfd_ecoff_debug_init (abfd, &debug, swap, info);
+ if (mdebug_handle == NULL)
+ return FALSE;
+
+ esym.jmptbl = 0;
+ esym.cobol_main = 0;
+ esym.weakext = 0;
+ esym.reserved = 0;
+ esym.ifd = ifdNil;
+ esym.asym.iss = issNil;
+ esym.asym.st = stLocal;
+ esym.asym.reserved = 0;
+ esym.asym.index = indexNil;
+ last = 0;
+ for (i = 0; i < sizeof (secname) / sizeof (secname[0]); i++)
+ {
+ esym.asym.sc = sc[i];
+ s = bfd_get_section_by_name (abfd, secname[i]);
+ if (s != NULL)
+ {
+ esym.asym.value = s->vma;
+ last = s->vma + s->size;
+ }
+ else
+ esym.asym.value = last;
+ if (!bfd_ecoff_debug_one_external (abfd, &debug, swap,
+ secname[i], &esym))
+ return FALSE;
+ }
+
+ for (p = o->map_head.link_order; p != NULL; p = p->next)
+ {
+ asection *input_section;
+ bfd *input_bfd;
+ const struct ecoff_debug_swap *input_swap;
+ struct ecoff_debug_info input_debug;
+ char *eraw_src;
+ char *eraw_end;
+
+ if (p->type != bfd_indirect_link_order)
+ {
+ if (p->type == bfd_data_link_order)
+ continue;
+ abort ();
+ }
+
+ input_section = p->u.indirect.section;
+ input_bfd = input_section->owner;
+
+ if (!is_mips_elf (input_bfd))
+ {
+ /* I don't know what a non MIPS ELF bfd would be
+ doing with a .mdebug section, but I don't really
+ want to deal with it. */
+ continue;
+ }
+
+ input_swap = (get_elf_backend_data (input_bfd)
+ ->elf_backend_ecoff_debug_swap);
+
+ BFD_ASSERT (p->size == input_section->size);
+
+ /* The ECOFF linking code expects that we have already
+ read in the debugging information and set up an
+ ecoff_debug_info structure, so we do that now. */
+ if (! _bfd_riscv_elf_read_ecoff_info (input_bfd, input_section,
+ &input_debug))
+ return FALSE;
+
+ if (! (bfd_ecoff_debug_accumulate
+ (mdebug_handle, abfd, &debug, swap, input_bfd,
+ &input_debug, input_swap, info)))
+ return FALSE;
+
+ /* Loop through the external symbols. For each one with
+ interesting information, try to find the symbol in
+ the linker global hash table and save the information
+ for the output external symbols. */
+ eraw_src = input_debug.external_ext;
+ eraw_end = (eraw_src
+ + (input_debug.symbolic_header.iextMax
+ * input_swap->external_ext_size));
+ for (;
+ eraw_src < eraw_end;
+ eraw_src += input_swap->external_ext_size)
+ {
+ EXTR ext;
+ const char *name;
+ struct mips_elf_link_hash_entry *h;
+
+ (*input_swap->swap_ext_in) (input_bfd, eraw_src, &ext);
+ if (ext.asym.sc == scNil
+ || ext.asym.sc == scUndefined
+ || ext.asym.sc == scSUndefined)
+ continue;
+
+ name = input_debug.ssext + ext.asym.iss;
+ h = mips_elf_link_hash_lookup (mips_elf_hash_table (info),
+ name, FALSE, FALSE, TRUE);
+ if (h == NULL || h->esym.ifd != -2)
+ continue;
+
+ if (ext.ifd != -1)
+ {
+ BFD_ASSERT (ext.ifd
+ < input_debug.symbolic_header.ifdMax);
+ ext.ifd = input_debug.ifdmap[ext.ifd];
+ }
+
+ h->esym = ext;
+ }
+
+ /* Free up the information we just read. */
+ free (input_debug.line);
+ free (input_debug.external_dnr);
+ free (input_debug.external_pdr);
+ free (input_debug.external_sym);
+ free (input_debug.external_opt);
+ free (input_debug.external_aux);
+ free (input_debug.ss);
+ free (input_debug.ssext);
+ free (input_debug.external_fdr);
+ free (input_debug.external_rfd);
+ free (input_debug.external_ext);
+
+ /* Hack: reset the SEC_HAS_CONTENTS flag so that
+ elf_link_input_bfd ignores this section. */
+ input_section->flags &= ~SEC_HAS_CONTENTS;
+ }
+
+ /* Build the external symbol information. */
+ einfo.abfd = abfd;
+ einfo.info = info;
+ einfo.debug = &debug;
+ einfo.swap = swap;
+ einfo.failed = FALSE;
+ mips_elf_link_hash_traverse (mips_elf_hash_table (info),
+ mips_elf_output_extsym, &einfo);
+ if (einfo.failed)
+ return FALSE;
+
+ /* Set the size of the .mdebug section. */
+ o->size = bfd_ecoff_debug_size (abfd, &debug, swap);
+
+ /* Skip this section later on (I don't think this currently
+ matters, but someday it might). */
+ o->map_head.link_order = NULL;
+
+ mdebug_sec = o;
+ }
+ }
+
+ /* Invoke the regular ELF backend linker to do all the work. */
+ if (!bfd_elf_final_link (abfd, info))
+ return FALSE;
+
+ /* Now write out the computed sections. */
+
+ if (reginfo_sec != NULL)
+ {
+ Elf32_External_RegInfo ext;
+
+ bfd_riscv_elf32_swap_reginfo_out (abfd, &reginfo, &ext);
+ if (! bfd_set_section_contents (abfd, reginfo_sec, &ext, 0, sizeof ext))
+ return FALSE;
+ }
+
+ if (mdebug_sec != NULL)
+ {
+ BFD_ASSERT (abfd->output_has_begun);
+ if (! bfd_ecoff_write_accumulated_debug (mdebug_handle, abfd, &debug,
+ swap, info,
+ mdebug_sec->filepos))
+ return FALSE;
+
+ bfd_ecoff_debug_free (mdebug_handle, abfd, &debug, swap, info);
+ }
+
+ return TRUE;
+}
+
+/* Return true if bfd machine EXTENSION is an extension of machine BASE. */
+
+static bfd_boolean
+mips_mach_extends_p (unsigned long base, unsigned long extension)
+{
+ return extension == base;
+}
+
+
+/* Return true if the given ELF header flags describe a 32-bit binary. */
+
+static bfd_boolean
+mips_32bit_flags_p (flagword flags)
+{
+ return ((flags & EF_MIPS_32BITMODE) != 0
+ || (flags & EF_MIPS_ABI) == E_RISCV_ABI_32
+ || (flags & EF_MIPS_ARCH) == E_RISCV_ARCH_RV32);
+}
+
+
+/* Merge object attributes from IBFD into OBFD. Raise an error if
+ there are conflicting attributes. */
+static bfd_boolean
+mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
+{
+ if (!elf_known_obj_attributes_proc (obfd)[0].i)
+ {
+ /* This is the first object. Copy the attributes. */
+ _bfd_elf_copy_obj_attributes (ibfd, obfd);
+
+ /* Use the Tag_null value to indicate the attributes have been
+ initialized. */
+ elf_known_obj_attributes_proc (obfd)[0].i = 1;
+
+ return TRUE;
+ }
+
+ /* Merge Tag_compatibility attributes and any common GNU ones. */
+ _bfd_elf_merge_object_attributes (ibfd, obfd);
+
+ return TRUE;
+}
+
+/* Merge backend specific data from an object file to the output
+ object file when linking. */
+
+bfd_boolean
+_bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
+{
+ flagword old_flags;
+ flagword new_flags;
+ bfd_boolean ok;
+ bfd_boolean null_input_bfd = TRUE;
+ asection *sec;
+
+ /* Check if we have the same endianess */
+ if (! _bfd_generic_verify_endian_match (ibfd, obfd))
+ {
+ (*_bfd_error_handler)
+ (_("%B: endianness incompatible with that of the selected emulation"),
+ ibfd);
+ return FALSE;
+ }
+
+ if (!is_mips_elf (ibfd) || !is_mips_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 (!mips_elf_merge_obj_attributes (ibfd, obfd))
+ return FALSE;
+
+ new_flags = elf_elfheader (ibfd)->e_flags;
+ elf_elfheader (obfd)->e_flags |= new_flags & EF_MIPS_NOREORDER;
+ old_flags = elf_elfheader (obfd)->e_flags;
+
+ if (! elf_flags_init (obfd))
+ {
+ elf_flags_init (obfd) = TRUE;
+ elf_elfheader (obfd)->e_flags = new_flags;
+ elf_elfheader (obfd)->e_ident[EI_CLASS]
+ = elf_elfheader (ibfd)->e_ident[EI_CLASS];
+
+ if (bfd_get_arch (obfd) == bfd_get_arch (ibfd)
+ && (bfd_get_arch_info (obfd)->the_default
+ || mips_mach_extends_p (bfd_get_mach (obfd),
+ bfd_get_mach (ibfd))))
+ {
+ if (! bfd_set_arch_mach (obfd, bfd_get_arch (ibfd),
+ bfd_get_mach (ibfd)))
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ /* Check flag compatibility. */
+
+ new_flags &= ~EF_MIPS_NOREORDER;
+ old_flags &= ~EF_MIPS_NOREORDER;
+
+ /* Some IRIX 6 BSD-compatibility objects have this bit set. It
+ doesn't seem to matter. */
+ new_flags &= ~EF_MIPS_XGOT;
+ old_flags &= ~EF_MIPS_XGOT;
+
+ /* MIPSpro generates ucode info in n64 objects. Again, we should
+ just be able to ignore this. */
+ new_flags &= ~EF_MIPS_UCODE;
+ old_flags &= ~EF_MIPS_UCODE;
+
+ /* DSOs should only be linked with CPIC code. */
+ if ((ibfd->flags & DYNAMIC) != 0)
+ new_flags |= EF_MIPS_PIC | EF_MIPS_CPIC;
+
+ if (new_flags == old_flags)
+ return TRUE;
+
+ /* Check to see if the input BFD actually contains any sections.
+ If not, its flags may not have been initialised either, but it cannot
+ actually cause any incompatibility. */
+ for (sec = ibfd->sections; sec != NULL; sec = sec->next)
+ {
+ /* Ignore synthetic sections and empty .text, .data and .bss sections
+ which are automatically generated by gas. Also ignore fake
+ (s)common sections, since merely defining a common symbol does
+ not affect compatibility. */
+ if ((sec->flags & SEC_IS_COMMON) == 0
+ && strcmp (sec->name, ".reginfo")
+ && strcmp (sec->name, ".mdebug")
+ && (sec->size != 0
+ || (strcmp (sec->name, ".text")
+ && strcmp (sec->name, ".data")
+ && strcmp (sec->name, ".bss"))))
+ {
+ null_input_bfd = FALSE;
+ break;
+ }
+ }
+ if (null_input_bfd)
+ return TRUE;
+
+ ok = TRUE;
+
+ if (new_flags & (EF_MIPS_PIC | EF_MIPS_CPIC))
+ elf_elfheader (obfd)->e_flags |= EF_MIPS_CPIC;
+ if (! (new_flags & EF_MIPS_PIC))
+ elf_elfheader (obfd)->e_flags &= ~EF_MIPS_PIC;
+
+ new_flags &= ~ (EF_MIPS_PIC | EF_MIPS_CPIC);
+ old_flags &= ~ (EF_MIPS_PIC | EF_MIPS_CPIC);
+
+ /* Compare the ISAs. */
+ if (mips_32bit_flags_p (old_flags) != mips_32bit_flags_p (new_flags))
+ {
+ (*_bfd_error_handler)
+ (_("%B: linking 32-bit code with 64-bit code"),
+ ibfd);
+ ok = FALSE;
+ }
+ else if (!mips_mach_extends_p (bfd_get_mach (ibfd), bfd_get_mach (obfd)))
+ {
+ /* OBFD's ISA isn't the same as, or an extension of, IBFD's. */
+ if (mips_mach_extends_p (bfd_get_mach (obfd), bfd_get_mach (ibfd)))
+ {
+ /* Copy the architecture info from IBFD to OBFD. Also copy
+ the 32-bit flag (if set) so that we continue to recognise
+ OBFD as a 32-bit binary. */
+ bfd_set_arch_info (obfd, bfd_get_arch_info (ibfd));
+ elf_elfheader (obfd)->e_flags &= ~(EF_MIPS_ARCH | EF_MIPS_MACH);
+ elf_elfheader (obfd)->e_flags
+ |= new_flags & (EF_MIPS_ARCH | EF_MIPS_MACH | EF_MIPS_32BITMODE);
+
+ /* Copy across the ABI flags if OBFD doesn't use them
+ and if that was what caused us to treat IBFD as 32-bit. */
+ if ((old_flags & EF_MIPS_ABI) == 0
+ && mips_32bit_flags_p (new_flags)
+ && !mips_32bit_flags_p (new_flags & ~EF_MIPS_ABI))
+ elf_elfheader (obfd)->e_flags |= new_flags & EF_MIPS_ABI;
+ }
+ else
+ {
+ /* The ISAs aren't compatible. */
+ (*_bfd_error_handler)
+ (_("%B: linking %s module with previous %s modules"),
+ ibfd,
+ bfd_printable_name (ibfd),
+ bfd_printable_name (obfd));
+ ok = FALSE;
+ }
+ }
+
+ new_flags &= ~(EF_MIPS_ARCH | EF_MIPS_MACH | EF_MIPS_32BITMODE);
+ old_flags &= ~(EF_MIPS_ARCH | EF_MIPS_MACH | EF_MIPS_32BITMODE);
+
+ /* Compare ABIs. The 64-bit ABI does not use EF_MIPS_ABI. But, it
+ does set EI_CLASS differently from any 32-bit ABI. */
+ if ((new_flags & EF_MIPS_ABI) != (old_flags & EF_MIPS_ABI)
+ || (elf_elfheader (ibfd)->e_ident[EI_CLASS]
+ != elf_elfheader (obfd)->e_ident[EI_CLASS]))
+ {
+ /* Only error if both are set (to different values). */
+ if (((new_flags & EF_MIPS_ABI) && (old_flags & EF_MIPS_ABI))
+ || (elf_elfheader (ibfd)->e_ident[EI_CLASS]
+ != elf_elfheader (obfd)->e_ident[EI_CLASS]))
+ {
+ (*_bfd_error_handler)
+ (_("%B: ABI mismatch: linking %s module with previous %s modules"),
+ ibfd,
+ elf_mips_abi_name (ibfd),
+ elf_mips_abi_name (obfd));
+ ok = FALSE;
+ }
+ new_flags &= ~EF_MIPS_ABI;
+ old_flags &= ~EF_MIPS_ABI;
+ }
+
+ /* For now, allow arbitrary mixing of ASEs (retain the union). */
+ if ((new_flags & EF_MIPS_ARCH_ASE) != (old_flags & EF_MIPS_ARCH_ASE))
+ {
+ elf_elfheader (obfd)->e_flags |= new_flags & EF_MIPS_ARCH_ASE;
+
+ new_flags &= ~ EF_MIPS_ARCH_ASE;
+ old_flags &= ~ EF_MIPS_ARCH_ASE;
+ }
+
+ /* Warn about any other mismatches */
+ if (new_flags != old_flags)
+ {
+ (*_bfd_error_handler)
+ (_("%B: uses different e_flags (0x%lx) fields than previous modules (0x%lx)"),
+ ibfd, (unsigned long) new_flags,
+ (unsigned long) old_flags);
+ ok = FALSE;
+ }
+
+ if (! ok)
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Function to keep MIPS specific file flags like as EF_MIPS_PIC. */
+
+bfd_boolean
+_bfd_riscv_elf_set_private_flags (bfd *abfd, flagword flags)
+{
+ BFD_ASSERT (!elf_flags_init (abfd)
+ || elf_elfheader (abfd)->e_flags == flags);
+
+ elf_elfheader (abfd)->e_flags = flags;
+ elf_flags_init (abfd) = TRUE;
+ return TRUE;
+}
+
+char *
+_bfd_riscv_elf_get_target_dtag (bfd_vma dtag)
+{
+ switch (dtag)
+ {
+ default: return "";
+ case DT_MIPS_RLD_VERSION:
+ return "MIPS_RLD_VERSION";
+ case DT_MIPS_TIME_STAMP:
+ return "MIPS_TIME_STAMP";
+ case DT_MIPS_ICHECKSUM:
+ return "MIPS_ICHECKSUM";
+ case DT_MIPS_IVERSION:
+ return "MIPS_IVERSION";
+ case DT_MIPS_FLAGS:
+ return "MIPS_FLAGS";
+ case DT_MIPS_BASE_ADDRESS:
+ return "MIPS_BASE_ADDRESS";
+ case DT_MIPS_MSYM:
+ return "MIPS_MSYM";
+ case DT_MIPS_CONFLICT:
+ return "MIPS_CONFLICT";
+ case DT_MIPS_LIBLIST:
+ return "MIPS_LIBLIST";
+ case DT_MIPS_LOCAL_GOTNO:
+ return "MIPS_LOCAL_GOTNO";
+ case DT_MIPS_CONFLICTNO:
+ return "MIPS_CONFLICTNO";
+ case DT_MIPS_LIBLISTNO:
+ return "MIPS_LIBLISTNO";
+ case DT_MIPS_SYMTABNO:
+ return "MIPS_SYMTABNO";
+ case DT_MIPS_UNREFEXTNO:
+ return "MIPS_UNREFEXTNO";
+ case DT_MIPS_GOTSYM:
+ return "MIPS_GOTSYM";
+ case DT_MIPS_HIPAGENO:
+ return "MIPS_HIPAGENO";
+ case DT_MIPS_RLD_MAP:
+ return "MIPS_RLD_MAP";
+ case DT_MIPS_DELTA_CLASS:
+ return "MIPS_DELTA_CLASS";
+ case DT_MIPS_DELTA_CLASS_NO:
+ return "MIPS_DELTA_CLASS_NO";
+ case DT_MIPS_DELTA_INSTANCE:
+ return "MIPS_DELTA_INSTANCE";
+ case DT_MIPS_DELTA_INSTANCE_NO:
+ return "MIPS_DELTA_INSTANCE_NO";
+ case DT_MIPS_DELTA_RELOC:
+ return "MIPS_DELTA_RELOC";
+ case DT_MIPS_DELTA_RELOC_NO:
+ return "MIPS_DELTA_RELOC_NO";
+ case DT_MIPS_DELTA_SYM:
+ return "MIPS_DELTA_SYM";
+ case DT_MIPS_DELTA_SYM_NO:
+ return "MIPS_DELTA_SYM_NO";
+ case DT_MIPS_DELTA_CLASSSYM:
+ return "MIPS_DELTA_CLASSSYM";
+ case DT_MIPS_DELTA_CLASSSYM_NO:
+ return "MIPS_DELTA_CLASSSYM_NO";
+ case DT_MIPS_CXX_FLAGS:
+ return "MIPS_CXX_FLAGS";
+ case DT_MIPS_PIXIE_INIT:
+ return "MIPS_PIXIE_INIT";
+ case DT_MIPS_SYMBOL_LIB:
+ return "MIPS_SYMBOL_LIB";
+ case DT_MIPS_LOCALPAGE_GOTIDX:
+ return "MIPS_LOCALPAGE_GOTIDX";
+ case DT_MIPS_LOCAL_GOTIDX:
+ return "MIPS_LOCAL_GOTIDX";
+ case DT_MIPS_HIDDEN_GOTIDX:
+ return "MIPS_HIDDEN_GOTIDX";
+ case DT_MIPS_PROTECTED_GOTIDX:
+ return "MIPS_PROTECTED_GOT_IDX";
+ case DT_MIPS_OPTIONS:
+ return "MIPS_OPTIONS";
+ case DT_MIPS_INTERFACE:
+ return "MIPS_INTERFACE";
+ case DT_MIPS_DYNSTR_ALIGN:
+ return "DT_MIPS_DYNSTR_ALIGN";
+ case DT_MIPS_INTERFACE_SIZE:
+ return "DT_MIPS_INTERFACE_SIZE";
+ case DT_MIPS_RLD_TEXT_RESOLVE_ADDR:
+ return "DT_MIPS_RLD_TEXT_RESOLVE_ADDR";
+ case DT_MIPS_PERF_SUFFIX:
+ return "DT_MIPS_PERF_SUFFIX";
+ case DT_MIPS_COMPACT_SIZE:
+ return "DT_MIPS_COMPACT_SIZE";
+ case DT_MIPS_GP_VALUE:
+ return "DT_MIPS_GP_VALUE";
+ case DT_MIPS_AUX_DYNAMIC:
+ return "DT_MIPS_AUX_DYNAMIC";
+ case DT_MIPS_PLTGOT:
+ return "DT_MIPS_PLTGOT";
+ case DT_MIPS_RWPLT:
+ return "DT_MIPS_RWPLT";
+ }
+}
+
+bfd_boolean
+_bfd_riscv_elf_print_private_bfd_data (bfd *abfd, void *ptr)
+{
+ FILE *file = ptr;
+
+ BFD_ASSERT (abfd != NULL && ptr != NULL);
+
+ /* Print normal ELF private data. */
+ _bfd_elf_print_private_bfd_data (abfd, ptr);
+
+ /* xgettext:c-format */
+ fprintf (file, _("private flags = %lx:"), elf_elfheader (abfd)->e_flags);
+
+ if (ABI_32_P (abfd))
+ fprintf (file, _(" [abi=32]"));
+ else if (ABI_64_P (abfd))
+ fprintf (file, _(" [abi=64]"));
+ else
+ fprintf (file, _(" [no abi set]"));
+
+ if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32)
+ fprintf (file, " [rv32]");
+ else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_64)
+ fprintf (file, " [rv64]");
+ else
+ fprintf (file, _(" [unknown ISA]"));
+
+ if (elf_elfheader (abfd)->e_flags & EF_MIPS_PIC)
+ fprintf (file, " [PIC]");
+
+ if (elf_elfheader (abfd)->e_flags & EF_MIPS_CPIC)
+ fprintf (file, " [CPIC]");
+
+ if (elf_elfheader (abfd)->e_flags & EF_MIPS_XGOT)
+ fprintf (file, " [XGOT]");
+
+ fputc ('\n', file);
+
+ return TRUE;
+}
+
+const struct bfd_elf_special_section _bfd_riscv_elf_special_sections[] =
+{
+ { STRING_COMMA_LEN (".lit4"), 0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
+ { STRING_COMMA_LEN (".lit8"), 0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
+ { STRING_COMMA_LEN (".mdebug"), 0, SHT_MIPS_DEBUG, 0 },
+ { STRING_COMMA_LEN (".sbss"), -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
+ { STRING_COMMA_LEN (".sdata"), -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
+ { STRING_COMMA_LEN (".ucode"), 0, SHT_MIPS_UCODE, 0 },
+ { NULL, 0, 0, 0, 0 }
+};
+
+/* Merge non visibility st_other attributes. Ensure that the
+ STO_OPTIONAL flag is copied into h->other, even if this is not a
+ definiton of the symbol. */
+void
+_bfd_riscv_elf_merge_symbol_attribute (struct elf_link_hash_entry *h,
+ const Elf_Internal_Sym *isym,
+ bfd_boolean definition,
+ bfd_boolean dynamic ATTRIBUTE_UNUSED)
+{
+ if ((isym->st_other & ~ELF_ST_VISIBILITY (-1)) != 0)
+ {
+ unsigned char other;
+
+ other = (definition ? isym->st_other : h->other);
+ other &= ~ELF_ST_VISIBILITY (-1);
+ h->other = other | ELF_ST_VISIBILITY (h->other);
+ }
+}
+
+bfd_boolean
+_bfd_riscv_elf_common_definition (Elf_Internal_Sym *sym)
+{
+ return (sym->st_shndx == SHN_COMMON
+ || sym->st_shndx == SHN_MIPS_ACOMMON
+ || sym->st_shndx == SHN_MIPS_SCOMMON);
+}
+
+/* Return address for Ith PLT stub in section PLT, for relocation REL
+ or (bfd_vma) -1 if it should not be included. */
+
+bfd_vma
+_bfd_riscv_elf_plt_sym_val (bfd_vma i, const asection *plt,
+ const arelent *rel ATTRIBUTE_UNUSED)
+{
+ return (plt->vma
+ + RISCV_PLT0_ENTRY_INSNS * 4
+ + i * RISCV_PLT_ENTRY_INSNS * 4);
+}
diff -ruN binutils-2.24/bfd/elfxx-riscv.h binutils-2.24-riscv/bfd/elfxx-riscv.h
--- binutils-2.24/bfd/elfxx-riscv.h 1969-12-31 16:00:00.000000000 -0800
+++ binutils-2.24-riscv/bfd/elfxx-riscv.h 2014-12-02 16:05:44.909434942 -0800
@@ -0,0 +1,144 @@
+/* RISC-V ELF specific backend routines.
+ Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+ Free Software Foundation, Inc.
+
+ 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; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#include "elf/common.h"
+#include "elf/internal.h"
+#include "elf/riscv.h"
+
+extern bfd_boolean _bfd_riscv_elf_new_section_hook
+ (bfd *, asection *);
+extern void _bfd_riscv_elf_symbol_processing
+ (bfd *, asymbol *);
+extern unsigned int _bfd_riscv_elf_eh_frame_address_size
+ (bfd *, asection *);
+extern bfd_boolean _bfd_riscv_elf_name_local_section_symbols
+ (bfd *);
+extern bfd_boolean _bfd_riscv_elf_section_processing
+ (bfd *, Elf_Internal_Shdr *);
+extern bfd_boolean _bfd_riscv_elf_section_from_shdr
+ (bfd *, Elf_Internal_Shdr *, const char *, int);
+extern bfd_boolean _bfd_riscv_elf_fake_sections
+ (bfd *, Elf_Internal_Shdr *, asection *);
+extern bfd_boolean _bfd_riscv_elf_section_from_bfd_section
+ (bfd *, asection *, int *);
+extern bfd_boolean _bfd_riscv_elf_add_symbol_hook
+ (bfd *, struct bfd_link_info *, Elf_Internal_Sym *,
+ const char **, flagword *, asection **, bfd_vma *);
+extern int _bfd_riscv_elf_link_output_symbol_hook
+ (struct bfd_link_info *, const char *, Elf_Internal_Sym *,
+ asection *, struct elf_link_hash_entry *);
+extern bfd_boolean _bfd_riscv_elf_create_dynamic_sections
+ (bfd *, struct bfd_link_info *);
+extern bfd_boolean _bfd_riscv_elf_check_relocs
+ (bfd *, struct bfd_link_info *, asection *, const Elf_Internal_Rela *);
+extern bfd_boolean _bfd_riscv_elf_adjust_dynamic_symbol
+ (struct bfd_link_info *, struct elf_link_hash_entry *);
+extern bfd_boolean _bfd_riscv_elf_always_size_sections
+ (bfd *, struct bfd_link_info *);
+extern bfd_boolean _bfd_riscv_elf_size_dynamic_sections
+ (bfd *, struct bfd_link_info *);
+extern bfd_boolean _bfd_riscv_elf_relocate_section
+ (bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
+ Elf_Internal_Rela *, Elf_Internal_Sym *, asection **);
+extern bfd_boolean _bfd_riscv_elf_finish_dynamic_symbol
+ (bfd *, struct bfd_link_info *, struct elf_link_hash_entry *,
+ Elf_Internal_Sym *);
+extern bfd_boolean _bfd_riscv_elf_finish_dynamic_sections
+ (bfd *, struct bfd_link_info *);
+extern void _bfd_riscv_elf_final_write_processing
+ (bfd *, bfd_boolean);
+extern int _bfd_riscv_elf_additional_program_headers
+ (bfd *, struct bfd_link_info *);
+extern bfd_boolean _bfd_riscv_elf_modify_segment_map
+ (bfd *, struct bfd_link_info *);
+extern asection * _bfd_riscv_elf_gc_mark_hook
+ (asection *, struct bfd_link_info *, Elf_Internal_Rela *,
+ struct elf_link_hash_entry *, Elf_Internal_Sym *);
+extern bfd_boolean _bfd_riscv_elf_gc_sweep_hook
+ (bfd *, struct bfd_link_info *, asection *, const Elf_Internal_Rela *);
+extern void _bfd_riscv_elf_copy_indirect_symbol
+ (struct bfd_link_info *, struct elf_link_hash_entry *,
+ struct elf_link_hash_entry *);
+extern bfd_boolean _bfd_riscv_elf_ignore_discarded_relocs
+ (asection *);
+extern bfd_boolean _bfd_riscv_elf_find_nearest_line
+ (bfd *, asection *, asymbol **, bfd_vma, const char **,
+ const char **, unsigned int *);
+extern bfd_boolean _bfd_riscv_elf_find_inliner_info
+ (bfd *, const char **, const char **, unsigned int *);
+extern bfd_boolean _bfd_riscv_elf_set_section_contents
+ (bfd *, asection *, const void *, file_ptr, bfd_size_type);
+extern bfd_byte *_bfd_elf_riscv_get_relocated_section_contents
+ (bfd *, struct bfd_link_info *, struct bfd_link_order *,
+ bfd_byte *, bfd_boolean, asymbol **);
+extern struct bfd_link_hash_table *_bfd_riscv_elf_link_hash_table_create
+ (bfd *);
+extern bfd_boolean _bfd_riscv_elf_final_link
+ (bfd *, struct bfd_link_info *);
+extern bfd_boolean _bfd_riscv_elf_merge_private_bfd_data
+ (bfd *, bfd *);
+extern bfd_boolean _bfd_riscv_elf_set_private_flags
+ (bfd *, flagword);
+extern bfd_boolean _bfd_riscv_elf_print_private_bfd_data
+ (bfd *, void *);
+extern bfd_boolean _bfd_riscv_elf_discard_info
+ (bfd *, struct elf_reloc_cookie *, struct bfd_link_info *);
+extern bfd_boolean _bfd_riscv_elf_write_section
+ (bfd *, struct bfd_link_info *, asection *, bfd_byte *);
+
+extern bfd_boolean _bfd_riscv_elf_read_ecoff_info
+ (bfd *, asection *, struct ecoff_debug_info *);
+extern bfd_reloc_status_type _bfd_riscv_elf_gprel16_with_gp
+ (bfd *, asymbol *, arelent *, asection *, bfd_boolean, void *, bfd_vma);
+extern bfd_reloc_status_type _bfd_riscv_elf32_gprel16_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+extern bfd_reloc_status_type _bfd_riscv_elf_hi16_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+extern bfd_reloc_status_type _bfd_riscv_elf_got16_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+extern bfd_reloc_status_type _bfd_riscv_elf_lo16_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+extern bfd_reloc_status_type _bfd_riscv_elf_generic_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+extern unsigned long _bfd_elf_riscv_mach
+ (flagword);
+extern bfd_boolean _bfd_riscv_relax_section
+ (bfd *, asection *, struct bfd_link_info *, bfd_boolean *);
+extern bfd_vma _bfd_riscv_elf_sign_extend
+ (bfd_vma, int);
+extern void _bfd_riscv_elf_merge_symbol_attribute
+ (struct elf_link_hash_entry *, const Elf_Internal_Sym *, bfd_boolean, bfd_boolean);
+extern char *_bfd_riscv_elf_get_target_dtag (bfd_vma);
+extern void _bfd_riscv_elf_use_plts_and_copy_relocs
+ (struct bfd_link_info *);
+extern bfd_vma _bfd_riscv_elf_plt_sym_val
+ (bfd_vma, const asection *, const arelent *rel);
+
+extern const struct bfd_elf_special_section _bfd_riscv_elf_special_sections [];
+
+extern bfd_boolean _bfd_riscv_elf_common_definition (Elf_Internal_Sym *);
+
+#define elf_backend_common_definition _bfd_riscv_elf_common_definition
+#define elf_backend_name_local_section_symbols \
+ _bfd_riscv_elf_name_local_section_symbols
+#define elf_backend_special_sections _bfd_riscv_elf_special_sections
+#define elf_backend_eh_frame_address_size _bfd_riscv_elf_eh_frame_address_size
+#define elf_backend_merge_symbol_attribute _bfd_riscv_elf_merge_symbol_attribute
diff -ruN binutils-2.24/bfd/targets.c binutils-2.24-riscv/bfd/targets.c
--- binutils-2.24/bfd/targets.c 2013-11-04 07:33:37.000000000 -0800
+++ binutils-2.24-riscv/bfd/targets.c 2014-12-02 16:20:33.686987626 -0800
@@ -646,6 +646,7 @@
extern const bfd_target bfd_elf32_littlemips_vxworks_vec;
extern const bfd_target bfd_elf32_littlemoxie_vec;
extern const bfd_target bfd_elf32_littlenios2_vec;
+extern const bfd_target bfd_elf32_littleriscv_vec;
extern const bfd_target bfd_elf32_m32c_vec;
extern const bfd_target bfd_elf32_m32r_vec;
extern const bfd_target bfd_elf32_m32rle_vec;
@@ -747,6 +748,7 @@
extern const bfd_target bfd_elf64_littlemips_vec;
extern const bfd_target bfd_elf64_littleaarch64_vec;
extern const bfd_target bfd_elf32_littleaarch64_vec;
+extern const bfd_target bfd_elf64_littleriscv_vec;
extern const bfd_target bfd_elf64_mmix_vec;
extern const bfd_target bfd_elf64_powerpc_vec;
extern const bfd_target bfd_elf64_powerpcle_vec;
diff -ruN binutils-2.24/binutils/readelf.c binutils-2.24-riscv/binutils/readelf.c
--- binutils-2.24/binutils/readelf.c 2013-11-18 00:40:15.000000000 -0800
+++ binutils-2.24-riscv/binutils/readelf.c 2014-12-02 16:05:44.913434966 -0800
@@ -125,6 +125,7 @@
#include "elf/metag.h"
#include "elf/microblaze.h"
#include "elf/mips.h"
+#include "elf/riscv.h"
#include "elf/mmix.h"
#include "elf/mn10200.h"
#include "elf/mn10300.h"
@@ -1157,6 +1158,10 @@
rtype = elf_mips_reloc_type (type);
break;
+ case EM_RISCV:
+ rtype = elf_riscv_reloc_type (type);
+ break;
+
case EM_ALPHA:
rtype = elf_alpha_reloc_type (type);
break;
@@ -2648,6 +2653,28 @@
default: strcat (buf, _(", unknown ISA")); break;
}
break;
+
+ case EM_RISCV:
+ if (e_flags & EF_RISCV_PIC)
+ strcat (buf, ", pic");
+
+ if (e_flags & EF_MIPS_OPTIONS_FIRST)
+ strcat (buf, ", odk first");
+
+ switch ((e_flags & EF_RISCV_ABI))
+ {
+ case E_RISCV_ABI_32: strcat (buf, ", abi32"); break;
+ case E_RISCV_ABI_64: strcat (buf, ", abi64"); break;
+ default: strcat (buf, _(", unknown ABI")); break;
+ }
+
+ switch ((e_flags & EF_RISCV_ARCH))
+ {
+ case E_RISCV_ARCH_RV32: strcat (buf, ", rv32"); break;
+ case E_RISCV_ARCH_RV64: strcat (buf, ", rv64"); break;
+ default: strcat (buf, _(", unknown ISA")); break;
+ }
+ break;
case EM_SH:
switch ((e_flags & EF_SH_MACH_MASK))
diff -ruN binutils-2.24/config.sub binutils-2.24-riscv/config.sub
--- binutils-2.24/config.sub 2013-11-26 03:37:33.000000000 -0800
+++ binutils-2.24-riscv/config.sub 2014-12-02 16:05:44.913434966 -0800
@@ -334,6 +334,9 @@
ms1)
basic_machine=mt-unknown
;;
+ riscv)
+ basic_machine=riscv-ucb
+ ;;
strongarm | thumb | xscale)
basic_machine=arm-unknown
diff -ruN binutils-2.24/gas/config/tc-riscv.c binutils-2.24-riscv/gas/config/tc-riscv.c
--- binutils-2.24/gas/config/tc-riscv.c 1969-12-31 16:00:00.000000000 -0800
+++ binutils-2.24-riscv/gas/config/tc-riscv.c 2014-12-02 16:05:44.913434966 -0800
@@ -0,0 +1,3857 @@
+/* tc-mips.c -- assemble code for a MIPS chip.
+ Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+ 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+ Contributed by the OSF and Ralph Campbell.
+ Written by Keith Knowles and Ralph Campbell, working independently.
+ Modified for ECOFF and R4000 support by Ian Lance Taylor of Cygnus
+ Support.
+
+ This file is part of GAS.
+
+ GAS 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, or (at your option)
+ any later version.
+
+ GAS 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 GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "config.h"
+#include "subsegs.h"
+#include "safe-ctype.h"
+
+#include "itbl-ops.h"
+#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
+
+#include <execinfo.h>
+#include <stdint.h>
+
+#ifdef DEBUG
+#define DBG(x) printf x
+#else
+#define DBG(x)
+#endif
+
+#ifdef OBJ_MAYBE_ELF
+/* Clean up namespace so we can include obj-elf.h too. */
+static int mips_output_flavor (void);
+static int mips_output_flavor (void) { return OUTPUT_FLAVOR; }
+#undef OBJ_PROCESS_STAB
+#undef OUTPUT_FLAVOR
+#undef S_GET_ALIGN
+#undef S_GET_SIZE
+#undef S_SET_ALIGN
+#undef S_SET_SIZE
+#undef obj_frob_file
+#undef obj_frob_file_after_relocs
+#undef obj_frob_symbol
+#undef obj_pop_insert
+#undef obj_sec_sym_ok_for_reloc
+#undef OBJ_COPY_SYMBOL_ATTRIBUTES
+
+#include "obj-elf.h"
+/* Fix any of them that we actually care about. */
+#undef OUTPUT_FLAVOR
+#define OUTPUT_FLAVOR mips_output_flavor()
+#endif
+
+#if defined (OBJ_ELF)
+#include "elf/riscv.h"
+#endif
+
+#include "ecoff.h"
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+static char *mips_regmask_frag;
+#endif
+
+#define ILLEGAL_REG (32)
+
+#define ZERO 0
+#define TREG 18
+#define PIC_CALL_REG 19
+#define KT0 ILLEGAL_REG
+#define KT1 ILLEGAL_REG
+#define GP ILLEGAL_REG
+#define FP 29
+#define SP 30
+#define ATREG ILLEGAL_REG
+#define RA LINK_REG
+
+/* Allow override of standard little-endian ECOFF format. */
+
+#ifndef ECOFF_LITTLE_FORMAT
+#define ECOFF_LITTLE_FORMAT "ecoff-littlemips"
+#endif
+
+extern int target_big_endian;
+
+/* Information about an instruction, including its format, operands
+ and fixups. */
+struct mips_cl_insn
+{
+ /* The opcode's entry in riscv_opcodes or mips16_opcodes. */
+ const struct riscv_opcode *insn_mo;
+
+ /* The 16-bit or 32-bit bitstring of the instruction itself. This is
+ a copy of INSN_MO->match with the operands filled in. */
+ unsigned long insn_opcode;
+
+ /* The frag that contains the instruction. */
+ struct frag *frag;
+
+ /* The offset into FRAG of the first instruction byte. */
+ long where;
+
+ /* The relocs associated with the instruction, if any. */
+ fixS *fixp[3];
+};
+
+static bfd_boolean rv64 = TRUE; /* RV64 (true) or RV32 (false) */
+#define HAVE_32BIT_SYMBOLS 1 /* LUI/ADDI for symbols, even in RV64 */
+#define HAVE_32BIT_ADDRESSES (!rv64)
+#define LOAD_ADDRESS_INSN (HAVE_32BIT_ADDRESSES ? "lw" : "ld")
+#define ADD32_INSN (rv64 ? "addiw" : "addi")
+
+/* This is the set of options which may be modified by the .set
+ pseudo-op. We use a struct so that .set push and .set pop are more
+ reliable. */
+
+struct mips_set_options
+{
+ /* Enable RVC instruction compression */
+ int rvc;
+};
+
+static struct mips_set_options mips_opts =
+{
+ /* rvc */ 0
+};
+
+/* These variables are filled in with the masks of registers used.
+ The object format code reads them and puts them in the appropriate
+ place. */
+unsigned long mips_gprmask;
+unsigned long mips_fprmask;
+
+/* Whether or not we're generating position-independent code. */
+static bfd_boolean is_pic = FALSE;
+
+/* handle of the OPCODE hash table */
+static struct hash_control *op_hash = NULL;
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful */
+const char comment_chars[] = "#";
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that C style comments are always supported. */
+const char line_comment_chars[] = "#";
+
+/* This array holds machine specific line separator characters. */
+const char line_separator_chars[] = ";";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c . Ideally it shouldn't have to know about it at all,
+ but nothing is ideal around here.
+ */
+
+static char *insn_error;
+
+static int auto_align = 1;
+
+/* To output NOP instructions correctly, we need to keep information
+ about the previous two instructions. */
+
+/* Debugging level. -g sets this to 2. -gN sets this to N. -g0 is
+ equivalent to seeing no -g option at all. */
+static int mips_debug = 0;
+
+/* For ECOFF and ELF, relocations against symbols are done in two
+ parts, with a HI relocation and a LO relocation. Each relocation
+ has only 16 bits of space to store an addend. This means that in
+ order for the linker to handle carries correctly, it must be able
+ to locate both the HI and the LO relocation. This means that the
+ relocations must appear in order in the relocation table.
+
+ In order to implement this, we keep track of each unmatched HI
+ relocation. We then sort them so that they immediately precede the
+ corresponding LO relocation. */
+
+struct mips_hi_fixup
+{
+ /* Next HI fixup. */
+ struct mips_hi_fixup *next;
+ /* This fixup. */
+ fixS *fixp;
+ /* The section this fixup is in. */
+ segT seg;
+};
+
+/* The frag containing the last explicit relocation operator.
+ Null if explicit relocations have not been used. */
+
+static fragS *prev_reloc_op_frag;
+
+#define RELAX_BRANCH_ENCODE(uncond, toofar) \
+ ((relax_substateT) \
+ (0xc0000000 \
+ | ((toofar) ? 1 : 0) \
+ | ((uncond) ? 8 : 0)))
+#define RELAX_BRANCH_P(i) (((i) & 0xf0000000) == 0xc0000000)
+#define RELAX_BRANCH_UNCOND(i) (((i) & 8) != 0)
+#define RELAX_BRANCH_TOOFAR(i) (((i) & 1) != 0)
+
+/* Is the given value a sign-extended 32-bit value? */
+#define IS_SEXT_32BIT_NUM(x) \
+ (((x) &~ (offsetT) 0x7fffffff) == 0 \
+ || (((x) &~ (offsetT) 0x7fffffff) == ~ (offsetT) 0x7fffffff))
+
+#define IS_SEXT_NBIT_NUM(x,n) \
+ ({ int64_t __tmp = (x); \
+ __tmp = (__tmp << (64-(n))) >> (64-(n)); \
+ __tmp == (x); })
+
+/* Is the given value a zero-extended 32-bit value? Or a negated one? */
+#define IS_ZEXT_32BIT_NUM(x) \
+ (((x) &~ (offsetT) 0xffffffff) == 0 \
+ || (((x) &~ (offsetT) 0xffffffff) == ~ (offsetT) 0xffffffff))
+
+/* Replace bits MASK << SHIFT of STRUCT with the equivalent bits in
+ VALUE << SHIFT. VALUE is evaluated exactly once. */
+#define INSERT_BITS(STRUCT, VALUE, MASK, SHIFT) \
+ (STRUCT) = (((STRUCT) & ~((MASK) << (SHIFT))) \
+ | (((VALUE) & (MASK)) << (SHIFT)))
+
+/* Extract bits MASK << SHIFT from STRUCT and shift them right
+ SHIFT places. */
+#define EXTRACT_BITS(STRUCT, MASK, SHIFT) \
+ (((STRUCT) >> (SHIFT)) & (MASK))
+
+/* Change INSN's opcode so that the operand given by FIELD has value VALUE.
+ INSN is a mips_cl_insn structure and VALUE is evaluated exactly once.
+
+ include/opcode/mips.h specifies operand fields using the macros
+ OP_MASK_<FIELD> and OP_SH_<FIELD>. The MIPS16 equivalents start
+ with "MIPS16OP" instead of "OP". */
+#define INSERT_OPERAND(FIELD, INSN, VALUE) \
+ INSERT_BITS ((INSN).insn_opcode, VALUE, OP_MASK_##FIELD, OP_SH_##FIELD)
+
+/* Extract the operand given by FIELD from mips_cl_insn INSN. */
+#define EXTRACT_OPERAND(FIELD, INSN) \
+ EXTRACT_BITS ((INSN).insn_opcode, OP_MASK_##FIELD, OP_SH_##FIELD)
+
+/* Determine if an instruction matches an opcode. */
+#define OPCODE_MATCHES(OPCODE, OP) \
+ (((OPCODE) & MASK_##OP) == MATCH_##OP)
+
+#define INSN_MATCHES(INSN, OP) \
+ (((INSN).insn_opcode & MASK_##OP) == MATCH_##OP)
+
+#define OPCODE_IS_STORE(OPCODE) \
+ (OPCODE_MATCHES(OPCODE, SD) || OPCODE_MATCHES(OPCODE, SW) || \
+ OPCODE_MATCHES(OPCODE, SH) || OPCODE_MATCHES(OPCODE, SB) || \
+ OPCODE_MATCHES(OPCODE, FSW) || OPCODE_MATCHES(OPCODE, FSD))
+
+/* Prototypes for static functions. */
+
+#define internalError() \
+ as_fatal (_("internal Error, line %d, %s"), __LINE__, __FILE__)
+
+enum mips_regclass { MIPS_GR_REG, MIPS_FP_REG };
+
+static void append_insn
+ (struct mips_cl_insn *ip, expressionS *p, bfd_reloc_code_real_type *r);
+static void macro (struct mips_cl_insn * ip);
+static void mips_ip (char *str, struct mips_cl_insn * ip);
+static size_t my_getSmallExpression
+ (expressionS *, bfd_reloc_code_real_type *, char *);
+static void my_getExpression (expressionS *, char *);
+static void s_align (int);
+static void s_change_sec (int);
+static void s_change_section (int);
+static void s_cons (int);
+static void s_float_cons (int);
+static void s_mips_globl (int);
+static void s_mipsset (int);
+static void s_dtprelword (int);
+static void s_dtpreldword (int);
+static void s_mips_weakext (int);
+static void s_mips_file (int);
+static void s_mips_loc (int);
+static int validate_mips_insn (const struct riscv_opcode *);
+static int relaxed_branch_length (fragS *fragp, asection *sec, int update);
+
+/* Pseudo-op table.
+
+ The following pseudo-ops from the Kane and Heinrich MIPS book
+ should be defined here, but are currently unsupported: .alias,
+ .galive, .gjaldef, .gjrlive, .livereg, .noalias.
+
+ The following pseudo-ops from the Kane and Heinrich MIPS book are
+ specific to the type of debugging information being generated, and
+ should be defined by the object format: .aent, .begin, .bend,
+ .bgnb, .end, .endb, .ent, .fmask, .frame, .loc, .mask, .verstamp,
+ .vreg.
+
+ The following pseudo-ops from the Kane and Heinrich MIPS book are
+ not MIPS CPU specific, but are also not specific to the object file
+ format. This file is probably the best place to define them, but
+ they are not currently supported: .asm0, .endr, .lab, .struct. */
+
+static const pseudo_typeS mips_pseudo_table[] =
+{
+ /* MIPS specific pseudo-ops. */
+ {"set", s_mipsset, 0},
+ {"rdata", s_change_sec, 'r'},
+ {"dtprelword", s_dtprelword, 0},
+ {"dtpreldword", s_dtpreldword, 0},
+
+ /* Relatively generic pseudo-ops that happen to be used on MIPS
+ chips. */
+ {"asciiz", stringer, 8 + 1},
+ {"bss", s_change_sec, 'b'},
+ {"err", s_err, 0},
+ {"half", s_cons, 1},
+ {"dword", s_cons, 3},
+ {"weakext", s_mips_weakext, 0},
+ {"origin", s_org, 0},
+ {"repeat", s_rept, 0},
+
+ /* These pseudo-ops are defined in read.c, but must be overridden
+ here for one reason or another. */
+ {"align", s_align, 0},
+ {"byte", s_cons, 0},
+ {"data", s_change_sec, 'd'},
+ {"double", s_float_cons, 'd'},
+ {"float", s_float_cons, 'f'},
+ {"globl", s_mips_globl, 0},
+ {"global", s_mips_globl, 0},
+ {"hword", s_cons, 1},
+ {"int", s_cons, 2},
+ {"long", s_cons, 2},
+ {"octa", s_cons, 4},
+ {"quad", s_cons, 3},
+ {"section", s_change_section, 0},
+ {"short", s_cons, 1},
+ {"single", s_float_cons, 'f'},
+ {"text", s_change_sec, 't'},
+ {"word", s_cons, 2},
+
+ { NULL, NULL, 0 },
+};
+
+static const pseudo_typeS mips_nonecoff_pseudo_table[] =
+{
+ /* These pseudo-ops should be defined by the object file format.
+ However, a.out doesn't support them, so we have versions here. */
+ {"bgnb", s_ignore, 0},
+ {"endb", s_ignore, 0},
+ {"file", s_mips_file, 0},
+ {"loc", s_mips_loc, 0},
+ {"verstamp", s_ignore, 0},
+ { NULL, NULL, 0 },
+};
+
+extern void pop_insert (const pseudo_typeS *);
+
+void
+mips_pop_insert (void)
+{
+ pop_insert (mips_pseudo_table);
+ pop_insert (mips_nonecoff_pseudo_table);
+}
+
+/* Symbols labelling the current insn. */
+
+struct insn_label_list
+{
+ struct insn_label_list *next;
+ symbolS *label;
+};
+
+static struct insn_label_list *free_insn_labels;
+#define label_list tc_segment_info_data.labels
+
+void
+mips_clear_insn_labels (void)
+{
+ register struct insn_label_list **pl;
+ segment_info_type *si;
+
+ if (now_seg)
+ {
+ for (pl = &free_insn_labels; *pl != NULL; pl = &(*pl)->next)
+ ;
+
+ si = seg_info (now_seg);
+ *pl = si->label_list;
+ si->label_list = NULL;
+ }
+}
+
+
+static char *expr_end;
+
+/* Expressions which appear in instructions. These are set by
+ mips_ip. */
+
+static expressionS imm_expr;
+static expressionS imm2_expr;
+static expressionS offset_expr;
+
+/* Relocs associated with imm_expr and offset_expr. */
+
+static bfd_reloc_code_real_type imm_reloc[3]
+ = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+static bfd_reloc_code_real_type offset_reloc[3]
+ = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+
+/* The default target format to use. */
+
+const char *
+mips_target_format (void)
+{
+ return rv64 ? "elf64-littleriscv" : "elf32-littleriscv";
+}
+
+/* Return the length of instruction INSN. */
+
+static inline unsigned int
+insn_length (const struct mips_cl_insn *insn)
+{
+ /* RVC instructions have insn[1:0] != 3 */
+ return mips_opts.rvc && (insn->insn_opcode & 0x3) != 0x3 ? 2 : 4;
+}
+
+static int
+imm_bits_needed(int32_t imm)
+{
+ int imm_bits = 32;
+ while(imm_bits > 1 && (imm << (32-(imm_bits-1)) >> (32-(imm_bits-1))) == imm)
+ imm_bits--;
+ return imm_bits;
+}
+
+/* return the rvc small register id, if it exists; else, return -1. */
+#define ARRAY_FIND(array, x) ({ \
+ size_t _pos = ARRAY_SIZE(array), _i; \
+ for(_i = 0; _i < ARRAY_SIZE(array); _i++) \
+ if((x) == (array)[_i]) \
+ { _pos = _i; break; } \
+ _pos; })
+#define IN_ARRAY(array, x) (ARRAY_FIND(array, x) != ARRAY_SIZE(array))
+
+#define is_rvc_reg(type, x) IN_ARRAY(rvc_##type##_regmap, x)
+#define rvc_reg(type, x) ARRAY_FIND(rvc_##type##_regmap, x)
+
+/* If insn can be compressed, compress it and return 1; else return 0. */
+static int
+riscv_rvc_compress(struct mips_cl_insn* insn)
+{
+ int rd = EXTRACT_OPERAND(RD, *insn);
+ int rs1 = EXTRACT_OPERAND(RS, *insn);
+ int rs2 ATTRIBUTE_UNUSED = EXTRACT_OPERAND(RT, *insn);
+ int32_t imm = EXTRACT_OPERAND(IMMEDIATE, *insn);
+ imm = imm << (32-RISCV_IMM_BITS) >> (32-RISCV_IMM_BITS);
+ int32_t shamt = imm & 0x3f;
+ int32_t bimm = EXTRACT_OPERAND(IMMLO, *insn) |
+ (EXTRACT_OPERAND(IMMHI, *insn) << RISCV_IMMLO_BITS);
+ bimm = bimm << (32-RISCV_IMM_BITS) >> (32-RISCV_IMM_BITS);
+ int32_t jt = EXTRACT_OPERAND(TARGET, *insn);
+ jt = jt << (32-RISCV_JUMP_BITS) >> (32-RISCV_JUMP_BITS);
+
+ gas_assert(insn_length(insn) == 4);
+
+ int imm_bits = imm_bits_needed(imm);
+ int bimm_bits = imm_bits_needed(bimm);
+ int jt_bits = imm_bits_needed(jt);
+
+ if(INSN_MATCHES(*insn, ADDI) && rd != 0 && rd == rs1 && imm_bits <= 6)
+ {
+ insn->insn_opcode = MATCH_C_ADDI;
+ INSERT_OPERAND(CRD, *insn, rd);
+ INSERT_OPERAND(CIMM6, *insn, imm);
+ }
+ else if(INSN_MATCHES(*insn, ADDIW) && rd != 0 && rd == rs1 && imm_bits <= 6)
+ {
+ insn->insn_opcode = MATCH_C_ADDIW;
+ INSERT_OPERAND(CRD, *insn, rd);
+ INSERT_OPERAND(CIMM6, *insn, imm);
+ }
+ else if(INSN_MATCHES(*insn, JALR_J) && rd == 0 && rs1 != 1 && imm == 0)
+ {
+ // jalr.j rd=0, rs1 != 1, imm=0 is encoded as c.addi rd=0, imm={1'b0,rs1}
+ insn->insn_opcode = MATCH_C_ADDI;
+ INSERT_OPERAND(CIMM6, *insn, rs1);
+ }
+ else if(INSN_MATCHES(*insn, JALR_R) && rd == 0 && rs1 == 1 && imm == 0)
+ {
+ // jalr.r rd=0, rs1=1, imm=0 is encoded as c.addi rd=0, imm={1'b0,rs1}
+ insn->insn_opcode = MATCH_C_ADDI;
+ INSERT_OPERAND(CIMM6, *insn, rs1);
+ }
+ else if(INSN_MATCHES(*insn, JALR_C) && rd == 1 && imm == 0)
+ {
+ // jalr.c rd=1, rs1, imm=0 is encoded as c.addi rd=0, imm={1'b1,rs1}
+ insn->insn_opcode = MATCH_C_ADDI;
+ INSERT_OPERAND(CIMM6, *insn, 0x20 | rs1);
+ }
+ else if((INSN_MATCHES(*insn, ADDI) || INSN_MATCHES(*insn, ORI) ||
+ INSN_MATCHES(*insn, XORI)) && rs1 == 0 && imm_bits <= 6)
+ {
+ insn->insn_opcode = MATCH_C_LI;
+ INSERT_OPERAND(CRD, *insn, rd);
+ INSERT_OPERAND(CIMM6, *insn, imm);
+ }
+ else if((INSN_MATCHES(*insn, ADDI) || INSN_MATCHES(*insn, ORI) ||
+ INSN_MATCHES(*insn, XORI)) && rs1 == 0 && imm_bits <= 6)
+ {
+ insn->insn_opcode = MATCH_C_LI;
+ INSERT_OPERAND(CRD, *insn, rd);
+ INSERT_OPERAND(CIMM6, *insn, imm);
+ }
+ else if((INSN_MATCHES(*insn, ADDI) || INSN_MATCHES(*insn, ORI) ||
+ INSN_MATCHES(*insn, XORI)) && imm == 0)
+ {
+ insn->insn_opcode = MATCH_C_MOVE;
+ INSERT_OPERAND(CRD, *insn, rd);
+ INSERT_OPERAND(CRS1, *insn, rs1);
+ }
+ else if((INSN_MATCHES(*insn, ADD) || INSN_MATCHES(*insn, OR) ||
+ INSN_MATCHES(*insn, XOR)) &&
+ (rs1 == 0 || rs2 == 0))
+ {
+ insn->insn_opcode = MATCH_C_MOVE;
+ INSERT_OPERAND(CRD, *insn, rd);
+ INSERT_OPERAND(CRS1, *insn, rs1 == 0 ? rs2 : rs1);
+ }
+ else if(INSN_MATCHES(*insn, ADD) && (rd == rs1 || rd == rs2))
+ {
+ insn->insn_opcode = MATCH_C_ADD;
+ INSERT_OPERAND(CRD, *insn, rd);
+ INSERT_OPERAND(CRS1, *insn, rd == rs1 ? rs2 : rs1);
+ }
+ else if(INSN_MATCHES(*insn, SUB) && rd == rs2)
+ {
+ insn->insn_opcode = MATCH_C_SUB;
+ INSERT_OPERAND(CRD, *insn, rd);
+ INSERT_OPERAND(CRS1, *insn, rs1);
+ }
+ else if(INSN_MATCHES(*insn, ADD) && is_rvc_reg(rd, rd) && is_rvc_reg(rs1, rs1) && is_rvc_reg(rs2b, rs2))
+ {
+ insn->insn_opcode = MATCH_C_ADD3;
+ INSERT_OPERAND(CRDS, *insn, rvc_reg(rd, rd));
+ INSERT_OPERAND(CRS1S, *insn, rvc_reg(rs1, rs1));
+ INSERT_OPERAND(CRS2BS, *insn, rvc_reg(rs2b, rs2));
+ }
+ else if(INSN_MATCHES(*insn, SUB) && is_rvc_reg(rd, rd) && is_rvc_reg(rs1, rs1) && is_rvc_reg(rs2b, rs2))
+ {
+ insn->insn_opcode = MATCH_C_SUB3;
+ INSERT_OPERAND(CRDS, *insn, rvc_reg(rd, rd));
+ INSERT_OPERAND(CRS1S, *insn, rvc_reg(rs1, rs1));
+ INSERT_OPERAND(CRS2BS, *insn, rvc_reg(rs2b, rs2));
+ }
+ else if(INSN_MATCHES(*insn, OR) && is_rvc_reg(rd, rd) && is_rvc_reg(rs1, rs1) && is_rvc_reg(rs2b, rs2))
+ {
+ insn->insn_opcode = MATCH_C_OR3;
+ INSERT_OPERAND(CRDS, *insn, rvc_reg(rd, rd));
+ INSERT_OPERAND(CRS1S, *insn, rvc_reg(rs1, rs1));
+ INSERT_OPERAND(CRS2BS, *insn, rvc_reg(rs2b, rs2));
+ }
+ else if(INSN_MATCHES(*insn, AND) && is_rvc_reg(rd, rd) && is_rvc_reg(rs1, rs1) && is_rvc_reg(rs2b, rs2))
+ {
+ insn->insn_opcode = MATCH_C_AND3;
+ INSERT_OPERAND(CRDS, *insn, rvc_reg(rd, rd));
+ INSERT_OPERAND(CRS1S, *insn, rvc_reg(rs1, rs1));
+ INSERT_OPERAND(CRS2BS, *insn, rvc_reg(rs2b, rs2));
+ }
+ else if(INSN_MATCHES(*insn, SLLI) && rd == rs1 && is_rvc_reg(rd, rd))
+ {
+ insn->insn_opcode = shamt >= 32 ? MATCH_C_SLLI32 : MATCH_C_SLLI;
+ INSERT_OPERAND(CRDS, *insn, rvc_reg(rd, rd));
+ INSERT_OPERAND(CIMM5, *insn, shamt);
+ }
+ else if(INSN_MATCHES(*insn, SRLI) && rd == rs1 && is_rvc_reg(rd, rd))
+ {
+ insn->insn_opcode = shamt >= 32 ? MATCH_C_SRLI32 : MATCH_C_SRLI;
+ INSERT_OPERAND(CRDS, *insn, rvc_reg(rd, rd));
+ INSERT_OPERAND(CIMM5, *insn, shamt);
+ }
+ else if(INSN_MATCHES(*insn, SRAI) && rd == rs1 && is_rvc_reg(rd, rd))
+ {
+ insn->insn_opcode = shamt >= 32 ? MATCH_C_SRAI32 : MATCH_C_SRAI;
+ INSERT_OPERAND(CRDS, *insn, rvc_reg(rd, rd));
+ INSERT_OPERAND(CIMM5, *insn, shamt);
+ }
+ else if(INSN_MATCHES(*insn, SLLIW) && rd == rs1 && is_rvc_reg(rd, rd))
+ {
+ insn->insn_opcode = MATCH_C_SLLIW;
+ INSERT_OPERAND(CRDS, *insn, rvc_reg(rd, rd));
+ INSERT_OPERAND(CIMM5, *insn, shamt);
+ }
+ else if(INSN_MATCHES(*insn, J) && jt_bits <= 10)
+ {
+ insn->insn_opcode = MATCH_C_J;
+ INSERT_OPERAND(CIMM10, *insn, jt);
+ }
+ else if(INSN_MATCHES(*insn, BEQ) && rs1 == rs2 && bimm_bits <= 10)
+ {
+ insn->insn_opcode = MATCH_C_J;
+ INSERT_OPERAND(CIMM10, *insn, bimm);
+ }
+ else if(INSN_MATCHES(*insn, BEQ) && is_rvc_reg(rs1, rs1) && is_rvc_reg(rs2, rs2) && bimm_bits <= 5)
+ {
+ insn->insn_opcode = MATCH_C_BEQ;
+ INSERT_OPERAND(CRS1S, *insn, rvc_reg(rs1, rs1));
+ INSERT_OPERAND(CRS2S, *insn, rvc_reg(rs2, rs2));
+ INSERT_OPERAND(CIMM5, *insn, bimm);
+ }
+ else if(INSN_MATCHES(*insn, BNE) && is_rvc_reg(rs1, rs1) && is_rvc_reg(rs2, rs2) && bimm_bits <= 5)
+ {
+ insn->insn_opcode = MATCH_C_BNE;
+ INSERT_OPERAND(CRS1S, *insn, rvc_reg(rs1, rs1));
+ INSERT_OPERAND(CRS2S, *insn, rvc_reg(rs2, rs2));
+ INSERT_OPERAND(CIMM5, *insn, bimm);
+ }
+ else if(INSN_MATCHES(*insn, LD) && rs1 == 30 && imm%8 == 0 && imm_bits <= 9)
+ {
+ insn->insn_opcode = MATCH_C_LDSP;
+ INSERT_OPERAND(CRD, *insn, rd);
+ INSERT_OPERAND(CIMM6, *insn, imm/8);
+ }
+ else if(INSN_MATCHES(*insn, LW) && rs1 == 30 && imm%4 == 0 && imm_bits <= 8)
+ {
+ insn->insn_opcode = MATCH_C_LWSP;
+ INSERT_OPERAND(CRD, *insn, rd);
+ INSERT_OPERAND(CIMM6, *insn, imm/4);
+ }
+ else if(INSN_MATCHES(*insn, SD) && rs1 == 30 && bimm%8 == 0 && bimm_bits <= 9)
+ {
+ insn->insn_opcode = MATCH_C_SDSP;
+ INSERT_OPERAND(CRS2, *insn, rs2);
+ INSERT_OPERAND(CIMM6, *insn, bimm/8);
+ }
+ else if(INSN_MATCHES(*insn, SW) && rs1 == 30 && bimm%4 == 0 && bimm_bits <= 8)
+ {
+ insn->insn_opcode = MATCH_C_SWSP;
+ INSERT_OPERAND(CRS2, *insn, rs2);
+ INSERT_OPERAND(CIMM6, *insn, bimm/4);
+ }
+ else if(INSN_MATCHES(*insn, LD) && is_rvc_reg(rs1, rs1) && is_rvc_reg(rd, rd) && imm%8 == 0 && imm_bits <= 8)
+ {
+ insn->insn_opcode = MATCH_C_LD;
+ INSERT_OPERAND(CRS1S, *insn, rvc_reg(rs1, rs1));
+ INSERT_OPERAND(CRDS, *insn, rvc_reg(rd, rd));
+ INSERT_OPERAND(CIMM5, *insn, imm/8);
+ }
+ else if(INSN_MATCHES(*insn, LW) && is_rvc_reg(rs1, rs1) && is_rvc_reg(rd, rd) && imm%4 == 0 && imm_bits <= 7)
+ {
+ insn->insn_opcode = MATCH_C_LW;
+ INSERT_OPERAND(CRS1S, *insn, rvc_reg(rs1, rs1));
+ INSERT_OPERAND(CRDS, *insn, rvc_reg(rd, rd));
+ INSERT_OPERAND(CIMM5, *insn, imm/4);
+ }
+ else if(INSN_MATCHES(*insn, SD) && is_rvc_reg(rs1, rs1) && is_rvc_reg(rs2, rs2) && bimm%8 == 0 && bimm_bits <= 8)
+ {
+ insn->insn_opcode = MATCH_C_SD;
+ INSERT_OPERAND(CRS1S, *insn, rvc_reg(rs1, rs1));
+ INSERT_OPERAND(CRS2S, *insn, rvc_reg(rs2, rs2));
+ INSERT_OPERAND(CIMM5, *insn, bimm/8);
+ }
+ else if(INSN_MATCHES(*insn, SW) && is_rvc_reg(rs1, rs1) && is_rvc_reg(rs2, rs2) && bimm%4 == 0 && bimm_bits <= 7)
+ {
+ insn->insn_opcode = MATCH_C_SW;
+ INSERT_OPERAND(CRS1S, *insn, rvc_reg(rs1, rs1));
+ INSERT_OPERAND(CRS2S, *insn, rvc_reg(rs2, rs2));
+ INSERT_OPERAND(CIMM5, *insn, bimm/4);
+ }
+ else if(INSN_MATCHES(*insn, LD) && imm == 0)
+ {
+ insn->insn_opcode = MATCH_C_LD0;
+ INSERT_OPERAND(CRS1, *insn, rs1);
+ INSERT_OPERAND(CRD, *insn, rd);
+ }
+ else if(INSN_MATCHES(*insn, LW) && imm == 0)
+ {
+ insn->insn_opcode = MATCH_C_LW0;
+ INSERT_OPERAND(CRS1, *insn, rs1);
+ INSERT_OPERAND(CRD, *insn, rd);
+ }
+ else if(INSN_MATCHES(*insn, FLD) && is_rvc_reg(rs1, rs1) && is_rvc_reg(rd, rd) && imm%8 == 0 && imm_bits <= 8)
+ {
+ insn->insn_opcode = MATCH_C_FLD;
+ INSERT_OPERAND(CRS1S, *insn, rvc_reg(rs1, rs1));
+ INSERT_OPERAND(CRDS, *insn, rvc_reg(rd, rd));
+ INSERT_OPERAND(CIMM5, *insn, imm/8);
+ }
+ else if(INSN_MATCHES(*insn, FLW) && is_rvc_reg(rs1, rs1) && is_rvc_reg(rd, rd) && imm%4 == 0 && imm_bits <= 7)
+ {
+ insn->insn_opcode = MATCH_C_FLW;
+ INSERT_OPERAND(CRS1S, *insn, rvc_reg(rs1, rs1));
+ INSERT_OPERAND(CRDS, *insn, rvc_reg(rd, rd));
+ INSERT_OPERAND(CIMM5, *insn, imm/4);
+ }
+ else if(INSN_MATCHES(*insn, FSD) && is_rvc_reg(rs1, rs1) && is_rvc_reg(rs2, rs2) && bimm%8 == 0 && bimm_bits <= 8)
+ {
+ insn->insn_opcode = MATCH_C_FSD;
+ INSERT_OPERAND(CRS1S, *insn, rvc_reg(rs1, rs1));
+ INSERT_OPERAND(CRS2S, *insn, rvc_reg(rs2, rs2));
+ INSERT_OPERAND(CIMM5, *insn, bimm/8);
+ }
+ else if(INSN_MATCHES(*insn, FSW) && is_rvc_reg(rs1, rs1) && is_rvc_reg(rs2, rs2) && bimm%4 == 0 && bimm_bits <= 7)
+ {
+ insn->insn_opcode = MATCH_C_FSW;
+ INSERT_OPERAND(CRS1S, *insn, rvc_reg(rs1, rs1));
+ INSERT_OPERAND(CRS2S, *insn, rvc_reg(rs2, rs2));
+ INSERT_OPERAND(CIMM5, *insn, bimm/4);
+ }
+ else
+ return 0;
+
+ gas_assert(insn_length(insn) == 2);
+
+ return 1;
+}
+
+/* Initialise INSN from opcode entry MO. Leave its position unspecified. */
+
+static void
+create_insn (struct mips_cl_insn *insn, const struct riscv_opcode *mo)
+{
+ size_t i;
+
+ insn->insn_mo = mo;
+ insn->insn_opcode = mo->match;
+ insn->frag = NULL;
+ insn->where = 0;
+ for (i = 0; i < ARRAY_SIZE (insn->fixp); i++)
+ insn->fixp[i] = NULL;
+}
+
+/* Install INSN at the location specified by its "frag" and "where" fields. */
+
+static void
+install_insn (const struct mips_cl_insn *insn)
+{
+ char *f = insn->frag->fr_literal + insn->where;
+ md_number_to_chars (f, insn->insn_opcode, insn_length(insn));
+}
+
+/* Move INSN to offset WHERE in FRAG. Adjust the fixups accordingly
+ and install the opcode in the new location. */
+
+static void
+move_insn (struct mips_cl_insn *insn, fragS *frag, long where)
+{
+ size_t i;
+
+ insn->frag = frag;
+ insn->where = where;
+ for (i = 0; i < ARRAY_SIZE (insn->fixp); i++)
+ if (insn->fixp[i] != NULL)
+ {
+ insn->fixp[i]->fx_frag = frag;
+ insn->fixp[i]->fx_where = where;
+ }
+ install_insn (insn);
+}
+
+/* Add INSN to the end of the output. */
+
+static void
+add_fixed_insn (struct mips_cl_insn *insn)
+{
+ char *f = frag_more (insn_length (insn));
+ move_insn (insn, frag_now, f - frag_now->fr_literal);
+}
+
+struct regname {
+ const char *name;
+ unsigned int num;
+};
+
+#define RTYPE_MASK 0x1ff00
+#define RTYPE_NUM 0x00100
+#define RTYPE_FPU 0x00200
+#define RTYPE_VEC 0x00800
+#define RTYPE_GP 0x01000
+#define RTYPE_CP0 0x02000
+#define RTYPE_VGR_REG 0x20000
+#define RTYPE_VFP_REG 0x40000
+#define RNUM_MASK 0x000ff
+#define RWARN 0x80000
+
+#define GENERIC_REGISTER_NUMBERS \
+ {"x0", RTYPE_NUM | 0}, \
+ {"x1", RTYPE_NUM | 1}, \
+ {"x2", RTYPE_NUM | 2}, \
+ {"x3", RTYPE_NUM | 3}, \
+ {"x4", RTYPE_NUM | 4}, \
+ {"x5", RTYPE_NUM | 5}, \
+ {"x6", RTYPE_NUM | 6}, \
+ {"x7", RTYPE_NUM | 7}, \
+ {"x8", RTYPE_NUM | 8}, \
+ {"x9", RTYPE_NUM | 9}, \
+ {"x10", RTYPE_NUM | 10}, \
+ {"x11", RTYPE_NUM | 11}, \
+ {"x12", RTYPE_NUM | 12}, \
+ {"x13", RTYPE_NUM | 13}, \
+ {"x14", RTYPE_NUM | 14}, \
+ {"x15", RTYPE_NUM | 15}, \
+ {"x16", RTYPE_NUM | 16}, \
+ {"x17", RTYPE_NUM | 17}, \
+ {"x18", RTYPE_NUM | 18}, \
+ {"x19", RTYPE_NUM | 19}, \
+ {"x20", RTYPE_NUM | 20}, \
+ {"x21", RTYPE_NUM | 21}, \
+ {"x22", RTYPE_NUM | 22}, \
+ {"x23", RTYPE_NUM | 23}, \
+ {"x24", RTYPE_NUM | 24}, \
+ {"x25", RTYPE_NUM | 25}, \
+ {"x26", RTYPE_NUM | 26}, \
+ {"x27", RTYPE_NUM | 27}, \
+ {"x28", RTYPE_NUM | 28}, \
+ {"x29", RTYPE_NUM | 29}, \
+ {"x30", RTYPE_NUM | 30}, \
+ {"x31", RTYPE_NUM | 31}
+
+#define FP_REGISTER_NAMES \
+ {"f0", RTYPE_FPU | 0}, \
+ {"f1", RTYPE_FPU | 1}, \
+ {"f2", RTYPE_FPU | 2}, \
+ {"f3", RTYPE_FPU | 3}, \
+ {"f4", RTYPE_FPU | 4}, \
+ {"f5", RTYPE_FPU | 5}, \
+ {"f6", RTYPE_FPU | 6}, \
+ {"f7", RTYPE_FPU | 7}, \
+ {"f8", RTYPE_FPU | 8}, \
+ {"f9", RTYPE_FPU | 9}, \
+ {"f10", RTYPE_FPU | 10}, \
+ {"f11", RTYPE_FPU | 11}, \
+ {"f12", RTYPE_FPU | 12}, \
+ {"f13", RTYPE_FPU | 13}, \
+ {"f14", RTYPE_FPU | 14}, \
+ {"f15", RTYPE_FPU | 15}, \
+ {"f16", RTYPE_FPU | 16}, \
+ {"f17", RTYPE_FPU | 17}, \
+ {"f18", RTYPE_FPU | 18}, \
+ {"f19", RTYPE_FPU | 19}, \
+ {"f20", RTYPE_FPU | 20}, \
+ {"f21", RTYPE_FPU | 21}, \
+ {"f22", RTYPE_FPU | 22}, \
+ {"f23", RTYPE_FPU | 23}, \
+ {"f24", RTYPE_FPU | 24}, \
+ {"f25", RTYPE_FPU | 25}, \
+ {"f26", RTYPE_FPU | 26}, \
+ {"f27", RTYPE_FPU | 27}, \
+ {"f28", RTYPE_FPU | 28}, \
+ {"f29", RTYPE_FPU | 29}, \
+ {"f30", RTYPE_FPU | 30}, \
+ {"f31", RTYPE_FPU | 31}
+
+#define CR_REGISTER_NUMBERS \
+ {"cr0", RTYPE_CP0 | 0}, \
+ {"cr1", RTYPE_CP0 | 1}, \
+ {"cr2", RTYPE_CP0 | 2}, \
+ {"cr3", RTYPE_CP0 | 3}, \
+ {"cr4", RTYPE_CP0 | 4}, \
+ {"cr5", RTYPE_CP0 | 5}, \
+ {"cr6", RTYPE_CP0 | 6}, \
+ {"cr7", RTYPE_CP0 | 7}, \
+ {"cr8", RTYPE_CP0 | 8}, \
+ {"cr9", RTYPE_CP0 | 9}, \
+ {"cr10", RTYPE_CP0 | 10}, \
+ {"cr11", RTYPE_CP0 | 11}, \
+ {"cr12", RTYPE_CP0 | 12}, \
+ {"cr13", RTYPE_CP0 | 13}, \
+ {"cr14", RTYPE_CP0 | 14}, \
+ {"cr15", RTYPE_CP0 | 15}, \
+ {"cr16", RTYPE_CP0 | 16}, \
+ {"cr17", RTYPE_CP0 | 17}, \
+ {"cr18", RTYPE_CP0 | 18}, \
+ {"cr19", RTYPE_CP0 | 19}, \
+ {"cr20", RTYPE_CP0 | 20}, \
+ {"cr21", RTYPE_CP0 | 21}, \
+ {"cr22", RTYPE_CP0 | 22}, \
+ {"cr23", RTYPE_CP0 | 23}, \
+ {"cr24", RTYPE_CP0 | 24}, \
+ {"cr25", RTYPE_CP0 | 25}, \
+ {"cr26", RTYPE_CP0 | 26}, \
+ {"cr27", RTYPE_CP0 | 27}, \
+ {"cr28", RTYPE_CP0 | 28}, \
+ {"cr29", RTYPE_CP0 | 29}, \
+ {"cr30", RTYPE_CP0 | 30}, \
+ {"cr31", RTYPE_CP0 | 31}
+
+/* Remaining symbolic register names */
+#define SYMBOLIC_REGISTER_NAMES \
+ { "zero", 0 | RTYPE_GP }, \
+ { "ra", 1 | RTYPE_GP }, \
+ { "s0", 2 | RTYPE_GP }, \
+ { "s1", 3 | RTYPE_GP }, \
+ { "s2", 4 | RTYPE_GP }, \
+ { "s3", 5 | RTYPE_GP }, \
+ { "s4", 6 | RTYPE_GP }, \
+ { "s5", 7 | RTYPE_GP }, \
+ { "s6", 8 | RTYPE_GP }, \
+ { "s7", 9 | RTYPE_GP }, \
+ { "s8", 10 | RTYPE_GP }, \
+ { "s9", 11 | RTYPE_GP }, \
+ { "s10", 12 | RTYPE_GP }, \
+ { "s11", 13 | RTYPE_GP }, \
+ { "sp", 14 | RTYPE_GP }, \
+ { "tp", 15 | RTYPE_GP }, \
+ { "v0", 16 | RTYPE_GP }, \
+ { "v1", 17 | RTYPE_GP }, \
+ { "a0", 18 | RTYPE_GP }, \
+ { "a1", 19 | RTYPE_GP }, \
+ { "a2", 20 | RTYPE_GP }, \
+ { "a3", 21 | RTYPE_GP }, \
+ { "a4", 22 | RTYPE_GP }, \
+ { "a5", 23 | RTYPE_GP }, \
+ { "a6", 24 | RTYPE_GP }, \
+ { "a7", 25 | RTYPE_GP }, \
+ { "a8", 26 | RTYPE_GP }, \
+ { "a9", 27 | RTYPE_GP }, \
+ { "a10", 28 | RTYPE_GP }, \
+ { "a11", 29 | RTYPE_GP }, \
+ { "a12", 30 | RTYPE_GP }, \
+ { "a13", 31 | RTYPE_GP }
+
+#define FP_SYMBOLIC_REGISTER_NAMES \
+ { "fs0", 0 | RTYPE_FPU }, \
+ { "fs1", 1 | RTYPE_FPU }, \
+ { "fs2", 2 | RTYPE_FPU }, \
+ { "fs3", 3 | RTYPE_FPU }, \
+ { "fs4", 4 | RTYPE_FPU }, \
+ { "fs5", 5 | RTYPE_FPU }, \
+ { "fs6", 6 | RTYPE_FPU }, \
+ { "fs7", 7 | RTYPE_FPU }, \
+ { "fs8", 8 | RTYPE_FPU }, \
+ { "fs9", 9 | RTYPE_FPU }, \
+ { "fs10", 10 | RTYPE_FPU }, \
+ { "fs11", 11 | RTYPE_FPU }, \
+ { "fs12", 12 | RTYPE_FPU }, \
+ { "fs13", 13 | RTYPE_FPU }, \
+ { "fs14", 14 | RTYPE_FPU }, \
+ { "fs15", 15 | RTYPE_FPU }, \
+ { "fv0", 16 | RTYPE_FPU }, \
+ { "fv1", 17 | RTYPE_FPU }, \
+ { "fa0", 18 | RTYPE_FPU }, \
+ { "fa1", 19 | RTYPE_FPU }, \
+ { "fa2", 20 | RTYPE_FPU }, \
+ { "fa3", 21 | RTYPE_FPU }, \
+ { "fa4", 22 | RTYPE_FPU }, \
+ { "fa5", 23 | RTYPE_FPU }, \
+ { "fa6", 24 | RTYPE_FPU }, \
+ { "fa7", 25 | RTYPE_FPU }, \
+ { "fa8", 26 | RTYPE_FPU }, \
+ { "fa9", 27 | RTYPE_FPU }, \
+ { "fa10", 28 | RTYPE_FPU }, \
+ { "fa11", 29 | RTYPE_FPU }, \
+ { "fa12", 30 | RTYPE_FPU }, \
+ { "fa13", 31 | RTYPE_FPU }
+
+#define RISCV_VEC_GR_REGISTER_NAMES \
+ {"vx0", RTYPE_VGR_REG | 0}, \
+ {"vx1", RTYPE_VGR_REG | 1}, \
+ {"vx2", RTYPE_VGR_REG | 2}, \
+ {"vx3", RTYPE_VGR_REG | 3}, \
+ {"vx4", RTYPE_VGR_REG | 4}, \
+ {"vx5", RTYPE_VGR_REG | 5}, \
+ {"vx6", RTYPE_VGR_REG | 6}, \
+ {"vx7", RTYPE_VGR_REG | 7}, \
+ {"vx8", RTYPE_VGR_REG | 8}, \
+ {"vx9", RTYPE_VGR_REG | 9}, \
+ {"vx10", RTYPE_VGR_REG | 10}, \
+ {"vx11", RTYPE_VGR_REG | 11}, \
+ {"vx12", RTYPE_VGR_REG | 12}, \
+ {"vx13", RTYPE_VGR_REG | 13}, \
+ {"vx14", RTYPE_VGR_REG | 14}, \
+ {"vx15", RTYPE_VGR_REG | 15}, \
+ {"vx16", RTYPE_VGR_REG | 16}, \
+ {"vx17", RTYPE_VGR_REG | 17}, \
+ {"vx18", RTYPE_VGR_REG | 18}, \
+ {"vx19", RTYPE_VGR_REG | 19}, \
+ {"vx20", RTYPE_VGR_REG | 20}, \
+ {"vx21", RTYPE_VGR_REG | 21}, \
+ {"vx22", RTYPE_VGR_REG | 22}, \
+ {"vx23", RTYPE_VGR_REG | 23}, \
+ {"vx24", RTYPE_VGR_REG | 24}, \
+ {"vx25", RTYPE_VGR_REG | 25}, \
+ {"vx26", RTYPE_VGR_REG | 26}, \
+ {"vx27", RTYPE_VGR_REG | 27}, \
+ {"vx28", RTYPE_VGR_REG | 28}, \
+ {"vx29", RTYPE_VGR_REG | 29}, \
+ {"vx30", RTYPE_VGR_REG | 30}, \
+ {"vx31", RTYPE_VGR_REG | 31}
+
+#define RISCV_VEC_FP_REGISTER_NAMES \
+ {"vf0", RTYPE_VFP_REG | 0}, \
+ {"vf1", RTYPE_VFP_REG | 1}, \
+ {"vf2", RTYPE_VFP_REG | 2}, \
+ {"vf3", RTYPE_VFP_REG | 3}, \
+ {"vf4", RTYPE_VFP_REG | 4}, \
+ {"vf5", RTYPE_VFP_REG | 5}, \
+ {"vf6", RTYPE_VFP_REG | 6}, \
+ {"vf7", RTYPE_VFP_REG | 7}, \
+ {"vf8", RTYPE_VFP_REG | 8}, \
+ {"vf9", RTYPE_VFP_REG | 9}, \
+ {"vf10", RTYPE_VFP_REG | 10}, \
+ {"vf11", RTYPE_VFP_REG | 11}, \
+ {"vf12", RTYPE_VFP_REG | 12}, \
+ {"vf13", RTYPE_VFP_REG | 13}, \
+ {"vf14", RTYPE_VFP_REG | 14}, \
+ {"vf15", RTYPE_VFP_REG | 15}, \
+ {"vf16", RTYPE_VFP_REG | 16}, \
+ {"vf17", RTYPE_VFP_REG | 17}, \
+ {"vf18", RTYPE_VFP_REG | 18}, \
+ {"vf19", RTYPE_VFP_REG | 19}, \
+ {"vf20", RTYPE_VFP_REG | 20}, \
+ {"vf21", RTYPE_VFP_REG | 21}, \
+ {"vf22", RTYPE_VFP_REG | 22}, \
+ {"vf23", RTYPE_VFP_REG | 23}, \
+ {"vf24", RTYPE_VFP_REG | 24}, \
+ {"vf25", RTYPE_VFP_REG | 25}, \
+ {"vf26", RTYPE_VFP_REG | 26}, \
+ {"vf27", RTYPE_VFP_REG | 27}, \
+ {"vf28", RTYPE_VFP_REG | 28}, \
+ {"vf29", RTYPE_VFP_REG | 29}, \
+ {"vf30", RTYPE_VFP_REG | 30}, \
+ {"vf31", RTYPE_VFP_REG | 31}
+
+static const struct regname reg_names[] = {
+ GENERIC_REGISTER_NUMBERS,
+ FP_REGISTER_NAMES,
+ CR_REGISTER_NUMBERS,
+
+ SYMBOLIC_REGISTER_NAMES,
+ FP_SYMBOLIC_REGISTER_NAMES,
+
+ RISCV_VEC_GR_REGISTER_NAMES,
+ RISCV_VEC_FP_REGISTER_NAMES,
+
+ {0, 0}
+};
+
+static struct hash_control *reg_names_hash = NULL;
+
+static int
+reg_lookup (char **s, unsigned int types, unsigned int *regnop)
+{
+ struct regname *r;
+ char *e;
+ char save_c;
+ int reg = -1;
+
+ /* Find end of name. */
+ e = *s;
+ if (is_name_beginner (*e))
+ ++e;
+ while (is_part_of_name (*e))
+ ++e;
+
+ /* Terminate name. */
+ save_c = *e;
+ *e = '\0';
+
+ /* Look for the register. */
+ r = (struct regname *) hash_find (reg_names_hash, *s);
+ if (r != NULL && (r->num & types))
+ reg = r->num & RNUM_MASK;
+
+ /* Advance to next token if a register was recognised. */
+ if (reg >= 0)
+ *s = e;
+ else if (types & RWARN)
+ as_warn ("Unrecognized register name `%s'", *s);
+
+ *e = save_c;
+ if (regnop)
+ *regnop = reg;
+ return reg >= 0;
+}
+
+/* This function is called once, at assembler startup time. It should set up
+ all the tables, etc. that the MD part of the assembler will need. */
+
+void
+md_begin (void)
+{
+ const char *retval = NULL;
+ int i = 0;
+
+ if (! bfd_set_arch_mach (stdoutput, bfd_arch_riscv, CPU_UNKNOWN))
+ as_warn (_("Could not set architecture and machine"));
+
+ op_hash = hash_new ();
+
+ for (i = 0; i < NUMOPCODES;)
+ {
+ const char *name = riscv_opcodes[i].name;
+
+ retval = hash_insert (op_hash, name, (void *) &riscv_opcodes[i]);
+ if (retval != NULL)
+ {
+ fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
+ riscv_opcodes[i].name, retval);
+ /* Probably a memory allocation problem? Give up now. */
+ as_fatal (_("Broken assembler. No assembly attempted."));
+ }
+ do
+ {
+ if (riscv_opcodes[i].pinfo != INSN_MACRO)
+ {
+ if (!validate_mips_insn (&riscv_opcodes[i]))
+ as_fatal (_("Broken assembler. No assembly attempted."));
+ }
+ ++i;
+ }
+ while ((i < NUMOPCODES) && !strcmp (riscv_opcodes[i].name, name));
+ }
+
+ reg_names_hash = hash_new ();
+ for (i = 0; reg_names[i].name; i++)
+ {
+ retval = hash_insert (reg_names_hash, reg_names[i].name,
+ (void*) &reg_names[i]);
+ if (retval != NULL)
+ {
+ fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
+ reg_names[i].name, retval);
+ /* Probably a memory allocation problem? Give up now. */
+ as_fatal (_("Broken assembler. No assembly attempted."));
+ }
+ }
+
+ mips_clear_insn_labels ();
+
+ mips_gprmask = 0;
+ mips_fprmask = 0;
+
+ /* set the default alignment for the text section (2**2) */
+ record_alignment (text_section, 2);
+
+#ifdef OBJ_ELF
+ if (IS_ELF)
+ {
+ /* Sections must be aligned to 16 byte boundaries. When configured
+ for an embedded ELF target, we don't bother. */
+ if (strncmp (TARGET_OS, "elf", 3) != 0)
+ {
+ (void) bfd_set_section_alignment (stdoutput, text_section, 4);
+ (void) bfd_set_section_alignment (stdoutput, data_section, 4);
+ (void) bfd_set_section_alignment (stdoutput, bss_section, 4);
+ }
+
+ /* Create a .reginfo section for register masks and a .mdebug
+ section for debugging information. */
+ {
+ segT seg;
+ subsegT subseg;
+ flagword flags;
+ segT sec;
+
+ seg = now_seg;
+ subseg = now_subseg;
+
+ /* The ABI says this section should be loaded so that the
+ running program can access it. However, we don't load it
+ if we are configured for an embedded target */
+ flags = SEC_READONLY | SEC_DATA;
+ if (strncmp (TARGET_OS, "elf", 3) != 0)
+ flags |= SEC_ALLOC | SEC_LOAD;
+
+ if (!rv64)
+ {
+ sec = subseg_new (".reginfo", (subsegT) 0);
+
+ bfd_set_section_flags (stdoutput, sec, flags);
+ bfd_set_section_alignment (stdoutput, sec, 3);
+
+ mips_regmask_frag = frag_more (sizeof (Elf32_External_RegInfo));
+ }
+ else
+ {
+ /* The 64-bit ABI uses a .MIPS.options section rather than
+ .reginfo section. */
+ sec = subseg_new (".MIPS.options", (subsegT) 0);
+ bfd_set_section_flags (stdoutput, sec, flags);
+ bfd_set_section_alignment (stdoutput, sec, 3);
+
+ /* Set up the option header. */
+ {
+ Elf_Internal_Options opthdr;
+ char *f;
+
+ opthdr.kind = ODK_REGINFO;
+ opthdr.size = (sizeof (Elf_External_Options)
+ + sizeof (Elf64_External_RegInfo));
+ opthdr.section = 0;
+ opthdr.info = 0;
+ f = frag_more (sizeof (Elf_External_Options));
+ bfd_riscv_elf_swap_options_out (stdoutput, &opthdr,
+ (Elf_External_Options *) f);
+
+ mips_regmask_frag = frag_more (sizeof (Elf64_External_RegInfo));
+ }
+ }
+
+ subseg_set (seg, subseg);
+ }
+ }
+#endif /* OBJ_ELF */
+}
+
+void
+md_assemble (char *str)
+{
+ struct mips_cl_insn insn;
+ bfd_reloc_code_real_type unused_reloc[3]
+ = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+
+ imm_expr.X_op = O_absent;
+ imm2_expr.X_op = O_absent;
+ offset_expr.X_op = O_absent;
+ imm_reloc[0] = BFD_RELOC_UNUSED;
+ imm_reloc[1] = BFD_RELOC_UNUSED;
+ imm_reloc[2] = BFD_RELOC_UNUSED;
+ offset_reloc[0] = BFD_RELOC_UNUSED;
+ offset_reloc[1] = BFD_RELOC_UNUSED;
+ offset_reloc[2] = BFD_RELOC_UNUSED;
+
+ mips_ip (str, &insn);
+ DBG ((_("returned from mips_ip(%s) insn_opcode = 0x%x\n"),
+ str, insn.insn_opcode));
+
+
+ if (insn_error)
+ {
+ as_bad ("%s `%s'", insn_error, str);
+ return;
+ }
+
+ if (insn.insn_mo->pinfo == INSN_MACRO)
+ macro (&insn);
+ else
+ {
+ if (imm_expr.X_op != O_absent)
+ append_insn (&insn, &imm_expr, imm_reloc);
+ else if (offset_expr.X_op != O_absent)
+ append_insn (&insn, &offset_expr, offset_reloc);
+ else
+ append_insn (&insn, NULL, unused_reloc);
+ }
+}
+
+static inline bfd_boolean
+got16_reloc_p (bfd_reloc_code_real_type reloc)
+{
+ return reloc == BFD_RELOC_MIPS_GOT16 || reloc == BFD_RELOC_MIPS16_GOT16;
+}
+
+static inline bfd_boolean
+hi16_reloc_p (bfd_reloc_code_real_type reloc)
+{
+ return reloc == BFD_RELOC_HI16_S || reloc == BFD_RELOC_MIPS16_HI16_S;
+}
+
+static inline bfd_boolean
+lo16_reloc_p (bfd_reloc_code_real_type reloc)
+{
+ return reloc == BFD_RELOC_LO16 || reloc == BFD_RELOC_MIPS16_LO16;
+}
+
+/* Return true if the given fixup is followed by a matching R_MIPS_LO16
+ relocation. */
+
+static inline bfd_boolean
+fixup_has_matching_lo_p (fixS *fixp)
+{
+ return (fixp->fx_next != NULL
+ && fixp->fx_next->fx_r_type == BFD_RELOC_LO16
+ && fixp->fx_addsy == fixp->fx_next->fx_addsy
+ && fixp->fx_offset == fixp->fx_next->fx_offset);
+}
+
+static void
+add_relaxed_insn (struct mips_cl_insn *insn, int max_chars, int var,
+ relax_substateT subtype, symbolS *symbol, offsetT offset)
+{
+ frag_grow (max_chars);
+ move_insn (insn, frag_now, frag_more (0) - frag_now->fr_literal);
+ frag_var (rs_machine_dependent, max_chars, var,
+ subtype, symbol, offset, NULL);
+}
+
+/* Output an instruction. IP is the instruction information.
+ ADDRESS_EXPR is an operand of the instruction to be used with
+ RELOC_TYPE. */
+
+static void
+append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
+ bfd_reloc_code_real_type *reloc_type)
+{
+ unsigned long pinfo;
+
+ pinfo = ip->insn_mo->pinfo;
+
+#ifdef OBJ_ELF
+ /* The value passed to dwarf2_emit_insn is the distance between
+ the beginning of the current instruction and the address that
+ should be recorded in the debug tables. For MIPS16 debug info
+ we want to use ISA-encoded addresses, so we pass -1 for an
+ address higher by one than the current. */
+ dwarf2_emit_insn (0);
+#endif
+
+ gas_assert(*reloc_type <= BFD_RELOC_UNUSED);
+
+ /* don't compress instructions with relocs */
+ int compressible = (*reloc_type == BFD_RELOC_UNUSED ||
+ address_expr == NULL || address_expr->X_op == O_constant) && mips_opts.rvc;
+
+ /* speculate that branches/jumps can be compressed. if not, we'll relax. */
+ if (address_expr != NULL && mips_opts.rvc)
+ {
+ int compressible_branch = *reloc_type == BFD_RELOC_16_PCREL_S2 &&
+ (INSN_MATCHES(*ip, BEQ) || INSN_MATCHES(*ip, BNE));
+ int compressible_jump = *reloc_type == BFD_RELOC_MIPS_JMP &&
+ INSN_MATCHES(*ip, J);
+ if(compressible_branch || compressible_jump)
+ {
+ if(riscv_rvc_compress(ip))
+ {
+ add_relaxed_insn(ip, 4 /* worst case length */, 0,
+ RELAX_BRANCH_ENCODE(compressible_jump, 0),
+ address_expr->X_add_symbol,
+ address_expr->X_add_number);
+ *reloc_type = BFD_RELOC_UNUSED;
+ return;
+ }
+ }
+ }
+
+ if(!compressible)
+ add_fixed_insn(ip);
+
+ if (address_expr != NULL)
+ {
+ if (address_expr->X_op == O_constant)
+ {
+ unsigned int tmp;
+
+ switch (*reloc_type)
+ {
+ case BFD_RELOC_32:
+ ip->insn_opcode |= address_expr->X_add_number;
+ break;
+
+ case BFD_RELOC_HI16_S:
+ tmp = (address_expr->X_add_number + RISCV_IMM_REACH/2) >> RISCV_IMM_BITS;
+ ip->insn_opcode |= (tmp & ((1<<(32-RISCV_IMM_BITS))-1)) << OP_SH_BIGIMMEDIATE; // assumes lui bits == 32 - imm bits
+ break;
+
+ case BFD_RELOC_HI16:
+ ip->insn_opcode |= ((address_expr->X_add_number >> RISCV_IMM_BITS) & (RISCV_BIGIMM_REACH-1)) << OP_SH_BIGIMMEDIATE;
+ break;
+
+ case BFD_RELOC_UNUSED:
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_MIPS_GOT_DISP:
+ /* Stores have a split immediate field. */
+ if (OPCODE_IS_STORE(ip->insn_opcode))
+ {
+ int value = address_expr->X_add_number & (RISCV_IMM_REACH-1);
+ value = ((value >> RISCV_IMMLO_BITS) << OP_SH_IMMHI) |
+ ((value & ((1<<RISCV_IMMLO_BITS)-1)) << OP_SH_IMMLO);
+ ip->insn_opcode |= value;
+ }
+ else
+ ip->insn_opcode |= (address_expr->X_add_number & (RISCV_IMM_REACH-1)) << OP_SH_IMMEDIATE;
+ break;
+
+ case BFD_RELOC_MIPS_JMP:
+ if ((address_expr->X_add_number & 1) != 0)
+ as_bad (_("jump to misaligned address (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ if ((address_expr->X_add_number + RISCV_JUMP_REACH/2) & (RISCV_JUMP_REACH-1))
+ as_bad (_("jump address range overflow (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ ip->insn_opcode |= ((unsigned long long)(address_expr->X_add_number & (RISCV_JUMP_REACH-1))/RISCV_JUMP_ALIGN) << OP_SH_TARGET;
+ break;
+
+ case BFD_RELOC_16_PCREL_S2:
+ if ((address_expr->X_add_number & 1) != 0)
+ as_bad (_("branch to misaligned address (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ if ((address_expr->X_add_number + RISCV_BRANCH_REACH/2) & (RISCV_BRANCH_REACH-1))
+ as_bad (_("branch address range overflow (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ unsigned delta = (((unsigned)address_expr->X_add_number & (RISCV_BRANCH_REACH-1)) >> RISCV_BRANCH_ALIGN_BITS) & ((1<<RISCV_BRANCH_BITS)-1);
+ ip->insn_opcode |= ((delta & ((1<<RISCV_IMMLO_BITS)-1)) << OP_SH_IMMLO) | (((delta >> RISCV_IMMLO_BITS) & ((1<<RISCV_IMMHI_BITS)-1)) << OP_SH_IMMHI);
+ break;
+
+ default:
+ internalError ();
+ }
+ *reloc_type = BFD_RELOC_UNUSED;
+ }
+ else if (*reloc_type < BFD_RELOC_UNUSED)
+ {
+ reloc_howto_type *howto;
+ int i;
+
+ /* In a compound relocation, it is the final (outermost)
+ operator that determines the relocated field. */
+ for (i = 1; i < 3; i++)
+ if (reloc_type[i] == BFD_RELOC_UNUSED)
+ break;
+
+ howto = bfd_reloc_type_lookup (stdoutput, reloc_type[i - 1]);
+ if (howto == NULL)
+ as_bad (_("Unsupported MIPS relocation number %d"), reloc_type[i - 1]);
+
+ ip->fixp[0] = fix_new_exp (ip->frag, ip->where,
+ bfd_get_reloc_size (howto),
+ address_expr,
+ reloc_type[0] == BFD_RELOC_16_PCREL_S2 ||
+ reloc_type[0] == BFD_RELOC_MIPS_JMP,
+ reloc_type[0]);
+
+ /* These relocations can have an addend that won't fit in
+ 4 octets for 64bit assembly. */
+ if (rv64
+ && ! howto->partial_inplace
+ && (reloc_type[0] == BFD_RELOC_32
+ || reloc_type[0] == BFD_RELOC_GPREL16
+ || reloc_type[0] == BFD_RELOC_MIPS_LITERAL
+ || reloc_type[0] == BFD_RELOC_GPREL32
+ || reloc_type[0] == BFD_RELOC_64
+ || reloc_type[0] == BFD_RELOC_CTOR
+ || reloc_type[0] == BFD_RELOC_MIPS_SUB
+ || reloc_type[0] == BFD_RELOC_MIPS_SCN_DISP
+ || reloc_type[0] == BFD_RELOC_MIPS_REL16
+ || reloc_type[0] == BFD_RELOC_MIPS_RELGOT
+ || reloc_type[0] == BFD_RELOC_MIPS16_GPREL
+ || hi16_reloc_p (reloc_type[0])
+ || lo16_reloc_p (reloc_type[0])))
+ ip->fixp[0]->fx_no_overflow = 1;
+
+ /* Add fixups for the second and third relocations, if given.
+ Note that the ABI allows the second relocation to be
+ against RSS_UNDEF, RSS_GP, RSS_GP0 or RSS_LOC. At the
+ moment we only use RSS_UNDEF, but we could add support
+ for the others if it ever becomes necessary. */
+ for (i = 1; i < 3; i++)
+ if (reloc_type[i] != BFD_RELOC_UNUSED)
+ {
+ ip->fixp[i] = fix_new (ip->frag, ip->where,
+ ip->fixp[0]->fx_size, NULL, 0,
+ FALSE, reloc_type[i]);
+
+ /* Use fx_tcbit to mark compound relocs. */
+ ip->fixp[0]->fx_tcbit = 1;
+ ip->fixp[i]->fx_tcbit = 1;
+ }
+ }
+ }
+
+ if(compressible)
+ {
+ riscv_rvc_compress(ip);
+ add_fixed_insn (ip);
+ }
+
+ install_insn (ip);
+
+ /* Update the register mask information. */
+ if (pinfo & INSN_WRITE_GPR_D)
+ mips_gprmask |= 1 << EXTRACT_OPERAND (RD, *ip);
+ if (pinfo & INSN_WRITE_GPR_RA)
+ mips_gprmask |= 1 << RA;
+ if (pinfo & INSN_WRITE_FPR_D)
+ mips_fprmask |= 1 << EXTRACT_OPERAND (FD, *ip);
+
+ if (pinfo & INSN_READ_GPR_S)
+ mips_gprmask |= 1 << EXTRACT_OPERAND (RS, *ip);
+ if (pinfo & INSN_READ_GPR_T)
+ mips_gprmask |= 1 << EXTRACT_OPERAND (RT, *ip);
+ if (pinfo & INSN_READ_FPR_S)
+ mips_fprmask |= 1 << EXTRACT_OPERAND (FS, *ip);
+ if (pinfo & INSN_READ_FPR_T)
+ mips_fprmask |= 1 << EXTRACT_OPERAND (FT, *ip);
+ if (pinfo & INSN_READ_FPR_R)
+ mips_fprmask |= 1 << EXTRACT_OPERAND (FR, *ip);
+ /* Never set the bit for $0, which is always zero. */
+ mips_gprmask &= ~1 << 0;
+
+ /* We just output an insn, so the next one doesn't have a label. */
+ mips_clear_insn_labels ();
+}
+
+/* Read a macro's relocation codes from *ARGS and store them in *R.
+ The first argument in *ARGS will be either the code for a single
+ relocation or -1 followed by the three codes that make up a
+ composite relocation. */
+
+static void
+macro_read_relocs (va_list *args, bfd_reloc_code_real_type *r)
+{
+ int i, next;
+
+ next = va_arg (*args, int);
+ if (next >= 0)
+ r[0] = (bfd_reloc_code_real_type) next;
+ else
+ for (i = 0; i < 3; i++)
+ r[i] = (bfd_reloc_code_real_type) va_arg (*args, int);
+}
+
+/* Build an instruction created by a macro expansion. This is passed
+ a pointer to the count of instructions created so far, an
+ expression, the name of the instruction to build, an operand format
+ string, and corresponding arguments. */
+
+static void
+macro_build (expressionS *ep, const char *name, const char *fmt, ...)
+{
+ const struct riscv_opcode *mo;
+ struct mips_cl_insn insn;
+ bfd_reloc_code_real_type r[3];
+ va_list args;
+
+ va_start (args, fmt);
+
+ r[0] = BFD_RELOC_UNUSED;
+ r[1] = BFD_RELOC_UNUSED;
+ r[2] = BFD_RELOC_UNUSED;
+ mo = (struct riscv_opcode *) hash_find (op_hash, name);
+ gas_assert (mo);
+ gas_assert (strcmp (name, mo->name) == 0);
+
+ create_insn (&insn, mo);
+ for (;;)
+ {
+ switch (*fmt++)
+ {
+ case '\0':
+ break;
+
+ case '#':
+ switch ( *fmt++ ) {
+ case 'g':
+ INSERT_OPERAND( IMMNGPR, insn, va_arg( args, int ) );
+ continue;
+ case 'f':
+ INSERT_OPERAND( IMMNFPR, insn, va_arg( args, int ) );
+ continue;
+ case 'n':
+ INSERT_OPERAND( IMMSEGNELM, insn, va_arg( args, int ) - 1 );
+ continue;
+ case 'm':
+ INSERT_OPERAND( IMMSEGSTNELM, insn, va_arg( args, int ) - 1 );
+ continue;
+ case 'd':
+ INSERT_OPERAND( VRD, insn, va_arg( args, int ) );
+ continue;
+ case 's':
+ INSERT_OPERAND( VRS, insn, va_arg( args, int ) );
+ continue;
+ case 't':
+ INSERT_OPERAND( VRT, insn, va_arg( args, int ) );
+ continue;
+ case 'r':
+ INSERT_OPERAND( VRR, insn, va_arg( args, int ) );
+ continue;
+ case 'D':
+ INSERT_OPERAND( VFD, insn, va_arg( args, int ) );
+ continue;
+ case 'S':
+ INSERT_OPERAND( VFS, insn, va_arg( args, int ) );
+ continue;
+ case 'T':
+ INSERT_OPERAND( VFT, insn, va_arg( args, int ) );
+ continue;
+ case 'R':
+ INSERT_OPERAND( VFR, insn, va_arg( args, int ) );
+ continue;
+ default:
+ internalError();
+ }
+ continue;
+
+ case ',':
+ case '(':
+ case ')':
+ continue;
+
+ case 't':
+ INSERT_OPERAND (RT, insn, va_arg (args, int));
+ continue;
+
+ case 'T':
+ case 'W':
+ INSERT_OPERAND (FT, insn, va_arg (args, int));
+ continue;
+
+ case 'd':
+ INSERT_OPERAND (RD, insn, va_arg (args, int));
+ continue;
+
+ case 'U':
+ {
+ int tmp = va_arg (args, int);
+
+ INSERT_OPERAND (RT, insn, tmp);
+ INSERT_OPERAND (RD, insn, tmp);
+ continue;
+ }
+
+ case 'S':
+ INSERT_OPERAND (FS, insn, va_arg (args, int));
+ continue;
+
+ case 'z':
+ continue;
+
+ case '<':
+ INSERT_OPERAND (SHAMTW, insn, va_arg (args, int));
+ continue;
+
+ case '>':
+ INSERT_OPERAND (SHAMT, insn, va_arg (args, int));
+ continue;
+
+ case 'D':
+ INSERT_OPERAND (FD, insn, va_arg (args, int));
+ continue;
+
+ case 'b':
+ case 's':
+ case 'E':
+ INSERT_OPERAND (RS, insn, va_arg (args, int));
+ continue;
+
+ case 'm':
+ INSERT_OPERAND (RM, insn, va_arg (args, int));
+ continue;
+
+ case 'O': /* An off-by-4 PC-relative address for PIC. */
+ INSERT_OPERAND (IMMEDIATE, insn, 4);
+ macro_read_relocs (&args, r);
+ gas_assert (*r == BFD_RELOC_RISCV_TLS_GD_LO16
+ || *r == BFD_RELOC_RISCV_TLS_GOT_LO16
+ || *r == BFD_RELOC_MIPS_GOT_LO16);
+ continue;
+
+ case 'j':
+ macro_read_relocs (&args, r);
+ gas_assert (*r == BFD_RELOC_GPREL16
+ || *r == BFD_RELOC_MIPS_LITERAL
+ || *r == BFD_RELOC_LO16
+ || *r == BFD_RELOC_MIPS_GOT16
+ || *r == BFD_RELOC_MIPS_CALL16
+ || *r == BFD_RELOC_MIPS_GOT_DISP
+ || *r == BFD_RELOC_MIPS_GOT_LO16
+ || *r == BFD_RELOC_MIPS_CALL_LO16);
+ continue;
+
+ case 'u':
+ macro_read_relocs (&args, r);
+ gas_assert (ep != NULL
+ && (ep->X_op == O_constant
+ || (ep->X_op == O_symbol
+ && (*r == BFD_RELOC_HI16_S
+ || *r == BFD_RELOC_HI16
+ /*|| *r == BFD_RELOC_GPREL16*/
+ || *r == BFD_RELOC_MIPS_GOT_HI16
+ || *r == BFD_RELOC_MIPS_CALL_HI16))));
+ continue;
+
+ case 'p':
+ gas_assert (ep != NULL);
+
+ /*
+ * This allows macro() to pass an immediate expression for
+ * creating short branches without creating a symbol.
+ *
+ * We don't allow branch relaxation for these branches, as
+ * they should only appear in ".set nomacro" anyway.
+ */
+ if (ep->X_op == O_constant)
+ {
+ unsigned long long delta;
+ if ((ep->X_add_number & (RISCV_BRANCH_ALIGN-1)) != 0)
+ as_bad (_("branch to misaligned address (0x%lx)"),
+ (unsigned long) ep->X_add_number);
+ if ((ep->X_add_number + RISCV_BRANCH_REACH/2) & ~(RISCV_BRANCH_REACH-1))
+ as_bad (_("branch address range overflow (0x%lx)"),
+ (unsigned long) ep->X_add_number);
+ delta = (unsigned long long)(ep->X_add_number & (RISCV_BRANCH_REACH-1))/RISCV_BRANCH_ALIGN;
+ insn.insn_opcode |= ((delta & ((1<<RISCV_IMMLO_BITS)-1)) << OP_SH_IMMLO) | (((delta >> RISCV_IMMLO_BITS) & ((1<<RISCV_IMMHI_BITS)-1)) << OP_SH_IMMHI);
+ ep = NULL;
+ }
+ else
+ *r = BFD_RELOC_16_PCREL_S2;
+ continue;
+
+ case 'a':
+ gas_assert (ep != NULL);
+ if (ep->X_op == O_constant)
+ {
+ if ((ep->X_add_number & (RISCV_JUMP_ALIGN-1)) != 0)
+ as_bad (_("jump to misaligned address (0x%lx)"),
+ (unsigned long) ep->X_add_number);
+ if ((ep->X_add_number + RISCV_JUMP_REACH/2) & ~(RISCV_JUMP_REACH-1))
+ as_bad (_("jump address range overflow (0x%lx)"),
+ (unsigned long) ep->X_add_number);
+ insn.insn_opcode |= ((unsigned long long)(ep->X_add_number & (RISCV_JUMP_REACH-1))/RISCV_JUMP_ALIGN) << OP_SH_TARGET;
+ ep = NULL;
+ }
+ else
+ *r = BFD_RELOC_MIPS_JMP;
+ continue;
+
+ default:
+ internalError ();
+ }
+ break;
+ }
+ va_end (args);
+ gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
+
+ append_insn (&insn, ep, r);
+}
+
+/*
+ * Sign-extend 32-bit mode constants that have bit 31 set and all
+ * higher bits unset.
+ */
+static void
+normalize_constant_expr (expressionS *ex)
+{
+ if (rv64)
+ return;
+ if (ex->X_op == O_constant
+ && IS_ZEXT_32BIT_NUM (ex->X_add_number))
+ ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
+ - 0x80000000);
+}
+
+/*
+ * Sign-extend 32-bit mode address offsets that have bit 31 set and
+ * all higher bits unset.
+ */
+static void
+normalize_address_expr (expressionS *ex)
+{
+ if (((ex->X_op == O_constant && HAVE_32BIT_ADDRESSES)
+ || (ex->X_op == O_symbol && HAVE_32BIT_SYMBOLS))
+ && IS_ZEXT_32BIT_NUM (ex->X_add_number))
+ ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
+ - 0x80000000);
+}
+
+/*
+ * Generate a "lui" instruction.
+ */
+static void
+macro_build_lui (const char* name, expressionS *ep, int regnum, bfd_reloc_code_real_type reloc)
+{
+ const struct riscv_opcode *mo;
+ struct mips_cl_insn insn;
+ bfd_reloc_code_real_type r[3] = {reloc, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+
+ gas_assert (ep->X_op == O_symbol);
+
+ mo = hash_find (op_hash, name);
+ gas_assert (mo);
+ create_insn (&insn, mo);
+
+ insn.insn_opcode = insn.insn_mo->match;
+ INSERT_OPERAND (RD, insn, regnum);
+ append_insn (&insn, ep, r);
+}
+
+/* Load an entry from the GOT. */
+static void
+load_static_addr (int destreg, expressionS *ep)
+{
+ macro_build_lui ("lui", ep, destreg, BFD_RELOC_HI16_S);
+ macro_build (ep, "addi", "d,s,j", destreg, destreg, BFD_RELOC_LO16);
+}
+
+/* Load an entry from the GOT. */
+static void
+load_got_addr (int destreg, expressionS *ep, const char* lo_insn,
+ bfd_reloc_code_real_type hi_reloc,
+ bfd_reloc_code_real_type lo_reloc)
+{
+ macro_build_lui ("auipc", ep, destreg, hi_reloc);
+ macro_build (ep, lo_insn, "d,O(b)", destreg, lo_reloc, destreg);
+}
+
+/* Warn if an expression is not a constant. */
+
+static void
+check_absolute_expr (struct mips_cl_insn *ip, expressionS *ex)
+{
+ if (ex->X_op == O_big)
+ as_bad (_("unsupported large constant"));
+ else if (ex->X_op != O_constant)
+ as_bad (_("Instruction %s requires absolute expression"),
+ ip->insn_mo->name);
+ normalize_constant_expr (ex);
+}
+
+/* load_const generates an unoptimized instruction sequence to load
+ * an absolute expression into a register. */
+static void
+load_const (int reg, expressionS *ep)
+{
+ gas_assert (ep->X_op == O_constant);
+ gas_assert (reg != ZERO);
+
+ // this is an awful way to generate arbitrary 64-bit constants.
+ // fortunately, this is just used for hand-coded assembly programs.
+ if (rv64 && !IS_SEXT_32BIT_NUM(ep->X_add_number))
+ {
+ expressionS upper = *ep, lower = *ep;
+ upper.X_add_number = (int64_t)ep->X_add_number >> (RISCV_IMM_BITS-1);
+ load_const(reg, &upper);
+
+ macro_build (NULL, "slli", "d,s,>", reg, reg, RISCV_IMM_BITS-1);
+
+ lower.X_add_number = ep->X_add_number & (RISCV_IMM_REACH/2-1);
+ if (lower.X_add_number != 0)
+ macro_build (&lower, "addi", "d,s,j", reg, reg, BFD_RELOC_LO16);
+ }
+ else // load a sign-extended 32-bit constant
+ {
+ int hi_reg = ZERO;
+
+ int32_t hi = ep->X_add_number & (RISCV_IMM_REACH-1);
+ hi = hi << (32-RISCV_IMM_BITS) >> (32-RISCV_IMM_BITS);
+ hi = (int32_t)ep->X_add_number - hi;
+ if(hi)
+ {
+ macro_build (ep, "lui", "d,u", reg, BFD_RELOC_HI16_S);
+ hi_reg = reg;
+ }
+
+ if((ep->X_add_number & (RISCV_IMM_REACH-1)) || hi_reg == ZERO)
+ macro_build (ep, ADD32_INSN, "d,s,j", reg, hi_reg, BFD_RELOC_LO16);
+ }
+}
+
+/*
+ * Build macros
+ * This routine implements the seemingly endless macro or synthesized
+ * instructions and addressing modes in the mips assembly language. Many
+ * of these macros are simple and are similar to each other. These could
+ * probably be handled by some kind of table or grammar approach instead of
+ * this verbose method. Others are not simple macros but are more like
+ * optimizing code generation.
+ * One interesting optimization is when several store macros appear
+ * consecutively that would load AT with the upper half of the same address.
+ * The ensuing load upper instructions are ommited. This implies some kind
+ * of global optimization. We currently only optimize within a single macro.
+ * For many of the load and store macros if the address is specified as a
+ * constant expression in the first 64k of memory (ie ld $2,0x4000c) we
+ * first load register 'at' with zero and use it as the base register. The
+ * mips assembler simply uses register $zero. Just one tiny optimization
+ * we're missing.
+ */
+static void
+macro (struct mips_cl_insn *ip)
+{
+ unsigned int sreg, dreg, breg;
+ int mask;
+
+ dreg = (ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD;
+ breg = sreg = (ip->insn_opcode >> OP_SH_RS) & OP_MASK_RS;
+ mask = ip->insn_mo->mask;
+
+ switch (mask)
+ {
+ case M_LA_AB:
+ /* Load the address of a symbol into a register. */
+ if (!IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+ as_bad(_("offset too large"));
+ if (breg == dreg && breg != ZERO)
+ as_bad(_("expression too complex: dest and base regs must differ"));
+
+ if (offset_expr.X_op == O_constant)
+ load_const (dreg, &offset_expr);
+ else if (is_pic) /* O_symbol */
+ load_got_addr (dreg, &offset_expr, LOAD_ADDRESS_INSN,
+ BFD_RELOC_MIPS_GOT_HI16, BFD_RELOC_MIPS_GOT_LO16);
+ else /* non-PIC O_symbol */
+ load_static_addr (dreg, &offset_expr);
+
+ if (breg != ZERO)
+ macro_build (NULL, "add", "d,s,t", dreg, dreg, breg);
+ break;
+
+ case M_LA_TLS_GD:
+ load_got_addr(dreg, &offset_expr, "addi",
+ BFD_RELOC_RISCV_TLS_GD_HI16, BFD_RELOC_RISCV_TLS_GD_LO16);
+ break;
+
+ case M_LA_TLS_IE:
+ load_got_addr(dreg, &offset_expr, LOAD_ADDRESS_INSN,
+ BFD_RELOC_RISCV_TLS_GOT_HI16, BFD_RELOC_RISCV_TLS_GOT_LO16);
+ break;
+
+ case M_J: /* replace "j $rs" with "ret" if rs=ra, else with "jr $rs" */
+ if (sreg == LINK_REG)
+ macro_build (NULL, "ret", "");
+ else
+ macro_build (NULL, "jr", "s", sreg);
+ break;
+
+ case M_LI:
+ load_const (dreg, &imm_expr);
+ break;
+
+ default:
+ as_bad (_("Macro %s not implemented"), ip->insn_mo->name);
+ break;
+ }
+}
+
+/* For consistency checking, verify that all bits are specified either
+ by the match/mask part of the instruction definition, or by the
+ operand list. */
+static int
+validate_mips_insn (const struct riscv_opcode *opc)
+{
+ const char *p = opc->args;
+ char c;
+ unsigned long used_bits = opc->mask;
+
+ if ((used_bits & opc->match) != opc->match)
+ {
+ as_bad (_("internal: bad mips opcode (mask error): %s %s"),
+ opc->name, opc->args);
+ return 0;
+ }
+#define USE_BITS(mask,shift) (used_bits |= ((mask) << (shift)))
+ while (*p)
+ switch (c = *p++)
+ {
+ case '#':
+ switch (c = *p++)
+ {
+ case 'g': USE_BITS (OP_MASK_IMMNGPR, OP_SH_IMMNGPR); break;
+ case 'f': USE_BITS (OP_MASK_IMMNFPR, OP_SH_IMMNFPR); break;
+ case 'n': USE_BITS (OP_MASK_IMMSEGNELM, OP_SH_IMMSEGNELM); break;
+ case 'm': USE_BITS (OP_MASK_IMMSEGSTNELM, OP_SH_IMMSEGSTNELM); break;
+ case 'd': USE_BITS (OP_MASK_VRD, OP_SH_VRD); break;
+ case 's': USE_BITS (OP_MASK_VRS, OP_SH_VRS); break;
+ case 't': USE_BITS (OP_MASK_VRT, OP_SH_VRT); break;
+ case 'r': USE_BITS (OP_MASK_VRR, OP_SH_VRR); break;
+ case 'D': USE_BITS (OP_MASK_VFD, OP_SH_VFD); break;
+ case 'S': USE_BITS (OP_MASK_VFS, OP_SH_VFS); break;
+ case 'T': USE_BITS (OP_MASK_VFT, OP_SH_VFT); break;
+ case 'R': USE_BITS (OP_MASK_VFR, OP_SH_VFR); break;
+
+ default:
+ as_bad (_("internal: bad mips opcode (unknown extension operand type `#%c'): %s %s"),
+ c, opc->name, opc->args);
+ return 0;
+ }
+ break;
+ case ',': break;
+ case '(': break;
+ case ')': break;
+ case '<': USE_BITS (OP_MASK_SHAMTW, OP_SH_SHAMTW); break;
+ case '>': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
+ case 'A': break;
+ case 'D': USE_BITS (OP_MASK_FD, OP_SH_FD); break;
+ case 'E': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
+ case 'I': break;
+ case 'R': USE_BITS (OP_MASK_FR, OP_SH_FR); break;
+ case 'S': USE_BITS (OP_MASK_FS, OP_SH_FS); break;
+ case 'T': USE_BITS (OP_MASK_FT, OP_SH_FT); break;
+ case 'a': USE_BITS (OP_MASK_TARGET, OP_SH_TARGET); break;
+ case 'b': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
+ case 'd': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
+ case 'j': USE_BITS (OP_MASK_IMMEDIATE, OP_SH_IMMEDIATE); break;
+ case 'm': USE_BITS (OP_MASK_RM, OP_SH_RM); break;
+ case 'o': USE_BITS (OP_MASK_IMMEDIATE, OP_SH_IMMEDIATE); break;
+ case 'p': USE_BITS (OP_MASK_IMMLO, OP_SH_IMMLO);
+ USE_BITS (OP_MASK_IMMHI, OP_SH_IMMHI); break;
+ case 'q': USE_BITS (OP_MASK_IMMLO, OP_SH_IMMLO);
+ USE_BITS (OP_MASK_IMMHI, OP_SH_IMMHI); break;
+ case 's': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
+ case 't': USE_BITS (OP_MASK_RT, OP_SH_RT); break;
+ case 'u': USE_BITS (OP_MASK_BIGIMMEDIATE, OP_SH_BIGIMMEDIATE); break;
+ case '[': break;
+ case ']': break;
+ case '0': break;
+ default:
+ as_bad (_("internal: bad mips opcode (unknown operand type `%c'): %s %s"),
+ c, opc->name, opc->args);
+ return 0;
+ }
+#undef USE_BITS
+ if ((used_bits&0xffffffff) != 0xffffffff)
+ {
+ as_bad (_("internal: bad mips opcode (bits 0x%lx undefined): %s %s"),
+ ~used_bits & 0xffffffff, opc->name, opc->args);
+ return 0;
+ }
+ return 1;
+}
+
+/* This routine assembles an instruction into its binary format. As a
+ side effect, it sets one of the global variables imm_reloc or
+ offset_reloc to the type of relocation to do if one of the operands
+ is an address expression. */
+
+static void
+mips_ip (char *str, struct mips_cl_insn *ip)
+{
+ char *s;
+ const char *args;
+ char c = 0;
+ struct riscv_opcode *insn;
+ char *argsStart;
+ unsigned int regno;
+ char save_c = 0;
+ int argnum;
+ unsigned int rtype;
+
+ insn_error = NULL;
+
+ /* If the instruction contains a '.', we first try to match an instruction
+ including the '.'. Then we try again without the '.'. */
+ insn = NULL;
+ for (s = str; *s != '\0' && !ISSPACE (*s); ++s)
+ continue;
+
+ /* If we stopped on whitespace, then replace the whitespace with null for
+ the call to hash_find. Save the character we replaced just in case we
+ have to re-parse the instruction. */
+ if (ISSPACE (*s))
+ {
+ save_c = *s;
+ *s++ = '\0';
+ }
+
+ insn = (struct riscv_opcode *) hash_find (op_hash, str);
+
+ /* If we didn't find the instruction in the opcode table, try again, but
+ this time with just the instruction up to, but not including the
+ first '.'. */
+ if (insn == NULL)
+ {
+ /* Restore the character we overwrite above (if any). */
+ if (save_c)
+ *(--s) = save_c;
+
+ /* Scan up to the first '.' or whitespace. */
+ for (s = str;
+ *s != '\0' && *s != '.' && !ISSPACE (*s);
+ ++s)
+ continue;
+
+ /* If we did not find a '.', then we can quit now. */
+ if (*s != '.')
+ {
+ insn_error = "unrecognized opcode";
+ return;
+ }
+
+ /* Lookup the instruction in the hash table. */
+ *s++ = '\0';
+ if ((insn = (struct riscv_opcode *) hash_find (op_hash, str)) == NULL)
+ {
+ insn_error = "unrecognized opcode";
+ return;
+ }
+ }
+
+ argsStart = s;
+ for (;;)
+ {
+ bfd_boolean ok = TRUE;
+ gas_assert (strcmp (insn->name, str) == 0);
+
+ create_insn (ip, insn);
+ insn_error = NULL;
+ argnum = 1;
+ for (args = insn->args;; ++args)
+ {
+ s += strspn (s, " \t");
+ switch (*args)
+ {
+ case '\0': /* end of args */
+ if (*s == '\0')
+ return;
+ break;
+
+ case '#':
+ switch ( *++args )
+ {
+ case 'g':
+ my_getExpression( &imm_expr, s );
+ check_absolute_expr( ip, &imm_expr );
+ if ((unsigned long) imm_expr.X_add_number > 32 )
+ as_warn( _( "Improper ngpr amount (%lu)" ),
+ (unsigned long) imm_expr.X_add_number );
+ INSERT_OPERAND( IMMNGPR, *ip, imm_expr.X_add_number );
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+ case 'f':
+ my_getExpression( &imm_expr, s );
+ check_absolute_expr( ip, &imm_expr );
+ if ((unsigned long) imm_expr.X_add_number > 32 )
+ as_warn( _( "Improper nfpr amount (%lu)" ),
+ (unsigned long) imm_expr.X_add_number );
+ INSERT_OPERAND( IMMNFPR, *ip, imm_expr.X_add_number );
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+ case 'n':
+ my_getExpression( &imm_expr, s );
+ check_absolute_expr( ip, &imm_expr );
+ if ((unsigned long) imm_expr.X_add_number > 32 )
+ as_warn( _( "Improper nelm amount (%lu)" ),
+ (unsigned long) imm_expr.X_add_number );
+ INSERT_OPERAND( IMMSEGNELM, *ip, imm_expr.X_add_number - 1 );
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+ case 'm':
+ my_getExpression( &imm_expr, s );
+ check_absolute_expr( ip, &imm_expr );
+ if ((unsigned long) imm_expr.X_add_number > 32 )
+ as_warn( _( "Improper nelm amount (%lu)" ),
+ (unsigned long) imm_expr.X_add_number );
+ INSERT_OPERAND( IMMSEGSTNELM, *ip, imm_expr.X_add_number - 1 );
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+ case 'd':
+ ok = reg_lookup( &s, RTYPE_NUM|RTYPE_VGR_REG, &regno );
+ if ( !ok )
+ as_bad( _( "Invalid vector register" ) );
+ INSERT_OPERAND( VRD, *ip, regno );
+ continue;
+ case 's':
+ ok = reg_lookup( &s, RTYPE_NUM|RTYPE_VGR_REG, &regno );
+ if ( !ok )
+ as_bad( _( "Invalid vector register" ) );
+ INSERT_OPERAND( VRS, *ip, regno );
+ continue;
+ case 't':
+ ok = reg_lookup( &s, RTYPE_NUM|RTYPE_VGR_REG, &regno );
+ if ( !ok )
+ as_bad( _( "Invalid vector register" ) );
+ INSERT_OPERAND( VRT, *ip, regno );
+ continue;
+ case 'r':
+ ok = reg_lookup( &s, RTYPE_NUM|RTYPE_VGR_REG, &regno );
+ if ( !ok )
+ as_bad( _( "Invalid vector register" ) );
+ INSERT_OPERAND( VRR, *ip, regno );
+ continue;
+ case 'D':
+ ok = reg_lookup( &s, RTYPE_NUM|RTYPE_VFP_REG, &regno );
+ if ( !ok )
+ as_bad( _( "Invalid vector register" ) );
+ INSERT_OPERAND( VFD, *ip, regno );
+ continue;
+ case 'S':
+ ok = reg_lookup( &s, RTYPE_NUM|RTYPE_VFP_REG, &regno );
+ if ( !ok )
+ as_bad( _( "Invalid vector register" ) );
+ INSERT_OPERAND( VFS, *ip, regno );
+ continue;
+ case 'T':
+ ok = reg_lookup( &s, RTYPE_NUM|RTYPE_VFP_REG, &regno );
+ if ( !ok )
+ as_bad( _( "Invalid vector register" ) );
+ INSERT_OPERAND( VFT, *ip, regno );
+ continue;
+ case 'R':
+ ok = reg_lookup( &s, RTYPE_NUM|RTYPE_VFP_REG, &regno );
+ if ( !ok )
+ as_bad( _( "Invalid vector register" ) );
+ INSERT_OPERAND( VFR, *ip, regno );
+ continue;
+ }
+ break;
+
+ case '0': /* memory instruction with 0-offset (namely, AMOs) */
+ if (my_getSmallExpression (&offset_expr, offset_reloc, s) == 0
+ && (offset_expr.X_op != O_constant
+ || offset_expr.X_add_number != 0))
+ break;
+
+ s = expr_end;
+ continue;
+
+ case ',':
+ ++argnum;
+ if (*s++ == *args)
+ continue;
+ s--;
+ break;
+
+ case '(':
+ /* Handle optional base register.
+ Either the base register is omitted or
+ we must have a left paren. */
+ /* This is dependent on the next operand specifier
+ is a base register specification. */
+ gas_assert (args[1] == 'b' || args[1] == '5'
+ || args[1] == '-' || args[1] == '4');
+ if (*s == '\0')
+ return;
+
+ case ')': /* these must match exactly */
+ case '[':
+ case ']':
+ if (*s++ == *args)
+ continue;
+ break;
+
+ case '<': /* must be at least one digit */
+ /*
+ * According to the manual, if the shift amount is greater
+ * than 31 or less than 0, then the shift amount should be
+ * mod 32. In reality the mips assembler issues an error.
+ * We issue a warning and mask out all but the low 5 bits.
+ */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number > 31)
+ as_warn (_("Improper shift amount (%lu)"),
+ (unsigned long) imm_expr.X_add_number);
+ INSERT_OPERAND (SHAMTW, *ip, imm_expr.X_add_number);
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case '>': /* shift amount, 0-63 */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ INSERT_OPERAND (SHAMT, *ip, imm_expr.X_add_number);
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'E': /* Control register. */
+ ok = reg_lookup (&s, RTYPE_NUM | RTYPE_CP0, &regno);
+ INSERT_OPERAND (RS, *ip, regno);
+ if (ok)
+ continue;
+ else
+ break;
+
+ case 'm': /* rounding mode */
+ {
+ size_t i, found = ARRAY_SIZE(riscv_rm);
+ for(i = 0; i < found; i++)
+ if(riscv_rm[i] && !strncmp(s,riscv_rm[i],strlen(riscv_rm[i])))
+ found = i;
+
+ if(found == ARRAY_SIZE(riscv_rm))
+ as_bad("bad rounding mode: `%s'",s);
+
+ INSERT_OPERAND(RM, *ip, found);
+ s += strlen(riscv_rm[found]);
+ continue;
+ }
+
+ case 'b': /* base register */
+ case 'd': /* destination register */
+ case 's': /* source register */
+ case 't': /* target register */
+ case 'z': /* must be zero register */
+ case 'U': /* destination register (clo/clz). */
+ case 'g': /* coprocessor destination register */
+ ok = reg_lookup (&s, RTYPE_NUM | RTYPE_GP, &regno);
+ if (ok)
+ {
+ c = *args;
+ if (*s == ' ')
+ ++s;
+ /* 'z' only matches $0. */
+ if (c == 'z' && regno != 0)
+ break;
+
+ /* Now that we have assembled one operand, we use the args string
+ * to figure out where it goes in the instruction. */
+ switch (c)
+ {
+ case 's':
+ case 'b':
+ case 'E':
+ INSERT_OPERAND (RS, *ip, regno);
+ break;
+ case 'd':
+ INSERT_OPERAND (RD, *ip, regno);
+ break;
+ case 'g':
+ INSERT_OPERAND (FS, *ip, regno);
+ break;
+ case 'U':
+ INSERT_OPERAND (RD, *ip, regno);
+ INSERT_OPERAND (RT, *ip, regno);
+ break;
+ case 't':
+ INSERT_OPERAND (RT, *ip, regno);
+ break;
+ case 'x':
+ /* This case exists because on the r3000 trunc
+ expands into a macro which requires a gp
+ register. On the r6000 or r4000 it is
+ assembled into a single instruction which
+ ignores the register. Thus the insn version
+ is MIPS_ISA2 and uses 'x', and the macro
+ version is MIPS_ISA1 and uses 't'. */
+ break;
+ case 'z':
+ /* This case is for the div instruction, which
+ acts differently if the destination argument
+ is $0. This only matches $0, and is checked
+ outside the switch. */
+ break;
+ case 'D':
+ /* Itbl operand; not yet implemented. FIXME ?? */
+ break;
+ /* What about all other operands like 'i', which
+ can be specified in the opcode table? */
+ }
+ continue;
+ }
+ break;
+
+ case 'D': /* floating point destination register */
+ case 'S': /* floating point source register */
+ case 'T': /* floating point target register */
+ case 'R': /* floating point source register */
+ rtype = RTYPE_FPU;
+ if (reg_lookup (&s, rtype, &regno))
+ {
+ c = *args;
+ if (*s == ' ')
+ ++s;
+ switch (c)
+ {
+ case 'D':
+ INSERT_OPERAND (FD, *ip, regno);
+ break;
+ case 'S':
+ INSERT_OPERAND (FS, *ip, regno);
+ break;
+ case 'T':
+ INSERT_OPERAND (FT, *ip, regno);
+ break;
+ case 'R':
+ INSERT_OPERAND (FR, *ip, regno);
+ break;
+ }
+ continue;
+ }
+
+ break;
+
+ case 'I':
+ my_getExpression (&imm_expr, s);
+ if (imm_expr.X_op != O_big
+ && imm_expr.X_op != O_constant)
+ insn_error = _("absolute expression required");
+ normalize_constant_expr (&imm_expr);
+ s = expr_end;
+ continue;
+
+ case 'A':
+ my_getExpression (&offset_expr, s);
+ normalize_address_expr (&offset_expr);
+ *imm_reloc = BFD_RELOC_32;
+ s = expr_end;
+ continue;
+
+ case 'j': /* sign-extended RISCV_IMM_BITS immediate */
+ *imm_reloc = BFD_RELOC_LO16;
+ if (my_getSmallExpression (&imm_expr, imm_reloc, s) == 0)
+ {
+ int more;
+ offsetT minval, maxval;
+
+ more = (insn + 1 < &riscv_opcodes[NUMOPCODES]
+ && strcmp (insn->name, insn[1].name) == 0);
+
+ /* If the expression was written as an unsigned number,
+ only treat it as signed if there are no more
+ alternatives. */
+ if (more
+ && *args == 'j'
+ && sizeof (imm_expr.X_add_number) <= 4
+ && imm_expr.X_op == O_constant
+ && imm_expr.X_add_number < 0
+ && imm_expr.X_unsigned
+ && rv64)
+ break;
+
+ /* For compatibility with older assemblers, we accept
+ 0x8000-0xffff as signed 16-bit numbers when only
+ signed numbers are allowed. */
+ if (more)
+ minval = -(signed)RISCV_IMM_REACH/2, maxval = RISCV_IMM_REACH/2-1;
+ else
+ minval = -(signed)RISCV_IMM_REACH/2, maxval = RISCV_IMM_REACH-1;
+
+ if (imm_expr.X_op != O_constant
+ || imm_expr.X_add_number < minval
+ || imm_expr.X_add_number > maxval)
+ {
+ if (more)
+ break;
+ if (imm_expr.X_op == O_constant
+ || imm_expr.X_op == O_big)
+ as_bad (_("expression out of range"));
+ }
+ }
+ s = expr_end;
+ continue;
+
+ case 'q': /* 16 bit offset */
+ case 'o': /* 16 bit offset */
+ /* Check whether there is only a single bracketed expression
+ left. If so, it must be the base register and the
+ constant must be zero. */
+ if (*s == '(' && strchr (s + 1, '(') == 0)
+ {
+ offset_expr.X_op = O_constant;
+ offset_expr.X_add_number = 0;
+ continue;
+ }
+
+ /* If this value won't fit into a 16 bit offset, then go
+ find a macro that will generate the 32 bit offset
+ code pattern. */
+ if (my_getSmallExpression (&offset_expr, offset_reloc, s) == 0
+ && (offset_expr.X_op != O_constant
+ || offset_expr.X_add_number >= (signed)RISCV_IMM_REACH/2
+ || offset_expr.X_add_number < -(signed)RISCV_IMM_REACH/2))
+ break;
+
+ s = expr_end;
+ continue;
+
+ case 'p': /* pc relative offset */
+ *offset_reloc = BFD_RELOC_16_PCREL_S2;
+ my_getExpression (&offset_expr, s);
+ s = expr_end;
+ continue;
+
+ case 'u': /* upper 20 bits */
+ if (my_getSmallExpression (&imm_expr, imm_reloc, s) == 0
+ && imm_expr.X_op == O_constant)
+ {
+ if (imm_expr.X_add_number < 0
+ || imm_expr.X_add_number >= (signed)RISCV_BIGIMM_REACH)
+ as_bad (_("lui expression not in range 0..1048575"));
+
+ *imm_reloc = BFD_RELOC_HI16;
+ imm_expr.X_add_number <<= RISCV_IMM_BITS;
+ }
+ s = expr_end;
+ continue;
+
+ case 'a': /* 26 bit address */
+ my_getExpression (&offset_expr, s);
+ s = expr_end;
+ *offset_reloc = BFD_RELOC_MIPS_JMP;
+ continue;
+
+ default:
+ as_bad (_("bad char = '%c'\n"), *args);
+ internalError ();
+ }
+ break;
+ }
+ /* Args don't match. */
+ if (insn + 1 < &riscv_opcodes[NUMOPCODES] &&
+ !strcmp (insn->name, insn[1].name))
+ {
+ ++insn;
+ s = argsStart;
+ insn_error = _("illegal operands");
+ continue;
+ }
+ if (save_c)
+ *(--argsStart) = save_c;
+ insn_error = _("illegal operands");
+ return;
+ }
+}
+
+struct percent_op_match
+{
+ const char *str;
+ bfd_reloc_code_real_type reloc;
+};
+
+static const struct percent_op_match mips_percent_op[] =
+{
+ {"%lo", BFD_RELOC_LO16},
+#ifdef OBJ_ELF
+ {"%tprel_hi", BFD_RELOC_MIPS_TLS_TPREL_HI16},
+ {"%tprel_lo", BFD_RELOC_MIPS_TLS_TPREL_LO16},
+#endif
+ {"%hi", BFD_RELOC_HI16_S}
+};
+
+/* Return true if *STR points to a relocation operator. When returning true,
+ move *STR over the operator and store its relocation code in *RELOC.
+ Leave both *STR and *RELOC alone when returning false. */
+
+static bfd_boolean
+parse_relocation (char **str, bfd_reloc_code_real_type *reloc)
+{
+ const struct percent_op_match *percent_op;
+ size_t limit, i;
+
+ percent_op = mips_percent_op;
+ limit = ARRAY_SIZE (mips_percent_op);
+
+ for (i = 0; i < limit; i++)
+ if (strncasecmp (*str, percent_op[i].str, strlen (percent_op[i].str)) == 0)
+ {
+ int len = strlen (percent_op[i].str);
+
+ if (!ISSPACE ((*str)[len]) && (*str)[len] != '(')
+ continue;
+
+ *str += strlen (percent_op[i].str);
+ *reloc = percent_op[i].reloc;
+
+ /* Check whether the output BFD supports this relocation.
+ If not, issue an error and fall back on something safe. */
+ if (!bfd_reloc_type_lookup (stdoutput, percent_op[i].reloc))
+ {
+ as_bad ("relocation %s isn't supported by the current ABI",
+ percent_op[i].str);
+ *reloc = BFD_RELOC_UNUSED;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/* Parse string STR as a 16-bit relocatable operand. Store the
+ expression in *EP and the relocations in the array starting
+ at RELOC. Return the number of relocation operators used.
+
+ On exit, EXPR_END points to the first character after the expression. */
+
+static size_t
+my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc,
+ char *str)
+{
+ bfd_reloc_code_real_type reversed_reloc[3];
+ size_t reloc_index, i;
+ int crux_depth, str_depth;
+ char *crux;
+
+ /* Search for the start of the main expression, recoding relocations
+ in REVERSED_RELOC. End the loop with CRUX pointing to the start
+ of the main expression and with CRUX_DEPTH containing the number
+ of open brackets at that point. */
+ reloc_index = -1;
+ str_depth = 0;
+ do
+ {
+ reloc_index++;
+ crux = str;
+ crux_depth = str_depth;
+
+ /* Skip over whitespace and brackets, keeping count of the number
+ of brackets. */
+ while (*str == ' ' || *str == '\t' || *str == '(')
+ if (*str++ == '(')
+ str_depth++;
+ }
+ while (*str == '%'
+ && reloc_index < 3
+ && parse_relocation (&str, &reversed_reloc[reloc_index]));
+
+ my_getExpression (ep, crux);
+ str = expr_end;
+
+ /* Match every open bracket. */
+ while (crux_depth > 0 && (*str == ')' || *str == ' ' || *str == '\t'))
+ if (*str++ == ')')
+ crux_depth--;
+
+ if (crux_depth > 0)
+ as_bad ("unclosed '('");
+
+ expr_end = str;
+
+ if (reloc_index != 0)
+ {
+ prev_reloc_op_frag = frag_now;
+ for (i = 0; i < reloc_index; i++)
+ reloc[i] = reversed_reloc[reloc_index - 1 - i];
+ }
+
+ return reloc_index;
+}
+
+static void
+my_getExpression (expressionS *ep, char *str)
+{
+ char *save_in;
+
+ save_in = input_line_pointer;
+ input_line_pointer = str;
+ expression (ep);
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+}
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, target_big_endian);
+}
+
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+const char *md_shortopts = "O::g::G:";
+
+enum options
+ {
+ OPTION_M32 = OPTION_MD_BASE,
+ OPTION_M64,
+ OPTION_PIC,
+ OPTION_NO_PIC,
+ OPTION_EB,
+ OPTION_EL,
+ OPTION_MRVC,
+ OPTION_MNO_RVC,
+ OPTION_END_OF_ENUM
+ };
+
+struct option md_longopts[] =
+{
+ {"m32", no_argument, NULL, OPTION_M32},
+ {"m64", no_argument, NULL, OPTION_M64},
+ {"fPIC", no_argument, NULL, OPTION_PIC},
+ {"fpic", no_argument, NULL, OPTION_PIC},
+ {"fno-pic", no_argument, NULL, OPTION_NO_PIC},
+ {"EB", no_argument, NULL, OPTION_EB},
+ {"EL", no_argument, NULL, OPTION_EL},
+ {"mrvc", no_argument, NULL, OPTION_MRVC},
+ {"mno-rvc", no_argument, NULL, OPTION_MNO_RVC},
+
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c, char *arg)
+{
+ switch (c)
+ {
+ case OPTION_EB:
+ target_big_endian = 1;
+ break;
+
+ case OPTION_EL:
+ target_big_endian = 0;
+ break;
+
+ case 'g':
+ if (arg == NULL)
+ mips_debug = 2;
+ else
+ mips_debug = atoi (arg);
+ break;
+
+ case OPTION_MRVC:
+ mips_opts.rvc = 1;
+ break;
+
+ case OPTION_MNO_RVC:
+ mips_opts.rvc = 0;
+ break;
+
+ case OPTION_M32:
+ rv64 = FALSE;
+ break;
+
+ case OPTION_M64:
+ rv64 = TRUE;
+ break;
+
+ case OPTION_NO_PIC:
+ is_pic = FALSE;
+ break;
+
+ case OPTION_PIC:
+ is_pic = TRUE;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+mips_after_parse_args (void)
+{
+}
+
+void
+mips_init_after_args (void)
+{
+ /* initialize opcodes */
+ bfd_riscv_num_opcodes = bfd_riscv_num_builtin_opcodes;
+ riscv_opcodes = (struct riscv_opcode *) riscv_builtin_opcodes;
+}
+
+long
+md_pcrel_from (fixS *fixP)
+{
+ return fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+/* We may have combined relocations without symbols in the N32/N64 ABI.
+ We have to prevent gas from dropping them. */
+
+int
+mips_force_relocation (fixS *fixp)
+{
+ if (generic_force_reloc (fixp))
+ return 1;
+
+ if (S_GET_SEGMENT (fixp->fx_addsy) == bfd_abs_section_ptr
+ && (fixp->fx_r_type == BFD_RELOC_MIPS_SUB
+ || hi16_reloc_p (fixp->fx_r_type)
+ || lo16_reloc_p (fixp->fx_r_type)))
+ return 1;
+
+ return 0;
+}
+
+/* Apply a fixup to the object file. */
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ bfd_byte *buf;
+ long insn;
+ reloc_howto_type *howto;
+
+
+ /* We ignore generic BFD relocations we don't know about. */
+ howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
+ if (! howto)
+ return;
+
+ gas_assert (fixP->fx_size == 4
+ || fixP->fx_r_type == BFD_RELOC_64
+ || fixP->fx_r_type == BFD_RELOC_CTOR
+ || fixP->fx_r_type == BFD_RELOC_MIPS_SUB
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY
+ || fixP->fx_r_type == BFD_RELOC_MIPS_TLS_DTPREL64);
+
+ buf = (bfd_byte *) (fixP->fx_frag->fr_literal + fixP->fx_where);
+
+ gas_assert (!fixP->fx_pcrel || (fixP->fx_r_type == BFD_RELOC_16_PCREL_S2 ||
+ fixP->fx_r_type == BFD_RELOC_MIPS_JMP));
+
+ /* Don't treat parts of a composite relocation as done. There are two
+ reasons for this:
+
+ (1) The second and third parts will be against 0 (RSS_UNDEF) but
+ should nevertheless be emitted if the first part is.
+
+ (2) In normal usage, composite relocations are never assembly-time
+ constants. The easiest way of dealing with the pathological
+ exceptions is to generate a relocation against STN_UNDEF and
+ leave everything up to the linker. */
+ if (fixP->fx_addsy == NULL && !fixP->fx_pcrel && fixP->fx_tcbit == 0)
+ fixP->fx_done = 1;
+
+ if (target_big_endian)
+ insn = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+ else
+ insn = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_MIPS_TLS_GD:
+ case BFD_RELOC_RISCV_TLS_GD_HI16:
+ case BFD_RELOC_RISCV_TLS_GD_LO16:
+ case BFD_RELOC_MIPS_TLS_LDM:
+ case BFD_RELOC_RISCV_TLS_LDM_HI16:
+ case BFD_RELOC_RISCV_TLS_LDM_LO16:
+ case BFD_RELOC_MIPS_TLS_DTPREL32:
+ case BFD_RELOC_MIPS_TLS_DTPREL64:
+ case BFD_RELOC_MIPS_TLS_DTPREL_HI16:
+ case BFD_RELOC_MIPS_TLS_DTPREL_LO16:
+ case BFD_RELOC_MIPS_TLS_GOTTPREL:
+ case BFD_RELOC_RISCV_TLS_GOT_HI16:
+ case BFD_RELOC_RISCV_TLS_GOT_LO16:
+ case BFD_RELOC_MIPS_TLS_TPREL_HI16:
+ case BFD_RELOC_MIPS_TLS_TPREL_LO16:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ /* fall through */
+
+ case BFD_RELOC_MIPS_GOT_DISP:
+ case BFD_RELOC_MIPS_SUB:
+ case BFD_RELOC_MIPS_INSERT_A:
+ case BFD_RELOC_MIPS_INSERT_B:
+ case BFD_RELOC_MIPS_DELETE:
+ case BFD_RELOC_MIPS_SCN_DISP:
+ case BFD_RELOC_MIPS_REL16:
+ case BFD_RELOC_MIPS_RELGOT:
+ case BFD_RELOC_HI16:
+ case BFD_RELOC_HI16_S:
+ case BFD_RELOC_GPREL16:
+ case BFD_RELOC_MIPS_LITERAL:
+ case BFD_RELOC_MIPS_CALL16:
+ case BFD_RELOC_MIPS_GOT16:
+ case BFD_RELOC_GPREL32:
+ case BFD_RELOC_MIPS_GOT_HI16:
+ case BFD_RELOC_MIPS_GOT_LO16:
+ case BFD_RELOC_MIPS_CALL_HI16:
+ case BFD_RELOC_MIPS_CALL_LO16:
+ case BFD_RELOC_MIPS16_GPREL:
+ case BFD_RELOC_MIPS16_GOT16:
+ case BFD_RELOC_MIPS16_CALL16:
+ case BFD_RELOC_MIPS16_HI16:
+ case BFD_RELOC_MIPS16_HI16_S:
+ case BFD_RELOC_MIPS16_JMP:
+ /* Nothing needed to do. The value comes from the reloc entry. */
+ break;
+
+ case BFD_RELOC_64:
+ /* This is handled like BFD_RELOC_32, but we output a sign
+ extended value if we are only 32 bits. */
+ if (fixP->fx_done)
+ {
+ if (8 <= sizeof (valueT))
+ md_number_to_chars ((char *) buf, *valP, 8);
+ else
+ {
+ valueT hiv;
+
+ if ((*valP & 0x80000000) != 0)
+ hiv = 0xffffffff;
+ else
+ hiv = 0;
+ md_number_to_chars ((char *)(buf + (target_big_endian ? 4 : 0)),
+ *valP, 4);
+ md_number_to_chars ((char *)(buf + (target_big_endian ? 0 : 4)),
+ hiv, 4);
+ }
+ }
+ break;
+
+ case BFD_RELOC_RVA:
+ case BFD_RELOC_32:
+ /* If we are deleting this reloc entry, we must fill in the
+ value now. This can happen if we have a .word which is not
+ resolved when it appears but is later defined. */
+ if (fixP->fx_done)
+ md_number_to_chars ((char *) buf, *valP, fixP->fx_size);
+ break;
+
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_MIPS16_LO16:
+ if (!fixP->fx_done)
+ break;
+
+ if (*valP + RISCV_IMM_REACH/2 > RISCV_IMM_REACH-1)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+
+ if (OPCODE_IS_STORE(insn)) /* Stores have a split immediate field. */
+ {
+ valueT value = *valP & (RISCV_IMM_REACH-1);
+ value = ((value >> RISCV_IMMLO_BITS) << OP_SH_IMMHI) |
+ ((value & ((1<<RISCV_IMMLO_BITS)-1)) << OP_SH_IMMLO);
+ insn |= value;
+ }
+ else
+ insn |= (*valP & ((1<<RISCV_IMM_BITS)-1)) << OP_SH_IMMEDIATE;
+
+ md_number_to_chars ((char *) buf, insn, 4);
+ break;
+
+ case BFD_RELOC_MIPS_JMP:
+ if ((*valP & (RISCV_JUMP_ALIGN-1)) != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Branch to misaligned address (%lx)"), (long) *valP);
+
+ /* We need to save the bits in the instruction since fixup_segment()
+ might be deleting the relocation entry (i.e., a branch within
+ the current segment). */
+ if (! fixP->fx_done)
+ break;
+
+ /* Update old instruction data. */
+
+ if (*valP + RISCV_JUMP_REACH/2 <= RISCV_JUMP_REACH-1)
+ {
+ insn |= ((*valP >> RISCV_JUMP_ALIGN_BITS) & ((1<<RISCV_JUMP_BITS)-1)) << OP_SH_TARGET;
+ md_number_to_chars ((char *) buf, insn, 4);
+ }
+ else
+ {
+ /* If we got here, we have branch-relaxation disabled,
+ and there's nothing we can do to fix this instruction
+ without turning it into a longer sequence. */
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Jump out of range"));
+ }
+ break;
+
+ case BFD_RELOC_16_PCREL_S2:
+ if ((*valP & (RISCV_BRANCH_ALIGN-1)) != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Branch to misaligned address (%lx)"), (long) *valP);
+
+ /* We need to save the bits in the instruction since fixup_segment()
+ might be deleting the relocation entry (i.e., a branch within
+ the current segment). */
+ if (! fixP->fx_done)
+ break;
+
+ /* Update old instruction data. */
+ if (*valP + RISCV_BRANCH_REACH/2 <= RISCV_BRANCH_REACH-1)
+ {
+ unsigned delta = ((unsigned)*valP >> RISCV_BRANCH_ALIGN_BITS) & ((1<<RISCV_BRANCH_BITS)-1);;
+ insn |= ((delta & ((1<<RISCV_IMMLO_BITS)-1)) << OP_SH_IMMLO) | (((delta >> RISCV_IMMLO_BITS) & ((1<<RISCV_IMMHI_BITS)-1)) << OP_SH_IMMHI);
+ md_number_to_chars ((char *) buf, insn, 4);
+ }
+ else
+ {
+ /* If we got here, we have branch-relaxation disabled,
+ and there's nothing we can do to fix this instruction
+ without turning it into a longer sequence. */
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Branch out of range"));
+ }
+ break;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ fixP->fx_done = 0;
+ if (fixP->fx_addsy
+ && !S_IS_DEFINED (fixP->fx_addsy)
+ && !S_IS_WEAK (fixP->fx_addsy))
+ S_SET_WEAK (fixP->fx_addsy);
+ break;
+
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ break;
+
+ default:
+ internalError ();
+ }
+
+ /* Remember value for tc_gen_reloc. */
+ fixP->fx_addnumber = *valP;
+}
+
+/* Align the current frag to a given power of two. If a particular
+ fill byte should be used, FILL points to an integer that contains
+ that byte, otherwise FILL is null.
+
+ The MIPS assembler also automatically adjusts any preceding
+ label. */
+
+static void
+mips_align (int to, int *fill, symbolS *label)
+{
+ mips_clear_insn_labels ();
+ if (fill == NULL && subseg_text_p (now_seg))
+ frag_align_code (to, 0);
+ else
+ frag_align (to, fill ? *fill : 0, 0);
+ record_alignment (now_seg, to);
+ if (label != NULL)
+ {
+ gas_assert (S_GET_SEGMENT (label) == now_seg);
+ symbol_set_frag (label, frag_now);
+ S_SET_VALUE (label, (valueT) frag_now_fix ());
+ }
+}
+
+/* Align to a given power of two. .align 0 turns off the automatic
+ alignment used by the data creating pseudo-ops. */
+
+static void
+s_align (int x ATTRIBUTE_UNUSED)
+{
+ int temp, fill_value, *fill_ptr;
+ long max_alignment = 28;
+
+ /* o Note that the assembler pulls down any immediately preceding label
+ to the aligned address.
+ o It's not documented but auto alignment is reinstated by
+ a .align pseudo instruction.
+ o Note also that after auto alignment is turned off the mips assembler
+ issues an error on attempt to assemble an improperly aligned data item.
+ We don't. */
+
+ temp = get_absolute_expression ();
+ if (temp > max_alignment)
+ as_bad (_("Alignment too large: %d. assumed."), temp = max_alignment);
+ else if (temp < 0)
+ {
+ as_warn (_("Alignment negative: 0 assumed."));
+ temp = 0;
+ }
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ fill_value = get_absolute_expression ();
+ fill_ptr = &fill_value;
+ }
+ else
+ fill_ptr = 0;
+ if (temp)
+ {
+ segment_info_type *si = seg_info (now_seg);
+ struct insn_label_list *l = si->label_list;
+ /* Auto alignment should be switched on by next section change. */
+ auto_align = 1;
+ mips_align (temp, fill_ptr, l != NULL ? l->label : NULL);
+ }
+ else
+ {
+ auto_align = 0;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_change_sec (int sec)
+{
+ segT seg;
+
+#ifdef OBJ_ELF
+ /* The ELF backend needs to know that we are changing sections, so
+ that .previous works correctly. We could do something like check
+ for an obj_section_change_hook macro, but that might be confusing
+ as it would not be appropriate to use it in the section changing
+ functions in read.c, since obj-elf.c intercepts those. FIXME:
+ This should be cleaner, somehow. */
+ if (IS_ELF)
+ obj_elf_section_change_hook ();
+#endif
+
+ mips_clear_insn_labels ();
+
+ switch (sec)
+ {
+ case 't':
+ s_text (0);
+ break;
+ case 'd':
+ s_data (0);
+ break;
+ case 'b':
+ subseg_set (bss_section, (subsegT) get_absolute_expression ());
+ demand_empty_rest_of_line ();
+ break;
+
+ case 'r':
+ seg = subseg_new (".rodata", (subsegT) get_absolute_expression ());
+ if (IS_ELF)
+ {
+ bfd_set_section_flags (stdoutput, seg, (SEC_ALLOC | SEC_LOAD
+ | SEC_READONLY | SEC_RELOC
+ | SEC_DATA));
+ if (strncmp (TARGET_OS, "elf", 3) != 0)
+ record_alignment (seg, 4);
+ }
+ demand_empty_rest_of_line ();
+ break;
+ }
+
+ auto_align = 1;
+}
+
+void
+s_change_section (int ignore ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_ELF
+ char *section_name;
+ char c;
+ char next_c = 0;
+ int section_type;
+ int section_flag;
+ int section_entry_size;
+
+ if (!IS_ELF)
+ return;
+
+ section_name = input_line_pointer;
+ c = get_symbol_end ();
+ if (c)
+ next_c = *(input_line_pointer + 1);
+
+ /* Do we have .section Name<,"flags">? */
+ if (c != ',' || (c == ',' && next_c == '"'))
+ {
+ /* just after name is now '\0'. */
+ *input_line_pointer = c;
+ input_line_pointer = section_name;
+ obj_elf_section (ignore);
+ return;
+ }
+ input_line_pointer++;
+
+ /* Do we have .section Name<,type><,flag><,entry_size><,alignment> */
+ if (c == ',')
+ section_type = get_absolute_expression ();
+ else
+ section_type = 0;
+ if (*input_line_pointer++ == ',')
+ section_flag = get_absolute_expression ();
+ else
+ section_flag = 0;
+ if (*input_line_pointer++ == ',')
+ section_entry_size = get_absolute_expression ();
+ else
+ section_entry_size = 0;
+
+ section_name = xstrdup (section_name);
+
+ /* When using the generic form of .section (as implemented by obj-elf.c),
+ there's no way to set the section type to SHT_MIPS_DWARF. Users have
+ traditionally had to fall back on the more common @progbits instead.
+
+ There's nothing really harmful in this, since bfd will correct
+ SHT_PROGBITS to SHT_MIPS_DWARF before writing out the file. But it
+ means that, for backwards compatibility, the special_section entries
+ for dwarf sections must use SHT_PROGBITS rather than SHT_MIPS_DWARF.
+
+ Even so, we shouldn't force users of the MIPS .section syntax to
+ incorrectly label the sections as SHT_PROGBITS. The best compromise
+ seems to be to map SHT_MIPS_DWARF to SHT_PROGBITS before calling the
+ generic type-checking code. */
+ if (section_type == SHT_MIPS_DWARF)
+ section_type = SHT_PROGBITS;
+
+ obj_elf_change_section (section_name, section_type, section_flag,
+ section_entry_size, 0, 0, 0);
+
+ if (now_seg->name != section_name)
+ free (section_name);
+#endif /* OBJ_ELF */
+}
+
+void
+mips_enable_auto_align (void)
+{
+ auto_align = 1;
+}
+
+static void
+s_cons (int log_size)
+{
+ segment_info_type *si = seg_info (now_seg);
+ struct insn_label_list *l = si->label_list;
+ symbolS *label;
+
+ label = l != NULL ? l->label : NULL;
+ mips_clear_insn_labels ();
+ if (log_size > 0 && auto_align)
+ mips_align (log_size, 0, label);
+ mips_clear_insn_labels ();
+ cons (1 << log_size);
+}
+
+static void
+s_float_cons (int type)
+{
+ segment_info_type *si = seg_info (now_seg);
+ struct insn_label_list *l = si->label_list;
+ symbolS *label;
+
+ label = l != NULL ? l->label : NULL;
+
+ mips_clear_insn_labels ();
+
+ if (auto_align)
+ {
+ if (type == 'd')
+ mips_align (3, 0, label);
+ else
+ mips_align (2, 0, label);
+ }
+
+ mips_clear_insn_labels ();
+
+ float_cons (type);
+}
+
+/* Handle .globl. We need to override it because on Irix 5 you are
+ permitted to say
+ .globl foo .text
+ where foo is an undefined symbol, to mean that foo should be
+ considered to be the address of a function. */
+
+static void
+s_mips_globl (int x ATTRIBUTE_UNUSED)
+{
+ char *name;
+ int c;
+ symbolS *symbolP;
+ flagword flag;
+
+ do
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+ S_SET_EXTERNAL (symbolP);
+
+ *input_line_pointer = c;
+ SKIP_WHITESPACE ();
+
+ /* On Irix 5, every global symbol that is not explicitly labelled as
+ being a function is apparently labelled as being an object. */
+ flag = BSF_OBJECT;
+
+ if (!is_end_of_line[(unsigned char) *input_line_pointer]
+ && (*input_line_pointer != ','))
+ {
+ char *secname;
+ asection *sec;
+
+ secname = input_line_pointer;
+ c = get_symbol_end ();
+ sec = bfd_get_section_by_name (stdoutput, secname);
+ if (sec == NULL)
+ as_bad (_("%s: no such section"), secname);
+ *input_line_pointer = c;
+
+ if (sec != NULL && (sec->flags & SEC_CODE) != 0)
+ flag = BSF_FUNCTION;
+ }
+
+ symbol_get_bfdsym (symbolP)->flags |= flag;
+
+ c = *input_line_pointer;
+ if (c == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (is_end_of_line[(unsigned char) *input_line_pointer])
+ c = '\n';
+ }
+ }
+ while (c == ',');
+
+ demand_empty_rest_of_line ();
+}
+
+/* This structure is used to hold a stack of .set values. */
+
+struct mips_option_stack
+{
+ struct mips_option_stack *next;
+ struct mips_set_options options;
+};
+
+static struct mips_option_stack *mips_opts_stack;
+
+/* Handle the .set pseudo-op. */
+
+static void
+s_mipsset (int x ATTRIBUTE_UNUSED)
+{
+ char *name = input_line_pointer, ch;
+
+ while (!is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ ch = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ if (strcmp (name, "rvc") == 0)
+ mips_opts.rvc = 1;
+ else if (strcmp (name, "norvc") == 0)
+ mips_opts.rvc = 0;
+ else if (strcmp (name, "push") == 0)
+ {
+ struct mips_option_stack *s;
+
+ s = (struct mips_option_stack *) xmalloc (sizeof *s);
+ s->next = mips_opts_stack;
+ s->options = mips_opts;
+ mips_opts_stack = s;
+ }
+ else if (strcmp (name, "pop") == 0)
+ {
+ struct mips_option_stack *s;
+
+ s = mips_opts_stack;
+ if (s == NULL)
+ as_bad (_(".set pop with no .set push"));
+ else
+ {
+ mips_opts = s->options;
+ mips_opts_stack = s->next;
+ free (s);
+ }
+ }
+ else if (strchr (name, ','))
+ {
+ /* Generic ".set" directive; use the generic handler. */
+ *input_line_pointer = ch;
+ input_line_pointer = name;
+ s_set (0);
+ return;
+ }
+ else
+ {
+ as_warn (_("Tried to set unrecognized symbol: %s\n"), name);
+ }
+ *input_line_pointer = ch;
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .dtprelword and .dtpreldword pseudo-ops. They generate
+ a 32-bit or 64-bit DTP-relative relocation (BYTES says which) for
+ use in DWARF debug information. */
+
+static void
+s_dtprel_internal (size_t bytes)
+{
+ expressionS ex;
+ char *p;
+
+ expression (&ex);
+
+ if (ex.X_op != O_symbol)
+ {
+ as_bad (_("Unsupported use of %s"), (bytes == 8
+ ? ".dtpreldword"
+ : ".dtprelword"));
+ ignore_rest_of_line ();
+ }
+
+ p = frag_more (bytes);
+ md_number_to_chars (p, 0, bytes);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, bytes, &ex, FALSE,
+ (bytes == 8
+ ? BFD_RELOC_MIPS_TLS_DTPREL64
+ : BFD_RELOC_MIPS_TLS_DTPREL32));
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle .dtprelword. */
+
+static void
+s_dtprelword (int ignore ATTRIBUTE_UNUSED)
+{
+ s_dtprel_internal (4);
+}
+
+/* Handle .dtpreldword. */
+
+static void
+s_dtpreldword (int ignore ATTRIBUTE_UNUSED)
+{
+ s_dtprel_internal (8);
+}
+
+/* Handle the .weakext pseudo-op as defined in Kane and Heinrich. */
+
+static void
+s_mips_weakext (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ int c;
+ symbolS *symbolP;
+ expressionS exp;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+ S_SET_WEAK (symbolP);
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE ();
+
+ if (! is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ if (S_IS_DEFINED (symbolP))
+ {
+ as_bad ("ignoring attempt to redefine symbol %s",
+ S_GET_NAME (symbolP));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ }
+
+ expression (&exp);
+ if (exp.X_op != O_symbol)
+ {
+ as_bad ("bad .weakext directive");
+ ignore_rest_of_line ();
+ return;
+ }
+ symbol_set_value_expression (symbolP, &exp);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+valueT
+md_section_align (asection *seg, valueT addr)
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+
+ if (IS_ELF)
+ {
+ /* We don't need to align ELF sections to the full alignment.
+ However, Irix 5 may prefer that we align them at least to a 16
+ byte boundary. We don't bother to align the sections if we
+ are targeted for an embedded system. */
+ if (strncmp (TARGET_OS, "elf", 3) == 0)
+ return addr;
+ if (align > 4)
+ align = 4;
+ }
+
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+/* Compute the length of a branch sequence, and adjust the
+ RELAX_BRANCH_TOOFAR bit accordingly. If FRAGP is NULL, the
+ worst-case length is computed, with UPDATE being used to indicate
+ whether an unconditional (-1), branch-likely (+1) or regular (0)
+ branch is to be computed. */
+static int
+relaxed_branch_length (fragS *fragp, asection *sec, int update)
+{
+ bfd_boolean toofar;
+
+ if (fragp
+ && S_IS_DEFINED (fragp->fr_symbol)
+ && sec == S_GET_SEGMENT (fragp->fr_symbol))
+ {
+ offsetT val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
+ val -= fragp->fr_address + fragp->fr_fix;
+
+ if(RELAX_BRANCH_UNCOND (fragp->fr_subtype))
+ toofar = (bfd_vma)(val + RVC_JUMP_REACH/2) >= RVC_JUMP_REACH;
+ else
+ toofar = (bfd_vma)(val + RVC_BRANCH_REACH/2) >= RVC_BRANCH_REACH;
+ }
+ else
+ /* If the symbol is not defined or it's in a different segment,
+ assume it's too far. */
+ toofar = TRUE;
+
+ if (fragp && update && toofar != RELAX_BRANCH_TOOFAR (fragp->fr_subtype))
+ fragp->fr_subtype
+ = RELAX_BRANCH_ENCODE (RELAX_BRANCH_UNCOND (fragp->fr_subtype), toofar);
+
+ return toofar ? 4 : 2;
+}
+
+int
+md_estimate_size_before_relax (fragS *fragp, asection *segtype)
+{
+ return (fragp->fr_var = relaxed_branch_length (fragp, segtype, FALSE));
+}
+
+/* This is called to see whether a reloc against a defined symbol
+ should be converted into a reloc against a section. */
+
+int
+mips_fix_adjustable (fixS *fixp)
+{
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ return 1;
+}
+
+/* Translate internal representation of relocation info to BFD target
+ format. */
+
+arelent **
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ static arelent *retval[4];
+ arelent *reloc;
+ bfd_reloc_code_real_type code;
+
+ memset (retval, 0, sizeof(retval));
+ reloc = retval[0] = (arelent *) xcalloc (1, sizeof (arelent));
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ if (fixp->fx_pcrel)
+ {
+ gas_assert (fixp->fx_r_type == BFD_RELOC_16_PCREL_S2 ||
+ fixp->fx_r_type == BFD_RELOC_MIPS_JMP);
+
+ /* At this point, fx_addnumber is "symbol offset - pcrel address".
+ Relocations want only the symbol offset. */
+ reloc->addend = fixp->fx_addnumber + reloc->address;
+ if (!IS_ELF)
+ {
+ /* A gruesome hack which is a result of the gruesome gas
+ reloc handling. What's worse, for COFF (as opposed to
+ ECOFF), we might need yet another copy of reloc->address.
+ See bfd_install_relocation. */
+ reloc->addend += reloc->address;
+ }
+ }
+ else
+ reloc->addend = fixp->fx_addnumber;
+
+ code = fixp->fx_r_type;
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Can not represent %s relocation in this object file format"),
+ bfd_get_reloc_code_name (code));
+ retval[0] = NULL;
+ }
+
+ return retval;
+}
+
+int
+mips_relax_frag (asection *sec, fragS *fragp, long stretch ATTRIBUTE_UNUSED)
+{
+ if (RELAX_BRANCH_P (fragp->fr_subtype))
+ {
+ offsetT old_var = fragp->fr_var;
+ fragp->fr_var = relaxed_branch_length (fragp, sec, TRUE);
+ return fragp->fr_var - old_var;
+ }
+
+ return 0;
+}
+
+/* Convert a machine dependent frag. */
+
+static void
+md_convert_frag_branch (bfd *abfd ATTRIBUTE_UNUSED, segT asec ATTRIBUTE_UNUSED,
+ fragS *fragp)
+{
+ bfd_byte *buf;
+ unsigned long insn;
+ expressionS exp;
+ fixS *fixp;
+ bfd_reloc_code_real_type reloc_type = BFD_RELOC_16_PCREL_S2;
+
+ buf = (bfd_byte *)fragp->fr_literal + fragp->fr_fix;
+
+ if (target_big_endian)
+ insn = bfd_getb16 (buf);
+ else
+ insn = bfd_getl16 (buf);
+
+ if (!RELAX_BRANCH_TOOFAR (fragp->fr_subtype))
+ {
+ gas_assert(S_IS_DEFINED(fragp->fr_symbol));
+ gas_assert(fragp->fr_var == 2);
+
+ offsetT target = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
+ target -= fragp->fr_address + fragp->fr_fix;
+ target >>= RVC_JUMP_ALIGN_BITS;
+ gas_assert(RVC_JUMP_ALIGN_BITS == RVC_BRANCH_ALIGN_BITS);
+
+ if((insn & MASK_C_J) == MATCH_C_J)
+ insn |= ((target & OP_MASK_CIMM10) << OP_SH_CIMM10);
+ else if((insn & MASK_C_BEQ) == MATCH_C_BEQ ||
+ (insn & MASK_C_BNE) == MATCH_C_BNE)
+ insn |= ((target & OP_MASK_CIMM5) << OP_SH_CIMM5);
+ else
+ gas_assert(0);
+
+ md_number_to_chars ((char *) buf, insn, 2);
+ buf += 2;
+ }
+ else
+ {
+ gas_assert(fragp->fr_var == 4);
+
+ int rs1 = rvc_rs1_regmap[(insn >> OP_SH_CRS1S) & OP_MASK_CRS1S];
+ int rs2 = rvc_rs2_regmap[(insn >> OP_SH_CRS2S) & OP_MASK_CRS2S];
+
+ if((insn & MASK_C_J) == MATCH_C_J)
+ {
+ insn = MATCH_J;
+ reloc_type = BFD_RELOC_MIPS_JMP;
+ }
+ else if((insn & MASK_C_BEQ) == MATCH_C_BEQ)
+ insn = MATCH_BEQ | (rs1 << OP_SH_RS) | (rs2 << OP_SH_RT);
+ else if((insn & MASK_C_BNE) == MATCH_C_BNE)
+ insn = MATCH_BNE | (rs1 << OP_SH_RS) | (rs2 << OP_SH_RT);
+ else
+ gas_assert(0);
+
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = fragp->fr_symbol;
+ exp.X_add_number = fragp->fr_offset;
+
+ fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
+ 4, &exp, FALSE, reloc_type);
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+ fixp->fx_pcrel = 1;
+
+ md_number_to_chars ((char *) buf, insn, 4);
+ buf += 4;
+ }
+
+ gas_assert (buf == (bfd_byte *)fragp->fr_literal
+ + fragp->fr_fix + fragp->fr_var);
+
+ fragp->fr_fix += fragp->fr_var;
+}
+
+/* Relax a machine dependent frag. This returns the amount by which
+ the current size of the frag should change. */
+
+void
+md_convert_frag(bfd *abfd, segT asec, fragS *fragp)
+{
+ if(RELAX_BRANCH_P(fragp->fr_subtype))
+ md_convert_frag_branch(abfd, asec, fragp);
+ else
+ gas_assert(0);
+}
+
+/* This function is called whenever a label is defined. It is used
+ when handling branch delays; if a branch has a label, we assume we
+ can not move it. */
+
+void
+mips_define_label (symbolS *sym)
+{
+ segment_info_type *si = seg_info (now_seg);
+ struct insn_label_list *l;
+
+ if (free_insn_labels == NULL)
+ l = (struct insn_label_list *) xmalloc (sizeof *l);
+ else
+ {
+ l = free_insn_labels;
+ free_insn_labels = l->next;
+ }
+
+ l->label = sym;
+ l->next = si->label_list;
+ si->label_list = l;
+
+#ifdef OBJ_ELF
+ dwarf2_emit_label (sym);
+#endif
+}
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+
+/* Some special processing for a MIPS ELF file. */
+
+void
+mips_elf_final_processing (void)
+{
+ /* Write out the register information. */
+ if (!rv64)
+ {
+ Elf32_RegInfo s;
+
+ s.ri_gprmask = mips_gprmask;
+ s.ri_cprmask[0] = 0;
+ s.ri_cprmask[1] = mips_fprmask;
+ s.ri_cprmask[2] = 0;
+ s.ri_cprmask[3] = 0;
+ /* The gp_value field is set by the MIPS ELF backend. */
+
+ bfd_riscv_elf32_swap_reginfo_out (stdoutput, &s,
+ ((Elf32_External_RegInfo *)
+ mips_regmask_frag));
+ }
+ else
+ {
+ Elf64_Internal_RegInfo s;
+
+ s.ri_gprmask = mips_gprmask;
+ s.ri_pad = 0;
+ s.ri_cprmask[0] = 0;
+ s.ri_cprmask[1] = mips_fprmask;
+ s.ri_cprmask[2] = 0;
+ s.ri_cprmask[3] = 0;
+ /* The gp_value field is set by the MIPS ELF backend. */
+
+ bfd_riscv_elf64_swap_reginfo_out (stdoutput, &s,
+ ((Elf64_External_RegInfo *)
+ mips_regmask_frag));
+ }
+
+ /* Set the MIPS ELF flag bits. FIXME: There should probably be some
+ sort of BFD interface for this. */
+ if (is_pic)
+ {
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_PIC;
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_CPIC;
+ }
+
+ elf_elfheader (stdoutput)->e_flags |= rv64 ? E_RISCV_ABI_64 : E_RISCV_ABI_32;
+}
+
+#endif /* OBJ_ELF || OBJ_MAYBE_ELF */
+
+void
+mips_handle_align (fragS *fragp)
+{
+ char *p;
+
+ if (fragp->fr_type != rs_align_code)
+ return;
+
+ p = fragp->fr_literal + fragp->fr_fix;
+ md_number_to_chars (p, RISCV_NOP, 4);
+ fragp->fr_var = 4;
+}
+
+/* The .file directive; just like the usual .file directive, but there
+ is an initial number which is the ECOFF file index. In the non-ECOFF
+ case .file implies DWARF-2. */
+
+static void
+s_mips_file (int x ATTRIBUTE_UNUSED)
+{
+ static int first_file_directive = 0;
+
+ char *filename;
+
+ filename = dwarf2_directive_file (0);
+
+ /* Versions of GCC up to 3.1 start files with a ".file"
+ directive even for stabs output. Make sure that this
+ ".file" is handled. Note that you need a version of GCC
+ after 3.1 in order to support DWARF-2 on MIPS. */
+ if (filename != NULL && ! first_file_directive)
+ {
+ (void) new_logical_line (filename, -1);
+ s_app_file_string (filename, 0);
+ }
+ first_file_directive = 1;
+}
+
+/* The .loc directive, implying DWARF-2. */
+
+static void
+s_mips_loc (int x ATTRIBUTE_UNUSED)
+{
+ dwarf2_directive_loc (0);
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, _("\
+RISC-V options:\n\
+ -m32 assemble RV32 code\n\
+ -m64 assemble RV64 code (default)\n\
+ -fpic generate position-independent code\n\
+ -fno-pic don't generate position-independent code (default)\n\
+"));
+}
+
+enum dwarf2_format
+mips_dwarf2_format (asection *sec ATTRIBUTE_UNUSED)
+{
+ if (HAVE_32BIT_SYMBOLS)
+ return dwarf2_format_32bit;
+ else
+ return dwarf2_format_64bit;
+}
+
+int
+mips_dwarf2_addr_size (void)
+{
+ return rv64 ? 8 : 4;
+}
+
+/* Standard calling conventions leave the CFA at SP on entry. */
+void
+mips_cfi_frame_initial_instructions (void)
+{
+ cfi_add_CFA_def_cfa_register (SP);
+}
+
+int
+tc_mips_regname_to_dw2regnum (char *regname)
+{
+ unsigned int regnum = -1;
+ unsigned int reg;
+
+ if (reg_lookup (&regname, RTYPE_GP | RTYPE_NUM, &reg))
+ regnum = reg;
+
+ return regnum;
+}
diff -ruN binutils-2.24/gas/config/tc-riscv.h binutils-2.24-riscv/gas/config/tc-riscv.h
--- binutils-2.24/gas/config/tc-riscv.h 1969-12-31 16:00:00.000000000 -0800
+++ binutils-2.24-riscv/gas/config/tc-riscv.h 2014-12-02 16:05:44.913434966 -0800
@@ -0,0 +1,146 @@
+/* tc-mips.h -- header file for tc-mips.c.
+ Copyright 1993, 1994, 1995, 1996, 1997, 2000, 2001, 2002, 2003, 2004,
+ 2005, 2006, 2007 Free Software Foundation, Inc.
+ Contributed by the OSF and Ralph Campbell.
+ Written by Keith Knowles and Ralph Campbell, working independently.
+ Modified for ECOFF support by Ian Lance Taylor of Cygnus Support.
+
+ This file is part of GAS.
+
+ GAS 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, or (at your option)
+ any later version.
+
+ GAS 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 GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef TC_RISCV
+#define TC_RISCV
+
+#include "opcode/riscv.h"
+
+struct frag;
+struct expressionS;
+
+/* Default to big endian. */
+#ifndef TARGET_BYTES_BIG_ENDIAN
+#define TARGET_BYTES_BIG_ENDIAN 1
+#endif
+
+#define TARGET_ARCH bfd_arch_riscv
+
+#define WORKING_DOT_WORD 1
+#define OLD_FLOAT_READS
+#define REPEAT_CONS_EXPRESSIONS
+#define RELOC_EXPANSION_POSSIBLE
+#define MAX_RELOC_EXPANSION 3
+#define LOCAL_LABELS_FB 1
+
+#define md_relax_frag(segment, fragp, stretch) \
+ mips_relax_frag(segment, fragp, stretch)
+extern int mips_relax_frag (asection *, struct frag *, long);
+
+#define md_undefined_symbol(name) (0)
+#define md_operand(x)
+
+#define NOP_OPCODE RISCV_NOP
+
+extern void mips_handle_align (struct frag *);
+#define HANDLE_ALIGN(fragp) mips_handle_align (fragp)
+
+#define MAX_MEM_FOR_RS_ALIGN_CODE (1 + 2)
+
+struct insn_label_list;
+struct mips_segment_info {
+ struct insn_label_list *labels;
+ unsigned int mips16 : 1;
+};
+#define TC_SEGMENT_INFO_TYPE struct mips_segment_info
+
+/* This field is nonzero if the symbol is the target of a MIPS16 jump. */
+#define TC_SYMFIELD_TYPE int
+
+/* The endianness of the target format may change based on command
+ line arguments. */
+#define TARGET_FORMAT mips_target_format()
+extern const char *mips_target_format (void);
+
+#define md_after_parse_args() mips_after_parse_args()
+extern void mips_after_parse_args (void);
+
+#define tc_init_after_args() mips_init_after_args()
+extern void mips_init_after_args (void);
+
+#define md_parse_long_option(arg) mips_parse_long_option (arg)
+extern int mips_parse_long_option (const char *);
+
+#define tc_frob_label(sym) mips_define_label (sym)
+extern void mips_define_label (symbolS *);
+
+#define tc_fix_adjustable(fixp) mips_fix_adjustable (fixp)
+extern int mips_fix_adjustable (struct fix *);
+
+/* Values passed to md_apply_fix don't include symbol values. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+/* Global syms must not be resolved, to support ELF shared libraries. */
+#define EXTERN_FORCE_RELOC \
+ (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+
+/* When generating NEWABI code, we may need to have to keep combined
+ relocations which don't have symbols. */
+#define TC_FORCE_RELOCATION(FIX) mips_force_relocation (FIX)
+extern int mips_force_relocation (struct fix *);
+
+#define TC_FORCE_RELOCATION_SUB_SAME(FIX, SEG) \
+ (! SEG_NORMAL (SEG) || mips_force_relocation (FIX))
+
+/* Register mask variables. These are set by the MIPS assembly code
+ and used by ECOFF and possibly other object file formats. */
+extern unsigned long mips_gprmask;
+extern unsigned long mips_cprmask[4];
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+
+#define elf_tc_final_processing mips_elf_final_processing
+extern void mips_elf_final_processing (void);
+
+#endif
+
+extern void mips_pop_insert (void);
+#define md_pop_insert() mips_pop_insert()
+
+extern void mips_clear_insn_labels (void);
+#define md_flush_pending_output mips_clear_insn_labels
+
+extern void mips_enable_auto_align (void);
+#define md_elf_section_change_hook() mips_enable_auto_align()
+
+enum dwarf2_format;
+extern enum dwarf2_format mips_dwarf2_format (asection *);
+#define DWARF2_FORMAT(SEC) mips_dwarf2_format (SEC)
+
+extern int mips_dwarf2_addr_size (void);
+#define DWARF2_ADDR_SIZE(bfd) mips_dwarf2_addr_size ()
+#define DWARF2_FDE_RELOC_SIZE mips_dwarf2_addr_size ()
+
+#define TARGET_USE_CFIPOP 1
+
+#define tc_cfi_frame_initial_instructions mips_cfi_frame_initial_instructions
+extern void mips_cfi_frame_initial_instructions (void);
+
+#define tc_regname_to_dw2regnum tc_mips_regname_to_dw2regnum
+extern int tc_mips_regname_to_dw2regnum (char *regname);
+
+#define DWARF2_DEFAULT_RETURN_COLUMN LINK_REG
+#define DWARF2_CIE_DATA_ALIGNMENT (-4)
+
+#endif /* TC_RISCV */
diff -ruN binutils-2.24/gas/configure.tgt binutils-2.24-riscv/gas/configure.tgt
--- binutils-2.24/gas/configure.tgt 2013-11-04 07:33:37.000000000 -0800
+++ binutils-2.24-riscv/gas/configure.tgt 2014-12-02 16:05:44.913434966 -0800
@@ -84,6 +84,7 @@
pj*) cpu_type=pj endian=big ;;
powerpc*le*) cpu_type=ppc endian=little ;;
powerpc*) cpu_type=ppc endian=big ;;
+ riscv*) cpu_type=riscv endian=little ;;
rs6000*) cpu_type=ppc ;;
rl78*) cpu_type=rl78 ;;
rx) cpu_type=rx ;;
@@ -375,6 +376,8 @@
ppc-*-kaos*) fmt=elf ;;
ppc-*-lynxos*) fmt=elf em=lynx ;;
+ riscv*-*-*) fmt=elf endian=little em=linux bfd_gas=yes ;;
+
s390-*-linux-*) fmt=elf em=linux ;;
s390-*-tpf*) fmt=elf ;;
diff -ruN binutils-2.24/gas/Makefile.am binutils-2.24-riscv/gas/Makefile.am
--- binutils-2.24/gas/Makefile.am 2013-11-04 07:33:37.000000000 -0800
+++ binutils-2.24-riscv/gas/Makefile.am 2014-12-02 16:14:51.804849445 -0800
@@ -171,6 +171,7 @@
config/tc-pdp11.c \
config/tc-pj.c \
config/tc-ppc.c \
+ config/tc-riscv.c \
config/tc-rl78.c \
config/tc-rx.c \
config/tc-s390.c \
@@ -242,6 +243,7 @@
config/tc-pdp11.h \
config/tc-pj.h \
config/tc-ppc.h \
+ config/tc-riscv.h \
config/tc-rl78.h \
config/tc-rx.h \
config/tc-s390.h \
diff -ruN binutils-2.24/gas/Makefile.in binutils-2.24-riscv/gas/Makefile.in
--- binutils-2.24/gas/Makefile.in 2013-11-04 07:33:37.000000000 -0800
+++ binutils-2.24-riscv/gas/Makefile.in 2014-12-02 16:16:06.621317359 -0800
@@ -440,6 +440,7 @@
config/tc-pdp11.c \
config/tc-pj.c \
config/tc-ppc.c \
+ config/tc-riscv.c \
config/tc-rl78.c \
config/tc-rx.c \
config/tc-s390.c \
@@ -511,6 +512,7 @@
config/tc-pdp11.h \
config/tc-pj.h \
config/tc-ppc.h \
+ config/tc-riscv.h \
config/tc-rl78.h \
config/tc-rx.h \
config/tc-s390.h \
@@ -861,6 +863,7 @@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-pdp11.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-pj.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-ppc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-riscv.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-rl78.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-rx.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-s390.Po@am__quote@
@@ -1580,6 +1583,20 @@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tc-rl78.obj `if test -f 'config/tc-rl78.c'; then $(CYGPATH_W) 'config/tc-rl78.c'; else $(CYGPATH_W) '$(srcdir)/config/tc-rl78.c'; fi`
+tc-riscv.o: config/tc-riscv.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tc-riscv.o -MD -MP -MF $(DEPDIR)/tc-riscv.Tpo -c -o tc-riscv.o `test -f 'config/tc-riscv.c' || echo '$(srcdir)/'`config/tc-riscv.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/tc-riscv.Tpo $(DEPDIR)/tc-riscv.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/tc-riscv.c' object='tc-riscv.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tc-riscv.o `test -f 'config/tc-riscv.c' || echo '$(srcdir)/'`config/tc-riscv.c
+
+tc-riscv.obj: config/tc-riscv.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tc-riscv.obj -MD -MP -MF $(DEPDIR)/tc-riscv.Tpo -c -o tc-riscv.obj `if test -f 'config/tc-riscv.c'; then $(CYGPATH_W) 'config/tc-riscv.c'; else $(CYGPATH_W) '$(srcdir)/config/tc-riscv.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/tc-riscv.Tpo $(DEPDIR)/tc-riscv.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/tc-riscv.c' object='tc-riscv.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tc-riscv.obj `if test -f 'config/tc-riscv.c'; then $(CYGPATH_W) 'config/tc-riscv.c'; else $(CYGPATH_W) '$(srcdir)/config/tc-riscv.c'; fi`
+
tc-rx.o: config/tc-rx.c
@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tc-rx.o -MD -MP -MF $(DEPDIR)/tc-rx.Tpo -c -o tc-rx.o `test -f 'config/tc-rx.c' || echo '$(srcdir)/'`config/tc-rx.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/tc-rx.Tpo $(DEPDIR)/tc-rx.Po
diff -ruN binutils-2.24/include/dis-asm.h binutils-2.24-riscv/include/dis-asm.h
--- binutils-2.24/include/dis-asm.h 2013-11-04 07:33:39.000000000 -0800
+++ binutils-2.24-riscv/include/dis-asm.h 2014-12-02 16:05:44.913434966 -0800
@@ -229,6 +229,7 @@
extern int print_insn_big_nios2 (bfd_vma, disassemble_info *);
extern int print_insn_big_or32 (bfd_vma, disassemble_info *);
extern int print_insn_big_powerpc (bfd_vma, disassemble_info *);
+extern int print_insn_big_riscv (bfd_vma, disassemble_info *);
extern int print_insn_big_score (bfd_vma, disassemble_info *);
extern int print_insn_cr16 (bfd_vma, disassemble_info *);
extern int print_insn_crx (bfd_vma, disassemble_info *);
@@ -257,6 +258,7 @@
extern int print_insn_little_nios2 (bfd_vma, disassemble_info *);
extern int print_insn_little_or32 (bfd_vma, disassemble_info *);
extern int print_insn_little_powerpc (bfd_vma, disassemble_info *);
+extern int print_insn_little_riscv (bfd_vma, disassemble_info *);
extern int print_insn_little_score (bfd_vma, disassemble_info *);
extern int print_insn_lm32 (bfd_vma, disassemble_info *);
extern int print_insn_m32c (bfd_vma, disassemble_info *);
diff -ruN binutils-2.24/include/elf/common.h binutils-2.24-riscv/include/elf/common.h
--- binutils-2.24/include/elf/common.h 2013-11-04 07:33:39.000000000 -0800
+++ binutils-2.24-riscv/include/elf/common.h 2014-12-02 16:21:36.427380014 -0800
@@ -301,6 +301,7 @@
#define EM_INTEL207 207 /* Reserved by Intel */
#define EM_INTEL208 208 /* Reserved by Intel */
#define EM_INTEL209 209 /* Reserved by Intel */
+#define EM_RISCV 243 /* RISC-V */
/* If it is necessary to assign new unofficial EM_* values, please pick large
random numbers (0x8523, 0xa7f2, etc.) to minimize the chances of collision
diff -ruN binutils-2.24/include/elf/riscv.h binutils-2.24-riscv/include/elf/riscv.h
--- binutils-2.24/include/elf/riscv.h 1969-12-31 16:00:00.000000000 -0800
+++ binutils-2.24-riscv/include/elf/riscv.h 2014-12-02 16:05:44.913434966 -0800
@@ -0,0 +1,484 @@
+/* RISC-V ELF support for BFD.
+ Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+ 2003, 2004, 2005, 2008, 2009, 2010
+ Free Software Foundation, Inc.
+
+ By Andrew Waterman, University of California,
+ <waterman@eecs.berkeley.edu>.
+ Based on MIPS ELF support for BFD, by Ian Lance Taylor.
+
+ 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; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+/* This file holds definitions specific to the RISCV ELF ABI. Note
+ that most of this is not actually implemented by BFD. */
+
+#ifndef _ELF_RISCV_H
+#define _ELF_RISCV_H
+
+#include "elf/reloc-macros.h"
+
+/* Relocation types. */
+START_RELOC_NUMBERS (elf_riscv_reloc_type)
+ RELOC_NUMBER (R_RISCV_NONE, 0)
+ RELOC_NUMBER (R_RISCV_32, 2) /* In Elf 64: alias R_RISCV_ADD */
+ RELOC_NUMBER (R_RISCV_REL32, 3) /* In Elf 64: alias R_RISCV_REL */
+ RELOC_NUMBER (R_RISCV_26, 4)
+ RELOC_NUMBER (R_RISCV_HI16, 5)
+ RELOC_NUMBER (R_RISCV_LO16, 6)
+ RELOC_NUMBER (R_RISCV_GPREL16, 7) /* In Elf 64: alias R_RISCV_GPREL */
+ RELOC_NUMBER (R_RISCV_LITERAL, 8)
+ RELOC_NUMBER (R_RISCV_GOT16, 9) /* In Elf 64: alias R_RISCV_GOT */
+ RELOC_NUMBER (R_RISCV_PC16, 10)
+ RELOC_NUMBER (R_RISCV_CALL16, 11) /* In Elf 64: alias R_RISCV_CALL */
+ RELOC_NUMBER (R_RISCV_GPREL32, 12)
+ /* The remaining relocs are defined on Irix, although they are not
+ in the RISC-V ELF ABI. */
+ RELOC_NUMBER (R_RISCV_UNUSED1, 13)
+ RELOC_NUMBER (R_RISCV_UNUSED2, 14)
+ RELOC_NUMBER (R_RISCV_UNUSED3, 15)
+
+ RELOC_NUMBER (R_RISCV_64, 18)
+ RELOC_NUMBER (R_RISCV_GOT_DISP, 19)
+ RELOC_NUMBER (R_RISCV_GOT_HI16, 22)
+ RELOC_NUMBER (R_RISCV_GOT_LO16, 23)
+ RELOC_NUMBER (R_RISCV_SUB, 24)
+ RELOC_NUMBER (R_RISCV_INSERT_A, 25)
+ RELOC_NUMBER (R_RISCV_INSERT_B, 26)
+ RELOC_NUMBER (R_RISCV_DELETE, 27)
+ RELOC_NUMBER (R_RISCV_CALL_HI16, 30)
+ RELOC_NUMBER (R_RISCV_CALL_LO16, 31)
+ RELOC_NUMBER (R_RISCV_SCN_DISP, 32)
+ RELOC_NUMBER (R_RISCV_REL16, 33)
+ RELOC_NUMBER (R_RISCV_ADD_IMMEDIATE, 34)
+ RELOC_NUMBER (R_RISCV_PJUMP, 35)
+ RELOC_NUMBER (R_RISCV_RELGOT, 36)
+ /* TLS relocations. */
+ RELOC_NUMBER (R_RISCV_TLS_DTPMOD32, 38)
+ RELOC_NUMBER (R_RISCV_TLS_DTPREL32, 39)
+ RELOC_NUMBER (R_RISCV_TLS_DTPMOD64, 40)
+ RELOC_NUMBER (R_RISCV_TLS_DTPREL64, 41)
+ RELOC_NUMBER (R_RISCV_TLS_GD, 42)
+ RELOC_NUMBER (R_RISCV_TLS_LDM, 43)
+ RELOC_NUMBER (R_RISCV_TLS_DTPREL_HI16, 44)
+ RELOC_NUMBER (R_RISCV_TLS_DTPREL_LO16, 45)
+ RELOC_NUMBER (R_RISCV_TLS_GOTTPREL, 46)
+ RELOC_NUMBER (R_RISCV_TLS_TPREL32, 47)
+ RELOC_NUMBER (R_RISCV_TLS_TPREL64, 48)
+ RELOC_NUMBER (R_RISCV_TLS_TPREL_HI16, 49)
+ RELOC_NUMBER (R_RISCV_TLS_TPREL_LO16, 50)
+ RELOC_NUMBER (R_RISCV_TLS_GOT_HI16, 51)
+ RELOC_NUMBER (R_RISCV_TLS_GOT_LO16, 52)
+ RELOC_NUMBER (R_RISCV_TLS_GD_HI16, 53)
+ RELOC_NUMBER (R_RISCV_TLS_GD_LO16, 54)
+ RELOC_NUMBER (R_RISCV_TLS_LDM_HI16, 55)
+ RELOC_NUMBER (R_RISCV_TLS_LDM_LO16, 56)
+ RELOC_NUMBER (R_RISCV_GLOB_DAT, 57)
+ FAKE_RELOC (R_RISCV_max, 58)
+ /* These relocations are specific to VxWorks. */
+ RELOC_NUMBER (R_RISCV_COPY, 126)
+ RELOC_NUMBER (R_RISCV_JUMP_SLOT, 127)
+ /* This was a GNU extension used by embedded-PIC. It was co-opted by
+ riscv-linux for exception-handling data. It is no longer used, but
+ should continue to be supported by the linker for backward
+ compatibility. (GCC stopped using it in May, 2004.) */
+ RELOC_NUMBER (R_RISCV_PC32, 248)
+ /* FIXME: this relocation is used internally by gas. */
+ RELOC_NUMBER (R_RISCV_GNU_REL16_S2, 250)
+ /* These are GNU extensions to enable C++ vtable garbage collection. */
+ RELOC_NUMBER (R_RISCV_GNU_VTINHERIT, 253)
+ RELOC_NUMBER (R_RISCV_GNU_VTENTRY, 254)
+END_RELOC_NUMBERS (R_RISCV_maxext)
+
+/* Processor specific flags for the ELF header e_flags field. */
+
+/* File contains position independent code. */
+#define EF_RISCV_PIC 0x00000002
+
+/* Process the .RISCV.options section first by ld */
+#define EF_RISCV_OPTIONS_FIRST 0x00000080
+
+/* Architectural Extensions used by this file */
+#define EF_RISCV_ARCH_ASE 0x0f000000
+
+/* Four bit RISCV architecture field. */
+#define EF_RISCV_ARCH 0xf0000000
+
+/* RV32 code. */
+#define E_RISCV_ARCH_RV32 0x10000000
+
+/* RV64 code. */
+#define E_RISCV_ARCH_RV64 0x20000000
+
+/* The ABI of the file. Also see EF_RISCV_ABI2 above. */
+#define EF_RISCV_ABI 0x0000f000
+
+/* The 32-bit abi. */
+#define E_RISCV_ABI_32 0x00001000
+
+/* The 64-bit abi. */
+#define E_RISCV_ABI_64 0x00002000
+
+/* Machine variant if we know it. This field was invented at Cygnus,
+ but it is hoped that other vendors will adopt it. If some standard
+ is developed, this code should be changed to follow it. */
+
+#define EF_RISCV_MACH 0x00ff0000
+
+/* Cygnus is choosing values between 80 and 9F;
+ 00 - 7F should be left for a future standard;
+ the rest are open. */
+
+#define E_RISCV_MACH_ROCKET32 0x00810000
+#define E_RISCV_MACH_ROCKET64 0x00820000
+
+/* Processor specific section indices. These sections do not actually
+ exist. Symbols with a st_shndx field corresponding to one of these
+ values have a special meaning. */
+
+/* Defined and allocated common symbol. Value is virtual address. If
+ relocated, alignment must be preserved. */
+#define SHN_RISCV_ACOMMON SHN_LORESERVE
+
+/* Defined and allocated text symbol. Value is virtual address.
+ Occur in the dynamic symbol table of Alpha OSF/1 and Irix 5 executables. */
+#define SHN_RISCV_TEXT (SHN_LORESERVE + 1)
+
+/* Defined and allocated data symbol. Value is virtual address.
+ Occur in the dynamic symbol table of Alpha OSF/1 and Irix 5 executables. */
+#define SHN_RISCV_DATA (SHN_LORESERVE + 2)
+
+/* Small common symbol. */
+#define SHN_RISCV_SCOMMON (SHN_LORESERVE + 3)
+
+/* Small undefined symbol. */
+#define SHN_RISCV_SUNDEFINED (SHN_LORESERVE + 4)
+
+/* Processor specific section types. */
+
+/* Section contains the set of dynamic shared objects used when
+ statically linking. */
+#define SHT_RISCV_LIBLIST 0x70000000
+
+/* Section contains list of symbols whose definitions conflict with
+ symbols defined in shared objects. */
+#define SHT_RISCV_CONFLICT 0x70000002
+
+/* Section contains the global pointer table. */
+#define SHT_RISCV_GPTAB 0x70000003
+
+/* Section contains some sort of debugging information. The exact
+ format is unspecified. It's probably ECOFF symbols. */
+#define SHT_RISCV_DEBUG 0x70000005
+
+/* Section contains register usage information. */
+#define SHT_RISCV_REGINFO 0x70000006
+
+/* Section contains interface information. */
+#define SHT_RISCV_IFACE 0x7000000b
+
+/* Section contains description of contents of another section. */
+#define SHT_RISCV_CONTENT 0x7000000c
+
+/* Section contains miscellaneous options. */
+#define SHT_RISCV_OPTIONS 0x7000000d
+
+/* List of libraries the binary depends on. Includes a time stamp, version
+ number. */
+#define SHT_RISCV_SYMBOL_LIB 0x70000020
+
+/* Events section. */
+#define SHT_RISCV_EVENTS 0x70000021
+
+/* Get ELf32_xxx struct definitions */
+#include "mips.h"
+
+/* RISC-V ELF .reginfo swapping routines. */
+extern void bfd_riscv_elf32_swap_reginfo_in
+ (bfd *, const Elf32_External_RegInfo *, Elf32_RegInfo *);
+extern void bfd_riscv_elf32_swap_reginfo_out
+ (bfd *, const Elf32_RegInfo *, Elf32_External_RegInfo *);
+
+/* Processor specific section flags. */
+
+/* This section must be in the global data area. */
+#define SHF_RISCV_GPREL 0x10000000
+
+/* This section may not be stripped. */
+#define SHF_RISCV_NOSTRIP 0x08000000
+
+/* Processor specific program header types. */
+
+/* Register usage information. Identifies one .reginfo section. */
+#define PT_RISCV_REGINFO 0x70000000
+
+/* Runtime procedure table. */
+#define PT_RISCV_RTPROC 0x70000001
+
+/* .RISCV.options section. */
+#define PT_RISCV_OPTIONS 0x70000002
+
+/* Processor specific dynamic array tags. */
+
+/* 32 bit version number for runtime linker interface. */
+#define DT_RISCV_RLD_VERSION 0x70000001
+
+/* Time stamp. */
+#define DT_RISCV_TIME_STAMP 0x70000002
+
+/* Checksum of external strings and common sizes. */
+#define DT_RISCV_ICHECKSUM 0x70000003
+
+/* Index of version string in string table. */
+#define DT_RISCV_IVERSION 0x70000004
+
+/* 32 bits of flags. */
+#define DT_RISCV_FLAGS 0x70000005
+
+/* Base address of the segment. */
+#define DT_RISCV_BASE_ADDRESS 0x70000006
+
+/* ??? */
+#define DT_RISCV_MSYM 0x70000007
+
+/* Address of .conflict section. */
+#define DT_RISCV_CONFLICT 0x70000008
+
+/* Address of .liblist section. */
+#define DT_RISCV_LIBLIST 0x70000009
+
+/* Number of local global offset table entries. */
+#define DT_RISCV_LOCAL_GOTNO 0x7000000a
+
+/* Number of entries in the .conflict section. */
+#define DT_RISCV_CONFLICTNO 0x7000000b
+
+/* Number of entries in the .liblist section. */
+#define DT_RISCV_LIBLISTNO 0x70000010
+
+/* Number of entries in the .dynsym section. */
+#define DT_RISCV_SYMTABNO 0x70000011
+
+/* Index of first external dynamic symbol not referenced locally. */
+#define DT_RISCV_UNREFEXTNO 0x70000012
+
+/* Index of first dynamic symbol in global offset table. */
+#define DT_RISCV_GOTSYM 0x70000013
+
+/* Number of page table entries in global offset table. */
+#define DT_RISCV_HIPAGENO 0x70000014
+
+/* Address of run time loader map, used for debugging. */
+#define DT_RISCV_RLD_MAP 0x70000016
+
+/* Delta C++ class definition. */
+#define DT_RISCV_DELTA_CLASS 0x70000017
+
+/* Number of entries in DT_RISCV_DELTA_CLASS. */
+#define DT_RISCV_DELTA_CLASS_NO 0x70000018
+
+/* Delta C++ class instances. */
+#define DT_RISCV_DELTA_INSTANCE 0x70000019
+
+/* Number of entries in DT_RISCV_DELTA_INSTANCE. */
+#define DT_RISCV_DELTA_INSTANCE_NO 0x7000001a
+
+/* Delta relocations. */
+#define DT_RISCV_DELTA_RELOC 0x7000001b
+
+/* Number of entries in DT_RISCV_DELTA_RELOC. */
+#define DT_RISCV_DELTA_RELOC_NO 0x7000001c
+
+/* Delta symbols that Delta relocations refer to. */
+#define DT_RISCV_DELTA_SYM 0x7000001d
+
+/* Number of entries in DT_RISCV_DELTA_SYM. */
+#define DT_RISCV_DELTA_SYM_NO 0x7000001e
+
+/* Delta symbols that hold class declarations. */
+#define DT_RISCV_DELTA_CLASSSYM 0x70000020
+
+/* Number of entries in DT_RISCV_DELTA_CLASSSYM. */
+#define DT_RISCV_DELTA_CLASSSYM_NO 0x70000021
+
+/* Flags indicating information about C++ flavor. */
+#define DT_RISCV_CXX_FLAGS 0x70000022
+
+/* Pixie information (???). */
+#define DT_RISCV_PIXIE_INIT 0x70000023
+
+/* Address of .RISCV.symlib */
+#define DT_RISCV_SYMBOL_LIB 0x70000024
+
+/* The GOT index of the first PTE for a segment */
+#define DT_RISCV_LOCALPAGE_GOTIDX 0x70000025
+
+/* The GOT index of the first PTE for a local symbol */
+#define DT_RISCV_LOCAL_GOTIDX 0x70000026
+
+/* The GOT index of the first PTE for a hidden symbol */
+#define DT_RISCV_HIDDEN_GOTIDX 0x70000027
+
+/* The GOT index of the first PTE for a protected symbol */
+#define DT_RISCV_PROTECTED_GOTIDX 0x70000028
+
+/* Address of `.RISCV.options'. */
+#define DT_RISCV_OPTIONS 0x70000029
+
+/* Address of `.interface'. */
+#define DT_RISCV_INTERFACE 0x7000002a
+
+/* ??? */
+#define DT_RISCV_DYNSTR_ALIGN 0x7000002b
+
+/* Size of the .interface section. */
+#define DT_RISCV_INTERFACE_SIZE 0x7000002c
+
+/* Size of rld_text_resolve function stored in the GOT. */
+#define DT_RISCV_RLD_TEXT_RESOLVE_ADDR 0x7000002d
+
+/* Default suffix of DSO to be added by rld on dlopen() calls. */
+#define DT_RISCV_PERF_SUFFIX 0x7000002e
+
+/* Size of compact relocation section (O32). */
+#define DT_RISCV_COMPACT_SIZE 0x7000002f
+
+/* GP value for auxiliary GOTs. */
+#define DT_RISCV_GP_VALUE 0x70000030
+
+/* Address of auxiliary .dynamic. */
+#define DT_RISCV_AUX_DYNAMIC 0x70000031
+
+/* Address of the base of the PLTGOT. */
+#define DT_RISCV_PLTGOT 0x70000032
+
+/* Points to the base of a writable PLT. */
+#define DT_RISCV_RWPLT 0x70000034
+
+/* The RISC-V psABI was updated in 2008 with support for PLTs and copy
+ relocs. There are therefore two types of nonzero SHN_UNDEF functions:
+ PLT entries and traditional RISC-V lazy binding stubs. We mark the former
+ with STO_RISCV_PLT to distinguish them from the latter. */
+#define STO_RISCV_PLT 0x8
+
+/* This value is used to mark PIC functions in an object that mixes
+ PIC and non-PIC. */
+#define STO_RISCV_PIC 0x20
+#define ELF_ST_IS_RISCV_PIC(OTHER) \
+ (((OTHER) & ~ELF_ST_VISIBILITY (-1)) == STO_RISCV_PIC)
+#define ELF_ST_SET_RISCV_PIC(OTHER) \
+ (STO_RISCV_PIC | ELF_ST_VISIBILITY (OTHER))
+
+/* The 64-bit RISC-V ELF ABI uses an unusual reloc format. Each
+ relocation entry specifies up to three actual relocations, all at
+ the same address. The first relocation which required a symbol
+ uses the symbol in the r_sym field. The second relocation which
+ requires a symbol uses the symbol in the r_ssym field. If all
+ three relocations require a symbol, the third one uses a zero
+ value. */
+
+/* An entry in a 64 bit SHT_REL section. */
+
+typedef struct
+{
+ /* Address of relocation. */
+ unsigned char r_offset[8];
+ /* Symbol index. */
+ unsigned char r_sym[4];
+ /* Special symbol. */
+ unsigned char r_ssym[1];
+ /* Third relocation. */
+ unsigned char r_type3[1];
+ /* Second relocation. */
+ unsigned char r_type2[1];
+ /* First relocation. */
+ unsigned char r_type[1];
+} Elf64_RISCV_External_Rel;
+
+typedef struct
+{
+ /* Address of relocation. */
+ bfd_vma r_offset;
+ /* Symbol index. */
+ unsigned long r_sym;
+ /* Special symbol. */
+ unsigned char r_ssym;
+ /* Third relocation. */
+ unsigned char r_type3;
+ /* Second relocation. */
+ unsigned char r_type2;
+ /* First relocation. */
+ unsigned char r_type;
+} Elf64_RISCV_Internal_Rel;
+
+/* An entry in a 64 bit SHT_RELA section. */
+
+typedef struct
+{
+ /* Address of relocation. */
+ unsigned char r_offset[8];
+ /* Symbol index. */
+ unsigned char r_sym[4];
+ /* Special symbol. */
+ unsigned char r_ssym[1];
+ /* Third relocation. */
+ unsigned char r_type3[1];
+ /* Second relocation. */
+ unsigned char r_type2[1];
+ /* First relocation. */
+ unsigned char r_type[1];
+ /* Addend. */
+ unsigned char r_addend[8];
+} Elf64_RISCV_External_Rela;
+
+typedef struct
+{
+ /* Address of relocation. */
+ bfd_vma r_offset;
+ /* Symbol index. */
+ unsigned long r_sym;
+ /* Special symbol. */
+ unsigned char r_ssym;
+ /* Third relocation. */
+ unsigned char r_type3;
+ /* Second relocation. */
+ unsigned char r_type2;
+ /* First relocation. */
+ unsigned char r_type;
+ /* Addend. */
+ bfd_signed_vma r_addend;
+} Elf64_RISCV_Internal_Rela;
+
+/* RISC-V ELF 64 relocation info access macros. */
+#define ELF64_RISCV_R_SSYM(i) (((i) >> 24) & 0xff)
+#define ELF64_RISCV_R_TYPE3(i) (((i) >> 16) & 0xff)
+#define ELF64_RISCV_R_TYPE2(i) (((i) >> 8) & 0xff)
+#define ELF64_RISCV_R_TYPE(i) ((i) & 0xff)
+
+/* RISC-V ELF option header swapping routines. */
+extern void bfd_riscv_elf_swap_options_in
+ (bfd *, const Elf_External_Options *, Elf_Internal_Options *);
+extern void bfd_riscv_elf_swap_options_out
+ (bfd *, const Elf_Internal_Options *, Elf_External_Options *);
+
+/* RISC-V ELF reginfo swapping routines. */
+extern void bfd_riscv_elf64_swap_reginfo_in
+ (bfd *, const Elf64_External_RegInfo *, Elf64_Internal_RegInfo *);
+extern void bfd_riscv_elf64_swap_reginfo_out
+ (bfd *, const Elf64_Internal_RegInfo *, Elf64_External_RegInfo *);
+
+#endif /* _ELF_RISCV_H */
diff -ruN binutils-2.24/include/opcode/riscv.h binutils-2.24-riscv/include/opcode/riscv.h
--- binutils-2.24/include/opcode/riscv.h 1969-12-31 16:00:00.000000000 -0800
+++ binutils-2.24-riscv/include/opcode/riscv.h 2014-12-02 16:05:44.917434991 -0800
@@ -0,0 +1,257 @@
+/* riscv.h. RISC-V opcode list for GDB, the GNU debugger.
+ Copyright 2011
+ Free Software Foundation, Inc.
+ Contributed by Andrew Waterman
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+1, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+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 file; see the file COPYING. If not, write to the Free
+Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#ifndef _RISCV_H_
+#define _RISCV_H_
+
+/* RVC fields */
+
+#define OP_MASK_COP 0x1f
+#define OP_SH_COP 0
+#define OP_MASK_CRD 0x1f
+#define OP_SH_CRD 5
+#define OP_MASK_CRS2 0x1f
+#define OP_SH_CRS2 5
+#define OP_MASK_CRS1 0x1f
+#define OP_SH_CRS1 10
+#define OP_MASK_CRDS 0x7
+#define OP_SH_CRDS 13
+#define OP_MASK_CRS2S 0x7
+#define OP_SH_CRS2S 13
+#define OP_MASK_CRS2BS 0x7
+#define OP_SH_CRS2BS 5
+#define OP_MASK_CRS1S 0x7
+#define OP_SH_CRS1S 10
+#define OP_MASK_CIMM6 0x3f
+#define OP_SH_CIMM6 10
+#define OP_MASK_CIMM5 0x1f
+#define OP_SH_CIMM5 5
+#define OP_MASK_CIMM10 0x3ff
+#define OP_SH_CIMM10 5
+
+static const char rvc_rs1_regmap[8] = { 20, 21, 2, 3, 4, 5, 6, 7 };
+#define rvc_rd_regmap rvc_rs1_regmap
+#define rvc_rs2b_regmap rvc_rs1_regmap
+static const char rvc_rs2_regmap[8] = { 20, 21, 2, 3, 4, 5, 6, 0 };
+
+#define RVC_JUMP_BITS 10
+#define RVC_JUMP_ALIGN_BITS 1
+#define RVC_JUMP_ALIGN (1 << RVC_JUMP_ALIGN_BITS)
+#define RVC_JUMP_REACH ((1ULL<<RVC_JUMP_BITS)*RVC_JUMP_ALIGN)
+
+#define RVC_BRANCH_BITS 5
+#define RVC_BRANCH_ALIGN_BITS RVC_JUMP_ALIGN_BITS
+#define RVC_BRANCH_ALIGN (1 << RVC_BRANCH_ALIGN_BITS)
+#define RVC_BRANCH_REACH ((1ULL<<RVC_BRANCH_BITS)*RVC_BRANCH_ALIGN)
+
+#define RISCV_JTYPE(insn, target) \
+ ((MATCH_ ## insn) | (((target) & ((1<<RISCV_JUMP_BITS)-1)) << OP_SH_TARGET))
+#define RISCV_LTYPE(insn, rd, bigimm) \
+ ((MATCH_ ## insn) | ((rd) << OP_SH_RD) | (((bigimm) & ((1<<RISCV_BIGIMM_BITS)-1)) << OP_SH_BIGIMMEDIATE))
+#define RISCV_ITYPE(insn, rd, rs1, imm) \
+ ((MATCH_ ## insn) | ((rd) << OP_SH_RD) | ((rs1) << OP_SH_RS) | (((imm) & (RISCV_IMM_REACH-1)) << OP_SH_IMMEDIATE))
+#define RISCV_BTYPE(insn, rs1, rs2, imm) \
+ ((MATCH_ ## insn) | ((rs1) << OP_SH_RS) | ((rs2) << OP_SH_RT) | (((imm) & OP_MASK_IMMLO) << OP_SH_IMMLO) | ((((imm) >> RISCV_IMMLO_BITS) & OP_MASK_IMMHI) << OP_SH_IMMHI))
+#define RISCV_RTYPE(insn, rd, rs1, rs2) \
+ ((MATCH_ ## insn) | ((rd) << OP_SH_RD) | ((rs1) << OP_SH_RS) | ((rs2) << OP_SH_RT))
+
+#define RISCV_NOP RISCV_ITYPE(ADDI, 0, 0, 0)
+
+#define RISCV_JUMP_TARGET(address) ((address) >> RISCV_JUMP_ALIGN_BITS)
+#define RISCV_CONST_HIGH_PART(VALUE) \
+ (((VALUE) + (RISCV_IMM_REACH/2)) & ~(RISCV_IMM_REACH-1))
+#define RISCV_CONST_LOW_PART(VALUE) ((VALUE) - RISCV_CONST_HIGH_PART (VALUE))
+#define RISCV_LUI_HIGH_PART(VALUE) (RISCV_CONST_HIGH_PART(VALUE) >> RISCV_IMM_BITS)
+
+/* RV fields */
+
+#define OP_MASK_OP 0x7f
+#define OP_SH_OP 0
+#define OP_MASK_RT 0x1f
+#define OP_SH_RT 17
+#define OP_MASK_FT 0x1f
+#define OP_SH_FT 17
+#define OP_MASK_RS 0x1f
+#define OP_SH_RS 22
+#define OP_MASK_FS 0x1f
+#define OP_SH_FS 22
+#define OP_MASK_FR 0x1f
+#define OP_SH_FR 12
+#define OP_MASK_RD 0x1f
+#define OP_SH_RD 27
+#define OP_MASK_FD 0x1f
+#define OP_SH_FD 27
+#define OP_MASK_SHAMT 0x3f
+#define OP_SH_SHAMT 10
+#define OP_MASK_SHAMTW 0x1f
+#define OP_SH_SHAMTW 10
+#define OP_MASK_RM 0x7
+#define OP_SH_RM 9
+
+static const char * const riscv_rm[8] =
+ { "rne", "rtz", "rdn", "rup", "rmm", 0, 0, "dyn" };
+
+#define OP_MASK_VRD 0x1f
+#define OP_SH_VRD 27
+#define OP_MASK_VRS 0x1f
+#define OP_SH_VRS 22
+#define OP_MASK_VRT 0x1f
+#define OP_SH_VRT 17
+#define OP_MASK_VRR 0x1f
+#define OP_SH_VRR 12
+
+#define OP_MASK_VFD 0x1f
+#define OP_SH_VFD 27
+#define OP_MASK_VFS 0x1f
+#define OP_SH_VFS 22
+#define OP_MASK_VFT 0x1f
+#define OP_SH_VFT 17
+#define OP_MASK_VFR 0x1f
+#define OP_SH_VFR 12
+
+#define OP_MASK_IMMNGPR 0x3f
+#define OP_SH_IMMNGPR 10
+#define OP_MASK_IMMNFPR 0x3f
+#define OP_SH_IMMNFPR 16
+#define OP_MASK_IMMSEGNELM 0x1f
+#define OP_SH_IMMSEGNELM 17
+#define OP_MASK_IMMSEGSTNELM 0x1f
+#define OP_SH_IMMSEGSTNELM 12
+
+#define LINK_REG 1
+
+#define RISCV_JUMP_BITS 25
+#define RISCV_JUMP_ALIGN_BITS 1
+#define RISCV_JUMP_ALIGN (1 << RISCV_JUMP_ALIGN_BITS)
+#define RISCV_JUMP_REACH ((1ULL<<RISCV_JUMP_BITS)*RISCV_JUMP_ALIGN)
+
+#define OP_MASK_TARGET ((1<<RISCV_JUMP_BITS)-1)
+#define OP_SH_TARGET 7
+
+#define RISCV_IMM_BITS 12
+#define RISCV_IMMLO_BITS 7
+#define RISCV_IMMHI_BITS (RISCV_IMM_BITS - RISCV_IMMLO_BITS)
+#define RISCV_BIGIMM_BITS (32-RISCV_IMM_BITS)
+#define RISCV_IMM_REACH (1LL<<RISCV_IMM_BITS)
+#define RISCV_BIGIMM_REACH (1LL<<RISCV_BIGIMM_BITS)
+#define RISCV_BRANCH_BITS RISCV_IMM_BITS
+#define RISCV_BRANCH_ALIGN_BITS RISCV_JUMP_ALIGN_BITS
+#define RISCV_BRANCH_ALIGN (1 << RISCV_BRANCH_ALIGN_BITS)
+#define RISCV_BRANCH_REACH (RISCV_IMM_REACH*RISCV_BRANCH_ALIGN)
+
+#define OP_MASK_BIGIMMEDIATE ((1<<RISCV_BIGIMM_BITS)-1)
+#define OP_SH_BIGIMMEDIATE 7
+#define OP_MASK_IMMEDIATE ((1<<RISCV_IMM_BITS)-1)
+#define OP_SH_IMMEDIATE 10
+#define OP_MASK_IMMLO ((1<<RISCV_IMMLO_BITS)-1)
+#define OP_SH_IMMLO 10
+#define OP_MASK_IMMHI ((1<<(RISCV_IMM_BITS-RISCV_IMMLO_BITS))-1)
+#define OP_SH_IMMHI 27
+
+#include "riscv-opc.h"
+
+/* This structure holds information for a particular instruction. */
+
+struct riscv_opcode
+{
+ /* The name of the instruction. */
+ const char *name;
+ /* A string describing the arguments for this instruction. */
+ const char *args;
+ /* The basic opcode for the instruction. When assembling, this
+ opcode is modified by the arguments to produce the actual opcode
+ that is used. If pinfo is INSN_MACRO, then this is 0. */
+ unsigned long match;
+ /* If pinfo is not INSN_MACRO, then this is a bit mask for the
+ relevant portions of the opcode when disassembling. If the
+ actual opcode anded with the match field equals the opcode field,
+ then we have found the correct instruction. If pinfo is
+ INSN_MACRO, then this field is the macro identifier. */
+ unsigned long mask;
+ /* For a macro, this is INSN_MACRO. Otherwise, it is a collection
+ of bits describing the instruction, notably any relevant hazard
+ information. */
+ unsigned long pinfo;
+};
+
+#define INSN_WRITE_GPR_D 0x00000001
+#define INSN_WRITE_GPR_RA 0x00000004
+#define INSN_WRITE_FPR_D 0x00000008
+#define INSN_READ_GPR_S 0x00000040
+#define INSN_READ_GPR_T 0x00000080
+#define INSN_READ_FPR_S 0x00000100
+#define INSN_READ_FPR_T 0x00000200
+#define INSN_READ_FPR_R 0x00000400
+/* Instruction is a simple alias (I.E. "move" for daddu/addu/or) */
+#define INSN_ALIAS 0x00001000
+/* Instruction is actually a macro. It should be ignored by the
+ disassembler, and requires special treatment by the assembler. */
+#define INSN_MACRO 0xffffffff
+
+/* These are the bits which may be set in the pinfo2 field of an
+ instruction. */
+
+/* MIPS ISA defines, use instead of hardcoding ISA level. */
+
+#define ISA_UNKNOWN 0 /* Gas internal use. */
+#define ISA_RV32 1
+#define ISA_RV64 2
+
+#define CPU_UNKNOWN 0
+#define CPU_ROCKET32 132
+#define CPU_ROCKET64 164
+
+/* This is a list of macro expanded instructions.
+
+ _I appended means immediate
+ _A appended means address
+ _AB appended means address with base register
+ _D appended means 64 bit floating point constant
+ _S appended means 32 bit floating point constant. */
+
+enum
+{
+ M_LA_AB,
+ M_LA_TLS_GD,
+ M_LA_TLS_IE,
+ M_J,
+ M_LI,
+ M_NUM_MACROS
+};
+
+
+/* The order of overloaded instructions matters. Label arguments and
+ register arguments look the same. Instructions that can have either
+ for arguments must apear in the correct order in this table for the
+ assembler to pick the right one. In other words, entries with
+ immediate operands must apear after the same instruction with
+ registers.
+
+ Many instructions are short hand for other instructions (i.e., The
+ jal <register> instruction is short for jalr <register>). */
+
+extern const struct riscv_opcode riscv_builtin_opcodes[];
+extern const int bfd_riscv_num_builtin_opcodes;
+extern struct riscv_opcode *riscv_opcodes;
+extern int bfd_riscv_num_opcodes;
+#define NUMOPCODES bfd_riscv_num_opcodes
+
+#endif /* _MIPS_H_ */
diff -ruN binutils-2.24/include/opcode/riscv-opc.h binutils-2.24-riscv/include/opcode/riscv-opc.h
--- binutils-2.24/include/opcode/riscv-opc.h 1969-12-31 16:00:00.000000000 -0800
+++ binutils-2.24-riscv/include/opcode/riscv-opc.h 2014-12-02 16:05:44.917434991 -0800
@@ -0,0 +1,571 @@
+/* Automatically generated by parse-opcodes */
+#define MATCH_MOVN 0x6f7
+#define MASK_MOVN 0x1ffff
+#define MATCH_VFSSTW 0x150f
+#define MASK_VFSSTW 0x1ffff
+#define MATCH_REMUW 0x7bb
+#define MASK_REMUW 0x1ffff
+#define MATCH_FMIN_D 0x180d3
+#define MASK_FMIN_D 0x1ffff
+#define MATCH_LR_W 0x1012b
+#define MASK_LR_W 0x3fffff
+#define MATCH_VLSTHU 0x128b
+#define MASK_VLSTHU 0x1ffff
+#define MATCH_C_SWSP 0x8
+#define MASK_C_SWSP 0x1f
+#define MATCH_BLTU 0x363
+#define MASK_BLTU 0x3ff
+#define MATCH_VLSEGSTWU 0xb0b
+#define MASK_VLSEGSTWU 0xfff
+#define MATCH_VVCFG 0x473
+#define MASK_VVCFG 0xf801ffff
+#define MATCH_MOVZ 0x2f7
+#define MASK_MOVZ 0x1ffff
+#define MATCH_C_LD 0x9
+#define MASK_C_LD 0x1f
+#define MATCH_C_SRLI32 0xc19
+#define MASK_C_SRLI32 0x1c1f
+#define MATCH_FMIN_S 0x18053
+#define MASK_FMIN_S 0x1ffff
+#define MATCH_C_LW0 0x12
+#define MASK_C_LW0 0x801f
+#define MATCH_SLLIW 0x9b
+#define MASK_SLLIW 0x3f83ff
+#define MATCH_LB 0x3
+#define MASK_LB 0x3ff
+#define MATCH_VLWU 0x30b
+#define MASK_VLWU 0x3fffff
+#define MATCH_FCVT_S_WU 0xf053
+#define MASK_FCVT_S_WU 0x3ff1ff
+#define MATCH_FCVT_D_L 0xc0d3
+#define MASK_FCVT_D_L 0x3ff1ff
+#define MATCH_LH 0x83
+#define MASK_LH 0x3ff
+#define MATCH_FCVT_D_W 0xe0d3
+#define MASK_FCVT_D_W 0x3ff1ff
+#define MATCH_LW 0x103
+#define MASK_LW 0x3ff
+#define MATCH_ADD 0x33
+#define MASK_ADD 0x1ffff
+#define MATCH_FCVT_D_S 0x100d3
+#define MASK_FCVT_D_S 0x3ff1ff
+#define MATCH_MFPCR 0x17b
+#define MASK_MFPCR 0x3fffff
+#define MATCH_C_FSD 0x18
+#define MASK_C_FSD 0x1f
+#define MATCH_FMAX_D 0x190d3
+#define MASK_FMAX_D 0x1ffff
+#define MATCH_BNE 0xe3
+#define MASK_BNE 0x3ff
+#define MATCH_RDCYCLE 0x277
+#define MASK_RDCYCLE 0x7ffffff
+#define MATCH_FCVT_S_D 0x11053
+#define MASK_FCVT_S_D 0x3ff1ff
+#define MATCH_VLH 0x8b
+#define MASK_VLH 0x3fffff
+#define MATCH_BGEU 0x3e3
+#define MASK_BGEU 0x3ff
+#define MATCH_VFLSTD 0x158b
+#define MASK_VFLSTD 0x1ffff
+#define MATCH_C_LI 0x0
+#define MASK_C_LI 0x1f
+#define MATCH_FADD_D 0xd3
+#define MASK_FADD_D 0x1f1ff
+#define MATCH_SLTIU 0x193
+#define MASK_SLTIU 0x3ff
+#define MATCH_MTPCR 0x1fb
+#define MASK_MTPCR 0x1ffff
+#define MATCH_VLB 0xb
+#define MASK_VLB 0x3fffff
+#define MATCH_STOP 0x177
+#define MASK_STOP 0xffffffff
+#define MATCH_VLD 0x18b
+#define MASK_VLD 0x3fffff
+#define MATCH_C_SLLI 0x19
+#define MASK_C_SLLI 0x1c1f
+#define MATCH_BREAK 0xf7
+#define MASK_BREAK 0xffffffff
+#define MATCH_CFLUSH 0x2fb
+#define MASK_CFLUSH 0xffffffff
+#define MATCH_FCVT_S_W 0xe053
+#define MASK_FCVT_S_W 0x3ff1ff
+#define MATCH_VFLSTW 0x150b
+#define MASK_VFLSTW 0x1ffff
+#define MATCH_MUL 0x433
+#define MASK_MUL 0x1ffff
+#define MATCH_C_LW 0xa
+#define MASK_C_LW 0x1f
+#define MATCH_VXCPTEVAC 0x237b
+#define MASK_VXCPTEVAC 0xf83fffff
+#define MATCH_VLW 0x10b
+#define MASK_VLW 0x3fffff
+#define MATCH_VSSEGSTW 0x90f
+#define MASK_VSSEGSTW 0xfff
+#define MATCH_AMOMINU_D 0x19ab
+#define MASK_AMOMINU_D 0x1ffff
+#define MATCH_C_SDSP 0x6
+#define MASK_C_SDSP 0x1f
+#define MATCH_UTIDX 0x1f7
+#define MASK_UTIDX 0x7ffffff
+#define MATCH_SRLI 0x293
+#define MASK_SRLI 0x3f03ff
+#define MATCH_C_SRLI 0x819
+#define MASK_C_SRLI 0x1c1f
+#define MATCH_C_LDSP 0x4
+#define MASK_C_LDSP 0x1f
+#define MATCH_C_FLW 0x14
+#define MASK_C_FLW 0x1f
+#define MATCH_C_SRAI32 0x1419
+#define MASK_C_SRAI32 0x1c1f
+#define MATCH_AMOMINU_W 0x192b
+#define MASK_AMOMINU_W 0x1ffff
+#define MATCH_DIVUW 0x6bb
+#define MASK_DIVUW 0x1ffff
+#define MATCH_MULW 0x43b
+#define MASK_MULW 0x1ffff
+#define MATCH_VSSEGSTD 0x98f
+#define MASK_VSSEGSTD 0xfff
+#define MATCH_SRLW 0x2bb
+#define MASK_SRLW 0x1ffff
+#define MATCH_VSSEGSTB 0x80f
+#define MASK_VSSEGSTB 0xfff
+#define MATCH_MFTX_D 0x1c0d3
+#define MASK_MFTX_D 0x3fffff
+#define MATCH_DIV 0x633
+#define MASK_DIV 0x1ffff
+#define MATCH_VTCFG 0xc73
+#define MASK_VTCFG 0xf801ffff
+#define MATCH_MFTX_S 0x1c053
+#define MASK_MFTX_S 0x3fffff
+#define MATCH_VSSEGSTH 0x88f
+#define MASK_VSSEGSTH 0xfff
+#define MATCH_VVCFGIVL 0xf3
+#define MASK_VVCFGIVL 0x3ff
+#define MATCH_J 0x67
+#define MASK_J 0x7f
+#define MATCH_FENCE 0x12f
+#define MASK_FENCE 0x3ff
+#define MATCH_VSW 0x10f
+#define MASK_VSW 0x3fffff
+#define MATCH_FNMSUB_S 0x4b
+#define MASK_FNMSUB_S 0x1ff
+#define MATCH_VFSSEGSTD 0xd8f
+#define MASK_VFSSEGSTD 0xfff
+#define MATCH_FCVT_L_S 0x8053
+#define MASK_FCVT_L_S 0x3ff1ff
+#define MATCH_FLE_S 0x17053
+#define MASK_FLE_S 0x1ffff
+#define MATCH_FENCE_V_L 0x22f
+#define MASK_FENCE_V_L 0x3ff
+#define MATCH_VSB 0xf
+#define MASK_VSB 0x3fffff
+#define MATCH_MFFSR 0x1d053
+#define MASK_MFFSR 0x7ffffff
+#define MATCH_FDIV_S 0x3053
+#define MASK_FDIV_S 0x1f1ff
+#define MATCH_VLSTBU 0x120b
+#define MASK_VLSTBU 0x1ffff
+#define MATCH_VSETVL 0x2f3
+#define MASK_VSETVL 0x3fffff
+#define MATCH_FLE_D 0x170d3
+#define MASK_FLE_D 0x1ffff
+#define MATCH_FENCE_I 0xaf
+#define MASK_FENCE_I 0x3ff
+#define MATCH_VLSEGBU 0x220b
+#define MASK_VLSEGBU 0x1ffff
+#define MATCH_FNMSUB_D 0xcb
+#define MASK_FNMSUB_D 0x1ff
+#define MATCH_ADDW 0x3b
+#define MASK_ADDW 0x1ffff
+#define MATCH_SLL 0xb3
+#define MASK_SLL 0x1ffff
+#define MATCH_XOR 0x233
+#define MASK_XOR 0x1ffff
+#define MATCH_SUB 0x10033
+#define MASK_SUB 0x1ffff
+#define MATCH_ERET 0x27b
+#define MASK_ERET 0xffffffff
+#define MATCH_BLT 0x263
+#define MASK_BLT 0x3ff
+#define MATCH_VSSTW 0x110f
+#define MASK_VSSTW 0x1ffff
+#define MATCH_MTFSR 0x1f053
+#define MASK_MTFSR 0x3fffff
+#define MATCH_VSSTH 0x108f
+#define MASK_VSSTH 0x1ffff
+#define MATCH_SC_W 0x1052b
+#define MASK_SC_W 0x1ffff
+#define MATCH_REM 0x733
+#define MASK_REM 0x1ffff
+#define MATCH_SRLIW 0x29b
+#define MASK_SRLIW 0x3f83ff
+#define MATCH_LUI 0x37
+#define MASK_LUI 0x7f
+#define MATCH_VSSTB 0x100f
+#define MASK_VSSTB 0x1ffff
+#define MATCH_FCVT_S_LU 0xd053
+#define MASK_FCVT_S_LU 0x3ff1ff
+#define MATCH_VSSTD 0x118f
+#define MASK_VSSTD 0x1ffff
+#define MATCH_ADDI 0x13
+#define MASK_ADDI 0x3ff
+#define MATCH_VFMST 0x1173
+#define MASK_VFMST 0x1ffff
+#define MATCH_MULH 0x4b3
+#define MASK_MULH 0x1ffff
+#define MATCH_FMUL_S 0x2053
+#define MASK_FMUL_S 0x1f1ff
+#define MATCH_VLSEGSTHU 0xa8b
+#define MASK_VLSEGSTHU 0xfff
+#define MATCH_SRAI 0x10293
+#define MASK_SRAI 0x3f03ff
+#define MATCH_AMOAND_D 0x9ab
+#define MASK_AMOAND_D 0x1ffff
+#define MATCH_FLT_D 0x160d3
+#define MASK_FLT_D 0x1ffff
+#define MATCH_SRAW 0x102bb
+#define MASK_SRAW 0x1ffff
+#define MATCH_FMUL_D 0x20d3
+#define MASK_FMUL_D 0x1f1ff
+#define MATCH_LD 0x183
+#define MASK_LD 0x3ff
+#define MATCH_ORI 0x313
+#define MASK_ORI 0x3ff
+#define MATCH_FLT_S 0x16053
+#define MASK_FLT_S 0x1ffff
+#define MATCH_ADDIW 0x1b
+#define MASK_ADDIW 0x3ff
+#define MATCH_AMOAND_W 0x92b
+#define MASK_AMOAND_W 0x1ffff
+#define MATCH_FEQ_S 0x15053
+#define MASK_FEQ_S 0x1ffff
+#define MATCH_FSGNJX_D 0x70d3
+#define MASK_FSGNJX_D 0x1ffff
+#define MATCH_SRA 0x102b3
+#define MASK_SRA 0x1ffff
+#define MATCH_C_LWSP 0x5
+#define MASK_C_LWSP 0x1f
+#define MATCH_BGE 0x2e3
+#define MASK_BGE 0x3ff
+#define MATCH_C_ADD3 0x1c
+#define MASK_C_ADD3 0x31f
+#define MATCH_SRAIW 0x1029b
+#define MASK_SRAIW 0x3f83ff
+#define MATCH_VSSEGD 0x218f
+#define MASK_VSSEGD 0x1ffff
+#define MATCH_SRL 0x2b3
+#define MASK_SRL 0x1ffff
+#define MATCH_VENQCMD 0x2b7b
+#define MASK_VENQCMD 0xf801ffff
+#define MATCH_FSUB_D 0x10d3
+#define MASK_FSUB_D 0x1f1ff
+#define MATCH_VFMTS 0x1973
+#define MASK_VFMTS 0x1ffff
+#define MATCH_VENQIMM1 0x2f7b
+#define MASK_VENQIMM1 0xf801ffff
+#define MATCH_FSGNJX_S 0x7053
+#define MASK_FSGNJX_S 0x1ffff
+#define MATCH_VFMSV 0x973
+#define MASK_VFMSV 0x3fffff
+#define MATCH_VENQIMM2 0x337b
+#define MASK_VENQIMM2 0xf801ffff
+#define MATCH_FCVT_D_WU 0xf0d3
+#define MASK_FCVT_D_WU 0x3ff1ff
+#define MATCH_VXCPTRESTORE 0x77b
+#define MASK_VXCPTRESTORE 0xf83fffff
+#define MATCH_VMTS 0x1873
+#define MASK_VMTS 0x1ffff
+#define MATCH_OR 0x333
+#define MASK_OR 0x1ffff
+#define MATCH_RDINSTRET 0xa77
+#define MASK_RDINSTRET 0x7ffffff
+#define MATCH_FCVT_WU_D 0xb0d3
+#define MASK_FCVT_WU_D 0x3ff1ff
+#define MATCH_SUBW 0x1003b
+#define MASK_SUBW 0x1ffff
+#define MATCH_JALR_C 0x6b
+#define MASK_JALR_C 0x3ff
+#define MATCH_FMAX_S 0x19053
+#define MASK_FMAX_S 0x1ffff
+#define MATCH_AMOMAXU_D 0x1dab
+#define MASK_AMOMAXU_D 0x1ffff
+#define MATCH_C_SLLIW 0x1819
+#define MASK_C_SLLIW 0x1c1f
+#define MATCH_JALR_J 0x16b
+#define MASK_JALR_J 0x3ff
+#define MATCH_C_FLD 0x15
+#define MASK_C_FLD 0x1f
+#define MATCH_VLSTW 0x110b
+#define MASK_VLSTW 0x1ffff
+#define MATCH_VLSTH 0x108b
+#define MASK_VLSTH 0x1ffff
+#define MATCH_XORI 0x213
+#define MASK_XORI 0x3ff
+#define MATCH_JALR_R 0xeb
+#define MASK_JALR_R 0x3ff
+#define MATCH_AMOMAXU_W 0x1d2b
+#define MASK_AMOMAXU_W 0x1ffff
+#define MATCH_FCVT_WU_S 0xb053
+#define MASK_FCVT_WU_S 0x3ff1ff
+#define MATCH_VLSTB 0x100b
+#define MASK_VLSTB 0x1ffff
+#define MATCH_VLSTD 0x118b
+#define MASK_VLSTD 0x1ffff
+#define MATCH_C_LD0 0x8012
+#define MASK_C_LD0 0x801f
+#define MATCH_RDTIME 0x677
+#define MASK_RDTIME 0x7ffffff
+#define MATCH_ANDI 0x393
+#define MASK_ANDI 0x3ff
+#define MATCH_CLEARPCR 0x7b
+#define MASK_CLEARPCR 0x3ff
+#define MATCH_VENQCNT 0x377b
+#define MASK_VENQCNT 0xf801ffff
+#define MATCH_FSGNJN_D 0x60d3
+#define MASK_FSGNJN_D 0x1ffff
+#define MATCH_FNMADD_S 0x4f
+#define MASK_FNMADD_S 0x1ff
+#define MATCH_JAL 0x6f
+#define MASK_JAL 0x7f
+#define MATCH_LWU 0x303
+#define MASK_LWU 0x3ff
+#define MATCH_VLSEGSTBU 0xa0b
+#define MASK_VLSEGSTBU 0xfff
+#define MATCH_C_BEQ 0x10
+#define MASK_C_BEQ 0x1f
+#define MATCH_VLHU 0x28b
+#define MASK_VLHU 0x3fffff
+#define MATCH_VFSSTD 0x158f
+#define MASK_VFSSTD 0x1ffff
+#define MATCH_C_BNE 0x11
+#define MASK_C_BNE 0x1f
+#define MATCH_FNMADD_D 0xcf
+#define MASK_FNMADD_D 0x1ff
+#define MATCH_AMOADD_D 0x1ab
+#define MASK_AMOADD_D 0x1ffff
+#define MATCH_C_SW 0xd
+#define MASK_C_SW 0x1f
+#define MATCH_LR_D 0x101ab
+#define MASK_LR_D 0x3fffff
+#define MATCH_C_MOVE 0x2
+#define MASK_C_MOVE 0x801f
+#define MATCH_FMOVN 0xef7
+#define MASK_FMOVN 0x1ffff
+#define MATCH_C_FSW 0x16
+#define MASK_C_FSW 0x1f
+#define MATCH_C_J 0x8002
+#define MASK_C_J 0x801f
+#define MATCH_MULHSU 0x533
+#define MASK_MULHSU 0x1ffff
+#define MATCH_C_SD 0xc
+#define MASK_C_SD 0x1f
+#define MATCH_AMOADD_W 0x12b
+#define MASK_AMOADD_W 0x1ffff
+#define MATCH_FCVT_D_LU 0xd0d3
+#define MASK_FCVT_D_LU 0x3ff1ff
+#define MATCH_AMOMAX_D 0x15ab
+#define MASK_AMOMAX_D 0x1ffff
+#define MATCH_FSD 0x1a7
+#define MASK_FSD 0x3ff
+#define MATCH_FCVT_W_D 0xa0d3
+#define MASK_FCVT_W_D 0x3ff1ff
+#define MATCH_FMOVZ 0xaf7
+#define MASK_FMOVZ 0x1ffff
+#define MATCH_FEQ_D 0x150d3
+#define MASK_FEQ_D 0x1ffff
+#define MATCH_C_OR3 0x21c
+#define MASK_C_OR3 0x31f
+#define MATCH_VMVV 0x73
+#define MASK_VMVV 0x3fffff
+#define MATCH_VFSSEGSTW 0xd0f
+#define MASK_VFSSEGSTW 0xfff
+#define MATCH_SLT 0x133
+#define MASK_SLT 0x1ffff
+#define MATCH_MXTF_D 0x1e0d3
+#define MASK_MXTF_D 0x3fffff
+#define MATCH_SLLW 0xbb
+#define MASK_SLLW 0x1ffff
+#define MATCH_AMOOR_D 0xdab
+#define MASK_AMOOR_D 0x1ffff
+#define MATCH_SLTI 0x113
+#define MASK_SLTI 0x3ff
+#define MATCH_REMU 0x7b3
+#define MASK_REMU 0x1ffff
+#define MATCH_FLW 0x107
+#define MASK_FLW 0x3ff
+#define MATCH_REMW 0x73b
+#define MASK_REMW 0x1ffff
+#define MATCH_SLTU 0x1b3
+#define MASK_SLTU 0x1ffff
+#define MATCH_SLLI 0x93
+#define MASK_SLLI 0x3f03ff
+#define MATCH_C_AND3 0x31c
+#define MASK_C_AND3 0x31f
+#define MATCH_VSSEGW 0x210f
+#define MASK_VSSEGW 0x1ffff
+#define MATCH_AMOOR_W 0xd2b
+#define MASK_AMOOR_W 0x1ffff
+#define MATCH_VSD 0x18f
+#define MASK_VSD 0x3fffff
+#define MATCH_BEQ 0x63
+#define MASK_BEQ 0x3ff
+#define MATCH_FLD 0x187
+#define MASK_FLD 0x3ff
+#define MATCH_MXTF_S 0x1e053
+#define MASK_MXTF_S 0x3fffff
+#define MATCH_FSUB_S 0x1053
+#define MASK_FSUB_S 0x1f1ff
+#define MATCH_AND 0x3b3
+#define MASK_AND 0x1ffff
+#define MATCH_VTCFGIVL 0x1f3
+#define MASK_VTCFGIVL 0x3ff
+#define MATCH_LBU 0x203
+#define MASK_LBU 0x3ff
+#define MATCH_VF 0x3f3
+#define MASK_VF 0xf80003ff
+#define MATCH_VLSEGSTW 0x90b
+#define MASK_VLSEGSTW 0xfff
+#define MATCH_SYSCALL 0x77
+#define MASK_SYSCALL 0xffffffff
+#define MATCH_FSGNJ_S 0x5053
+#define MASK_FSGNJ_S 0x1ffff
+#define MATCH_C_ADDI 0x1
+#define MASK_C_ADDI 0x1f
+#define MATCH_VFMVV 0x173
+#define MASK_VFMVV 0x3fffff
+#define MATCH_VLSTWU 0x130b
+#define MASK_VLSTWU 0x1ffff
+#define MATCH_C_SUB3 0x11c
+#define MASK_C_SUB3 0x31f
+#define MATCH_VSH 0x8f
+#define MASK_VSH 0x3fffff
+#define MATCH_VLSEGSTB 0x80b
+#define MASK_VLSEGSTB 0xfff
+#define MATCH_VXCPTSAVE 0x37b
+#define MASK_VXCPTSAVE 0xf83fffff
+#define MATCH_VLSEGSTD 0x98b
+#define MASK_VLSEGSTD 0xfff
+#define MATCH_VFLSEGD 0x258b
+#define MASK_VFLSEGD 0x1ffff
+#define MATCH_VFLSEGW 0x250b
+#define MASK_VFLSEGW 0x1ffff
+#define MATCH_VLSEGSTH 0x88b
+#define MASK_VLSEGSTH 0xfff
+#define MATCH_AMOMAX_W 0x152b
+#define MASK_AMOMAX_W 0x1ffff
+#define MATCH_FSGNJ_D 0x50d3
+#define MASK_FSGNJ_D 0x1ffff
+#define MATCH_VFLSEGSTW 0xd0b
+#define MASK_VFLSEGSTW 0xfff
+#define MATCH_C_SUB 0x801a
+#define MASK_C_SUB 0x801f
+#define MATCH_MULHU 0x5b3
+#define MASK_MULHU 0x1ffff
+#define MATCH_FENCE_V_G 0x2af
+#define MASK_FENCE_V_G 0x3ff
+#define MATCH_VMSV 0x873
+#define MASK_VMSV 0x3fffff
+#define MATCH_VMST 0x1073
+#define MASK_VMST 0x1ffff
+#define MATCH_SETPCR 0xfb
+#define MASK_SETPCR 0x3ff
+#define MATCH_FCVT_LU_S 0x9053
+#define MASK_FCVT_LU_S 0x3ff1ff
+#define MATCH_VXCPTHOLD 0x277b
+#define MASK_VXCPTHOLD 0xffffffff
+#define MATCH_FCVT_S_L 0xc053
+#define MASK_FCVT_S_L 0x3ff1ff
+#define MATCH_VFLSEGSTD 0xd8b
+#define MASK_VFLSEGSTD 0xfff
+#define MATCH_AUIPC 0x17
+#define MASK_AUIPC 0x7f
+#define MATCH_C_ADD 0x1a
+#define MASK_C_ADD 0x801f
+#define MATCH_FCVT_LU_D 0x90d3
+#define MASK_FCVT_LU_D 0x3ff1ff
+#define MATCH_VFLD 0x58b
+#define MASK_VFLD 0x3fffff
+#define MATCH_SC_D 0x105ab
+#define MASK_SC_D 0x1ffff
+#define MATCH_FMADD_S 0x43
+#define MASK_FMADD_S 0x1ff
+#define MATCH_FCVT_W_S 0xa053
+#define MASK_FCVT_W_S 0x3ff1ff
+#define MATCH_VSSEGH 0x208f
+#define MASK_VSSEGH 0x1ffff
+#define MATCH_FSQRT_S 0x4053
+#define MASK_FSQRT_S 0x3ff1ff
+#define MATCH_VXCPTKILL 0xb7b
+#define MASK_VXCPTKILL 0xffffffff
+#define MATCH_C_SRAI 0x1019
+#define MASK_C_SRAI 0x1c1f
+#define MATCH_AMOMIN_W 0x112b
+#define MASK_AMOMIN_W 0x1ffff
+#define MATCH_FSGNJN_S 0x6053
+#define MASK_FSGNJN_S 0x1ffff
+#define MATCH_C_SLLI32 0x419
+#define MASK_C_SLLI32 0x1c1f
+#define MATCH_VLSEGWU 0x230b
+#define MASK_VLSEGWU 0x1ffff
+#define MATCH_VFSW 0x50f
+#define MASK_VFSW 0x3fffff
+#define MATCH_AMOSWAP_D 0x5ab
+#define MASK_AMOSWAP_D 0x1ffff
+#define MATCH_FSQRT_D 0x40d3
+#define MASK_FSQRT_D 0x3ff1ff
+#define MATCH_VFLW 0x50b
+#define MASK_VFLW 0x3fffff
+#define MATCH_FDIV_D 0x30d3
+#define MASK_FDIV_D 0x1f1ff
+#define MATCH_FMADD_D 0xc3
+#define MASK_FMADD_D 0x1ff
+#define MATCH_DIVW 0x63b
+#define MASK_DIVW 0x1ffff
+#define MATCH_AMOMIN_D 0x11ab
+#define MASK_AMOMIN_D 0x1ffff
+#define MATCH_DIVU 0x6b3
+#define MASK_DIVU 0x1ffff
+#define MATCH_AMOSWAP_W 0x52b
+#define MASK_AMOSWAP_W 0x1ffff
+#define MATCH_VFSD 0x58f
+#define MASK_VFSD 0x3fffff
+#define MATCH_FADD_S 0x53
+#define MASK_FADD_S 0x1f1ff
+#define MATCH_VLSEGB 0x200b
+#define MASK_VLSEGB 0x1ffff
+#define MATCH_FCVT_L_D 0x80d3
+#define MASK_FCVT_L_D 0x3ff1ff
+#define MATCH_VLSEGD 0x218b
+#define MASK_VLSEGD 0x1ffff
+#define MATCH_VLSEGH 0x208b
+#define MASK_VLSEGH 0x1ffff
+#define MATCH_SW 0x123
+#define MASK_SW 0x3ff
+#define MATCH_FMSUB_S 0x47
+#define MASK_FMSUB_S 0x1ff
+#define MATCH_VFSSEGW 0x250f
+#define MASK_VFSSEGW 0x1ffff
+#define MATCH_C_ADDIW 0x1d
+#define MASK_C_ADDIW 0x1f
+#define MATCH_LHU 0x283
+#define MASK_LHU 0x3ff
+#define MATCH_SH 0xa3
+#define MASK_SH 0x3ff
+#define MATCH_VLSEGW 0x210b
+#define MASK_VLSEGW 0x1ffff
+#define MATCH_FSW 0x127
+#define MASK_FSW 0x3ff
+#define MATCH_VLBU 0x20b
+#define MASK_VLBU 0x3fffff
+#define MATCH_SB 0x23
+#define MASK_SB 0x3ff
+#define MATCH_FMSUB_D 0xc7
+#define MASK_FMSUB_D 0x1ff
+#define MATCH_VLSEGHU 0x228b
+#define MASK_VLSEGHU 0x1ffff
+#define MATCH_VSSEGB 0x200f
+#define MASK_VSSEGB 0x1ffff
+#define MATCH_VFSSEGD 0x258f
+#define MASK_VFSSEGD 0x1ffff
+#define MATCH_SD 0x1a3
+#define MASK_SD 0x3ff
diff -ruN binutils-2.24/ld/configure.tgt binutils-2.24-riscv/ld/configure.tgt
--- binutils-2.24/ld/configure.tgt 2013-11-26 03:37:33.000000000 -0800
+++ binutils-2.24-riscv/ld/configure.tgt 2014-12-02 16:05:44.917434991 -0800
@@ -592,6 +592,9 @@
powerpc-*-beos*) targ_emul=aixppc ;;
powerpc-*-windiss*) targ_emul=elf32ppcwindiss ;;
powerpc-*-lynxos*) targ_emul=ppclynx ;;
+riscv*-*-*) targ_emul=elf64lriscv
+ targ_extra_emuls="elf32lriscv"
+ targ_extra_libpath=$targ_extra_emuls ;;
rs6000-*-aix[5-9]*) targ_emul=aix5rs6 ;;
rs6000-*-aix*) targ_emul=aixrs6
;;
diff -ruN binutils-2.24/ld/emulparams/elf32lriscv-defs.sh binutils-2.24-riscv/ld/emulparams/elf32lriscv-defs.sh
--- binutils-2.24/ld/emulparams/elf32lriscv-defs.sh 1969-12-31 16:00:00.000000000 -0800
+++ binutils-2.24-riscv/ld/emulparams/elf32lriscv-defs.sh 2014-12-02 16:05:44.917434991 -0800
@@ -0,0 +1,90 @@
+# This is an ELF platform.
+SCRIPT_NAME=elf
+
+# Handle both big- and little-ended 32-bit MIPS objects.
+ARCH=riscv
+OUTPUT_FORMAT="elf32-bigriscv"
+BIG_OUTPUT_FORMAT="elf32-bigriscv"
+LITTLE_OUTPUT_FORMAT="elf32-littleriscv"
+
+TEMPLATE_NAME=elf32
+EXTRA_EM_FILE=riscvelf
+
+case "$EMULATION_NAME" in
+elf32*) ELFSIZE=32; LIBPATH_SUFFIX=32 ;;
+elf64*) ELFSIZE=64; LIBPATH_SUFFIX= ;;
+*) echo $0: unhandled emulation $EMULATION_NAME >&2; exit 1 ;;
+esac
+
+if test `echo "$host" | sed -e s/64//` = `echo "$target" | sed -e s/64//`; then
+ case " $EMULATION_LIBPATH " in
+ *" ${EMULATION_NAME} "*)
+ NATIVE=yes
+ ;;
+ esac
+fi
+
+GENERATE_SHLIB_SCRIPT=yes
+GENERATE_PIE_SCRIPT=yes
+
+TEXT_START_ADDR=0x10000000
+MAXPAGESIZE="CONSTANT (MAXPAGESIZE)"
+ENTRY=_start
+
+# Unlike most targets, the MIPS backend puts all dynamic relocations
+# in a single dynobj section, which it also calls ".rel.dyn". It does
+# this so that it can easily sort all dynamic relocations before the
+# output section has been populated.
+OTHER_GOT_RELOC_SECTIONS="
+ .rel.dyn ${RELOCATING-0} : { *(.rel.dyn) }
+"
+# GOT-related settings.
+# If the output has a GOT section, there must be exactly 0x7ff0 bytes
+# between .got and _gp. The ". = ." below stops the orphan code from
+# inserting other sections between the assignment to _gp and the start
+# of .got.
+OTHER_GOT_SYMBOLS='
+ . = .;
+ _gp = ALIGN(16);
+'
+# .got.plt is only used for the PLT psABI extension. It should not be
+# included in the .sdata block with .got, as there is no need to access
+# the section from _gp. Note that the traditional:
+#
+# . = .
+# _gp = ALIGN (16) + 0x7ff0;
+# .got : { *(.got.plt) *(.got) }
+#
+# would set _gp to the wrong value; _gp - 0x7ff0 must point to the start
+# of *(.got).
+GOT=".got ${RELOCATING-0} : { *(.got) }"
+unset OTHER_READWRITE_SECTIONS
+unset OTHER_RELRO_SECTIONS
+if test -n "$RELRO_NOW"; then
+ OTHER_RELRO_SECTIONS=".got.plt ${RELOCATING-0} : { *(.got.plt) }"
+else
+ OTHER_READWRITE_SECTIONS=".got.plt ${RELOCATING-0} : { *(.got.plt) }"
+fi
+
+OTHER_SDATA_SECTIONS="
+ .lit8 ${RELOCATING-0} : { *(.lit8) }
+ .lit4 ${RELOCATING-0} : { *(.lit4) }
+ .srdata ${RELOCATING-0} : { *(.srdata) }
+"
+
+# Magic symbols.
+TEXT_START_SYMBOLS='_ftext = . ;'
+DATA_START_SYMBOLS='_fdata = . ;'
+OTHER_BSS_SYMBOLS='_fbss = .;'
+
+INITIAL_READONLY_SECTIONS=
+if test -z "${CREATE_SHLIB}"; then
+ INITIAL_READONLY_SECTIONS=".interp ${RELOCATING-0} : { *(.interp) }"
+fi
+INITIAL_READONLY_SECTIONS="${INITIAL_READONLY_SECTIONS}
+ .reginfo ${RELOCATING-0} : { *(.reginfo) }"
+# Discard any .MIPS.content* or .MIPS.events* sections. The linker
+# doesn't know how to adjust them.
+OTHER_SECTIONS="/DISCARD/ : { *(.MIPS.content*) *(.MIPS.events*) }"
+
+TEXT_DYNAMIC=
diff -ruN binutils-2.24/ld/emulparams/elf32lriscv.sh binutils-2.24-riscv/ld/emulparams/elf32lriscv.sh
--- binutils-2.24/ld/emulparams/elf32lriscv.sh 1969-12-31 16:00:00.000000000 -0800
+++ binutils-2.24-riscv/ld/emulparams/elf32lriscv.sh 2014-12-02 16:05:44.917434991 -0800
@@ -0,0 +1,15 @@
+# If you change this file, please also look at files which source this one:
+# elf32ltsmipn32.sh
+
+. ${srcdir}/emulparams/elf32lriscv-defs.sh
+OUTPUT_FORMAT="elf32-littleriscv"
+BIG_OUTPUT_FORMAT="elf32-bigriscv"
+LITTLE_OUTPUT_FORMAT="elf32-littleriscv"
+COMMONPAGESIZE="CONSTANT (COMMONPAGESIZE)"
+
+# Magic sections.
+OTHER_TEXT_SECTIONS='*(.mips16.fn.*) *(.mips16.call.*)'
+OTHER_SECTIONS='
+ .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) }
+ .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) }
+'
diff -ruN binutils-2.24/ld/emulparams/elf64lriscv-defs.sh binutils-2.24-riscv/ld/emulparams/elf64lriscv-defs.sh
--- binutils-2.24/ld/emulparams/elf64lriscv-defs.sh 1969-12-31 16:00:00.000000000 -0800
+++ binutils-2.24-riscv/ld/emulparams/elf64lriscv-defs.sh 2014-12-02 16:05:44.917434991 -0800
@@ -0,0 +1,3 @@
+. ${srcdir}/emulparams/elf32lriscv-defs.sh
+COMMONPAGESIZE="CONSTANT (COMMONPAGESIZE)"
+INITIAL_READONLY_SECTIONS=".MIPS.options : { *(.MIPS.options) }"
diff -ruN binutils-2.24/ld/emulparams/elf64lriscv.sh binutils-2.24-riscv/ld/emulparams/elf64lriscv.sh
--- binutils-2.24/ld/emulparams/elf64lriscv.sh 1969-12-31 16:00:00.000000000 -0800
+++ binutils-2.24-riscv/ld/emulparams/elf64lriscv.sh 2014-12-02 16:05:44.917434991 -0800
@@ -0,0 +1,13 @@
+# If you change this file, please also look at files which source this one:
+# elf64ltsmip.sh
+
+. ${srcdir}/emulparams/elf64lriscv-defs.sh
+OUTPUT_FORMAT="elf64-littleriscv"
+BIG_OUTPUT_FORMAT="elf64-bigriscv"
+LITTLE_OUTPUT_FORMAT="elf64-littleriscv"
+
+# Magic sections.
+OTHER_SECTIONS='
+ .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) }
+ .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) }
+'
diff -ruN binutils-2.24/ld/emultempl/riscvelf.em binutils-2.24-riscv/ld/emultempl/riscvelf.em
--- binutils-2.24/ld/emultempl/riscvelf.em 1969-12-31 16:00:00.000000000 -0800
+++ binutils-2.24-riscv/ld/emultempl/riscvelf.em 2014-12-02 16:05:44.917434991 -0800
@@ -0,0 +1,49 @@
+# This shell script emits a C file. -*- C -*-
+# Copyright 2004, 2006, 2007, 2008 Free Software Foundation, Inc.
+#
+# This file is part of the GNU Binutils.
+#
+# 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; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+fragment <<EOF
+
+#include "ldctor.h"
+#include "elf/riscv.h"
+#include "elfxx-riscv.h"
+
+#define is_riscv_elf(bfd) \
+ (bfd_get_flavour (bfd) == bfd_target_elf_flavour \
+ && elf_tdata (bfd) != NULL \
+ && elf_object_id (bfd) == MIPS_ELF_DATA)
+
+static void
+riscv_after_parse (void)
+{
+ /* .gnu.hash and the MIPS ABI require .dynsym to be sorted in different
+ ways. .gnu.hash needs symbols to be grouped by hash code whereas the
+ MIPS ABI requires a mapping between the GOT and the symbol table. */
+ if (link_info.emit_gnu_hash)
+ {
+ einfo ("%X%P: .gnu.hash is incompatible with the MIPS ABI\n");
+ link_info.emit_hash = TRUE;
+ link_info.emit_gnu_hash = FALSE;
+ }
+ after_parse_default ();
+}
+
+EOF
+
+LDEMUL_AFTER_PARSE=riscv_after_parse
diff -ruN binutils-2.24/ld/Makefile.am binutils-2.24-riscv/ld/Makefile.am
--- binutils-2.24/ld/Makefile.am 2013-11-26 03:37:33.000000000 -0800
+++ binutils-2.24-riscv/ld/Makefile.am 2014-12-02 16:11:22.363539568 -0800
@@ -234,6 +234,7 @@
eelf32lppclinux.c \
eelf32lppcnto.c \
eelf32lppcsim.c \
+ eelf32lriscv.c \
eelf32m32c.c \
eelf32mb_linux.c \
eelf32mcore.c \
@@ -510,6 +511,7 @@
eelf64btsmip_fbsd.c \
eelf64hppa.c \
eelf64lppc.c \
+ eelf64lriscv.c \
eelf64ltsmip.c \
eelf64ltsmip_fbsd.c \
eelf64mmix.c \
@@ -1123,6 +1125,10 @@
ldemul-list.h \
$(ELF_DEPS) $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
${GENSCRIPTS} elf32lppcsim "$(tdir_elf32lppcsim)"
+eelf32lriscv.c: $(srcdir)/emulparams/elf32lriscv.sh \
+ $(srcdir)/emulparams/elf32lriscv-defs.sh $(ELF_DEPS) \
+ $(srcdir)/emultempl/mipself.em $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
+ ${GENSCRIPTS} elf32lriscv "$(tdir_elf32lriscv)"
eelf32lsmip.c: $(srcdir)/emulparams/elf32lsmip.sh \
$(srcdir)/emulparams/elf32lmip.sh $(srcdir)/emulparams/elf32bmip.sh \
$(ELF_DEPS) $(srcdir)/emultempl/mipself.em $(srcdir)/scripttempl/elf.sc \
@@ -2088,6 +2094,11 @@
ldemul-list.h \
$(ELF_DEPS) $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
${GENSCRIPTS} elf64lppc "$(tdir_elf64lppc)"
+eelf64lriscv.c: $(srcdir)/emulparams/elf64lriscv.sh \
+ $(srcdir)/emulparams/elf64lriscv-defs.sh \
+ $(srcdir)/emulparams/elf32lriscv-defs.sh $(ELF_DEPS) \
+ $(srcdir)/emultempl/mipself.em $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
+ ${GENSCRIPTS} elf64lriscv "$(tdir_elf64lriscv)"
eelf64ltsmip.c: $(srcdir)/emulparams/elf64ltsmip.sh \
$(srcdir)/emulparams/elf64btsmip.sh $(srcdir)/emulparams/elf64bmip-defs.sh \
$(srcdir)/emulparams/elf32bmipn32-defs.sh $(ELF_DEPS) \
diff -ruN binutils-2.24/ld/Makefile.in binutils-2.24-riscv/ld/Makefile.in
--- binutils-2.24/ld/Makefile.in 2013-11-26 03:37:33.000000000 -0800
+++ binutils-2.24-riscv/ld/Makefile.in 2014-12-02 16:13:49.804461687 -0800
@@ -542,6 +542,7 @@
eelf32lppclinux.c \
eelf32lppcnto.c \
eelf32lppcsim.c \
+ eelf32lriscv.c \
eelf32m32c.c \
eelf32mb_linux.c \
eelf32mcore.c \
@@ -817,6 +818,7 @@
eelf64btsmip_fbsd.c \
eelf64hppa.c \
eelf64lppc.c \
+ eelf64lriscv.c \
eelf64ltsmip.c \
eelf64ltsmip_fbsd.c \
eelf64mmix.c \
@@ -1194,6 +1196,7 @@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eelf32lppclinux.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eelf32lppcnto.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eelf32lppcsim.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eelf32lriscv.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eelf32lr5900.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eelf32lr5900n32.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eelf32lsmip.Po@am__quote@
@@ -1246,6 +1249,7 @@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eelf64btsmip_fbsd.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eelf64hppa.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eelf64lppc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eelf64lriscv.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eelf64ltsmip.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eelf64ltsmip_fbsd.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eelf64mmix.Po@am__quote@
@@ -2607,6 +2611,10 @@
ldemul-list.h \
$(ELF_DEPS) $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
${GENSCRIPTS} elf32lppcsim "$(tdir_elf32lppcsim)"
+eelf32lriscv.c: $(srcdir)/emulparams/elf32lriscv.sh \
+ $(srcdir)/emulparams/elf32lriscv-defs.sh $(ELF_DEPS) \
+ $(srcdir)/emultempl/mipself.em $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
+ ${GENSCRIPTS} elf32lriscv "$(tdir_elf32lriscv)"
eelf32lsmip.c: $(srcdir)/emulparams/elf32lsmip.sh \
$(srcdir)/emulparams/elf32lmip.sh $(srcdir)/emulparams/elf32bmip.sh \
$(ELF_DEPS) $(srcdir)/emultempl/mipself.em $(srcdir)/scripttempl/elf.sc \
@@ -3572,6 +3580,11 @@
ldemul-list.h \
$(ELF_DEPS) $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
${GENSCRIPTS} elf64lppc "$(tdir_elf64lppc)"
+eelf64lriscv.c: $(srcdir)/emulparams/elf64lriscv.sh \
+ $(srcdir)/emulparams/elf64lriscv-defs.sh \
+ $(srcdir)/emulparams/elf32lriscv-defs.sh $(ELF_DEPS) \
+ $(srcdir)/emultempl/mipself.em $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
+ ${GENSCRIPTS} elf64lriscv "$(tdir_elf64lriscv)"
eelf64ltsmip.c: $(srcdir)/emulparams/elf64ltsmip.sh \
$(srcdir)/emulparams/elf64btsmip.sh $(srcdir)/emulparams/elf64bmip-defs.sh \
$(srcdir)/emulparams/elf32bmipn32-defs.sh $(ELF_DEPS) \
diff -ruN binutils-2.24/opcodes/configure binutils-2.24-riscv/opcodes/configure
--- binutils-2.24/opcodes/configure 2013-11-04 07:33:40.000000000 -0800
+++ binutils-2.24-riscv/opcodes/configure 2014-12-02 16:05:44.917434991 -0800
@@ -12555,6 +12555,7 @@
bfd_powerpc_arch) ta="$ta ppc-dis.lo ppc-opc.lo" ;;
bfd_powerpc_64_arch) ta="$ta ppc-dis.lo ppc-opc.lo" ;;
bfd_pyramid_arch) ;;
+ bfd_riscv_arch) ta="$ta riscv-dis.lo riscv-opc.lo" ;;
bfd_romp_arch) ;;
bfd_rs6000_arch) ta="$ta ppc-dis.lo ppc-opc.lo" ;;
bfd_rl78_arch) ta="$ta rl78-dis.lo rl78-decode.lo";;
diff -ruN binutils-2.24/opcodes/configure.in binutils-2.24-riscv/opcodes/configure.in
--- binutils-2.24/opcodes/configure.in 2013-11-04 07:33:40.000000000 -0800
+++ binutils-2.24-riscv/opcodes/configure.in 2014-12-02 16:05:44.921435015 -0800
@@ -302,6 +302,7 @@
bfd_powerpc_arch) ta="$ta ppc-dis.lo ppc-opc.lo" ;;
bfd_powerpc_64_arch) ta="$ta ppc-dis.lo ppc-opc.lo" ;;
bfd_pyramid_arch) ;;
+ bfd_riscv_arch) ta="$ta riscv-dis.lo riscv-opc.lo" ;;
bfd_romp_arch) ;;
bfd_rs6000_arch) ta="$ta ppc-dis.lo ppc-opc.lo" ;;
bfd_rl78_arch) ta="$ta rl78-dis.lo rl78-decode.lo";;
diff -ruN binutils-2.24/opcodes/disassemble.c binutils-2.24-riscv/opcodes/disassemble.c
--- binutils-2.24/opcodes/disassemble.c 2013-11-04 07:33:40.000000000 -0800
+++ binutils-2.24-riscv/opcodes/disassemble.c 2014-12-02 16:05:44.921435015 -0800
@@ -378,6 +378,14 @@
disassemble = print_insn_little_powerpc;
break;
#endif
+#ifdef ARCH_riscv
+ case bfd_arch_riscv:
+ if (bfd_big_endian (abfd))
+ disassemble = print_insn_big_riscv;
+ else
+ disassemble = print_insn_little_riscv;
+ break;
+#endif
#ifdef ARCH_rs6000
case bfd_arch_rs6000:
if (bfd_get_mach (abfd) == bfd_mach_ppc_620)
diff -ruN binutils-2.24/opcodes/riscv-dis.c binutils-2.24-riscv/opcodes/riscv-dis.c
--- binutils-2.24/opcodes/riscv-dis.c 1969-12-31 16:00:00.000000000 -0800
+++ binutils-2.24-riscv/opcodes/riscv-dis.c 2014-12-02 16:05:44.921435015 -0800
@@ -0,0 +1,902 @@
+/* Print mips instructions for GDB, the GNU debugger, or for objdump.
+ Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ 2000, 2001, 2002, 2003, 2005, 2007, 2008
+ Free Software Foundation, Inc.
+ Contributed by Nobuyuki Hikichi(hikichi@sra.co.jp).
+
+ This file is part of the GNU opcodes library.
+
+ This library 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, or (at your option)
+ any later version.
+
+ It 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; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#include "sysdep.h"
+#include "dis-asm.h"
+#include "libiberty.h"
+#include "opcode/riscv.h"
+#include "opintl.h"
+
+/* FIXME: These are needed to figure out if the code is mips16 or
+ not. The low bit of the address is often a good indicator. No
+ symbol table is available when this code runs out in an embedded
+ system as when it is used for disassembler support in a monitor. */
+
+#if !defined(EMBEDDED_ENV)
+#define SYMTAB_AVAILABLE 1
+#include "elf-bfd.h"
+#include "elf/riscv.h"
+#endif
+
+#include <assert.h>
+
+/* Mips instructions are at maximum this many bytes long. */
+#define INSNLEN 4
+
+
+/* FIXME: These should be shared with gdb somehow. */
+
+static const char * const mips_gpr_names_numeric[32] =
+{
+ "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
+ "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
+ "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
+ "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31"
+};
+
+static const char* mips_gpr_names_abi[32] = {
+ "zero", "ra", "s0", "s1", "s2", "s3", "s4", "s5",
+ "s6", "s7", "s8", "s9", "s10", "s11", "sp", "tp",
+ "v0", "v1", "a0", "a1", "a2", "a3", "a4", "a5",
+ "a6", "a7", "a8", "a9", "a10", "a11", "a12", "a13"
+};
+
+
+static const char * const mips_fpr_names_numeric[32] =
+{
+ "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
+ "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
+ "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
+ "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31"
+};
+
+static const char* mips_fpr_names_abi[32] = {
+ "fs0", "fs1", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7",
+ "fs8", "fs9", "fs10", "fs11", "fs12", "fs13", "fs14", "fs15",
+ "fv0", "fv1", "fa0", "fa1", "fa2", "fa3", "fa4", "fa5",
+ "fa6", "fa7", "fa8", "fa9", "fa10", "fa11", "fa12", "fa13"
+};
+
+static const char * const mips_cp0_names_numeric[32] =
+{
+ "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7",
+ "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15",
+ "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23",
+ "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31"
+};
+
+static const char * const mips_vgr_reg_names_riscv[32] =
+{
+ "vzero","vra", "vv0", "vv1", "va0", "va1", "va2", "va3",
+ "va4", "va5", "va6", "va7", "vt0", "vt1", "vt2", "vt3",
+ "vt4", "vt5", "vt6", "vt7", "vs0", "vs1", "vs2", "vs3",
+ "vs4", "vs5", "vs6", "vs7", "vs8", "vs9", "vsp", "vtp"
+};
+
+static const char * const mips_vfp_reg_names_riscv[32] =
+{
+ "vf0", "vf1", "vf2", "vf3", "vf4", "vf5", "vf6", "vf7",
+ "vf8", "vf9", "vf10", "vf11", "vf12", "vf13", "vf14", "vf15",
+ "vf16", "vf17", "vf18", "vf19", "vf20", "vf21", "vf22", "vf23",
+ "vf24", "vf25", "vf26", "vf27", "vf28", "vf29", "vf30", "vf31"
+};
+
+struct mips_abi_choice
+{
+ const char * name;
+ const char * const *gpr_names;
+ const char * const *fpr_names;
+};
+
+struct mips_abi_choice mips_abi_choices[] =
+{
+ { "numeric", mips_gpr_names_numeric, mips_fpr_names_numeric },
+ { "32", mips_gpr_names_abi, mips_fpr_names_abi },
+ { "64", mips_gpr_names_abi, mips_fpr_names_abi },
+};
+
+struct mips_arch_choice
+{
+ const char *name;
+ int bfd_mach_valid;
+ unsigned long bfd_mach;
+ int processor;
+ int isa;
+ const char * const *cp0_names;
+};
+
+const struct mips_arch_choice mips_arch_choices[] =
+{
+ { "numeric", 0, 0, 0, 0,
+ mips_cp0_names_numeric },
+
+ { "rv32", 1, bfd_mach_riscv_rocket32, CPU_ROCKET32, ISA_RV32,
+ mips_cp0_names_numeric },
+
+ { "rv64", 1, bfd_mach_riscv_rocket64, CPU_ROCKET64, ISA_RV64,
+ mips_cp0_names_numeric },
+};
+
+/* ISA and processor type to disassemble for, and register names to use.
+ set_default_mips_dis_options and parse_mips_dis_options fill in these
+ values. */
+static int mips_processor;
+static int mips_isa;
+static const char * const *mips_gpr_names;
+static const char * const *mips_fpr_names;
+static const char * const *mips_cp0_names;
+
+/* Other options */
+static int no_aliases; /* If set disassemble as most general inst. */
+
+static const struct mips_abi_choice *
+choose_abi_by_name (const char *name, unsigned int namelen)
+{
+ const struct mips_abi_choice *c;
+ unsigned int i;
+
+ for (i = 0, c = NULL; i < ARRAY_SIZE (mips_abi_choices) && c == NULL; i++)
+ if (strncmp (mips_abi_choices[i].name, name, namelen) == 0
+ && strlen (mips_abi_choices[i].name) == namelen)
+ c = &mips_abi_choices[i];
+
+ return c;
+}
+
+static const struct mips_arch_choice *
+choose_arch_by_name (const char *name, unsigned int namelen)
+{
+ const struct mips_arch_choice *c = NULL;
+ unsigned int i;
+
+ for (i = 0, c = NULL; i < ARRAY_SIZE (mips_arch_choices) && c == NULL; i++)
+ if (strncmp (mips_arch_choices[i].name, name, namelen) == 0
+ && strlen (mips_arch_choices[i].name) == namelen)
+ c = &mips_arch_choices[i];
+
+ return c;
+}
+
+static const struct mips_arch_choice *
+choose_arch_by_number (unsigned long mach)
+{
+ static unsigned long hint_bfd_mach;
+ static const struct mips_arch_choice *hint_arch_choice;
+ const struct mips_arch_choice *c;
+ unsigned int i;
+
+ /* We optimize this because even if the user specifies no
+ flags, this will be done for every instruction! */
+ if (hint_bfd_mach == mach
+ && hint_arch_choice != NULL
+ && hint_arch_choice->bfd_mach == hint_bfd_mach)
+ return hint_arch_choice;
+
+ for (i = 0, c = NULL; i < ARRAY_SIZE (mips_arch_choices) && c == NULL; i++)
+ {
+ if (mips_arch_choices[i].bfd_mach_valid
+ && mips_arch_choices[i].bfd_mach == mach)
+ {
+ c = &mips_arch_choices[i];
+ hint_bfd_mach = mach;
+ hint_arch_choice = c;
+ }
+ }
+ return c;
+}
+
+static void
+set_default_mips_dis_options (struct disassemble_info *info)
+{
+ const struct mips_arch_choice *chosen_arch;
+
+ /* Defaults: mipsIII/r3000 (?!), (o)32-style ("oldabi") GPR names,
+ and numeric FPR, CP0 register, and HWR names. */
+ mips_isa = ISA_RV64;
+ mips_processor = CPU_ROCKET64;
+ mips_gpr_names = mips_gpr_names_abi;
+ mips_fpr_names = mips_fpr_names_abi;
+ mips_cp0_names = mips_cp0_names_numeric;
+ no_aliases = 0;
+
+ /* Set ISA, architecture, and cp0 register names as best we can. */
+#if ! SYMTAB_AVAILABLE
+ /* This is running out on a target machine, not in a host tool.
+ FIXME: Where does mips_target_info come from? */
+ target_processor = mips_target_info.processor;
+ mips_isa = mips_target_info.isa;
+#else
+ chosen_arch = choose_arch_by_number (info->mach);
+ if (chosen_arch != NULL)
+ {
+ mips_processor = chosen_arch->processor;
+ mips_isa = chosen_arch->isa;
+ mips_cp0_names = chosen_arch->cp0_names;
+ }
+#endif
+}
+
+static void
+parse_mips_dis_option (const char *option, unsigned int len)
+{
+ unsigned int i, optionlen, vallen;
+ const char *val;
+ const struct mips_abi_choice *chosen_abi;
+ const struct mips_arch_choice *chosen_arch;
+
+ /* Try to match options that are simple flags */
+ if (CONST_STRNEQ (option, "no-aliases"))
+ {
+ no_aliases = 1;
+ return;
+ }
+
+ /* Look for the = that delimits the end of the option name. */
+ for (i = 0; i < len; i++)
+ if (option[i] == '=')
+ break;
+
+ if (i == 0) /* Invalid option: no name before '='. */
+ return;
+ if (i == len) /* Invalid option: no '='. */
+ return;
+ if (i == (len - 1)) /* Invalid option: no value after '='. */
+ return;
+
+ optionlen = i;
+ val = option + (optionlen + 1);
+ vallen = len - (optionlen + 1);
+
+ if (strncmp ("gpr-names", option, optionlen) == 0
+ && strlen ("gpr-names") == optionlen)
+ {
+ chosen_abi = choose_abi_by_name (val, vallen);
+ if (chosen_abi != NULL)
+ mips_gpr_names = chosen_abi->gpr_names;
+ return;
+ }
+
+ if (strncmp ("fpr-names", option, optionlen) == 0
+ && strlen ("fpr-names") == optionlen)
+ {
+ chosen_abi = choose_abi_by_name (val, vallen);
+ if (chosen_abi != NULL)
+ mips_fpr_names = chosen_abi->fpr_names;
+ return;
+ }
+
+ if (strncmp ("cp0-names", option, optionlen) == 0
+ && strlen ("cp0-names") == optionlen)
+ {
+ chosen_arch = choose_arch_by_name (val, vallen);
+ if (chosen_arch != NULL)
+ {
+ mips_cp0_names = chosen_arch->cp0_names;
+ }
+ return;
+ }
+
+ if (strncmp ("hwr-names", option, optionlen) == 0
+ && strlen ("hwr-names") == optionlen)
+ {
+ chosen_arch = choose_arch_by_name (val, vallen);
+ return;
+ }
+
+ if (strncmp ("reg-names", option, optionlen) == 0
+ && strlen ("reg-names") == optionlen)
+ {
+ /* We check both ABI and ARCH here unconditionally, so
+ that "numeric" will do the desirable thing: select
+ numeric register names for all registers. Other than
+ that, a given name probably won't match both. */
+ chosen_abi = choose_abi_by_name (val, vallen);
+ if (chosen_abi != NULL)
+ {
+ mips_gpr_names = chosen_abi->gpr_names;
+ mips_fpr_names = chosen_abi->fpr_names;
+ }
+ chosen_arch = choose_arch_by_name (val, vallen);
+ if (chosen_arch != NULL)
+ mips_cp0_names = chosen_arch->cp0_names;
+ return;
+ }
+
+ /* Invalid option. */
+}
+
+static void
+parse_mips_dis_options (const char *options)
+{
+ const char *option_end;
+
+ if (options == NULL)
+ return;
+
+ while (*options != '\0')
+ {
+ /* Skip empty options. */
+ if (*options == ',')
+ {
+ options++;
+ continue;
+ }
+
+ /* We know that *options is neither NUL or a comma. */
+ option_end = options + 1;
+ while (*option_end != ',' && *option_end != '\0')
+ option_end++;
+
+ parse_mips_dis_option (options, option_end - options);
+
+ /* Go on to the next one. If option_end points to a comma, it
+ will be skipped above. */
+ options = option_end;
+ }
+}
+
+/* Print insn arguments for 32/64-bit code. */
+
+static void
+print_insn_args (const char *d,
+ register unsigned long int l,
+ bfd_vma pc,
+ struct disassemble_info *info)
+{
+ int delta;
+
+ for (; *d != '\0'; d++)
+ {
+ switch (*d)
+ {
+ case '#':
+ switch ( *++d ) {
+ case 'g':
+ (*info->fprintf_func)
+ ( info->stream, "%d",
+ ((l >> OP_SH_IMMNGPR) & OP_MASK_IMMNGPR));
+ break;
+ case 'f':
+ (*info->fprintf_func)
+ ( info->stream, "%d",
+ ((l >> OP_SH_IMMNFPR) & OP_MASK_IMMNFPR));
+ break;
+ case 'n':
+ (*info->fprintf_func)
+ ( info->stream, "%d",
+ (((l >> OP_SH_IMMSEGNELM) & OP_MASK_IMMSEGNELM) + 1));
+ break;
+ case 'm':
+ (*info->fprintf_func)
+ ( info->stream, "%d",
+ (((l >> OP_SH_IMMSEGSTNELM) & OP_MASK_IMMSEGSTNELM) + 1));
+ break;
+ case 'd':
+ (*info->fprintf_func)
+ ( info->stream, "%s",
+ mips_vgr_reg_names_riscv[(l >> OP_SH_VRD) & OP_MASK_VRD]);
+ break;
+ case 's':
+ (*info->fprintf_func)
+ ( info->stream, "%s",
+ mips_vgr_reg_names_riscv[(l >> OP_SH_VRS) & OP_MASK_VRS]);
+ break;
+ case 't':
+ (*info->fprintf_func)
+ ( info->stream, "%s",
+ mips_vgr_reg_names_riscv[(l >> OP_SH_VRT) & OP_MASK_VRT]);
+ break;
+ case 'r':
+ (*info->fprintf_func)
+ ( info->stream, "%s",
+ mips_vgr_reg_names_riscv[(l >> OP_SH_VRR) & OP_MASK_VRR]);
+ break;
+ case 'D':
+ (*info->fprintf_func)
+ ( info->stream, "%s",
+ mips_vfp_reg_names_riscv[(l >> OP_SH_VFD) & OP_MASK_VFD]);
+ break;
+ case 'S':
+ (*info->fprintf_func)
+ ( info->stream, "%s",
+ mips_vfp_reg_names_riscv[(l >> OP_SH_VFS) & OP_MASK_VFS]);
+ break;
+ case 'T':
+ (*info->fprintf_func)
+ ( info->stream, "%s",
+ mips_vfp_reg_names_riscv[(l >> OP_SH_VFT) & OP_MASK_VFT]);
+ break;
+ case 'R':
+ (*info->fprintf_func)
+ ( info->stream, "%s",
+ mips_vfp_reg_names_riscv[(l >> OP_SH_VFR) & OP_MASK_VFR]);
+ break;
+ }
+ break;
+
+ case ',':
+ case '(':
+ case ')':
+ case '[':
+ case ']':
+ (*info->fprintf_func) (info->stream, "%c", *d);
+ break;
+
+ case '0':
+ (*info->fprintf_func) (info->stream, "0");
+ break;
+
+ case 'b':
+ case 's':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[(l >> OP_SH_RS) & OP_MASK_RS]);
+ break;
+
+ case 't':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[(l >> OP_SH_RT) & OP_MASK_RT]);
+ break;
+
+ case 'u':
+ (*info->fprintf_func) (info->stream, "0x%lx",
+ (l >> OP_SH_BIGIMMEDIATE) & OP_MASK_BIGIMMEDIATE);
+ break;
+
+ case 'm':
+ {
+ assert(OP_MASK_RM < ARRAY_SIZE(riscv_rm));
+ const char* rm = riscv_rm[(l >> OP_SH_RM) & OP_MASK_RM];
+ if(rm == NULL)
+ rm = "unknown";
+
+ (*info->fprintf_func) (info->stream, "%s", rm);
+ break;
+ }
+ case 'j': /* Same as i, but sign-extended. */
+ case 'o':
+ delta = (l >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE;
+ if (delta & (RISCV_IMM_REACH/2))
+ delta |= ~(RISCV_IMM_REACH-1);
+ (*info->fprintf_func) (info->stream, "%d",
+ delta);
+ break;
+
+ case 'q':
+ delta = ((l >> OP_SH_IMMLO) & OP_MASK_IMMLO) | (((l >> OP_SH_IMMHI) & OP_MASK_IMMHI) << RISCV_IMMLO_BITS);
+ if (delta & (RISCV_IMM_REACH/2))
+ delta |= ~(RISCV_IMM_REACH-1);
+ (*info->fprintf_func) (info->stream, "%d",
+ delta);
+ break;
+
+ case 'a':
+ delta = (l >> OP_SH_TARGET) & OP_MASK_TARGET;
+ if (delta & ((1<<RISCV_JUMP_BITS)/2))
+ delta |= ~((1<<RISCV_JUMP_BITS)-1);
+ info->target = (delta << RISCV_JUMP_ALIGN_BITS) + pc;
+ (*info->print_address_func) (info->target, info);
+ break;
+
+ case 'p':
+ /* Sign extend the displacement. */
+ delta = ((l >> OP_SH_IMMLO) & OP_MASK_IMMLO) | (((l >> OP_SH_IMMHI) & OP_MASK_IMMHI) << RISCV_IMMLO_BITS);
+ if (delta & (RISCV_IMM_REACH/2))
+ delta |= ~(RISCV_IMM_REACH-1);
+ info->target = (delta << RISCV_BRANCH_ALIGN_BITS) + pc;
+ (*info->print_address_func) (info->target, info);
+ break;
+
+ case 'd':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[(l >> OP_SH_RD) & OP_MASK_RD]);
+ break;
+
+ case 'z':
+ (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[0]);
+ break;
+
+ case '>':
+ (*info->fprintf_func) (info->stream, "0x%lx",
+ (l >> OP_SH_SHAMT) & OP_MASK_SHAMT);
+ break;
+
+ case '<':
+ (*info->fprintf_func) (info->stream, "0x%lx",
+ (l >> OP_SH_SHAMTW) & OP_MASK_SHAMTW);
+ break;
+
+ case 'S':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_fpr_names[(l >> OP_SH_FS) & OP_MASK_FS]);
+ break;
+
+ case 'T':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_fpr_names[(l >> OP_SH_FT) & OP_MASK_FT]);
+ break;
+
+ case 'D':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_fpr_names[(l >> OP_SH_FD) & OP_MASK_FD]);
+ break;
+
+ case 'R':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_fpr_names[(l >> OP_SH_FR) & OP_MASK_FR]);
+ break;
+
+ case 'E':
+ /* Coprocessor register for lwcN instructions, et al.
+
+ Note that there is no load/store cp0 instructions, and
+ that FPU (cp1) instructions disassemble this field using
+ 'T' format. Therefore, until we gain understanding of
+ cp2 register names, we can simply print the register
+ numbers. */
+ (*info->fprintf_func) (info->stream, "cr%ld",
+ (l >> OP_SH_RS) & OP_MASK_RS);
+ break;
+
+ case 'G':
+ /* Control registers */
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_cp0_names[(l >> OP_SH_RS) & OP_MASK_RS]);
+ break;
+
+ default:
+ /* xgettext:c-format */
+ (*info->fprintf_func) (info->stream,
+ _("# internal error, undefined modifier (%c)"),
+ *d);
+ return;
+ }
+ }
+}
+
+/* Print the mips instruction at address MEMADDR in debugged memory,
+ on using INFO. Returns length of the instruction, in bytes.
+ BIGENDIAN must be 1 if this is big-endian code, 0 if
+ this is little-endian code. */
+
+static unsigned long
+riscv_rvc_uncompress(unsigned long rvc_insn)
+{
+ #define IS_INSN(x, op) (((x) & MASK_##op) == MATCH_##op)
+ #define EXTRACT_OPERAND(x, op) (((x) >> OP_SH_##op) & OP_MASK_##op)
+
+ int crd = EXTRACT_OPERAND(rvc_insn, CRD);
+ int crs1 = EXTRACT_OPERAND(rvc_insn, CRS1);
+ int crs2 = EXTRACT_OPERAND(rvc_insn, CRS2);
+ int crds = EXTRACT_OPERAND(rvc_insn, CRDS);
+ int crs1s = EXTRACT_OPERAND(rvc_insn, CRS1S);
+ int crs2s = EXTRACT_OPERAND(rvc_insn, CRS2S);
+ int crs2bs = EXTRACT_OPERAND(rvc_insn, CRS2BS);
+
+ int cimm6 = EXTRACT_OPERAND(rvc_insn, CIMM6);
+ int imm6 = ((int32_t)cimm6 << 26 >> 26) & (RISCV_IMM_REACH-1);
+ //int imm6lo = imm6 & ((1<<RISCV_IMMLO_BITS)-1);
+ //int imm6hi = (imm6 >> RISCV_IMMLO_BITS) & ((1<<RISCV_IMMHI_BITS)-1);
+ int imm6x4 = (((int32_t)cimm6 << 26 >> 26)*4) & (RISCV_IMM_REACH-1);
+ int imm6x4lo = imm6x4 & ((1<<RISCV_IMMLO_BITS)-1);
+ int imm6x4hi = (imm6x4 >> RISCV_IMMLO_BITS) & ((1<<RISCV_IMMHI_BITS)-1);
+ int imm6x8 = (((int32_t)cimm6 << 26 >> 26)*8) & (RISCV_IMM_REACH-1);
+ int imm6x8lo = imm6x8 & ((1<<RISCV_IMMLO_BITS)-1);
+ int imm6x8hi = (imm6x8 >> RISCV_IMMLO_BITS) & ((1<<RISCV_IMMHI_BITS)-1);
+
+ int cimm5 = EXTRACT_OPERAND(rvc_insn, CIMM5);
+ int imm5 = ((int32_t)cimm5 << 27 >> 27) & (RISCV_IMM_REACH-1);
+ int imm5lo = imm5 & ((1<<RISCV_IMMLO_BITS)-1);
+ int imm5hi = (imm5 >> RISCV_IMMLO_BITS) & ((1<<RISCV_IMMHI_BITS)-1);
+ int imm5x4 = (((int32_t)cimm5 << 27 >> 27)*4) & (RISCV_IMM_REACH-1);
+ int imm5x4lo = imm5x4 & ((1<<RISCV_IMMLO_BITS)-1);
+ int imm5x4hi = (imm5x4 >> RISCV_IMMLO_BITS) & ((1<<RISCV_IMMHI_BITS)-1);
+ int imm5x8 = (((int32_t)cimm5 << 27 >> 27)*8) & (RISCV_IMM_REACH-1);
+ int imm5x8lo = imm5x8 & ((1<<RISCV_IMMLO_BITS)-1);
+ int imm5x8hi = (imm5x8 >> RISCV_IMMLO_BITS) & ((1<<RISCV_IMMHI_BITS)-1);
+
+ int cimm10 = EXTRACT_OPERAND(rvc_insn, CIMM10);
+ int jt10 = ((int32_t)cimm10 << 22 >> 22) & ((1<<RISCV_JUMP_BITS)-1);
+
+ if(IS_INSN(rvc_insn, C_ADDI))
+ {
+ if(crd == 0)
+ {
+ if(imm6 & 0x20)
+ return MATCH_JALR_C | (LINK_REG << OP_SH_RD) | (crs1 << OP_SH_RS);
+ else if(crs1 == 1)
+ return MATCH_JALR_R | (crs1 << OP_SH_RS);
+ else
+ return MATCH_JALR_J | (crs1 << OP_SH_RS);
+ }
+ return MATCH_ADDI | (crd << OP_SH_RD) | (crd << OP_SH_RS) |
+ (imm6 << OP_SH_IMMEDIATE);
+ }
+ if(IS_INSN(rvc_insn, C_ADDIW))
+ return MATCH_ADDIW | (crd << OP_SH_RD) | (crd << OP_SH_RS) | (imm6 << OP_SH_IMMEDIATE);
+ if(IS_INSN(rvc_insn, C_LI))
+ return MATCH_ADDI | (crd << OP_SH_RD) | (imm6 << OP_SH_IMMEDIATE);
+ if(IS_INSN(rvc_insn, C_MOVE))
+ return MATCH_ADDI | (crd << OP_SH_RD) | (crs1 << OP_SH_RS);
+ if(IS_INSN(rvc_insn, C_SLLI))
+ return MATCH_SLLI | (cimm5 << OP_SH_SHAMT) | (rvc_rd_regmap[crds] << OP_SH_RD) | (rvc_rd_regmap[crds] << OP_SH_RS);
+ if(IS_INSN(rvc_insn, C_SLLI32))
+ return MATCH_SLLI | ((cimm5+32) << OP_SH_SHAMT) | (rvc_rd_regmap[crds] << OP_SH_RD) | (rvc_rd_regmap[crds] << OP_SH_RS);
+ if(IS_INSN(rvc_insn, C_SRLI))
+ return MATCH_SRLI | (cimm5 << OP_SH_SHAMT) | (rvc_rd_regmap[crds] << OP_SH_RD) | (rvc_rd_regmap[crds] << OP_SH_RS);
+ if(IS_INSN(rvc_insn, C_SRLI32))
+ return MATCH_SRLI | ((cimm5+32) << OP_SH_SHAMT) | (rvc_rd_regmap[crds] << OP_SH_RD) | (rvc_rd_regmap[crds] << OP_SH_RS);
+ if(IS_INSN(rvc_insn, C_SRAI))
+ return MATCH_SRAI | (cimm5 << OP_SH_SHAMT) | (rvc_rd_regmap[crds] << OP_SH_RD) | (rvc_rd_regmap[crds] << OP_SH_RS);
+ if(IS_INSN(rvc_insn, C_SRAI32))
+ return MATCH_SRAI | ((cimm5+32) << OP_SH_SHAMT) | (rvc_rd_regmap[crds] << OP_SH_RD) | (rvc_rd_regmap[crds] << OP_SH_RS);
+ if(IS_INSN(rvc_insn, C_SLLIW))
+ return MATCH_SLLIW | (cimm5 << OP_SH_SHAMT) | (rvc_rd_regmap[crds] << OP_SH_RD) | (rvc_rd_regmap[crds] << OP_SH_RS);
+ if(IS_INSN(rvc_insn, C_ADD))
+ return MATCH_ADD | (crd << OP_SH_RD) | (crs1 << OP_SH_RS) | (crd << OP_SH_RT);
+ if(IS_INSN(rvc_insn, C_SUB))
+ return MATCH_SUB | (crd << OP_SH_RD) | (crs1 << OP_SH_RS) | (crd << OP_SH_RT);
+ if(IS_INSN(rvc_insn, C_ADD3))
+ return MATCH_ADD | (rvc_rd_regmap[crds] << OP_SH_RD) | (rvc_rs1_regmap[crs1s] << OP_SH_RS) | (rvc_rs2b_regmap[crs2bs] << OP_SH_RT);
+ if(IS_INSN(rvc_insn, C_SUB3))
+ return MATCH_SUB | (rvc_rd_regmap[crds] << OP_SH_RD) | (rvc_rs1_regmap[crs1s] << OP_SH_RS) | (rvc_rs2b_regmap[crs2bs] << OP_SH_RT);
+ if(IS_INSN(rvc_insn, C_AND3))
+ return MATCH_AND | (rvc_rd_regmap[crds] << OP_SH_RD) | (rvc_rs1_regmap[crs1s] << OP_SH_RS) | (rvc_rs2b_regmap[crs2bs] << OP_SH_RT);
+ if(IS_INSN(rvc_insn, C_OR3))
+ return MATCH_OR | (rvc_rd_regmap[crds] << OP_SH_RD) | (rvc_rs1_regmap[crs1s] << OP_SH_RS) | (rvc_rs2b_regmap[crs2bs] << OP_SH_RT);
+ if(IS_INSN(rvc_insn, C_J))
+ return MATCH_J | (jt10 << OP_SH_TARGET);
+ if(IS_INSN(rvc_insn, C_BEQ))
+ return MATCH_BEQ | (rvc_rs1_regmap[crs1s] << OP_SH_RS) | (rvc_rs2_regmap[crs2s] << OP_SH_RT) | (imm5lo << OP_SH_IMMLO) | (imm5hi << OP_SH_IMMHI);
+ if(IS_INSN(rvc_insn, C_BNE))
+ return MATCH_BNE | (rvc_rs1_regmap[crs1s] << OP_SH_RS) | (rvc_rs2_regmap[crs2s] << OP_SH_RT) | (imm5lo << OP_SH_IMMLO) | (imm5hi << OP_SH_IMMHI);
+ if(IS_INSN(rvc_insn, C_LDSP))
+ return MATCH_LD | (30 << OP_SH_RS) | (crd << OP_SH_RD) | (imm6x8 << OP_SH_IMMEDIATE);
+ if(IS_INSN(rvc_insn, C_LWSP))
+ return MATCH_LW | (30 << OP_SH_RS) | (crd << OP_SH_RD) | (imm6x4 << OP_SH_IMMEDIATE);
+ if(IS_INSN(rvc_insn, C_SDSP))
+ return MATCH_SD | (30 << OP_SH_RS) | (crs2 << OP_SH_RT) | (imm6x8lo << OP_SH_IMMLO) | (imm6x8hi << OP_SH_IMMHI);
+ if(IS_INSN(rvc_insn, C_SWSP))
+ return MATCH_SW | (30 << OP_SH_RS) | (crs2 << OP_SH_RT) | (imm6x4lo << OP_SH_IMMLO) | (imm6x4hi << OP_SH_IMMHI);
+ if(IS_INSN(rvc_insn, C_LD))
+ return MATCH_LD | (rvc_rs1_regmap[crs1s] << OP_SH_RS) | (rvc_rd_regmap[crds] << OP_SH_RD) | (imm5x8 << OP_SH_IMMEDIATE);
+ if(IS_INSN(rvc_insn, C_LW))
+ return MATCH_LW | (rvc_rs1_regmap[crs1s] << OP_SH_RS) | (rvc_rd_regmap[crds] << OP_SH_RD) | (imm5x4 << OP_SH_IMMEDIATE);
+ if(IS_INSN(rvc_insn, C_SD))
+ return MATCH_SD | (rvc_rs1_regmap[crs1s] << OP_SH_RS) | (rvc_rs2_regmap[crs2s] << OP_SH_RT) | (imm5x8lo << OP_SH_IMMLO) | (imm5x8hi << OP_SH_IMMHI);
+ if(IS_INSN(rvc_insn, C_SW))
+ return MATCH_SW | (rvc_rs1_regmap[crs1s] << OP_SH_RS) | (rvc_rs2_regmap[crs2s] << OP_SH_RT) | (imm5x4lo << OP_SH_IMMLO) | (imm5x4hi << OP_SH_IMMHI);
+ if(IS_INSN(rvc_insn, C_LD0))
+ return MATCH_LD | (crs1 << OP_SH_RS) | (crd << OP_SH_RD);
+ if(IS_INSN(rvc_insn, C_LW0))
+ return MATCH_LW | (crs1 << OP_SH_RS) | (crd << OP_SH_RD);
+ if(IS_INSN(rvc_insn, C_FLD))
+ return MATCH_FLD | (rvc_rs1_regmap[crs1s] << OP_SH_RS) | (rvc_rd_regmap[crds] << OP_SH_RD) | (imm5x8 << OP_SH_IMMEDIATE);
+ if(IS_INSN(rvc_insn, C_FLW))
+ return MATCH_FLW | (rvc_rs1_regmap[crs1s] << OP_SH_RS) | (rvc_rd_regmap[crds] << OP_SH_RD) | (imm5x4 << OP_SH_IMMEDIATE);
+ if(IS_INSN(rvc_insn, C_FSD))
+ return MATCH_FSD | (rvc_rs1_regmap[crs1s] << OP_SH_RS) | (rvc_rs2_regmap[crs2s] << OP_SH_RT) | (imm5x8lo << OP_SH_IMMLO) | (imm5x8hi << OP_SH_IMMHI);
+ if(IS_INSN(rvc_insn, C_FSW))
+ return MATCH_FSW | (rvc_rs1_regmap[crs1s] << OP_SH_RS) | (rvc_rs2_regmap[crs2s] << OP_SH_RT) | (imm5x4lo << OP_SH_IMMLO) | (imm5x4hi << OP_SH_IMMHI);
+
+ return rvc_insn;
+}
+
+static int
+print_insn_mips (bfd_vma memaddr,
+ unsigned long int word,
+ struct disassemble_info *info)
+{
+ const struct riscv_opcode *op;
+ static bfd_boolean init = 0;
+ static const struct riscv_opcode *mips_hash[OP_MASK_OP + 1];
+ int insnlen;
+
+ /* Build a hash table to shorten the search time. */
+ if (! init)
+ {
+ unsigned int i;
+
+ for (i = 0; i <= OP_MASK_OP; i++)
+ {
+ for (op = riscv_opcodes; op < &riscv_opcodes[NUMOPCODES]; op++)
+ {
+ if (op->pinfo == INSN_MACRO
+ || (no_aliases && (op->pinfo & INSN_ALIAS)))
+ continue;
+ if (i == ((op->match >> OP_SH_OP) & OP_MASK_OP))
+ {
+ mips_hash[i] = op;
+ break;
+ }
+ }
+ }
+
+ init = 1;
+ }
+
+ insnlen = 4;
+#if 0
+ /* this enables rvc disassembly */
+ if ((word & 0x3) < 3)
+ insnlen = 2;
+#endif
+
+ if (insnlen == 2)
+ word = riscv_rvc_uncompress(word);
+
+ info->bytes_per_chunk = insnlen;
+ info->display_endian = info->endian;
+ info->insn_info_valid = 1;
+ info->branch_delay_insns = 0;
+ info->data_size = 0;
+ info->insn_type = dis_nonbranch;
+ info->target = 0;
+ info->target2 = 0;
+
+ op = mips_hash[(word >> OP_SH_OP) & OP_MASK_OP];
+ if (op != NULL)
+ {
+ for (; op < &riscv_opcodes[NUMOPCODES]; op++)
+ {
+ if (op->pinfo != INSN_MACRO
+ && !(no_aliases && (op->pinfo & INSN_ALIAS))
+ && (word & op->mask) == op->match)
+ {
+ const char *d;
+
+ (*info->fprintf_func) (info->stream, "%s", op->name);
+
+ d = op->args;
+ if (d != NULL && *d != '\0')
+ {
+ (*info->fprintf_func) (info->stream, "\t");
+ print_insn_args (d, word, memaddr, info);
+ }
+
+ return insnlen;
+ }
+ }
+ }
+
+ /* Handle undefined instructions. */
+ info->insn_type = dis_noninsn;
+ (*info->fprintf_func) (info->stream, "0x%lx", word);
+ return insnlen;
+}
+
+
+/* In an environment where we do not know the symbol type of the
+ instruction we are forced to assume that the low order bit of the
+ instructions' address may mark it as a mips16 instruction. If we
+ are single stepping, or the pc is within the disassembled function,
+ this works. Otherwise, we need a clue. Sometimes. */
+
+static int
+_print_insn_mips (bfd_vma memaddr,
+ struct disassemble_info *info,
+ enum bfd_endian endianness)
+{
+ bfd_byte buffer[INSNLEN];
+ int status;
+
+ set_default_mips_dis_options (info);
+ parse_mips_dis_options (info->disassembler_options);
+
+ status = (*info->read_memory_func) (memaddr, buffer, 2, info);
+ if(status == 0)
+ {
+ unsigned long insn;
+
+ if (endianness == BFD_ENDIAN_BIG)
+ insn = (unsigned long) bfd_getb16 (buffer);
+ else
+ insn = (unsigned long) bfd_getl16 (buffer);
+
+ if ((insn & 0x3) < 3)
+ return print_insn_mips (memaddr, insn, info);
+ }
+
+ status = (*info->read_memory_func) (memaddr, buffer, INSNLEN, info);
+ if (status == 0)
+ {
+ unsigned long insn;
+
+ if (endianness == BFD_ENDIAN_BIG)
+ insn = (unsigned long) bfd_getb32 (buffer);
+ else
+ insn = (unsigned long) bfd_getl32 (buffer);
+
+ return print_insn_mips (memaddr, insn, info);
+ }
+ else
+ {
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+}
+
+int
+print_insn_big_riscv (bfd_vma memaddr ATTRIBUTE_UNUSED,
+ struct disassemble_info *info ATTRIBUTE_UNUSED)
+{
+ assert(0);
+}
+
+int
+print_insn_little_riscv (bfd_vma memaddr, struct disassemble_info *info)
+{
+ return _print_insn_mips (memaddr, info, BFD_ENDIAN_LITTLE);
+}
+
+void
+print_mips_disassembler_options (FILE *stream)
+{
+ unsigned int i;
+
+ fprintf (stream, _("\n\
+The following MIPS specific disassembler options are supported for use\n\
+with the -M switch (multiple options should be separated by commas):\n"));
+
+ fprintf (stream, _("\n\
+ gpr-names=ABI Print GPR names according to specified ABI.\n\
+ Default: based on binary being disassembled.\n"));
+
+ fprintf (stream, _("\n\
+ fpr-names=ABI Print FPR names according to specified ABI.\n\
+ Default: numeric.\n"));
+
+ fprintf (stream, _("\n\
+ cp0-names=ARCH Print CP0 register names according to\n\
+ specified architecture.\n\
+ Default: based on binary being disassembled.\n"));
+
+ fprintf (stream, _("\n\
+ hwr-names=ARCH Print HWR names according to specified \n\
+ architecture.\n\
+ Default: based on binary being disassembled.\n"));
+
+ fprintf (stream, _("\n\
+ reg-names=ABI Print GPR and FPR names according to\n\
+ specified ABI.\n"));
+
+ fprintf (stream, _("\n\
+ reg-names=ARCH Print CP0 register and HWR names according to\n\
+ specified architecture.\n"));
+
+ fprintf (stream, _("\n\
+ For the options above, the following values are supported for \"ABI\":\n\
+ "));
+ for (i = 0; i < ARRAY_SIZE (mips_abi_choices); i++)
+ fprintf (stream, " %s", mips_abi_choices[i].name);
+ fprintf (stream, _("\n"));
+
+ fprintf (stream, _("\n\
+ For the options above, The following values are supported for \"ARCH\":\n\
+ "));
+ for (i = 0; i < ARRAY_SIZE (mips_arch_choices); i++)
+ if (*mips_arch_choices[i].name != '\0')
+ fprintf (stream, " %s", mips_arch_choices[i].name);
+ fprintf (stream, _("\n"));
+
+ fprintf (stream, _("\n"));
+}
diff -ruN binutils-2.24/opcodes/riscv-opc.c binutils-2.24-riscv/opcodes/riscv-opc.c
--- binutils-2.24/opcodes/riscv-opc.c 1969-12-31 16:00:00.000000000 -0800
+++ binutils-2.24-riscv/opcodes/riscv-opc.c 2014-12-02 16:05:44.921435015 -0800
@@ -0,0 +1,470 @@
+/* mips-opc.c -- MIPS opcode list.
+ Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
+ 2003, 2004, 2005, 2007, 2008, 2009 Free Software Foundation, Inc.
+ Contributed by Ralph Campbell and OSF
+ Commented and modified by Ian Lance Taylor, Cygnus Support
+ Extended for MIPS32 support by Anders Norlander, and by SiByte, Inc.
+ MIPS-3D, MDMX, and MIPS32 Release 2 support added by Broadcom
+ Corporation (SiByte).
+
+ This file is part of the GNU opcodes library.
+
+ This library 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, or (at your option)
+ any later version.
+
+ It 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 file; see the file COPYING. If not, write to the
+ Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#include <stdio.h>
+#include "sysdep.h"
+#include "opcode/riscv.h"
+
+/* Short hand so the lines aren't too long. */
+
+/* The order of overloaded instructions matters. Label arguments and
+ register arguments look the same. Instructions that can have either
+ for arguments must apear in the correct order in this table for the
+ assembler to pick the right one. In other words, entries with
+ immediate operands must apear after the same instruction with
+ registers.
+
+ Because of the lookup algorithm used, entries with the same opcode
+ name must be contiguous.
+
+ Many instructions are short hand for other instructions (i.e., The
+ jal <register> instruction is short for jalr <register>). */
+
+#define WR_xd INSN_WRITE_GPR_D
+#define WR_ra INSN_WRITE_GPR_RA
+#define WR_fd INSN_WRITE_FPR_D
+#define RD_xs1 INSN_READ_GPR_S
+#define RD_xs2 INSN_READ_GPR_T
+#define RD_fs1 INSN_READ_FPR_S
+#define RD_fs2 INSN_READ_FPR_T
+#define RD_fs3 INSN_READ_FPR_R
+
+#define MASK_RS (OP_MASK_RS << OP_SH_RS)
+#define MASK_RT (OP_MASK_RT << OP_SH_RT)
+#define MASK_RD (OP_MASK_RD << OP_SH_RD)
+#define MASK_IMM (OP_MASK_IMMEDIATE << OP_SH_IMMEDIATE)
+#define MASK_BIGIMM (OP_MASK_BIGIMMEDIATE << OP_SH_BIGIMMEDIATE)
+#define MASK_RM (OP_MASK_RM << OP_SH_RM)
+
+const struct riscv_opcode riscv_builtin_opcodes[] =
+{
+/* These instructions appear first so that the disassembler will find
+ them first. The assemblers uses a hash table based on the
+ instruction name anyhow. */
+/* name, args, match, mask, pinfo, pinfo2, membership */
+{"unimp", "", 0, 0xffffffff, 0 },
+{"nop", "", MATCH_ADDI, MASK_ADDI | MASK_RD | MASK_RS | MASK_IMM, 0 },
+{"li", "d,j", MATCH_ADDI, MASK_ADDI | MASK_RS, WR_xd }, /* addi */
+{"li", "d,I", 0, (int) M_LI, INSN_MACRO },
+{"move", "d,s", MATCH_ADDI, MASK_ADDI | MASK_IMM, WR_xd|RD_xs1 },
+{"b", "p", MATCH_BEQ, MASK_BEQ | MASK_RS | MASK_RT, 0 },/* beq 0,0 */
+
+{"addw", "d,s,t", MATCH_ADDW, MASK_ADDW, WR_xd|RD_xs1|RD_xs2 },
+{"addw", "d,s,j", MATCH_ADDIW, MASK_ADDIW, WR_xd|RD_xs1 },
+{"addiw", "d,s,j", MATCH_ADDIW, MASK_ADDIW, WR_xd|RD_xs1 },
+{"fadd.s", "D,S,T", MATCH_FADD_S | MASK_RM, MASK_FADD_S | MASK_RM, WR_fd|RD_fs1|RD_fs2 },
+{"fadd.s", "D,S,T,m", MATCH_FADD_S, MASK_FADD_S, WR_fd|RD_fs1|RD_fs2 },
+{"fadd.d", "D,S,T", MATCH_FADD_D | MASK_RM, MASK_FADD_D | MASK_RM, WR_fd|RD_fs1|RD_fs2 },
+{"fadd.d", "D,S,T,m", MATCH_FADD_D, MASK_FADD_D, WR_fd|RD_fs1|RD_fs2 },
+{"fsub.d", "D,S,T", MATCH_FSUB_D | MASK_RM, MASK_FSUB_D | MASK_RM, WR_fd|RD_fs1|RD_fs2 },
+{"fsub.d", "D,S,T,m", MATCH_FSUB_D, MASK_FSUB_D, WR_fd|RD_fs1|RD_fs2 },
+{"fsub.s", "D,S,T", MATCH_FSUB_S | MASK_RM, MASK_FSUB_S | MASK_RM, WR_fd|RD_fs1|RD_fs2 },
+{"fsub.s", "D,S,T,m", MATCH_FSUB_S, MASK_FSUB_S, WR_fd|RD_fs1|RD_fs2 },
+{"fmul.d", "D,S,T", MATCH_FMUL_D | MASK_RM, MASK_FMUL_D | MASK_RM, WR_fd|RD_fs1|RD_fs2 },
+{"fmul.d", "D,S,T,m", MATCH_FMUL_D, MASK_FMUL_D, WR_fd|RD_fs1|RD_fs2 },
+{"fmul.s", "D,S,T", MATCH_FMUL_S | MASK_RM, MASK_FMUL_S | MASK_RM, WR_fd|RD_fs1|RD_fs2 },
+{"fmul.s", "D,S,T,m", MATCH_FMUL_S, MASK_FMUL_S, WR_fd|RD_fs1|RD_fs2 },
+{"fdiv.d", "D,S,T", MATCH_FDIV_D | MASK_RM, MASK_FDIV_D | MASK_RM, WR_fd|RD_fs1|RD_fs2 },
+{"fdiv.d", "D,S,T,m", MATCH_FDIV_D, MASK_FDIV_D, WR_fd|RD_fs1|RD_fs2 },
+{"fdiv.s", "D,S,T", MATCH_FDIV_S | MASK_RM, MASK_FDIV_S | MASK_RM, WR_fd|RD_fs1|RD_fs2 },
+{"fdiv.s", "D,S,T,m", MATCH_FDIV_S, MASK_FDIV_S, WR_fd|RD_fs1|RD_fs2 },
+{"fsqrt.d", "D,S", MATCH_FSQRT_D | MASK_RM, MASK_FSQRT_D | MASK_RM, WR_fd|RD_fs1 },
+{"fsqrt.d", "D,S,m", MATCH_FSQRT_D, MASK_FSQRT_D, WR_fd|RD_fs1 },
+{"fsqrt.s", "D,S", MATCH_FSQRT_S | MASK_RM, MASK_FSQRT_S | MASK_RM, WR_fd|RD_fs1 },
+{"fsqrt.s", "D,S,m", MATCH_FSQRT_S, MASK_FSQRT_S, WR_fd|RD_fs1 },
+{"fmadd.s", "D,S,T,R", MATCH_FMADD_S | MASK_RM, MASK_FMADD_S | MASK_RM, WR_fd|RD_fs1|RD_fs2|RD_fs3 },
+{"fmadd.s", "D,S,T,R,m", MATCH_FMADD_S, MASK_FMADD_S, WR_fd|RD_fs1|RD_fs2|RD_fs3 },
+{"fmadd.d", "D,S,T,R", MATCH_FMADD_D | MASK_RM, MASK_FMADD_D | MASK_RM, WR_fd|RD_fs1|RD_fs2|RD_fs3 },
+{"fmadd.d", "D,S,T,R,m", MATCH_FMADD_D, MASK_FMADD_D, WR_fd|RD_fs1|RD_fs2|RD_fs3 },
+{"fnmadd.s", "D,S,T,R", MATCH_FNMADD_S | MASK_RM, MASK_FNMADD_S | MASK_RM, WR_fd|RD_fs1|RD_fs2|RD_fs3 },
+{"fnmadd.s", "D,S,T,R,m", MATCH_FNMADD_S, MASK_FNMADD_S, WR_fd|RD_fs1|RD_fs2|RD_fs3 },
+{"fnmadd.d", "D,S,T,R", MATCH_FNMADD_D | MASK_RM, MASK_FNMADD_D | MASK_RM, WR_fd|RD_fs1|RD_fs2|RD_fs3 },
+{"fnmadd.d", "D,S,T,R,m", MATCH_FNMADD_D, MASK_FNMADD_D, WR_fd|RD_fs1|RD_fs2|RD_fs3 },
+{"fmsub.s", "D,S,T,R", MATCH_FMSUB_S | MASK_RM, MASK_FMSUB_S | MASK_RM, WR_fd|RD_fs1|RD_fs2|RD_fs3 },
+{"fmsub.s", "D,S,T,R,m", MATCH_FMSUB_S, MASK_FMSUB_S, WR_fd|RD_fs1|RD_fs2|RD_fs3 },
+{"fmsub.d", "D,S,T,R", MATCH_FMSUB_D | MASK_RM, MASK_FMSUB_D | MASK_RM, WR_fd|RD_fs1|RD_fs2|RD_fs3 },
+{"fmsub.d", "D,S,T,R,m", MATCH_FMSUB_D, MASK_FMSUB_D, WR_fd|RD_fs1|RD_fs2|RD_fs3 },
+{"fnmsub.s", "D,S,T,R", MATCH_FNMSUB_S | MASK_RM, MASK_FNMSUB_S | MASK_RM, WR_fd|RD_fs1|RD_fs2|RD_fs3 },
+{"fnmsub.s", "D,S,T,R,m", MATCH_FNMSUB_S, MASK_FNMSUB_S, WR_fd|RD_fs1|RD_fs2|RD_fs3 },
+{"fnmsub.d", "D,S,T,R", MATCH_FNMSUB_D | MASK_RM, MASK_FNMSUB_D | MASK_RM, WR_fd|RD_fs1|RD_fs2|RD_fs3 },
+{"fnmsub.d", "D,S,T,R,m", MATCH_FNMSUB_D, MASK_FNMSUB_D, WR_fd|RD_fs1|RD_fs2|RD_fs3 },
+{"amoadd.d", "d,t,0(b)", MATCH_AMOADD_D, MASK_AMOADD_D, WR_xd|RD_xs1|RD_xs2 },
+{"amoswap.d", "d,t,0(b)", MATCH_AMOSWAP_D, MASK_AMOSWAP_D, WR_xd|RD_xs1|RD_xs2 },
+{"amoand.d", "d,t,0(b)", MATCH_AMOAND_D, MASK_AMOAND_D, WR_xd|RD_xs1|RD_xs2 },
+{"amoor.d", "d,t,0(b)", MATCH_AMOOR_D, MASK_AMOOR_D, WR_xd|RD_xs1|RD_xs2 },
+{"amomax.d", "d,t,0(b)", MATCH_AMOMAX_D, MASK_AMOMAX_D, WR_xd|RD_xs1|RD_xs2 },
+{"amomaxu.d", "d,t,0(b)", MATCH_AMOMAXU_D, MASK_AMOMAXU_D, WR_xd|RD_xs1|RD_xs2 },
+{"amomin.d", "d,t,0(b)", MATCH_AMOMIN_D, MASK_AMOMIN_D, WR_xd|RD_xs1|RD_xs2 },
+{"amominu.d", "d,t,0(b)", MATCH_AMOMINU_D, MASK_AMOMINU_D, WR_xd|RD_xs1|RD_xs2 },
+{"amoadd.w", "d,t,0(b)", MATCH_AMOADD_W, MASK_AMOADD_W, WR_xd|RD_xs1|RD_xs2 },
+{"amoswap.w", "d,t,0(b)", MATCH_AMOSWAP_W, MASK_AMOSWAP_W, WR_xd|RD_xs1|RD_xs2 },
+{"amoand.w", "d,t,0(b)", MATCH_AMOAND_W, MASK_AMOAND_W, WR_xd|RD_xs1|RD_xs2 },
+{"amoor.w", "d,t,0(b)", MATCH_AMOOR_W, MASK_AMOOR_W, WR_xd|RD_xs1|RD_xs2 },
+{"amomax.w", "d,t,0(b)", MATCH_AMOMAX_W, MASK_AMOMAX_W, WR_xd|RD_xs1|RD_xs2 },
+{"amomaxu.w", "d,t,0(b)", MATCH_AMOMAXU_W, MASK_AMOMAXU_W, WR_xd|RD_xs1|RD_xs2 },
+{"amomin.w", "d,t,0(b)", MATCH_AMOMIN_W, MASK_AMOMIN_W, WR_xd|RD_xs1|RD_xs2 },
+{"amominu.w", "d,t,0(b)", MATCH_AMOMINU_W, MASK_AMOMINU_W, WR_xd|RD_xs1|RD_xs2 },
+{"lr.w", "d,0(b)", MATCH_LR_W, MASK_LR_W, WR_xd|RD_xs1 },
+{"lr.d", "d,0(b)", MATCH_LR_D, MASK_LR_D, WR_xd|RD_xs1 },
+{"sc.w", "d,t,0(b)", MATCH_SC_W, MASK_SC_W, WR_xd|RD_xs1|RD_xs2 },
+{"sc.d", "d,t,0(b)", MATCH_SC_D, MASK_SC_D, WR_xd|RD_xs1|RD_xs2 },
+{"and", "d,s,t", MATCH_AND, MASK_AND, WR_xd|RD_xs1|RD_xs2 },
+{"and", "d,s,j", MATCH_ANDI, MASK_ANDI, WR_xd|RD_xs1 },
+{"andi", "d,s,j", MATCH_ANDI, MASK_ANDI, WR_xd|RD_xs1 },
+/* b is at the top of the table. */
+/* bal is at the top of the table. */
+{"beqz", "s,p", MATCH_BEQ, MASK_BEQ | MASK_RT, RD_xs1 },
+{"beq", "s,t,p", MATCH_BEQ, MASK_BEQ, RD_xs1|RD_xs2 },
+{"blez", "t,p", MATCH_BGE, MASK_BGE | MASK_RS, RD_xs2 },
+{"bgez", "s,p", MATCH_BGE, MASK_BGE | MASK_RT, RD_xs1 },
+{"ble", "t,s,p", MATCH_BGE, MASK_BGE, RD_xs1|RD_xs2 },
+{"bleu", "t,s,p", MATCH_BGEU, MASK_BGEU, RD_xs1|RD_xs2 },
+{"bge", "s,t,p", MATCH_BGE, MASK_BGE, RD_xs1|RD_xs2 },
+{"bgeu", "s,t,p", MATCH_BGEU, MASK_BGEU, RD_xs1|RD_xs2 },
+{"bltz", "s,p", MATCH_BLT, MASK_BLT | MASK_RT, RD_xs1 },
+{"bgtz", "t,p", MATCH_BLT, MASK_BLT | MASK_RS, RD_xs2 },
+{"blt", "s,t,p", MATCH_BLT, MASK_BLT, RD_xs1|RD_xs2 },
+{"bltu", "s,t,p", MATCH_BLTU, MASK_BLTU, RD_xs1|RD_xs2 },
+{"bgt", "t,s,p", MATCH_BLT, MASK_BLT, RD_xs1|RD_xs2 },
+{"bgtu", "t,s,p", MATCH_BLTU, MASK_BLTU, RD_xs1|RD_xs2 },
+{"bnez", "s,p", MATCH_BNE, MASK_BNE | MASK_RT, RD_xs1 },
+{"bne", "s,t,p", MATCH_BNE, MASK_BNE, RD_xs1|RD_xs2 },
+{"feq.d", "d,S,T", MATCH_FEQ_D, MASK_FEQ_D, WR_xd|RD_fs1|RD_fs2 },
+{"feq.s", "d,S,T", MATCH_FEQ_S, MASK_FEQ_S, WR_xd|RD_fs1|RD_fs2 },
+{"flt.d", "d,S,T", MATCH_FLT_D, MASK_FLT_D, WR_xd|RD_fs1|RD_fs2 },
+{"flt.s", "d,S,T", MATCH_FLT_S, MASK_FLT_S, WR_xd|RD_fs1|RD_fs2 },
+{"fle.d", "d,S,T", MATCH_FLE_D, MASK_FLE_D, WR_xd|RD_fs1|RD_fs2 },
+{"fle.s", "d,S,T", MATCH_FLE_S, MASK_FLE_S, WR_xd|RD_fs1|RD_fs2 },
+/* CW4010 instructions which are aliases for the cache instruction. */
+{"fcvt.d.l", "D,s", MATCH_FCVT_D_L | MASK_RM, MASK_FCVT_D_L | MASK_RM, WR_fd|RD_xs1 },
+{"fcvt.d.l", "D,s,m", MATCH_FCVT_D_L, MASK_FCVT_D_L, WR_fd|RD_xs1 },
+{"fcvt.d.s", "D,S", MATCH_FCVT_D_S, MASK_FCVT_D_S | MASK_RM, WR_fd|RD_fs1 },
+{"fcvt.d.w", "D,s", MATCH_FCVT_D_W, MASK_FCVT_D_W | MASK_RM, WR_fd|RD_xs1 },
+{"fcvt.s.l", "D,s", MATCH_FCVT_S_L | MASK_RM, MASK_FCVT_S_L | MASK_RM, WR_fd|RD_xs1 },
+{"fcvt.s.l", "D,s,m", MATCH_FCVT_S_L, MASK_FCVT_S_L, WR_fd|RD_xs1 },
+{"fcvt.s.d", "D,S", MATCH_FCVT_S_D | MASK_RM, MASK_FCVT_S_D | MASK_RM, WR_fd|RD_fs1 },
+{"fcvt.s.d", "D,S,m", MATCH_FCVT_S_D, MASK_FCVT_S_D, WR_fd|RD_fs1 },
+{"fcvt.s.w", "D,s", MATCH_FCVT_S_W | MASK_RM, MASK_FCVT_S_W | MASK_RM, WR_fd|RD_xs1 },
+{"fcvt.s.w", "D,s,m", MATCH_FCVT_S_W, MASK_FCVT_S_W, WR_fd|RD_xs1 },
+{"fcvt.d.lu", "D,s", MATCH_FCVT_D_LU | MASK_RM, MASK_FCVT_D_L | MASK_RM, WR_fd|RD_xs1 },
+{"fcvt.d.lu", "D,s,m", MATCH_FCVT_D_LU, MASK_FCVT_D_LU, WR_fd|RD_xs1 },
+{"fcvt.d.wu", "D,s", MATCH_FCVT_D_WU, MASK_FCVT_D_WU | MASK_RM, WR_fd|RD_xs1 },
+{"fcvt.s.lu", "D,s", MATCH_FCVT_S_LU | MASK_RM, MASK_FCVT_S_L | MASK_RM, WR_fd|RD_xs1 },
+{"fcvt.s.lu", "D,s,m", MATCH_FCVT_S_LU, MASK_FCVT_S_LU, WR_fd|RD_xs1 },
+{"fcvt.s.wu", "D,s", MATCH_FCVT_S_WU | MASK_RM, MASK_FCVT_S_W | MASK_RM, WR_fd|RD_xs1 },
+{"fcvt.s.wu", "D,s,m", MATCH_FCVT_S_WU, MASK_FCVT_S_WU, WR_fd|RD_xs1 },
+{"add", "d,s,t", MATCH_ADD, MASK_ADD, WR_xd|RD_xs1|RD_xs2 },
+{"add", "d,s,j", MATCH_ADDI, MASK_ADDI, WR_xd|RD_xs1 },
+{"addi", "d,s,j", MATCH_ADDI, MASK_ADDI, WR_xd|RD_xs1 },
+{"clearpcr", "E,j", MATCH_CLEARPCR, MASK_CLEARPCR | MASK_RD, 0 },
+{"clearpcr", "d,E,j", MATCH_CLEARPCR, MASK_CLEARPCR | MASK_RD, WR_xd },
+{"div", "d,s,t", MATCH_DIV, MASK_DIV, WR_xd|RD_xs1|RD_xs2 },
+{"divw", "d,s,t", MATCH_DIVW, MASK_DIVW, WR_xd|RD_xs1|RD_xs2 },
+{"divu", "d,s,t", MATCH_DIVU, MASK_DIVU, WR_xd|RD_xs1|RD_xs2 },
+{"divuw", "d,s,t", MATCH_DIVUW, MASK_DIVUW, WR_xd|RD_xs1|RD_xs2 },
+{"la", "d,A(b)", 0, (int) M_LA_AB, INSN_MACRO },
+{"la.tls.gd", "d,A", 0, (int) M_LA_TLS_GD, INSN_MACRO },
+{"la.tls.ie", "d,A", 0, (int) M_LA_TLS_IE, INSN_MACRO },
+{"mffsr", "d", MATCH_MFFSR, MASK_MFFSR, WR_xd },
+{"mtfsr", "s", MATCH_MTFSR, MASK_MTFSR | MASK_RD, RD_xs1 },
+{"mtfsr", "d,s", MATCH_MTFSR, MASK_MTFSR, WR_xd|RD_xs1 },
+{"mfpcr", "d,E", MATCH_MFPCR, MASK_MFPCR, WR_xd },
+{"mtpcr", "d,t,E", MATCH_MTPCR, MASK_MTPCR, WR_xd|RD_xs1 },
+{"mtpcr", "t,E", MATCH_MTPCR, MASK_MTPCR | MASK_RD, RD_xs1 },
+{"mftx.s", "d,S", MATCH_MFTX_S, MASK_MFTX_S, WR_xd|RD_fs1 },
+{"mftx.s", "d,s", MATCH_MFTX_S, MASK_MFTX_S, WR_xd|RD_fs1 },
+{"mxtf.s", "D,s", MATCH_MXTF_S, MASK_MXTF_S, WR_fd|RD_xs1 },
+{"mxtf.s", "d,s", MATCH_MXTF_S, MASK_MXTF_S, WR_fd|RD_xs1 },
+{"mftx.d", "d,S", MATCH_MFTX_D, MASK_MFTX_D, WR_xd|RD_fs1 },
+{"mftx.d", "d,s", MATCH_MFTX_D, MASK_MFTX_D, WR_xd|RD_fs1 },
+{"mxtf.d", "D,s", MATCH_MXTF_D, MASK_MXTF_D, WR_fd|RD_xs1 },
+{"mxtf.d", "d,s", MATCH_MXTF_D, MASK_MXTF_D, WR_fd|RD_xs1 },
+{"mul", "d,s,t", MATCH_MUL, MASK_MUL, WR_xd|RD_xs1|RD_xs2 },
+{"mulh", "d,s,t", MATCH_MULH, MASK_MULH, WR_xd|RD_xs1|RD_xs2 },
+{"mulhu", "d,s,t", MATCH_MULHU, MASK_MULHU, WR_xd|RD_xs1|RD_xs2 },
+{"mulhsu", "d,s,t", MATCH_MULHSU, MASK_MULHSU, WR_xd|RD_xs1|RD_xs2 },
+{"neg", "d,t", MATCH_SUB, MASK_SUB | MASK_RS, WR_xd|RD_xs2 }, /* sub 0 */
+{"neg", "d,t", MATCH_SUBW, MASK_SUBW | MASK_RS, WR_xd|RD_xs2 }, /* subw 0 */
+{"sll", "d,s,t", MATCH_SLL, MASK_SLL, WR_xd|RD_xs1|RD_xs2 },
+{"sll", "d,s,>", MATCH_SLLI, MASK_SLLI, WR_xd|RD_xs1 },
+{"slli", "d,s,>", MATCH_SLLI, MASK_SLLI, WR_xd|RD_xs1 },
+{"srl", "d,s,t", MATCH_SRL, MASK_SRL, WR_xd|RD_xs1|RD_xs2 },
+{"srl", "d,s,>", MATCH_SRLI, MASK_SRLI, WR_xd|RD_xs1 },
+{"srli", "d,s,>", MATCH_SRLI, MASK_SRLI, WR_xd|RD_xs1 },
+{"sra", "d,s,t", MATCH_SRA, MASK_SRA, WR_xd|RD_xs1|RD_xs2 },
+{"sra", "d,s,>", MATCH_SRAI, MASK_SRAI, WR_xd|RD_xs1 },
+{"srai", "d,s,>", MATCH_SRAI, MASK_SRAI, WR_xd|RD_xs1 },
+{"sub", "d,s,t", MATCH_SUB, MASK_SUB, WR_xd|RD_xs1|RD_xs2 },
+{"setpcr", "E,j", MATCH_SETPCR, MASK_SETPCR | MASK_RD, 0 },
+{"setpcr", "d,E,j", MATCH_SETPCR, MASK_SETPCR, WR_xd },
+{"eret", "", MATCH_ERET, MASK_ERET, 0 },
+{"cflush", "", MATCH_CFLUSH, MASK_CFLUSH, 0 },
+{"ret", "", MATCH_JALR_R | (LINK_REG << OP_SH_RS), MASK_JALR_R | MASK_RD | MASK_RS | MASK_IMM, WR_xd|RD_xs1 },
+{"jr.r", "s", MATCH_JALR_R, MASK_JALR_R | MASK_RD | MASK_IMM, WR_xd|RD_xs1 },
+{"jr.r", "s,j", MATCH_JALR_R, MASK_JALR_R | MASK_RD, WR_xd|RD_xs1 },
+{"j.r", "s", MATCH_JALR_R, MASK_JALR_R | MASK_RD | MASK_IMM, WR_xd|RD_xs1 }, /* jr */
+{"j.r", "s,j", MATCH_JALR_R, MASK_JALR_R | MASK_RD, WR_xd|RD_xs1 }, /* jr */
+{"jr", "s", MATCH_JALR_J, MASK_JALR_J | MASK_RD | MASK_IMM, WR_xd|RD_xs1 },
+{"jr", "s,j", MATCH_JALR_J, MASK_JALR_J | MASK_RD, WR_xd|RD_xs1 },
+{"j", "s", 0, (int) M_J, INSN_MACRO },
+{"j", "a", MATCH_J, MASK_J, 0 },
+{"jalr", "s", MATCH_JALR_C | (LINK_REG << OP_SH_RD), MASK_JALR_C | MASK_RD | MASK_IMM, WR_xd|RD_xs1 },
+{"jalr", "s,j", MATCH_JALR_C | (LINK_REG << OP_SH_RD), MASK_JALR_C | MASK_RD, WR_xd|RD_xs1 },
+{"jalr", "d,s", MATCH_JALR_C, MASK_JALR_C | MASK_IMM, WR_xd|RD_xs1 },
+{"jalr", "d,s,j", MATCH_JALR_C, MASK_JALR_C, WR_xd|RD_xs1 },
+{"jalr.j", "s", MATCH_JALR_J | (LINK_REG << OP_SH_RD), MASK_JALR_J | MASK_RD | MASK_IMM, WR_xd|RD_xs1 },
+{"jalr.j", "s,j", MATCH_JALR_J | (LINK_REG << OP_SH_RD), MASK_JALR_J | MASK_RD, WR_xd|RD_xs1 },
+{"jalr.j", "d,s", MATCH_JALR_J, MASK_JALR_J | MASK_IMM, WR_xd|RD_xs1 },
+{"jalr.j", "d,s,j", MATCH_JALR_J, MASK_JALR_J, WR_xd|RD_xs1 },
+{"jalr.r", "s", MATCH_JALR_R | (LINK_REG << OP_SH_RD), MASK_JALR_R | MASK_RD | MASK_IMM, WR_xd|RD_xs1 },
+{"jalr.r", "s,j", MATCH_JALR_R | (LINK_REG << OP_SH_RD), MASK_JALR_R | MASK_RD, WR_xd|RD_xs1 },
+{"jalr.r", "d,s", MATCH_JALR_R, MASK_JALR_R | MASK_IMM, WR_xd|RD_xs1 },
+{"jalr.r", "d,s,j", MATCH_JALR_R, MASK_JALR_R, WR_xd|RD_xs1 },
+/* SVR4 PIC code requires special handling for jal, so it must be a
+ macro. */
+{"jal", "a", MATCH_JAL, MASK_JAL, WR_ra },
+{"lb", "d,o(b)", MATCH_LB, MASK_LB, WR_xd|RD_xs1 },
+{"lbu", "d,o(b)", MATCH_LBU, MASK_LBU, WR_xd|RD_xs1 },
+{"lh", "d,o(b)", MATCH_LH, MASK_LH, WR_xd|RD_xs1 },
+{"lhu", "d,o(b)", MATCH_LHU, MASK_LHU, WR_xd|RD_xs1 },
+{"lw", "d,o(b)", MATCH_LW, MASK_LW, WR_xd|RD_xs1 },
+{"lwu", "d,o(b)", MATCH_LWU, MASK_LWU, WR_xd|RD_xs1 },
+{"ld", "d,o(b)", MATCH_LD, MASK_LD, WR_xd|RD_xs1 },
+{"flw", "D,o(b)", MATCH_FLW, MASK_FLW, WR_fd|RD_xs1 },
+{"fld", "D,o(b)", MATCH_FLD, MASK_FLD, WR_fd|RD_xs1 },
+{"lui", "d,u", MATCH_LUI, MASK_LUI, WR_xd },
+{"mulw", "d,s,t", MATCH_MULW, MASK_MULW, WR_xd|RD_xs1|RD_xs2 },
+{"negw", "d,t", MATCH_SUBW, MASK_SUBW | MASK_RS, WR_xd|RD_xs2 }, /* sub 0 */
+/* nop is at the start of the table. */
+{"not", "d,s", MATCH_XORI | MASK_IMM, MASK_XORI | MASK_IMM, WR_xd|RD_xs1 },/*nor d,s,0*/
+{"or", "d,s,t", MATCH_OR, MASK_OR, WR_xd|RD_xs1|RD_xs2 },
+{"or", "d,s,j", MATCH_ORI, MASK_ORI, WR_xd|RD_xs1 },
+{"ori", "d,s,j", MATCH_ORI, MASK_ORI, WR_xd|RD_xs1 },
+ /* pref and prefx are at the start of the table. */
+{"auipc", "d,u", MATCH_AUIPC, MASK_AUIPC, WR_xd },
+{"rdpc", "d", MATCH_AUIPC, MASK_AUIPC | MASK_BIGIMM, WR_xd },
+{"rem", "d,s,t", MATCH_REM, MASK_REM, WR_xd|RD_xs1|RD_xs2 },
+{"remw", "d,s,t", MATCH_REMW, MASK_REMW, WR_xd|RD_xs1|RD_xs2 },
+{"remu", "d,s,t", MATCH_REMU, MASK_REMU, WR_xd|RD_xs1|RD_xs2 },
+{"remuw", "d,s,t", MATCH_REMUW, MASK_REMUW, WR_xd|RD_xs1|RD_xs2 },
+{"fsgnj.s", "D,S,T", MATCH_FSGNJ_S, MASK_FSGNJ_S, WR_fd|RD_fs1|RD_fs2 },
+{"fsgnj.d", "D,S,T", MATCH_FSGNJ_D, MASK_FSGNJ_D, WR_fd|RD_fs1|RD_fs2 },
+{"fsgnjn.s", "D,S,T", MATCH_FSGNJN_S, MASK_FSGNJN_S, WR_fd|RD_fs1|RD_fs2 },
+{"fsgnjn.d", "D,S,T", MATCH_FSGNJN_D, MASK_FSGNJN_D, WR_fd|RD_fs1|RD_fs2 },
+{"fsgnjx.s", "D,S,T", MATCH_FSGNJX_S, MASK_FSGNJX_S, WR_fd|RD_fs1|RD_fs2 },
+{"fsgnjx.d", "D,S,T", MATCH_FSGNJX_D, MASK_FSGNJX_D, WR_fd|RD_fs1|RD_fs2 },
+{"fmin.s", "D,S,T", MATCH_FMIN_S, MASK_FMIN_S, WR_fd|RD_fs1|RD_fs2 },
+{"fmin.d", "D,S,T", MATCH_FMIN_D, MASK_FMIN_D, WR_fd|RD_fs1|RD_fs2 },
+{"fmax.s", "D,S,T", MATCH_FMAX_S, MASK_FMAX_S, WR_fd|RD_fs1|RD_fs2 },
+{"fmax.d", "D,S,T", MATCH_FMAX_D, MASK_FMAX_D, WR_fd|RD_fs1|RD_fs2 },
+{"sllw", "d,s,t", MATCH_SLLW, MASK_SLLW, WR_xd|RD_xs1|RD_xs2 },
+{"sllw", "d,s,<", MATCH_SLLIW, MASK_SLLIW, WR_xd|RD_xs1 },
+{"slliw", "d,s,<", MATCH_SLLIW, MASK_SLLIW, WR_xd|RD_xs1 },
+{"srlw", "d,s,t", MATCH_SRLW, MASK_SRLW, WR_xd|RD_xs1|RD_xs2 },
+{"srlw", "d,s,<", MATCH_SRLIW, MASK_SRLIW, WR_xd|RD_xs1 },
+{"srliw", "d,s,<", MATCH_SRLIW, MASK_SRLIW, WR_xd|RD_xs1 },
+{"sraw", "d,s,t", MATCH_SRAW, MASK_SRAW, WR_xd|RD_xs1|RD_xs2 },
+{"sraw", "d,s,<", MATCH_SRAIW, MASK_SRAIW, WR_xd|RD_xs1 },
+{"sraiw", "d,s,<", MATCH_SRAIW, MASK_SRAIW, WR_xd|RD_xs1 },
+{"slt", "d,s,t", MATCH_SLT, MASK_SLT, WR_xd|RD_xs1|RD_xs2 },
+{"slt", "d,s,j", MATCH_SLTI, MASK_SLTI, WR_xd|RD_xs1 },
+{"slti", "d,s,j", MATCH_SLTI, MASK_SLTI, WR_xd|RD_xs1 },
+{"sltu", "d,s,t", MATCH_SLTU, MASK_SLTU, WR_xd|RD_xs1|RD_xs2 },
+{"sltu", "d,s,j", MATCH_SLTIU, MASK_SLTIU, WR_xd|RD_xs1 },
+{"sltiu", "d,s,j", MATCH_SLTIU, MASK_SLTIU, WR_xd|RD_xs1 },
+{"subw", "d,s,t", MATCH_SUBW, MASK_SUBW, WR_xd|RD_xs1|RD_xs2 },
+{"sb", "t,q(b)", MATCH_SB, MASK_SB, RD_xs1|RD_xs2 },
+{"sh", "t,q(b)", MATCH_SH, MASK_SH, RD_xs1|RD_xs2 },
+{"sw", "t,q(b)", MATCH_SW, MASK_SW, RD_xs1|RD_xs2 },
+{"sd", "t,q(b)", MATCH_SD, MASK_SD, RD_xs1|RD_xs2 },
+{"fsw", "T,q(b)", MATCH_FSW, MASK_FSW, RD_xs1|RD_fs2 },
+{"fsd", "T,q(b)", MATCH_FSD, MASK_FSD, RD_xs1|RD_fs2 },
+{"fence", "", MATCH_FENCE, MASK_FENCE | MASK_RD | MASK_RS | MASK_IMM, 0 },
+{"fence.i", "", MATCH_FENCE_I, MASK_FENCE_I | MASK_RD | MASK_RS | MASK_IMM, 0 },
+{"fence.v.l", "", MATCH_FENCE_V_L, MASK_FENCE_V_L | MASK_RD | MASK_RS | MASK_IMM, 0 },
+{"fence.v.g", "", MATCH_FENCE_V_G, MASK_FENCE_V_G | MASK_RD | MASK_RS | MASK_IMM, 0 },
+{"rdcycle", "d", MATCH_RDCYCLE, MASK_RDCYCLE, WR_xd },
+{"rdinstret", "d", MATCH_RDINSTRET, MASK_RDINSTRET, WR_xd },
+{"rdtime", "d", MATCH_RDTIME, MASK_RDTIME, WR_xd },
+{"break", "", MATCH_BREAK, MASK_BREAK, 0 },
+{"syscall", "", MATCH_SYSCALL, MASK_SYSCALL, 0 },
+{"stop", "", MATCH_STOP, MASK_STOP, 0 },
+{"utidx", "d", MATCH_UTIDX, MASK_UTIDX, WR_xd },
+{"movz", "d,s,t", MATCH_MOVZ, MASK_MOVZ, WR_xd|RD_xs1|RD_xs2 },
+{"movn", "d,s,t", MATCH_MOVN, MASK_MOVN, WR_xd|RD_xs1|RD_xs2 },
+{"fmovz", "D,s,T", MATCH_FMOVZ, MASK_FMOVZ, WR_fd|RD_xs1|RD_fs2 },
+{"fmovn", "D,s,T", MATCH_FMOVN, MASK_FMOVN, WR_fd|RD_xs1|RD_fs2 },
+{"fcvt.l.d", "d,S", MATCH_FCVT_L_D | MASK_RM, MASK_FCVT_L_D | MASK_RM, WR_xd|RD_fs1 },
+{"fcvt.l.d", "d,S,m", MATCH_FCVT_L_D, MASK_FCVT_L_D, WR_xd|RD_fs1 },
+{"fcvt.l.s", "d,S", MATCH_FCVT_L_S | MASK_RM, MASK_FCVT_L_S | MASK_RM, WR_xd|RD_fs1 },
+{"fcvt.l.s", "d,S,m", MATCH_FCVT_L_S, MASK_FCVT_L_S, WR_xd|RD_fs1 },
+{"fcvt.w.d", "d,S", MATCH_FCVT_W_D | MASK_RM, MASK_FCVT_W_D | MASK_RM, WR_xd|RD_fs1 },
+{"fcvt.w.d", "d,S,m", MATCH_FCVT_W_D, MASK_FCVT_W_D, WR_xd|RD_fs1 },
+{"fcvt.w.s", "d,S", MATCH_FCVT_W_S | MASK_RM, MASK_FCVT_W_S | MASK_RM, WR_xd|RD_fs1 },
+{"fcvt.w.s", "d,S,m", MATCH_FCVT_W_S, MASK_FCVT_W_S, WR_xd|RD_fs1 },
+{"fcvt.lu.d", "d,S", MATCH_FCVT_LU_D | MASK_RM, MASK_FCVT_LU_D | MASK_RM, WR_xd|RD_fs1 },
+{"fcvt.lu.d", "d,S,m", MATCH_FCVT_LU_D, MASK_FCVT_LU_D, WR_xd|RD_fs1 },
+{"fcvt.lu.s", "d,S", MATCH_FCVT_LU_S | MASK_RM, MASK_FCVT_LU_S | MASK_RM, WR_xd|RD_fs1 },
+{"fcvt.lu.s", "d,S,m", MATCH_FCVT_LU_S, MASK_FCVT_LU_S, WR_xd|RD_fs1 },
+{"fcvt.wu.d", "d,S", MATCH_FCVT_WU_D | MASK_RM, MASK_FCVT_WU_D | MASK_RM, WR_xd|RD_fs1 },
+{"fcvt.wu.d", "d,S,m", MATCH_FCVT_WU_D, MASK_FCVT_WU_D, WR_xd|RD_fs1 },
+{"fcvt.wu.s", "d,S", MATCH_FCVT_WU_S | MASK_RM, MASK_FCVT_WU_S | MASK_RM, WR_xd|RD_fs1 },
+{"fcvt.wu.s", "d,S,m", MATCH_FCVT_WU_S, MASK_FCVT_WU_S, WR_xd|RD_fs1 },
+{"xor", "d,s,t", MATCH_XOR, MASK_XOR, WR_xd|RD_xs1|RD_xs2 },
+{"xor", "d,s,j", MATCH_XORI, MASK_XORI, WR_xd|RD_xs1 },
+{"xori", "d,s,j", MATCH_XORI, MASK_XORI, WR_xd|RD_xs1 },
+
+/* unit stride */
+/* xloads */
+{"vld", "#d,s", MATCH_VLD, MASK_VLD, 0 },
+{"vlw", "#d,s", MATCH_VLW, MASK_VLW, 0 },
+{"vlwu", "#d,s", MATCH_VLWU, MASK_VLWU, 0 },
+{"vlh", "#d,s", MATCH_VLH, MASK_VLH, 0 },
+{"vlhu", "#d,s", MATCH_VLHU, MASK_VLHU, 0 },
+{"vlb", "#d,s", MATCH_VLB, MASK_VLB, 0 },
+{"vlbu", "#d,s", MATCH_VLBU, MASK_VLBU, 0 },
+/* floads */
+{"vfld", "#D,s", MATCH_VFLD, MASK_VFLD, 0 },
+{"vflw", "#D,s", MATCH_VFLW, MASK_VFLW, 0 },
+
+/* stride */
+/* xloads */
+{"vlstd", "#d,s,t", MATCH_VLSTD, MASK_VLSTD, 0 },
+{"vlstw", "#d,s,t", MATCH_VLSTW, MASK_VLSTW, 0 },
+{"vlstwu", "#d,s,t", MATCH_VLSTWU, MASK_VLSTWU, 0 },
+{"vlsth", "#d,s,t", MATCH_VLSTH, MASK_VLSTH, 0 },
+{"vlsthu", "#d,s,t", MATCH_VLSTHU, MASK_VLSTHU, 0 },
+{"vlstb", "#d,s,t", MATCH_VLSTB, MASK_VLSTB, 0 },
+{"vlstbu", "#d,s,t", MATCH_VLSTBU, MASK_VLSTBU, 0 },
+/* floads */
+{"vflstd", "#D,s,t", MATCH_VFLSTD, MASK_VFLSTD, 0 },
+{"vflstw", "#D,s,t", MATCH_VFLSTW, MASK_VFLSTW, 0 },
+
+/* segment */
+/* xloads */
+{"vlsegd", "#d,s,#n", MATCH_VLSEGD, MASK_VLSEGD, 0 },
+{"vlsegw", "#d,s,#n", MATCH_VLSEGW, MASK_VLSEGW, 0 },
+{"vlsegwu", "#d,s,#n", MATCH_VLSEGWU, MASK_VLSEGWU, 0 },
+{"vlsegh", "#d,s,#n", MATCH_VLSEGH, MASK_VLSEGH, 0 },
+{"vlseghu", "#d,s,#n", MATCH_VLSEGHU, MASK_VLSEGHU, 0 },
+{"vlsegb", "#d,s,#n", MATCH_VLSEGB, MASK_VLSEGB, 0 },
+{"vlsegbu", "#d,s,#n", MATCH_VLSEGBU, MASK_VLSEGBU, 0 },
+/* floads */
+{"vflsegd", "#D,s,#n", MATCH_VFLSEGD, MASK_VFLSEGD, 0 },
+{"vflsegw", "#D,s,#n", MATCH_VFLSEGW, MASK_VFLSEGW, 0 },
+
+/* stride segment */
+/* xloads */
+{"vlsegstd", "#d,s,t,#m", MATCH_VLSEGSTD, MASK_VLSEGSTD, 0 },
+{"vlsegstw", "#d,s,t,#m", MATCH_VLSEGSTW, MASK_VLSEGSTW, 0 },
+{"vlsegstwu", "#d,s,t,#m", MATCH_VLSEGSTWU, MASK_VLSEGSTWU, 0 },
+{"vlsegsth", "#d,s,t,#m", MATCH_VLSEGSTH, MASK_VLSEGSTH, 0 },
+{"vlsegsthu", "#d,s,t,#m", MATCH_VLSEGSTHU, MASK_VLSEGSTHU, 0 },
+{"vlsegstb", "#d,s,t,#m", MATCH_VLSEGSTB, MASK_VLSEGSTB, 0 },
+{"vlsegstbu", "#d,s,t,#m", MATCH_VLSEGSTBU, MASK_VLSEGSTBU, 0 },
+/* floads */
+{"vflsegstd", "#D,s,t,#m", MATCH_VFLSEGSTD, MASK_VFLSEGSTD, 0 },
+{"vflsegstw", "#D,s,t,#m", MATCH_VFLSEGSTW, MASK_VFLSEGSTW, 0 },
+
+/* unit stride */
+/* xstores */
+{"vsd", "#d,s", MATCH_VSD, MASK_VSD, 0 },
+{"vsw", "#d,s", MATCH_VSW, MASK_VSW, 0 },
+{"vsh", "#d,s", MATCH_VSH, MASK_VSH, 0 },
+{"vsb", "#d,s", MATCH_VSB, MASK_VSB, 0 },
+/* fstores */
+{"vfsd", "#D,s", MATCH_VFSD, MASK_VFSD, 0 },
+{"vfsw", "#D,s", MATCH_VFSW, MASK_VFSW, 0 },
+
+/* stride */
+/* xstores */
+{"vsstd", "#d,s,t", MATCH_VSSTD, MASK_VSSTD, 0 },
+{"vsstw", "#d,s,t", MATCH_VSSTW, MASK_VSSTW, 0 },
+{"vssth", "#d,s,t", MATCH_VSSTH, MASK_VSSTH, 0 },
+{"vsstb", "#d,s,t", MATCH_VSSTB, MASK_VSSTB, 0 },
+/* fstores */
+{"vfsstd", "#D,s,t", MATCH_VFSSTD, MASK_VFSSTD, 0 },
+{"vfsstw", "#D,s,t", MATCH_VFSSTW, MASK_VFSSTW, 0 },
+
+/* segment */
+/* xstores */
+{"vssegd", "#d,s,#n", MATCH_VSSEGD, MASK_VSSEGD, 0 },
+{"vssegw", "#d,s,#n", MATCH_VSSEGW, MASK_VSSEGW, 0 },
+{"vssegh", "#d,s,#n", MATCH_VSSEGH, MASK_VSSEGH, 0 },
+{"vssegb", "#d,s,#n", MATCH_VSSEGB, MASK_VSSEGB, 0 },
+/* fstores */
+{"vfssegd", "#D,s,#n", MATCH_VFSSEGD, MASK_VFSSEGD, 0 },
+{"vfssegw", "#D,s,#n", MATCH_VFSSEGW, MASK_VFSSEGW, 0 },
+
+/* stride segment */
+/* xsegstores */
+{"vssegstd", "#d,s,t,#m", MATCH_VSSEGSTD, MASK_VSSEGSTD, 0 },
+{"vssegstw", "#d,s,t,#m", MATCH_VSSEGSTW, MASK_VSSEGSTW, 0 },
+{"vssegsth", "#d,s,t,#m", MATCH_VSSEGSTH, MASK_VSSEGSTH, 0 },
+{"vssegstb", "#d,s,t,#m", MATCH_VSSEGSTB, MASK_VSSEGSTB, 0 },
+/* fsegstores */
+{"vfssegstd", "#D,s,t,#m", MATCH_VFSSEGSTD, MASK_VFSSEGSTD, 0 },
+{"vfssegstw", "#D,s,t,#m", MATCH_VFSSEGSTW, MASK_VFSSEGSTW, 0 },
+
+{"vsetvl", "d,s", MATCH_VSETVL, MASK_VSETVL, WR_xd|RD_xs1 },
+{"vmvv", "#d,#s", MATCH_VMVV, MASK_VMVV, 0 },
+{"vmsv", "#d,s", MATCH_VMSV, MASK_VMSV, 0 },
+{"vmst", "#d,s,t", MATCH_VMST, MASK_VMST, 0 },
+{"vmts", "d,#s,t", MATCH_VMTS, MASK_VMTS, 0 },
+{"vfmvv", "#D,#S", MATCH_VFMVV, MASK_VFMVV, 0 },
+{"vfmsv", "#D,S", MATCH_VFMSV, MASK_VFMSV, 0 },
+{"vfmst", "#D,S,T", MATCH_VFMST, MASK_VFMST, 0 },
+{"vfmts", "D,#S,T", MATCH_VFMTS, MASK_VFMTS, 0 },
+
+{"vvcfg", "s,t", MATCH_VVCFG, MASK_VVCFG, RD_xs1|RD_xs2 },
+{"vtcfg", "s,t", MATCH_VTCFG, MASK_VTCFG, RD_xs1|RD_xs2 },
+
+{"vvcfgivl", "d,s,#g,#f", MATCH_VVCFGIVL, MASK_VVCFGIVL, 0 },
+{"vtcfgivl", "d,s,#g,#f", MATCH_VTCFGIVL, MASK_VTCFGIVL, 0 },
+{"vf", "j(b)", MATCH_VF, MASK_VF, 0 },
+
+{"venqcmd", "s,t", MATCH_VENQCMD, MASK_VENQCMD, RD_xs1|RD_xs2 },
+{"venqimm1", "s,t", MATCH_VENQIMM1, MASK_VENQIMM1, RD_xs1|RD_xs2 },
+{"venqimm2", "s,t", MATCH_VENQIMM2, MASK_VENQIMM2, RD_xs1|RD_xs2 },
+{"venqcnt", "s,t", MATCH_VENQCNT, MASK_VENQCNT, RD_xs1|RD_xs2 },
+
+{"vxcptsave", "s", MATCH_VXCPTSAVE, MASK_VXCPTSAVE, RD_xs1 },
+{"vxcptrestore","s", MATCH_VXCPTRESTORE, MASK_VXCPTRESTORE, RD_xs1 },
+{"vxcptevac", "s", MATCH_VXCPTEVAC, MASK_VXCPTEVAC, RD_xs1 },
+{"vxcptkill", "", MATCH_VXCPTKILL, MASK_VXCPTKILL, 0 },
+{"vxcpthold", "", MATCH_VXCPTHOLD, MASK_VXCPTHOLD, 0 },
+};
+
+#define RISCV_NUM_OPCODES \
+ ((sizeof riscv_builtin_opcodes) / (sizeof (riscv_builtin_opcodes[0])))
+const int bfd_riscv_num_builtin_opcodes = RISCV_NUM_OPCODES;
+
+/* const removed from the following to allow for dynamic extensions to the
+ * built-in instruction set. */
+struct riscv_opcode *riscv_opcodes =
+ (struct riscv_opcode *) riscv_builtin_opcodes;
+int bfd_riscv_num_opcodes = RISCV_NUM_OPCODES;
+#undef RISCV_NUM_OPCODES