| 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 (®info, 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, ®info, &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*) ®_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, ®no ); |
| + if ( !ok ) |
| + as_bad( _( "Invalid vector register" ) ); |
| + INSERT_OPERAND( VRD, *ip, regno ); |
| + continue; |
| + case 's': |
| + ok = reg_lookup( &s, RTYPE_NUM|RTYPE_VGR_REG, ®no ); |
| + if ( !ok ) |
| + as_bad( _( "Invalid vector register" ) ); |
| + INSERT_OPERAND( VRS, *ip, regno ); |
| + continue; |
| + case 't': |
| + ok = reg_lookup( &s, RTYPE_NUM|RTYPE_VGR_REG, ®no ); |
| + if ( !ok ) |
| + as_bad( _( "Invalid vector register" ) ); |
| + INSERT_OPERAND( VRT, *ip, regno ); |
| + continue; |
| + case 'r': |
| + ok = reg_lookup( &s, RTYPE_NUM|RTYPE_VGR_REG, ®no ); |
| + if ( !ok ) |
| + as_bad( _( "Invalid vector register" ) ); |
| + INSERT_OPERAND( VRR, *ip, regno ); |
| + continue; |
| + case 'D': |
| + ok = reg_lookup( &s, RTYPE_NUM|RTYPE_VFP_REG, ®no ); |
| + if ( !ok ) |
| + as_bad( _( "Invalid vector register" ) ); |
| + INSERT_OPERAND( VFD, *ip, regno ); |
| + continue; |
| + case 'S': |
| + ok = reg_lookup( &s, RTYPE_NUM|RTYPE_VFP_REG, ®no ); |
| + if ( !ok ) |
| + as_bad( _( "Invalid vector register" ) ); |
| + INSERT_OPERAND( VFS, *ip, regno ); |
| + continue; |
| + case 'T': |
| + ok = reg_lookup( &s, RTYPE_NUM|RTYPE_VFP_REG, ®no ); |
| + if ( !ok ) |
| + as_bad( _( "Invalid vector register" ) ); |
| + INSERT_OPERAND( VFT, *ip, regno ); |
| + continue; |
| + case 'R': |
| + ok = reg_lookup( &s, RTYPE_NUM|RTYPE_VFP_REG, ®no ); |
| + 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, ®no); |
| + 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, ®no); |
| + 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, ®no)) |
| + { |
| + 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 (®name, RTYPE_GP | RTYPE_NUM, ®)) |
| + 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 |