diff options
119 files changed, 28734 insertions, 18 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 7e41ce8..ce6fc95 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,32 @@ +2013-12-13 Kuan-Lin Chen <kuanlinchentw@gmail.com> + Wei-Cheng Wang <cole945@gmail.com> + + * Makefile.am (BFD32_BACKENDS, BFD32_BACKENDS_CFILES): Add nds32 + files. + * Makefile.in: Regenerate. + * archures.c (bfd_nds32_arch): Add nds32 target. + * bfd-in2.h: Regenerate. + * config.bfd (nds32*le-*-linux): Add bfd_elf32_nds32lelin_vec + and bfd_elf32_nds32belin_vec. + (nds32*be-*-linux*): Likewise. + (nds32*le-*-*): Add bfd_elf32_nds32le_vec and bfd_elf32_nds32be_vec. + (nds32*be-*-*): Likewise. + * configure.in (bfd_elf32_nds32be_vec): Add elf32-nds32.lo. + (bfd_elf32_nds32le_vec): Likewise. + (bfd_elf32_nds32belin_vec): Likewise. + (bfd_elf32_nds32lelin_vec): Likewise. + * configure: Regenerate. + * cpu-nds32.c: New file for nds32. + * elf-bfd.h: Add NDS32_ELF_DATA. + * elf32-nds32.c: New file for nds32. + * elf32-nds32.h: New file for nds32. + * libbfd.h: Regenerate. + * reloc.c: Add relocations for nds32. + * targets.c (bfd_elf32_nds32be_vec): New declaration for nds32. + (bfd_elf32_nds32le_vec): Likewise. + (bfd_elf32_nds32belin_vec): Likewise. + (bfd_elf32_nds32lelin_vec): Likewise. + 2013-12-12 H.J. Lu <hongjiu.lu@intel.com> PR binutils/16318 diff --git a/bfd/Makefile.am b/bfd/Makefile.am index 6f0e477..1183e3e 100644 --- a/bfd/Makefile.am +++ b/bfd/Makefile.am @@ -136,6 +136,7 @@ ALL_MACHINES = \ cpu-moxie.lo \ cpu-msp430.lo \ cpu-mt.lo \ + cpu-nds32.lo \ cpu-nios2.lo \ cpu-ns32k.lo \ cpu-openrisc.lo \ @@ -220,6 +221,7 @@ ALL_MACHINES_CFILES = \ cpu-moxie.c \ cpu-msp430.c \ cpu-mt.c \ + cpu-nds32.c \ cpu-ns32k.c \ cpu-nios2.c \ cpu-openrisc.c \ @@ -349,6 +351,7 @@ BFD32_BACKENDS = \ elf32-moxie.lo \ elf32-msp430.lo \ elf32-mt.lo \ + elf32-nds32.lo \ elf32-nios2.lo \ elf32-openrisc.lo \ elf32-or32.lo \ @@ -537,6 +540,7 @@ BFD32_BACKENDS_CFILES = \ elf32-moxie.c \ elf32-msp430.c \ elf32-mt.c \ + elf32-nds32.c \ elf32-nios2.c \ elf32-openrisc.c \ elf32-or32.c \ diff --git a/bfd/Makefile.in b/bfd/Makefile.in index 8a38a765..a81da7c 100644 --- a/bfd/Makefile.in +++ b/bfd/Makefile.in @@ -437,6 +437,7 @@ ALL_MACHINES = \ cpu-moxie.lo \ cpu-msp430.lo \ cpu-mt.lo \ + cpu-nds32.lo \ cpu-nios2.lo \ cpu-ns32k.lo \ cpu-openrisc.lo \ @@ -521,6 +522,7 @@ ALL_MACHINES_CFILES = \ cpu-moxie.c \ cpu-msp430.c \ cpu-mt.c \ + cpu-nds32.c \ cpu-ns32k.c \ cpu-nios2.c \ cpu-openrisc.c \ @@ -651,6 +653,7 @@ BFD32_BACKENDS = \ elf32-moxie.lo \ elf32-msp430.lo \ elf32-mt.lo \ + elf32-nds32.lo \ elf32-nios2.lo \ elf32-openrisc.lo \ elf32-or32.lo \ @@ -839,6 +842,7 @@ BFD32_BACKENDS_CFILES = \ elf32-moxie.c \ elf32-msp430.c \ elf32-mt.c \ + elf32-nds32.c \ elf32-nios2.c \ elf32-openrisc.c \ elf32-or32.c \ @@ -1352,6 +1356,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-moxie.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-msp430.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-mt.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-nds32.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-nios2.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-ns32k.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-openrisc.Plo@am__quote@ @@ -1442,6 +1447,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-moxie.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-msp430.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-mt.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-nds32.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-nios2.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-openrisc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-or32.Plo@am__quote@ diff --git a/bfd/archures.c b/bfd/archures.c index 97c540a..8a0b23a 100644 --- a/bfd/archures.c +++ b/bfd/archures.c @@ -316,6 +316,12 @@ DESCRIPTION .#define bfd_mach_arm_ep9312 11 .#define bfd_mach_arm_iWMMXt 12 .#define bfd_mach_arm_iWMMXt2 13 +. bfd_arch_nds32, {* Andes NDS32 *} +.#define bfd_mach_n1 1 +.#define bfd_mach_n1h 2 +.#define bfd_mach_n1h_v2 3 +.#define bfd_mach_n1h_v3 4 +.#define bfd_mach_n1h_v3m 5 . bfd_arch_ns32k, {* National Semiconductors ns32000 *} . bfd_arch_w65, {* WDC 65816 *} . bfd_arch_tic30, {* Texas Instruments TMS320C30 *} @@ -574,6 +580,7 @@ extern const bfd_arch_info_type bfd_mn10300_arch; extern const bfd_arch_info_type bfd_moxie_arch; extern const bfd_arch_info_type bfd_msp430_arch; extern const bfd_arch_info_type bfd_mt_arch; +extern const bfd_arch_info_type bfd_nds32_arch; extern const bfd_arch_info_type bfd_nios2_arch; extern const bfd_arch_info_type bfd_ns32k_arch; extern const bfd_arch_info_type bfd_openrisc_arch; @@ -663,6 +670,7 @@ static const bfd_arch_info_type * const bfd_archures_list[] = &bfd_moxie_arch, &bfd_msp430_arch, &bfd_mt_arch, + &bfd_nds32_arch, &bfd_nios2_arch, &bfd_ns32k_arch, &bfd_openrisc_arch, diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index 846116b..71996db 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -2073,6 +2073,12 @@ enum bfd_architecture #define bfd_mach_arm_ep9312 11 #define bfd_mach_arm_iWMMXt 12 #define bfd_mach_arm_iWMMXt2 13 + bfd_arch_nds32, /* Andes NDS32 */ +#define bfd_mach_n1 1 +#define bfd_mach_n1h 2 +#define bfd_mach_n1h_v2 3 +#define bfd_mach_n1h_v3 4 +#define bfd_mach_n1h_v3m 5 bfd_arch_ns32k, /* National Semiconductors ns32000 */ bfd_arch_w65, /* WDC 65816 */ bfd_arch_tic30, /* Texas Instruments TMS320C30 */ @@ -3796,6 +3802,178 @@ add3, load, and store instructions. */ BFD_RELOC_M32R_GOTPC_HI_SLO, BFD_RELOC_M32R_GOTPC_LO, +/* NDS32 relocs. +This is a 20 bit absolute address. */ + BFD_RELOC_NDS32_20, + +/* This is a 9-bit pc-relative reloc with the right 1 bit assumed to be 0. */ + BFD_RELOC_NDS32_9_PCREL, + +/* This is a 9-bit pc-relative reloc with the right 1 bit assumed to be 0. */ + BFD_RELOC_NDS32_WORD_9_PCREL, + +/* This is an 15-bit reloc with the right 1 bit assumed to be 0. */ + BFD_RELOC_NDS32_15_PCREL, + +/* This is an 17-bit reloc with the right 1 bit assumed to be 0. */ + BFD_RELOC_NDS32_17_PCREL, + +/* This is a 25-bit reloc with the right 1 bit assumed to be 0. */ + BFD_RELOC_NDS32_25_PCREL, + +/* This is a 20-bit reloc containing the high 20 bits of an address +used with the lower 12 bits */ + BFD_RELOC_NDS32_HI20, + +/* This is a 12-bit reloc containing the lower 12 bits of an address +then shift right by 3. This is used with ldi,sdi... */ + BFD_RELOC_NDS32_LO12S3, + +/* This is a 12-bit reloc containing the lower 12 bits of an address +then shift left by 2. This is used with lwi,swi... */ + BFD_RELOC_NDS32_LO12S2, + +/* This is a 12-bit reloc containing the lower 12 bits of an address +then shift left by 1. This is used with lhi,shi... */ + BFD_RELOC_NDS32_LO12S1, + +/* This is a 12-bit reloc containing the lower 12 bits of an address +then shift left by 0. This is used with lbisbi... */ + BFD_RELOC_NDS32_LO12S0, + +/* This is a 12-bit reloc containing the lower 12 bits of an address +then shift left by 0. This is only used with branch relaxations */ + BFD_RELOC_NDS32_LO12S0_ORI, + +/* This is a 15-bit reloc containing the small data area 18-bit signed offset +and shift left by 3 for use in ldi, sdi... */ + BFD_RELOC_NDS32_SDA15S3, + +/* This is a 15-bit reloc containing the small data area 17-bit signed offset +and shift left by 2 for use in lwi, swi... */ + BFD_RELOC_NDS32_SDA15S2, + +/* This is a 15-bit reloc containing the small data area 16-bit signed offset +and shift left by 1 for use in lhi, shi... */ + BFD_RELOC_NDS32_SDA15S1, + +/* This is a 15-bit reloc containing the small data area 15-bit signed offset +and shift left by 0 for use in lbi, sbi... */ + BFD_RELOC_NDS32_SDA15S0, + +/* This is a 16-bit reloc containing the small data area 16-bit signed offset +and shift left by 3 */ + BFD_RELOC_NDS32_SDA16S3, + +/* This is a 17-bit reloc containing the small data area 17-bit signed offset +and shift left by 2 for use in lwi.gp, swi.gp... */ + BFD_RELOC_NDS32_SDA17S2, + +/* This is a 18-bit reloc containing the small data area 18-bit signed offset +and shift left by 1 for use in lhi.gp, shi.gp... */ + BFD_RELOC_NDS32_SDA18S1, + +/* This is a 19-bit reloc containing the small data area 19-bit signed offset +and shift left by 0 for use in lbi.gp, sbi.gp... */ + BFD_RELOC_NDS32_SDA19S0, + +/* for PIC */ + BFD_RELOC_NDS32_GOT20, + BFD_RELOC_NDS32_9_PLTREL, + BFD_RELOC_NDS32_25_PLTREL, + BFD_RELOC_NDS32_COPY, + BFD_RELOC_NDS32_GLOB_DAT, + BFD_RELOC_NDS32_JMP_SLOT, + BFD_RELOC_NDS32_RELATIVE, + BFD_RELOC_NDS32_GOTOFF, + BFD_RELOC_NDS32_GOTOFF_HI20, + BFD_RELOC_NDS32_GOTOFF_LO12, + BFD_RELOC_NDS32_GOTPC20, + BFD_RELOC_NDS32_GOT_HI20, + BFD_RELOC_NDS32_GOT_LO12, + BFD_RELOC_NDS32_GOTPC_HI20, + BFD_RELOC_NDS32_GOTPC_LO12, + +/* for relax */ + BFD_RELOC_NDS32_INSN16, + BFD_RELOC_NDS32_LABEL, + BFD_RELOC_NDS32_LONGCALL1, + BFD_RELOC_NDS32_LONGCALL2, + BFD_RELOC_NDS32_LONGCALL3, + BFD_RELOC_NDS32_LONGJUMP1, + BFD_RELOC_NDS32_LONGJUMP2, + BFD_RELOC_NDS32_LONGJUMP3, + BFD_RELOC_NDS32_LOADSTORE, + BFD_RELOC_NDS32_9_FIXED, + BFD_RELOC_NDS32_15_FIXED, + BFD_RELOC_NDS32_17_FIXED, + BFD_RELOC_NDS32_25_FIXED, + +/* for PIC */ + BFD_RELOC_NDS32_PLTREL_HI20, + BFD_RELOC_NDS32_PLTREL_LO12, + BFD_RELOC_NDS32_PLT_GOTREL_HI20, + BFD_RELOC_NDS32_PLT_GOTREL_LO12, + +/* for floating point */ + BFD_RELOC_NDS32_SDA12S2_DP, + BFD_RELOC_NDS32_SDA12S2_SP, + BFD_RELOC_NDS32_LO12S2_DP, + BFD_RELOC_NDS32_LO12S2_SP, + +/* for dwarf2 debug_line. */ + BFD_RELOC_NDS32_DWARF2_OP1, + BFD_RELOC_NDS32_DWARF2_OP2, + BFD_RELOC_NDS32_DWARF2_LEB, + +/* for eliminate 16-bit instructions */ + BFD_RELOC_NDS32_UPDATE_TA, + +/* for PIC object relaxation */ + BFD_RELOC_NDS32_PLT_GOTREL_LO20, + BFD_RELOC_NDS32_PLT_GOTREL_LO15, + BFD_RELOC_NDS32_PLT_GOTREL_LO19, + BFD_RELOC_NDS32_GOT_LO15, + BFD_RELOC_NDS32_GOT_LO19, + BFD_RELOC_NDS32_GOTOFF_LO15, + BFD_RELOC_NDS32_GOTOFF_LO19, + BFD_RELOC_NDS32_GOT15S2, + BFD_RELOC_NDS32_GOT17S2, + +/* NDS32 relocs. +This is a 5 bit absolute address. */ + BFD_RELOC_NDS32_5, + +/* This is a 10-bit unsigned pc-relative reloc with the right 1 bit assumed to be 0. */ + BFD_RELOC_NDS32_10_UPCREL, + +/* If fp were omitted, fp can used as another gp. */ + BFD_RELOC_NDS32_SDA_FP7U2_RELA, + +/* relaxation relative relocation types */ + BFD_RELOC_NDS32_RELAX_ENTRY, + BFD_RELOC_NDS32_GOT_SUFF, + BFD_RELOC_NDS32_GOTOFF_SUFF, + BFD_RELOC_NDS32_PLT_GOT_SUFF, + BFD_RELOC_NDS32_MULCALL_SUFF, + BFD_RELOC_NDS32_PTR, + BFD_RELOC_NDS32_PTR_COUNT, + BFD_RELOC_NDS32_PTR_RESOLVED, + BFD_RELOC_NDS32_PLTBLOCK, + BFD_RELOC_NDS32_RELAX_REGION_BEGIN, + BFD_RELOC_NDS32_RELAX_REGION_END, + BFD_RELOC_NDS32_MINUEND, + BFD_RELOC_NDS32_SUBTRAHEND, + BFD_RELOC_NDS32_DIFF8, + BFD_RELOC_NDS32_DIFF16, + BFD_RELOC_NDS32_DIFF32, + BFD_RELOC_NDS32_DIFF_ULEB128, + BFD_RELOC_NDS32_25_ABS, + BFD_RELOC_NDS32_DATA, + BFD_RELOC_NDS32_TRAN, + BFD_RELOC_NDS32_17IFC_PCREL, + BFD_RELOC_NDS32_10IFCU_PCREL, + /* This is a 9-bit reloc */ BFD_RELOC_V850_9_PCREL, diff --git a/bfd/config.bfd b/bfd/config.bfd index 5324d39..4edcc6a 100644 --- a/bfd/config.bfd +++ b/bfd/config.bfd @@ -109,6 +109,7 @@ m68*) targ_archs=bfd_m68k_arch ;; m88*) targ_archs=bfd_m88k_arch ;; microblaze*) targ_archs=bfd_microblaze_arch ;; mips*) targ_archs=bfd_mips_arch ;; +nds32*) targ_archs=bfd_nds32_arch ;; nios2*) targ_archs=bfd_nios2_arch ;; or32*) targ_archs=bfd_or32_arch ;; pdp11*) targ_archs=bfd_pdp11_arch ;; @@ -1120,6 +1121,26 @@ case "${targ}" in targ_selvecs=bfd_elf32_msp430_ti_vec ;; + nds32*le-*-linux*) + targ_defvec=bfd_elf32_nds32lelin_vec + targ_selvecs=bfd_elf32_nds32belin_vec + ;; + + nds32*be-*-linux*) + targ_defvec=bfd_elf32_nds32belin_vec + targ_selvecs=bfd_elf32_nds32lelin_vec + ;; + + nds32*le-*-*) + targ_defvec=bfd_elf32_nds32le_vec + targ_selvecs=bfd_elf32_nds32be_vec + ;; + + nds32*be-*-*) + targ_defvec=bfd_elf32_nds32be_vec + targ_selvecs=bfd_elf32_nds32le_vec + ;; + ns32k-pc532-mach* | ns32k-pc532-ux*) targ_defvec=pc532machaout_vec targ_underscore=yes diff --git a/bfd/configure b/bfd/configure index bdda6dc..382dd01 100755 --- a/bfd/configure +++ b/bfd/configure @@ -15307,6 +15307,10 @@ do tb="$tb elfn32-mips.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;; bfd_elf32_ntradlittlemips_vec | bfd_elf32_ntradlittlemips_freebsd_vec) tb="$tb elfn32-mips.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;; + bfd_elf32_nds32be_vec) tb="$tb elf32-nds32.lo elf32.lo $elf" ;; + bfd_elf32_nds32le_vec) tb="$tb elf32-nds32.lo elf32.lo $elf" ;; + bfd_elf32_nds32belin_vec) tb="$tb elf32-nds32.lo elf32.lo $elf" ;; + bfd_elf32_nds32lelin_vec) tb="$tb elf32-nds32.lo elf32.lo $elf" ;; bfd_elf32_openrisc_vec) tb="$tb elf32-openrisc.lo elf32.lo $elf" ;; bfd_elf32_or32_big_vec) tb="$tb elf32-or32.lo elf32.lo $elf" ;; bfd_elf32_pj_vec) tb="$tb elf32-pj.lo elf32.lo $elf";; diff --git a/bfd/configure.in b/bfd/configure.in index 748c293..0d41867 100644 --- a/bfd/configure.in +++ b/bfd/configure.in @@ -796,6 +796,10 @@ do tb="$tb elfn32-mips.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;; bfd_elf32_ntradlittlemips_vec | bfd_elf32_ntradlittlemips_freebsd_vec) tb="$tb elfn32-mips.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;; + bfd_elf32_nds32be_vec) tb="$tb elf32-nds32.lo elf32.lo $elf" ;; + bfd_elf32_nds32le_vec) tb="$tb elf32-nds32.lo elf32.lo $elf" ;; + bfd_elf32_nds32belin_vec) tb="$tb elf32-nds32.lo elf32.lo $elf" ;; + bfd_elf32_nds32lelin_vec) tb="$tb elf32-nds32.lo elf32.lo $elf" ;; bfd_elf32_openrisc_vec) tb="$tb elf32-openrisc.lo elf32.lo $elf" ;; bfd_elf32_or32_big_vec) tb="$tb elf32-or32.lo elf32.lo $elf" ;; bfd_elf32_pj_vec) tb="$tb elf32-pj.lo elf32.lo $elf";; diff --git a/bfd/cpu-nds32.c b/bfd/cpu-nds32.c new file mode 100644 index 0000000..d5b2bf4 --- /dev/null +++ b/bfd/cpu-nds32.c @@ -0,0 +1,45 @@ +/* BFD support for the NDS32 processor + Copyright (C) 2012-2013 Free Software Foundation, Inc. + Contributed by Andes Technology Corporation. + + 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" +#include "elf-bfd.h" + +#define N(number, print, default, next) \ + {32, 32, 8, bfd_arch_nds32, number, "nds32", print, 4, default, \ + bfd_default_compatible, bfd_default_scan, bfd_arch_default_fill, next } + +#define NEXT &arch_info_struct[0] +#define NDS32V2_NEXT &arch_info_struct[1] +#define NDS32V3_NEXT &arch_info_struct[2] +#define NDS32V3M_NEXT &arch_info_struct[3] + +static const bfd_arch_info_type arch_info_struct[] = +{ + N (bfd_mach_n1h, "n1h", FALSE, NDS32V2_NEXT), + N (bfd_mach_n1h_v2, "n1h_v2", FALSE, NDS32V3_NEXT), + N (bfd_mach_n1h_v3, "n1h_v3", FALSE, NDS32V3M_NEXT), + N (bfd_mach_n1h_v3m, "n1h_v3m", FALSE, NULL), +}; + +const bfd_arch_info_type bfd_nds32_arch = + N (bfd_mach_n1, "n1h", TRUE, NEXT); diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index 15dab94..0446725 100644 --- a/bfd/elf-bfd.h +++ b/bfd/elf-bfd.h @@ -419,6 +419,7 @@ enum elf_target_id MICROBLAZE_ELF_DATA, MIPS_ELF_DATA, MN10300_ELF_DATA, + NDS32_ELF_DATA, NIOS2_ELF_DATA, PPC32_ELF_DATA, PPC64_ELF_DATA, diff --git a/bfd/elf32-nds32.c b/bfd/elf32-nds32.c new file mode 100644 index 0000000..c9c372a --- /dev/null +++ b/bfd/elf32-nds32.c @@ -0,0 +1,14271 @@ +/* NDS32-specific support for 32-bit ELF. + Copyright (C) 2012-2013 Free Software Foundation, Inc. + Contributed by Andes Technology Corporation. + + 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 "bfd_stdint.h" +#include "bfdlink.h" +#include "libbfd.h" +#include "elf-bfd.h" +#include "libiberty.h" +#include "bfd_stdint.h" +#include "elf/nds32.h" +#include "opcode/nds32.h" +#include "elf32-nds32.h" +#include "opcode/cgen.h" +#include "../opcodes/nds32-opc.h" + +/* Relocation HOWTO functions. */ +static bfd_reloc_status_type nds32_elf_ignore_reloc + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nds32_elf_9_pcrel_reloc + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nds32_elf_hi20_reloc + (bfd *, arelent *, asymbol *, void *, + asection *, bfd *, char **); +static bfd_reloc_status_type nds32_elf_lo12_reloc + (bfd *, arelent *, asymbol *, void *, + asection *, bfd *, char **); +static bfd_reloc_status_type nds32_elf_generic_reloc + (bfd *, arelent *, asymbol *, void *, + asection *, bfd *, char **); +static bfd_reloc_status_type nds32_elf_sda15_reloc + (bfd *, arelent *, asymbol *, void *, + asection *, bfd *, char **); + +/* Helper functions for HOWTO. */ +static bfd_reloc_status_type nds32_elf_do_9_pcrel_reloc + (bfd *, reloc_howto_type *, asection *, bfd_byte *, bfd_vma, + asection *, bfd_vma, bfd_vma); +static void nds32_elf_relocate_hi20 + (bfd *, int, Elf_Internal_Rela *, Elf_Internal_Rela *, bfd_byte *, bfd_vma); +static reloc_howto_type *bfd_elf32_bfd_reloc_type_table_lookup + (enum elf_nds32_reloc_type); +static reloc_howto_type *bfd_elf32_bfd_reloc_type_lookup + (bfd *, bfd_reloc_code_real_type); + +/* Target hooks. */ +static void nds32_info_to_howto_rel + (bfd *, arelent *, Elf_Internal_Rela *dst); +static void nds32_info_to_howto + (bfd *, arelent *, Elf_Internal_Rela *dst); +static bfd_boolean nds32_elf_add_symbol_hook + (bfd *, struct bfd_link_info *, Elf_Internal_Sym *, const char **, + flagword *, asection **, bfd_vma *); +static bfd_boolean nds32_elf_relocate_section + (bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *, + Elf_Internal_Rela *, Elf_Internal_Sym *, asection **); +static bfd_boolean nds32_elf_object_p (bfd *); +static void nds32_elf_final_write_processing (bfd *, bfd_boolean); +static bfd_boolean nds32_elf_set_private_flags (bfd *, flagword); +static bfd_boolean nds32_elf_merge_private_bfd_data (bfd *, bfd *); +static bfd_boolean nds32_elf_print_private_bfd_data (bfd *, void *); +static bfd_boolean nds32_elf_gc_sweep_hook + (bfd *, struct bfd_link_info *, asection *, const Elf_Internal_Rela *); +static bfd_boolean nds32_elf_check_relocs + (bfd *, struct bfd_link_info *, asection *, const Elf_Internal_Rela *); +static asection *nds32_elf_gc_mark_hook + (asection *, struct bfd_link_info *, Elf_Internal_Rela *, + struct elf_link_hash_entry *, Elf_Internal_Sym *); +static bfd_boolean nds32_elf_adjust_dynamic_symbol + (struct bfd_link_info *, struct elf_link_hash_entry *); +static bfd_boolean nds32_elf_size_dynamic_sections + (bfd *, struct bfd_link_info *); +static bfd_boolean nds32_elf_create_dynamic_sections + (bfd *, struct bfd_link_info *); +static bfd_boolean nds32_elf_finish_dynamic_sections + (bfd *, struct bfd_link_info *info); +static bfd_boolean nds32_elf_finish_dynamic_symbol + (bfd *, struct bfd_link_info *, struct elf_link_hash_entry *, + Elf_Internal_Sym *); + +/* Nds32 helper functions. */ +static bfd_reloc_status_type nds32_elf_final_sda_base + (bfd *, struct bfd_link_info *, bfd_vma *, bfd_boolean); +static bfd_boolean allocate_dynrelocs (struct elf_link_hash_entry *, void *); +static bfd_boolean readonly_dynrelocs (struct elf_link_hash_entry *, void *); +static Elf_Internal_Rela *find_relocs_at_address + (Elf_Internal_Rela *, Elf_Internal_Rela *, + Elf_Internal_Rela *, enum elf_nds32_reloc_type); +static bfd_vma calculate_memory_address + (bfd *, Elf_Internal_Rela *, Elf_Internal_Sym *, Elf_Internal_Shdr *); +static int nds32_get_section_contents (bfd *, asection *, bfd_byte **); +static bfd_boolean nds32_elf_ex9_build_hash_table + (bfd *, asection *, struct bfd_link_info *); +static void nds32_elf_get_insn_with_reg + (Elf_Internal_Rela *, unsigned long, unsigned long *); +static int nds32_get_local_syms (bfd *, asection *ATTRIBUTE_UNUSED, + Elf_Internal_Sym **); +static bfd_boolean nds32_elf_ex9_replace_instruction + (struct bfd_link_info *, bfd *, asection *); +static bfd_boolean nds32_elf_ifc_calc (struct bfd_link_info *, bfd *, + asection *); +static bfd_boolean nds32_elf_ifc_replace (struct bfd_link_info *); +static bfd_boolean nds32_relax_fp_as_gp + (struct bfd_link_info *link_info, bfd *abfd, asection *sec, + Elf_Internal_Rela *internal_relocs, Elf_Internal_Rela *irelend, + Elf_Internal_Sym *isymbuf); +static bfd_boolean nds32_fag_remove_unused_fpbase + (bfd *abfd, asection *sec, Elf_Internal_Rela *internal_relocs, + Elf_Internal_Rela *irelend); + +enum +{ + MACH_V1 = bfd_mach_n1h, + MACH_V2 = bfd_mach_n1h_v2, + MACH_V3 = bfd_mach_n1h_v3, + MACH_V3M = bfd_mach_n1h_v3m +}; + +#define MIN(a, b) ((a) > (b) ? (b) : (a)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +/* The name of the dynamic interpreter. This is put in the .interp + section. */ +#define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1" + +/* The nop opcode we use. */ +#define NDS32_NOP32 0x40000009 +#define NDS32_NOP16 0x9200 + +/* The size in bytes of an entry in the procedure linkage table. */ +#define PLT_ENTRY_SIZE 24 +#define PLT_HEADER_SIZE 24 + +/* The first entry in a procedure linkage table are reserved, + and the initial contents are unimportant (we zero them out). + Subsequent entries look like this. */ +#define PLT0_ENTRY_WORD0 0x46f00000 /* sethi r15, HI20(.got+4) */ +#define PLT0_ENTRY_WORD1 0x58f78000 /* ori r15, r25, LO12(.got+4) */ +#define PLT0_ENTRY_WORD2 0x05178000 /* lwi r17, [r15+0] */ +#define PLT0_ENTRY_WORD3 0x04f78001 /* lwi r15, [r15+4] */ +#define PLT0_ENTRY_WORD4 0x4a003c00 /* jr r15 */ + +/* $ta is change to $r15 (from $r25). */ +#define PLT0_PIC_ENTRY_WORD0 0x46f00000 /* sethi r15, HI20(got[1]@GOT) */ +#define PLT0_PIC_ENTRY_WORD1 0x58f78000 /* ori r15, r15, LO12(got[1]@GOT) */ +#define PLT0_PIC_ENTRY_WORD2 0x40f7f400 /* add r15, gp, r15 */ +#define PLT0_PIC_ENTRY_WORD3 0x05178000 /* lwi r17, [r15+0] */ +#define PLT0_PIC_ENTRY_WORD4 0x04f78001 /* lwi r15, [r15+4] */ +#define PLT0_PIC_ENTRY_WORD5 0x4a003c00 /* jr r15 */ + +#define PLT_ENTRY_WORD0 0x46f00000 /* sethi r15, HI20(&got[n+3]) */ +#define PLT_ENTRY_WORD1 0x04f78000 /* lwi r15, r15, LO12(&got[n+3]) */ +#define PLT_ENTRY_WORD2 0x4a003c00 /* jr r15 */ +#define PLT_ENTRY_WORD3 0x45000000 /* movi r16, sizeof(RELA) * n */ +#define PLT_ENTRY_WORD4 0x48000000 /* j .plt0. */ + +#define PLT_PIC_ENTRY_WORD0 0x46f00000 /* sethi r15, HI20(got[n+3]@GOT) */ +#define PLT_PIC_ENTRY_WORD1 0x58f78000 /* ori r15, r15, LO12(got[n+3]@GOT) */ +#define PLT_PIC_ENTRY_WORD2 0x38febc02 /* lw r15, [gp+r15] */ +#define PLT_PIC_ENTRY_WORD3 0x4a003c00 /* jr r15 */ +#define PLT_PIC_ENTRY_WORD4 0x45000000 /* movi r16, sizeof(RELA) * n */ +#define PLT_PIC_ENTRY_WORD5 0x48000000 /* j .plt0 */ + +/* Size of small data/bss sections, used to calculate SDA_BASE. */ +static long got_size = 0; +static int is_SDA_BASE_set = 0; +static int is_ITB_BASE_set = 0; + +static int relax_active = 0; + +/* Convert ELF-VER in eflags to string for debugging purpose. */ +static const char *const nds32_elfver_strtab[] = +{ + "ELF-1.2", + "ELF-1.3", + "ELF-1.4", +}; + +/* The nds32 linker needs to keep track of the number of relocs that it + decides to copy in check_relocs for each symbol. This is so that + it can discard PC relative relocs if it doesn't need them when + linking with -Bsymbolic. We store the information in a field + extending the regular ELF linker hash table. */ + +/* This structure keeps track of the number of PC relative relocs we + have copied for a given symbol. */ + +struct elf_nds32_pcrel_relocs_copied +{ + /* Next section. */ + struct elf_nds32_pcrel_relocs_copied *next; + /* A section in dynobj. */ + asection *section; + /* Number of relocs copied in this section. */ + bfd_size_type count; +}; + +/* The sh linker needs to keep track of the number of relocs that it + decides to copy as dynamic relocs in check_relocs for each symbol. + This is so that it can later discard them if they are found to be + unnecessary. We store the information in a field extending the + regular ELF linker hash table. */ + +struct elf_nds32_dyn_relocs +{ + struct elf_nds32_dyn_relocs *next; + + /* The input section of the reloc. */ + asection *sec; + + /* Total number of relocs copied for the input section. */ + bfd_size_type count; + + /* Number of pc-relative relocs copied for the input section. */ + bfd_size_type pc_count; +}; + +/* Nds32 ELF linker hash entry. */ + +struct elf_nds32_link_hash_entry +{ + struct elf_link_hash_entry root; + + /* Track dynamic relocs copied for this symbol. */ + struct elf_nds32_dyn_relocs *dyn_relocs; +}; + +/* Get the nds32 ELF linker hash table from a link_info structure. */ + +#define FP_BASE_NAME "_FP_BASE_" +static int check_start_export_sym = 0; +static size_t ex9_relax_size = 0; /* Save ex9 predicted reducing size. */ + +/* Relocations used for relocation. */ +static reloc_howto_type nds32_elf_howto_table[] = +{ + /* This reloc does nothing. */ + HOWTO (R_NDS32_NONE, /* 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_NDS32_NONE", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* A 16 bit absolute relocation. */ + HOWTO (R_NDS32_16, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + nds32_elf_generic_reloc, /* special_function */ + "R_NDS32_16", /* name */ + FALSE, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* A 32 bit absolute relocation. */ + HOWTO (R_NDS32_32, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + nds32_elf_generic_reloc, /* special_function */ + "R_NDS32_32", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* A 20 bit address. */ + HOWTO (R_NDS32_20, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 20, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_unsigned, /* complain_on_overflow */ + nds32_elf_generic_reloc, /* special_function */ + "R_NDS32_20", /* name */ + FALSE, /* partial_inplace */ + 0xfffff, /* src_mask */ + 0xfffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* An PC Relative 9-bit relocation, shifted by 2. + This reloc is complicated because relocations are relative to pc & -4. + i.e. branches in the right insn slot use the address of the left insn + slot for pc. */ + /* ??? It's not clear whether this should have partial_inplace set or not. + Branch relaxing in the assembler can store the addend in the insn, + and if bfd_install_relocation gets called the addend may get added + again. */ + HOWTO (R_NDS32_9_PCREL, /* type */ + 1, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 8, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + nds32_elf_9_pcrel_reloc, /* special_function */ + "R_NDS32_9_PCREL", /* name */ + FALSE, /* partial_inplace */ + 0xff, /* src_mask */ + 0xff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + /* A relative 15 bit relocation, right shifted by 1. */ + HOWTO (R_NDS32_15_PCREL, /* type */ + 1, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 14, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_15_PCREL", /* name */ + FALSE, /* partial_inplace */ + 0x3fff, /* src_mask */ + 0x3fff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + /* A relative 17 bit relocation, right shifted by 1. */ + HOWTO (R_NDS32_17_PCREL, /* type */ + 1, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_17_PCREL", /* name */ + FALSE, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + /* A relative 25 bit relocation, right shifted by 1. */ + /* ??? It's not clear whether this should have partial_inplace set or not. + Branch relaxing in the assembler can store the addend in the insn, + and if bfd_install_relocation gets called the addend may get added + again. */ + HOWTO (R_NDS32_25_PCREL, /* type */ + 1, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 24, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_25_PCREL", /* name */ + FALSE, /* partial_inplace */ + 0xffffff, /* src_mask */ + 0xffffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + /* High 20 bits of address when lower 12 is or'd in. */ + HOWTO (R_NDS32_HI20, /* type */ + 12, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 20, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_hi20_reloc, /* special_function */ + "R_NDS32_HI20", /* name */ + FALSE, /* partial_inplace */ + 0x000fffff, /* src_mask */ + 0x000fffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Lower 12 bits of address. */ + HOWTO (R_NDS32_LO12S3, /* type */ + 3, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 9, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_lo12_reloc, /* special_function */ + "R_NDS32_LO12S3", /* name */ + FALSE, /* partial_inplace */ + 0x000001ff, /* src_mask */ + 0x000001ff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Lower 12 bits of address. */ + HOWTO (R_NDS32_LO12S2, /* type */ + 2, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 10, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_lo12_reloc, /* special_function */ + "R_NDS32_LO12S2", /* name */ + FALSE, /* partial_inplace */ + 0x000003ff, /* src_mask */ + 0x000003ff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Lower 12 bits of address. */ + HOWTO (R_NDS32_LO12S1, /* type */ + 1, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 11, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_lo12_reloc, /* special_function */ + "R_NDS32_LO12S1", /* name */ + FALSE, /* partial_inplace */ + 0x000007ff, /* src_mask */ + 0x000007ff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Lower 12 bits of address. */ + HOWTO (R_NDS32_LO12S0, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 12, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_lo12_reloc, /* special_function */ + "R_NDS32_LO12S0", /* name */ + FALSE, /* partial_inplace */ + 0x00000fff, /* src_mask */ + 0x00000fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Small data area 15 bits offset. */ + HOWTO (R_NDS32_SDA15S3, /* type */ + 3, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 15, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + nds32_elf_sda15_reloc, /* special_function */ + "R_NDS32_SDA15S3", /* name */ + FALSE, /* partial_inplace */ + 0x00007fff, /* src_mask */ + 0x00007fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Small data area 15 bits offset. */ + HOWTO (R_NDS32_SDA15S2, /* type */ + 2, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 15, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + nds32_elf_sda15_reloc, /* special_function */ + "R_NDS32_SDA15S2", /* name */ + FALSE, /* partial_inplace */ + 0x00007fff, /* src_mask */ + 0x00007fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Small data area 15 bits offset. */ + HOWTO (R_NDS32_SDA15S1, /* type */ + 1, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 15, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + nds32_elf_sda15_reloc, /* special_function */ + "R_NDS32_SDA15S1", /* name */ + FALSE, /* partial_inplace */ + 0x00007fff, /* src_mask */ + 0x00007fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Small data area 15 bits offset. */ + HOWTO (R_NDS32_SDA15S0, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 15, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + nds32_elf_sda15_reloc, /* special_function */ + "R_NDS32_SDA15S0", /* name */ + FALSE, /* partial_inplace */ + 0x00007fff, /* src_mask */ + 0x00007fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* GNU extension to record C++ vtable hierarchy */ + HOWTO (R_NDS32_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_NDS32_GNU_VTINHERIT", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* GNU extension to record C++ vtable member usage */ + HOWTO (R_NDS32_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_NDS32_GNU_VTENTRY", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* A 16 bit absolute relocation. */ + HOWTO (R_NDS32_16_RELA, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_16_RELA", /* name */ + FALSE, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* A 32 bit absolute relocation. */ + HOWTO (R_NDS32_32_RELA, /* 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_NDS32_32_RELA", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* A 20 bit address. */ + HOWTO (R_NDS32_20_RELA, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 20, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_20_RELA", /* name */ + FALSE, /* partial_inplace */ + 0xfffff, /* src_mask */ + 0xfffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_NDS32_9_PCREL_RELA, /* type */ + 1, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 8, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_9_PCREL_RELA",/* name */ + FALSE, /* partial_inplace */ + 0xff, /* src_mask */ + 0xff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + /* A relative 15 bit relocation, right shifted by 1. */ + HOWTO (R_NDS32_15_PCREL_RELA, /* type */ + 1, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 14, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_15_PCREL_RELA", /* name */ + FALSE, /* partial_inplace */ + 0x3fff, /* src_mask */ + 0x3fff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + /* A relative 17 bit relocation, right shifted by 1. */ + HOWTO (R_NDS32_17_PCREL_RELA, /* type */ + 1, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_17_PCREL_RELA", /* name */ + FALSE, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + /* A relative 25 bit relocation, right shifted by 2. */ + HOWTO (R_NDS32_25_PCREL_RELA, /* type */ + 1, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 24, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_25_PCREL_RELA", /* name */ + FALSE, /* partial_inplace */ + 0xffffff, /* src_mask */ + 0xffffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + /* High 20 bits of address when lower 16 is or'd in. */ + HOWTO (R_NDS32_HI20_RELA, /* type */ + 12, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 20, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_HI20_RELA", /* name */ + FALSE, /* partial_inplace */ + 0x000fffff, /* src_mask */ + 0x000fffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Lower 12 bits of address. */ + HOWTO (R_NDS32_LO12S3_RELA, /* type */ + 3, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 9, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_LO12S3_RELA", /* name */ + FALSE, /* partial_inplace */ + 0x000001ff, /* src_mask */ + 0x000001ff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Lower 12 bits of address. */ + HOWTO (R_NDS32_LO12S2_RELA, /* type */ + 2, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 10, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_LO12S2_RELA", /* name */ + FALSE, /* partial_inplace */ + 0x000003ff, /* src_mask */ + 0x000003ff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Lower 12 bits of address. */ + HOWTO (R_NDS32_LO12S1_RELA, /* type */ + 1, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 11, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_LO12S1_RELA", /* name */ + FALSE, /* partial_inplace */ + 0x000007ff, /* src_mask */ + 0x000007ff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Lower 12 bits of address. */ + HOWTO (R_NDS32_LO12S0_RELA, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 12, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_LO12S0_RELA", /* name */ + FALSE, /* partial_inplace */ + 0x00000fff, /* src_mask */ + 0x00000fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Small data area 15 bits offset. */ + HOWTO (R_NDS32_SDA15S3_RELA, /* type */ + 3, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 15, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_SDA15S3_RELA",/* name */ + FALSE, /* partial_inplace */ + 0x00007fff, /* src_mask */ + 0x00007fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Small data area 15 bits offset. */ + HOWTO (R_NDS32_SDA15S2_RELA, /* type */ + 2, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 15, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_SDA15S2_RELA",/* name */ + FALSE, /* partial_inplace */ + 0x00007fff, /* src_mask */ + 0x00007fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_NDS32_SDA15S1_RELA, /* type */ + 1, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 15, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_SDA15S1_RELA",/* name */ + FALSE, /* partial_inplace */ + 0x00007fff, /* src_mask */ + 0x00007fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_NDS32_SDA15S0_RELA, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 15, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_SDA15S0_RELA",/* name */ + FALSE, /* partial_inplace */ + 0x00007fff, /* src_mask */ + 0x00007fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* GNU extension to record C++ vtable hierarchy */ + HOWTO (R_NDS32_RELA_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_NDS32_RELA_GNU_VTINHERIT", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* GNU extension to record C++ vtable member usage */ + HOWTO (R_NDS32_RELA_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_NDS32_RELA_GNU_VTENTRY", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Like R_NDS32_20, but referring to the GOT table entry for + the symbol. */ + HOWTO (R_NDS32_GOT20, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 20, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_GOT20", /* name */ + FALSE, /* partial_inplace */ + 0xfffff, /* src_mask */ + 0xfffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Like R_NDS32_PCREL, but referring to the procedure linkage table + entry for the symbol. */ + HOWTO (R_NDS32_25_PLTREL, /* type */ + 1, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 24, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_25_PLTREL", /* name */ + FALSE, /* partial_inplace */ + 0xffffff, /* src_mask */ + 0xffffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + /* This is used only by the dynamic linker. The symbol should exist + both in the object being run and in some shared library. The + dynamic linker copies the data addressed by the symbol from the + shared library into the object, because the object being + run has to have the data at some particular address. */ + HOWTO (R_NDS32_COPY, /* 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_NDS32_COPY", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Like R_NDS32_20, but used when setting global offset table + entries. */ + HOWTO (R_NDS32_GLOB_DAT, /* 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_NDS32_GLOB_DAT", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Marks a procedure linkage table entry for a symbol. */ + HOWTO (R_NDS32_JMP_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_NDS32_JMP_SLOT", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Used only by the dynamic linker. When the object is run, this + longword is set to the load address of the object, plus the + addend. */ + HOWTO (R_NDS32_RELATIVE, /* 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_NDS32_RELATIVE", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_NDS32_GOTOFF, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 20, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_GOTOFF", /* name */ + FALSE, /* partial_inplace */ + 0xfffff, /* src_mask */ + 0xfffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* An PC Relative 20-bit relocation used when setting PIC offset + table register. */ + HOWTO (R_NDS32_GOTPC20, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 20, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_GOTPC20", /* name */ + FALSE, /* partial_inplace */ + 0xfffff, /* src_mask */ + 0xfffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + /* Like R_NDS32_HI20, but referring to the GOT table entry for + the symbol. */ + HOWTO (R_NDS32_GOT_HI20, /* type */ + 12, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 20, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_GOT_HI20", /* name */ + FALSE, /* partial_inplace */ + 0x000fffff, /* src_mask */ + 0x000fffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_GOT_LO12, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 12, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_GOT_LO12", /* name */ + FALSE, /* partial_inplace */ + 0x00000fff, /* src_mask */ + 0x00000fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* An PC Relative relocation used when setting PIC offset table register. + Like R_NDS32_HI20, but referring to the GOT table entry for + the symbol. */ + HOWTO (R_NDS32_GOTPC_HI20, /* type */ + 12, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 20, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_GOTPC_HI20", /* name */ + FALSE, /* partial_inplace */ + 0x000fffff, /* src_mask */ + 0x000fffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + HOWTO (R_NDS32_GOTPC_LO12, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 12, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_GOTPC_LO12", /* name */ + FALSE, /* partial_inplace */ + 0x00000fff, /* src_mask */ + 0x00000fff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + HOWTO (R_NDS32_GOTOFF_HI20, /* type */ + 12, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 20, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_GOTOFF_HI20", /* name */ + FALSE, /* partial_inplace */ + 0x000fffff, /* src_mask */ + 0x000fffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_GOTOFF_LO12, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 12, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_GOTOFF_LO12", /* name */ + FALSE, /* partial_inplace */ + 0x00000fff, /* src_mask */ + 0x00000fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Alignment hint for relaxable instruction. This is used with + R_NDS32_LABEL as a pair. Relax this instruction from 4 bytes to 2 + in order to make next label aligned on word boundary. */ + HOWTO (R_NDS32_INSN16, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_INSN16", /* name */ + FALSE, /* partial_inplace */ + 0x00000fff, /* src_mask */ + 0x00000fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Alignment hint for label. */ + HOWTO (R_NDS32_LABEL, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_LABEL", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Relax hint for unconditional call sequence */ + HOWTO (R_NDS32_LONGCALL1, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_LONGCALL1", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Relax hint for conditional call sequence. */ + HOWTO (R_NDS32_LONGCALL2, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_LONGCALL2", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Relax hint for conditional call sequence. */ + HOWTO (R_NDS32_LONGCALL3, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_LONGCALL3", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Relax hint for unconditional branch sequence. */ + HOWTO (R_NDS32_LONGJUMP1, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_LONGJUMP1", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Relax hint for conditional branch sequence. */ + HOWTO (R_NDS32_LONGJUMP2, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_LONGJUMP2", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Relax hint for conditional branch sequence. */ + HOWTO (R_NDS32_LONGJUMP3, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_LONGJUMP3", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Relax hint for load/store sequence. */ + HOWTO (R_NDS32_LOADSTORE, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_LOADSTORE", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Relax hint for load/store sequence. */ + HOWTO (R_NDS32_9_FIXED_RELA, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_9_FIXED_RELA",/* name */ + FALSE, /* partial_inplace */ + 0x000000ff, /* src_mask */ + 0x000000ff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Relax hint for load/store sequence. */ + HOWTO (R_NDS32_15_FIXED_RELA, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_15_FIXED_RELA", /* name */ + FALSE, /* partial_inplace */ + 0x00003fff, /* src_mask */ + 0x00003fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Relax hint for load/store sequence. */ + HOWTO (R_NDS32_17_FIXED_RELA, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_17_FIXED_RELA", /* name */ + FALSE, /* partial_inplace */ + 0x0000ffff, /* src_mask */ + 0x0000ffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Relax hint for load/store sequence. */ + HOWTO (R_NDS32_25_FIXED_RELA, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_25_FIXED_RELA", /* name */ + FALSE, /* partial_inplace */ + 0x00ffffff, /* src_mask */ + 0x00ffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* High 20 bits of PLT symbol offset relative to PC. */ + HOWTO (R_NDS32_PLTREL_HI20, /* type */ + 12, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 20, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_PLTREL_HI20", /* name */ + FALSE, /* partial_inplace */ + 0x000fffff, /* src_mask */ + 0x000fffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Low 12 bits of PLT symbol offset relative to PC. */ + HOWTO (R_NDS32_PLTREL_LO12, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 12, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_PLTREL_LO12", /* name */ + FALSE, /* partial_inplace */ + 0x00000fff, /* src_mask */ + 0x00000fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* High 20 bits of PLT symbol offset relative to GOT (GP). */ + HOWTO (R_NDS32_PLT_GOTREL_HI20, /* type */ + 12, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 20, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_PLT_GOTREL_HI20", /* name */ + FALSE, /* partial_inplace */ + 0x000fffff, /* src_mask */ + 0x000fffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Low 12 bits of PLT symbol offset relative to GOT (GP). */ + HOWTO (R_NDS32_PLT_GOTREL_LO12, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 12, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_PLT_GOTREL_LO12", /* name */ + FALSE, /* partial_inplace */ + 0x00000fff, /* src_mask */ + 0x00000fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Small data area 12 bits offset. */ + HOWTO (R_NDS32_SDA12S2_DP_RELA, /* type */ + 2, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 12, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_SDA12S2_DP_RELA", /* name */ + FALSE, /* partial_inplace */ + 0x00000fff, /* src_mask */ + 0x00000fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Small data area 12 bits offset. */ + HOWTO (R_NDS32_SDA12S2_SP_RELA, /* type */ + 2, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 12, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_SDA12S2_SP_RELA", /* name */ + FALSE, /* partial_inplace */ + 0x00000fff, /* src_mask */ + 0x00000fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + /* Lower 12 bits of address. */ + + HOWTO (R_NDS32_LO12S2_DP_RELA, /* type */ + 2, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 10, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_LO12S2_DP_RELA", /* name */ + FALSE, /* partial_inplace */ + 0x000003ff, /* src_mask */ + 0x000003ff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Lower 12 bits of address. */ + HOWTO (R_NDS32_LO12S2_SP_RELA,/* type */ + 2, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 10, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_LO12S2_SP_RELA", /* name */ + FALSE, /* partial_inplace */ + 0x000003ff, /* src_mask */ + 0x000003ff, /* dst_mask */ + FALSE), /* pcrel_offset */ + /* Lower 12 bits of address. Special identity for or case. */ + HOWTO (R_NDS32_LO12S0_ORI_RELA, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 12, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_LO12S0_ORI_RELA", /* name */ + FALSE, /* partial_inplace */ + 0x00000fff, /* src_mask */ + 0x00000fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + /* Small data area 19 bits offset. */ + HOWTO (R_NDS32_SDA16S3_RELA, /* type */ + 3, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_SDA16S3_RELA",/* name */ + FALSE, /* partial_inplace */ + 0x0000ffff, /* src_mask */ + 0x0000ffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Small data area 15 bits offset. */ + HOWTO (R_NDS32_SDA17S2_RELA, /* type */ + 2, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 17, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_SDA17S2_RELA",/* name */ + FALSE, /* partial_inplace */ + 0x0001ffff, /* src_mask */ + 0x0001ffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_NDS32_SDA18S1_RELA, /* type */ + 1, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 18, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_SDA18S1_RELA",/* name */ + FALSE, /* partial_inplace */ + 0x0003ffff, /* src_mask */ + 0x0003ffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_NDS32_SDA19S0_RELA, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 19, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_SDA19S0_RELA",/* name */ + FALSE, /* partial_inplace */ + 0x0007ffff, /* src_mask */ + 0x0007ffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_DWARF2_OP1_RELA, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 8, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_DWARF2_OP1_RELA", /* name */ + FALSE, /* partial_inplace */ + 0xff, /* src_mask */ + 0xff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_DWARF2_OP2_RELA, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_DWARF2_OP2_RELA", /* name */ + FALSE, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_DWARF2_LEB_RELA, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_DWARF2_LEB_RELA", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_UPDATE_TA_RELA,/* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_UPDATE_TA_RELA", /* name */ + FALSE, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + /* Like R_NDS32_PCREL, but referring to the procedure linkage table + entry for the symbol. */ + HOWTO (R_NDS32_9_PLTREL, /* type */ + 1, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 8, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_9_PLTREL", /* name */ + FALSE, /* partial_inplace */ + 0xff, /* src_mask */ + 0xff, /* dst_mask */ + TRUE), /* pcrel_offset */ + /* Low 20 bits of PLT symbol offset relative to GOT (GP). */ + HOWTO (R_NDS32_PLT_GOTREL_LO20, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 20, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_PLT_GOTREL_LO20", /* name */ + FALSE, /* partial_inplace */ + 0x000fffff, /* src_mask */ + 0x000fffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + /* low 15 bits of PLT symbol offset relative to GOT (GP) */ + HOWTO (R_NDS32_PLT_GOTREL_LO15, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 15, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_PLT_GOTREL_LO15", /* name */ + FALSE, /* partial_inplace */ + 0x00007fff, /* src_mask */ + 0x00007fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + /* Low 19 bits of PLT symbol offset relative to GOT (GP). */ + HOWTO (R_NDS32_PLT_GOTREL_LO19, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 19, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_PLT_GOTREL_LO19", /* name */ + FALSE, /* partial_inplace */ + 0x0007ffff, /* src_mask */ + 0x0007ffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_GOT_LO15, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 15, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_GOT_LO15", /* name */ + FALSE, /* partial_inplace */ + 0x00007fff, /* src_mask */ + 0x00007fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_GOT_LO19, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 19, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_GOT_LO19", /* name */ + FALSE, /* partial_inplace */ + 0x0007ffff, /* src_mask */ + 0x0007ffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_GOTOFF_LO15, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 15, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_GOTOFF_LO15", /* name */ + FALSE, /* partial_inplace */ + 0x00007fff, /* src_mask */ + 0x00007fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_GOTOFF_LO19, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 19, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_GOTOFF_LO19", /* name */ + FALSE, /* partial_inplace */ + 0x0007ffff, /* src_mask */ + 0x0007ffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + /* GOT 15 bits offset. */ + HOWTO (R_NDS32_GOT15S2_RELA, /* type */ + 2, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 15, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_GOT15S2_RELA",/* name */ + FALSE, /* partial_inplace */ + 0x00007fff, /* src_mask */ + 0x00007fff, /* dst_mask */ + FALSE), /* pcrel_offset */ + /* GOT 17 bits offset. */ + HOWTO (R_NDS32_GOT17S2_RELA, /* type */ + 2, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 17, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_GOT17S2_RELA",/* name */ + FALSE, /* partial_inplace */ + 0x0001ffff, /* src_mask */ + 0x0001ffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + /* A 5 bit address. */ + HOWTO (R_NDS32_5_RELA, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 5, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_5_RELA", /* name */ + FALSE, /* partial_inplace */ + 0x1f, /* src_mask */ + 0x1f, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_10_UPCREL_RELA,/* type */ + 1, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 9, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_unsigned, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_10_UPCREL_RELA", /* name */ + FALSE, /* partial_inplace */ + 0x1ff, /* src_mask */ + 0x1ff, /* dst_mask */ + TRUE), /* pcrel_offset */ + HOWTO (R_NDS32_SDA_FP7U2_RELA,/* type */ + 2, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 7, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_unsigned, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_SDA_FP7U2_RELA", /* name */ + FALSE, /* partial_inplace */ + 0x0000007f, /* src_mask */ + 0x0000007f, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_WORD_9_PCREL_RELA, /* type */ + 1, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 8, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_WORD_9_PCREL_RELA", /* name */ + FALSE, /* partial_inplace */ + 0xff, /* src_mask */ + 0xff, /* dst_mask */ + TRUE), /* pcrel_offset */ + HOWTO (R_NDS32_25_ABS_RELA, /* type */ + 1, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 24, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_25_ABS_RELA", /* name */ + FALSE, /* partial_inplace */ + 0xffffff, /* src_mask */ + 0xffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* A relative 17 bit relocation for ifc, right shifted by 1. */ + HOWTO (R_NDS32_17IFC_PCREL_RELA, /* type */ + 1, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_17IFC_PCREL_RELA", /* name */ + FALSE, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + /* A relative unsigned 10 bit relocation for ifc, right shifted by 1. */ + HOWTO (R_NDS32_10IFCU_PCREL_RELA, /* type */ + 1, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 9, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_unsigned, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NDS32_10IFCU_PCREL_RELA", /* name */ + FALSE, /* partial_inplace */ + 0x1ff, /* src_mask */ + 0x1ff, /* dst_mask */ + TRUE), /* pcrel_offset */ +}; + +/* Relocations used for relaxation. */ +static reloc_howto_type nds32_elf_relax_howto_table[] = +{ + HOWTO (R_NDS32_RELAX_ENTRY, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_RELAX_ENTRY", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_GOT_SUFF, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_GOT_SUFF", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_GOTOFF_SUFF, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_GOTOFF_SUFF", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_PLT_GOT_SUFF, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_PLT_GOT_SUFF",/* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_MULCALL_SUFF, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_MULCALL_SUFF",/* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_PTR, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_PTR", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_PTR_COUNT, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_PTR_COUNT", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_PTR_RESOLVED, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_PTR_RESOLVED",/* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_PLTBLOCK, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_PLTBLOCK", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_RELAX_REGION_BEGIN, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_RELAX_REGION_BEGIN", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_RELAX_REGION_END, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_RELAX_REGION_END", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_MINUEND, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_MINUEND", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_SUBTRAHEND, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_SUBTRAHEND", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_DIFF8, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 8, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_DIFF8", /* name */ + FALSE, /* partial_inplace */ + 0x000000ff, /* src_mask */ + 0x000000ff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_DIFF16, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_DIFF16", /* name */ + FALSE, /* partial_inplace */ + 0x0000ffff, /* src_mask */ + 0x0000ffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_DIFF32, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_DIFF32", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_DIFF_ULEB128, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 0, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_DIFF_ULEB128",/* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_DATA, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_DATA", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_NDS32_TRAN, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + nds32_elf_ignore_reloc,/* special_function */ + "R_NDS32_TRAN", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ +}; + + +/* nds32_insertion_sort sorts an array with nmemb elements of size size. + This prototype is the same as qsort (). */ + +void +nds32_insertion_sort (void *base, size_t nmemb, size_t size, + int (*compar) (const void *lhs, const void *rhs)) +{ + char *ptr = (char *) base; + unsigned int i, j; + char *tmp = alloca (size); + + /* If i is less than j, i is inserted before j. + + |---- j ----- i --------------| + \ / \ / + sorted unsorted + */ + + for (i = 1; i < nmemb; i++) + { + for (j = 0; j < i; j++) + if (compar (ptr + i * size, ptr + j * size) < 0) + break; + + if (i == j) + continue; /* j is in order. */ + + memcpy (tmp, ptr + i * size, size); + memmove (ptr + (j + 1) * size, ptr + j * size, (i - j) * size); + memcpy (ptr + j * size, tmp, size); + } +} + +/* Sort relocation by r_offset. + + We didn't use qsort () in stdlib, because quick-sort is not a stable sorting + algorithm. Relocations at the same r_offset must keep their order. + For example, RELAX_ENTRY must be the very first relocation entry. + + Currently, this function implements insertion-sort. + + FIXME: If we already sort them in assembler, why bother sort them + here again? */ + +static int +compar_reloc (const void *lhs, const void *rhs) +{ + const Elf_Internal_Rela *l = (const Elf_Internal_Rela *) lhs; + const Elf_Internal_Rela *r = (const Elf_Internal_Rela *) rhs; + + if (l->r_offset > r->r_offset) + return 1; + else if (l->r_offset == r->r_offset) + return 0; + else + return -1; +} + +/* Functions listed below are only used for old relocs. + * nds32_elf_9_pcrel_reloc + * nds32_elf_do_9_pcrel_reloc + * nds32_elf_hi20_reloc + * nds32_elf_relocate_hi20 + * nds32_elf_lo12_reloc + * nds32_elf_sda15_reloc + * nds32_elf_generic_reloc + */ + +/* Handle the R_NDS32_9_PCREL & R_NDS32_9_PCREL_RELA reloc. */ + +static bfd_reloc_status_type +nds32_elf_9_pcrel_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, + void *data, asection *input_section, bfd *output_bfd, + char **error_message ATTRIBUTE_UNUSED) +{ + /* This part is from bfd_elf_generic_reloc. */ + if (output_bfd != (bfd *) NULL + && (symbol->flags & BSF_SECTION_SYM) == 0 + && (!reloc_entry->howto->partial_inplace || reloc_entry->addend == 0)) + { + reloc_entry->address += input_section->output_offset; + return bfd_reloc_ok; + } + + if (output_bfd != NULL) + { + /* FIXME: See bfd_perform_relocation. Is this right? */ + return bfd_reloc_continue; + } + + return nds32_elf_do_9_pcrel_reloc (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + symbol->section, + (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +/* Utility to actually perform an R_NDS32_9_PCREL reloc. */ +#define N_ONES(n) (((((bfd_vma) 1 << ((n) - 1)) - 1) << 1) | 1) + +static bfd_reloc_status_type +nds32_elf_do_9_pcrel_reloc (bfd *abfd, reloc_howto_type *howto, + asection *input_section, bfd_byte *data, + bfd_vma offset, asection *symbol_section ATTRIBUTE_UNUSED, + bfd_vma symbol_value, bfd_vma addend) +{ + bfd_signed_vma relocation; + unsigned short x; + bfd_reloc_status_type status; + + /* Sanity check the address (offset in section). */ + if (offset > bfd_get_section_limit (abfd, input_section)) + return bfd_reloc_outofrange; + + relocation = symbol_value + addend; + /* Make it pc relative. */ + relocation -= (input_section->output_section->vma + + input_section->output_offset); + /* These jumps mask off the lower two bits of the current address + before doing pcrel calculations. */ + relocation -= (offset & -(bfd_vma) 2); + + if (relocation < -0x100 || relocation > 0xff) + status = bfd_reloc_overflow; + else + status = bfd_reloc_ok; + + x = bfd_getb16 (data + offset); + + relocation >>= howto->rightshift; + relocation <<= howto->bitpos; + x = (x & ~howto->dst_mask) + | (((x & howto->src_mask) + relocation) & howto->dst_mask); + + bfd_putb16 ((bfd_vma) x, data + offset); + + return status; +} + +/* Handle the R_NDS32_HI20_[SU]LO relocs. + HI20_SLO is for the add3 and load/store with displacement instructions. + HI20 is for the or3 instruction. + For R_NDS32_HI20_SLO, the lower 16 bits are sign extended when added to + the high 16 bytes so if the lower 16 bits are negative (bit 15 == 1) then + we must add one to the high 16 bytes (which will get subtracted off when + the low 16 bits are added). + These relocs have to be done in combination with an R_NDS32_LO12 reloc + because there is a carry from the LO12 to the HI20. Here we just save + the information we need; we do the actual relocation when we see the LO12. + This code is copied from the elf32-mips.c. We also support an arbitrary + number of HI20 relocs to be associated with a single LO12 reloc. The + assembler sorts the relocs to ensure each HI20 immediately precedes its + LO12. However if there are multiple copies, the assembler may not find + the real LO12 so it picks the first one it finds. */ + +struct nds32_hi20 +{ + struct nds32_hi20 *next; + bfd_byte *addr; + bfd_vma addend; +}; + +static struct nds32_hi20 *nds32_hi20_list; + +static bfd_reloc_status_type +nds32_elf_hi20_reloc (bfd *abfd ATTRIBUTE_UNUSED, arelent *reloc_entry, + asymbol *symbol, void *data, asection *input_section, + bfd *output_bfd, char **error_message ATTRIBUTE_UNUSED) +{ + bfd_reloc_status_type ret; + bfd_vma relocation; + struct nds32_hi20 *n; + + /* This part is from bfd_elf_generic_reloc. + If we're relocating, and this an external symbol, we don't want + to change anything. */ + if (output_bfd != (bfd *) NULL + && (symbol->flags & BSF_SECTION_SYM) == 0 && reloc_entry->addend == 0) + { + reloc_entry->address += input_section->output_offset; + return bfd_reloc_ok; + } + + /* Sanity check the address (offset in section). */ + if (reloc_entry->address > bfd_get_section_limit (abfd, input_section)) + return bfd_reloc_outofrange; + + ret = bfd_reloc_ok; + if (bfd_is_und_section (symbol->section) && output_bfd == (bfd *) NULL) + ret = bfd_reloc_undefined; + + if (bfd_is_com_section (symbol->section)) + relocation = 0; + else + relocation = symbol->value; + + relocation += symbol->section->output_section->vma; + relocation += symbol->section->output_offset; + relocation += reloc_entry->addend; + + /* Save the information, and let LO12 do the actual relocation. */ + n = (struct nds32_hi20 *) bfd_malloc ((bfd_size_type) sizeof *n); + if (n == NULL) + return bfd_reloc_outofrange; + + n->addr = (bfd_byte *) data + reloc_entry->address; + n->addend = relocation; + n->next = nds32_hi20_list; + nds32_hi20_list = n; + + if (output_bfd != (bfd *) NULL) + reloc_entry->address += input_section->output_offset; + + return ret; +} + +/* Handle an NDS32 ELF HI20 reloc. */ + +static void +nds32_elf_relocate_hi20 (bfd *input_bfd ATTRIBUTE_UNUSED, + int type ATTRIBUTE_UNUSED, Elf_Internal_Rela *relhi, + Elf_Internal_Rela *rello, bfd_byte *contents, + bfd_vma addend) +{ + unsigned long insn; + bfd_vma addlo; + + insn = bfd_getb32 (contents + relhi->r_offset); + + addlo = bfd_getb32 (contents + rello->r_offset); + addlo &= 0xfff; + + addend += ((insn & 0xfffff) << 20) + addlo; + + insn = (insn & 0xfff00000) | ((addend >> 12) & 0xfffff); + bfd_putb32 (insn, contents + relhi->r_offset); +} + +/* Do an R_NDS32_LO12 relocation. This is a straightforward 12 bit + inplace relocation; this function exists in order to do the + R_NDS32_HI20_[SU]LO relocation described above. */ + +static bfd_reloc_status_type +nds32_elf_lo12_reloc (bfd *input_bfd, arelent *reloc_entry, asymbol *symbol, + void *data, asection *input_section, bfd *output_bfd, + char **error_message) +{ + /* This part is from bfd_elf_generic_reloc. + If we're relocating, and this an external symbol, we don't want + to change anything. */ + if (output_bfd != NULL && (symbol->flags & BSF_SECTION_SYM) == 0 + && reloc_entry->addend == 0) + { + reloc_entry->address += input_section->output_offset; + return bfd_reloc_ok; + } + + if (nds32_hi20_list != NULL) + { + struct nds32_hi20 *l; + + l = nds32_hi20_list; + while (l != NULL) + { + unsigned long insn; + unsigned long val; + unsigned long vallo; + struct nds32_hi20 *next; + + /* Do the HI20 relocation. Note that we actually don't need + to know anything about the LO12 itself, except where to + find the low 12 bits of the addend needed by the LO12. */ + insn = bfd_getb32 (l->addr); + vallo = bfd_getb32 ((bfd_byte *) data + reloc_entry->address); + vallo &= 0xfff; + switch (reloc_entry->howto->type) + { + case R_NDS32_LO12S3: + vallo <<= 3; + break; + + case R_NDS32_LO12S2: + vallo <<= 2; + break; + + case R_NDS32_LO12S1: + vallo <<= 1; + break; + + case R_NDS32_LO12S0: + vallo <<= 0; + break; + } + + val = ((insn & 0xfffff) << 12) + vallo; + val += l->addend; + + insn = (insn & ~(bfd_vma) 0xfffff) | ((val >> 12) & 0xfffff); + bfd_putb32 ((bfd_vma) insn, l->addr); + + next = l->next; + free (l); + l = next; + } + + nds32_hi20_list = NULL; + } + + /* Now do the LO12 reloc in the usual way. + ??? It would be nice to call bfd_elf_generic_reloc here, + but we have partial_inplace set. bfd_elf_generic_reloc will + pass the handling back to bfd_install_relocation which will install + a section relative addend which is wrong. */ + return nds32_elf_generic_reloc (input_bfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message); +} + +/* Do generic partial_inplace relocation. + This is a local replacement for bfd_elf_generic_reloc. */ + +static bfd_reloc_status_type +nds32_elf_generic_reloc (bfd *input_bfd, arelent *reloc_entry, + asymbol *symbol, void *data, asection *input_section, + bfd *output_bfd, char **error_message ATTRIBUTE_UNUSED) +{ + bfd_reloc_status_type ret; + bfd_vma relocation; + bfd_byte *inplace_address; + + /* This part is from bfd_elf_generic_reloc. + If we're relocating, and this an external symbol, we don't want + to change anything. */ + if (output_bfd != NULL && (symbol->flags & BSF_SECTION_SYM) == 0 + && reloc_entry->addend == 0) + { + reloc_entry->address += input_section->output_offset; + return bfd_reloc_ok; + } + + /* Now do the reloc in the usual way. + ??? It would be nice to call bfd_elf_generic_reloc here, + but we have partial_inplace set. bfd_elf_generic_reloc will + pass the handling back to bfd_install_relocation which will install + a section relative addend which is wrong. */ + + /* Sanity check the address (offset in section). */ + if (reloc_entry->address > bfd_get_section_limit (input_bfd, input_section)) + return bfd_reloc_outofrange; + + ret = bfd_reloc_ok; + if (bfd_is_und_section (symbol->section) && output_bfd == (bfd *) NULL) + ret = bfd_reloc_undefined; + + if (bfd_is_com_section (symbol->section) || output_bfd != (bfd *) NULL) + relocation = 0; + else + relocation = symbol->value; + + /* Only do this for a final link. */ + if (output_bfd == (bfd *) NULL) + { + relocation += symbol->section->output_section->vma; + relocation += symbol->section->output_offset; + } + + relocation += reloc_entry->addend; + switch (reloc_entry->howto->type) + { + case R_NDS32_LO12S3: + relocation >>= 3; + break; + + case R_NDS32_LO12S2: + relocation >>= 2; + break; + + case R_NDS32_LO12S1: + relocation >>= 1; + break; + + case R_NDS32_LO12S0: + default: + relocation >>= 0; + break; + } + + inplace_address = (bfd_byte *) data + reloc_entry->address; + +#define DOIT(x) \ + x = ((x & ~reloc_entry->howto->dst_mask) | \ + (((x & reloc_entry->howto->src_mask) + relocation) & \ + reloc_entry->howto->dst_mask)) + + switch (reloc_entry->howto->size) + { + case 1: + { + short x = bfd_getb16 (inplace_address); + + DOIT (x); + bfd_putb16 ((bfd_vma) x, inplace_address); + } + break; + case 2: + { + unsigned long x = bfd_getb32 (inplace_address); + + DOIT (x); + bfd_putb32 ((bfd_vma) x, inplace_address); + } + break; + default: + BFD_ASSERT (0); + } + + if (output_bfd != (bfd *) NULL) + reloc_entry->address += input_section->output_offset; + + return ret; +} + +/* Handle the R_NDS32_SDA15 reloc. + This reloc is used to compute the address of objects in the small data area + and to perform loads and stores from that area. + The lower 15 bits are sign extended and added to the register specified + in the instruction, which is assumed to point to _SDA_BASE_. + + Since the lower 15 bits offset is left-shifted 0, 1 or 2 bits depending on + the access size, this must be taken care of. */ + +static bfd_reloc_status_type +nds32_elf_sda15_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) +{ + /* This part is from bfd_elf_generic_reloc. */ + if (output_bfd != (bfd *) NULL + && (symbol->flags & BSF_SECTION_SYM) == 0 + && (!reloc_entry->howto->partial_inplace || reloc_entry->addend == 0)) + { + reloc_entry->address += input_section->output_offset; + return bfd_reloc_ok; + } + + if (output_bfd != NULL) + { + /* FIXME: See bfd_perform_relocation. Is this right? */ + return bfd_reloc_continue; + } + + /* FIXME: not sure what to do here yet. But then again, the linker + may never call us. */ + abort (); +} + +/* nds32_elf_ignore_reloc is the special function for + relocation types which don't need to be relocated + like relaxation relocation types. + This function simply return bfd_reloc_ok when it is + invoked. */ + +static bfd_reloc_status_type +nds32_elf_ignore_reloc (bfd *abfd ATTRIBUTE_UNUSED, arelent *reloc_entry, + asymbol *symbol ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED, asection *input_section, + bfd *output_bfd, char **error_message ATTRIBUTE_UNUSED) +{ + if (output_bfd != NULL) + reloc_entry->address += input_section->output_offset; + + return bfd_reloc_ok; +} + + +/* Map BFD reloc types to NDS32 ELF reloc types. */ + +struct nds32_reloc_map_entry +{ + bfd_reloc_code_real_type bfd_reloc_val; + unsigned char elf_reloc_val; +}; + +static const struct nds32_reloc_map_entry nds32_reloc_map[] = +{ + {BFD_RELOC_NONE, R_NDS32_NONE}, + {BFD_RELOC_16, R_NDS32_16_RELA}, + {BFD_RELOC_32, R_NDS32_32_RELA}, + {BFD_RELOC_NDS32_20, R_NDS32_20_RELA}, + {BFD_RELOC_NDS32_5, R_NDS32_5_RELA}, + {BFD_RELOC_NDS32_9_PCREL, R_NDS32_9_PCREL_RELA}, + {BFD_RELOC_NDS32_WORD_9_PCREL, R_NDS32_WORD_9_PCREL_RELA}, + {BFD_RELOC_NDS32_15_PCREL, R_NDS32_15_PCREL_RELA}, + {BFD_RELOC_NDS32_17_PCREL, R_NDS32_17_PCREL_RELA}, + {BFD_RELOC_NDS32_25_PCREL, R_NDS32_25_PCREL_RELA}, + {BFD_RELOC_NDS32_10_UPCREL, R_NDS32_10_UPCREL_RELA}, + {BFD_RELOC_NDS32_HI20, R_NDS32_HI20_RELA}, + {BFD_RELOC_NDS32_LO12S3, R_NDS32_LO12S3_RELA}, + {BFD_RELOC_NDS32_LO12S2, R_NDS32_LO12S2_RELA}, + {BFD_RELOC_NDS32_LO12S1, R_NDS32_LO12S1_RELA}, + {BFD_RELOC_NDS32_LO12S0, R_NDS32_LO12S0_RELA}, + {BFD_RELOC_NDS32_LO12S0_ORI, R_NDS32_LO12S0_ORI_RELA}, + {BFD_RELOC_NDS32_SDA15S3, R_NDS32_SDA15S3_RELA}, + {BFD_RELOC_NDS32_SDA15S2, R_NDS32_SDA15S2_RELA}, + {BFD_RELOC_NDS32_SDA15S1, R_NDS32_SDA15S1_RELA}, + {BFD_RELOC_NDS32_SDA15S0, R_NDS32_SDA15S0_RELA}, + {BFD_RELOC_VTABLE_INHERIT, R_NDS32_RELA_GNU_VTINHERIT}, + {BFD_RELOC_VTABLE_ENTRY, R_NDS32_RELA_GNU_VTENTRY}, + + {BFD_RELOC_NDS32_GOT20, R_NDS32_GOT20}, + {BFD_RELOC_NDS32_9_PLTREL, R_NDS32_9_PLTREL}, + {BFD_RELOC_NDS32_25_PLTREL, R_NDS32_25_PLTREL}, + {BFD_RELOC_NDS32_COPY, R_NDS32_COPY}, + {BFD_RELOC_NDS32_GLOB_DAT, R_NDS32_GLOB_DAT}, + {BFD_RELOC_NDS32_JMP_SLOT, R_NDS32_JMP_SLOT}, + {BFD_RELOC_NDS32_RELATIVE, R_NDS32_RELATIVE}, + {BFD_RELOC_NDS32_GOTOFF, R_NDS32_GOTOFF}, + {BFD_RELOC_NDS32_GOTPC20, R_NDS32_GOTPC20}, + {BFD_RELOC_NDS32_GOT_HI20, R_NDS32_GOT_HI20}, + {BFD_RELOC_NDS32_GOT_LO12, R_NDS32_GOT_LO12}, + {BFD_RELOC_NDS32_GOT_LO15, R_NDS32_GOT_LO15}, + {BFD_RELOC_NDS32_GOT_LO19, R_NDS32_GOT_LO19}, + {BFD_RELOC_NDS32_GOTPC_HI20, R_NDS32_GOTPC_HI20}, + {BFD_RELOC_NDS32_GOTPC_LO12, R_NDS32_GOTPC_LO12}, + {BFD_RELOC_NDS32_GOTOFF_HI20, R_NDS32_GOTOFF_HI20}, + {BFD_RELOC_NDS32_GOTOFF_LO12, R_NDS32_GOTOFF_LO12}, + {BFD_RELOC_NDS32_GOTOFF_LO15, R_NDS32_GOTOFF_LO15}, + {BFD_RELOC_NDS32_GOTOFF_LO19, R_NDS32_GOTOFF_LO19}, + {BFD_RELOC_NDS32_INSN16, R_NDS32_INSN16}, + {BFD_RELOC_NDS32_LABEL, R_NDS32_LABEL}, + {BFD_RELOC_NDS32_LONGCALL1, R_NDS32_LONGCALL1}, + {BFD_RELOC_NDS32_LONGCALL2, R_NDS32_LONGCALL2}, + {BFD_RELOC_NDS32_LONGCALL3, R_NDS32_LONGCALL3}, + {BFD_RELOC_NDS32_LONGJUMP1, R_NDS32_LONGJUMP1}, + {BFD_RELOC_NDS32_LONGJUMP2, R_NDS32_LONGJUMP2}, + {BFD_RELOC_NDS32_LONGJUMP3, R_NDS32_LONGJUMP3}, + {BFD_RELOC_NDS32_LOADSTORE, R_NDS32_LOADSTORE}, + {BFD_RELOC_NDS32_9_FIXED, R_NDS32_9_FIXED_RELA}, + {BFD_RELOC_NDS32_15_FIXED, R_NDS32_15_FIXED_RELA}, + {BFD_RELOC_NDS32_17_FIXED, R_NDS32_17_FIXED_RELA}, + {BFD_RELOC_NDS32_25_FIXED, R_NDS32_25_FIXED_RELA}, + {BFD_RELOC_NDS32_PLTREL_HI20, R_NDS32_PLTREL_HI20}, + {BFD_RELOC_NDS32_PLTREL_LO12, R_NDS32_PLTREL_LO12}, + {BFD_RELOC_NDS32_PLT_GOTREL_HI20, R_NDS32_PLT_GOTREL_HI20}, + {BFD_RELOC_NDS32_PLT_GOTREL_LO12, R_NDS32_PLT_GOTREL_LO12}, + {BFD_RELOC_NDS32_PLT_GOTREL_LO15, R_NDS32_PLT_GOTREL_LO15}, + {BFD_RELOC_NDS32_PLT_GOTREL_LO19, R_NDS32_PLT_GOTREL_LO19}, + {BFD_RELOC_NDS32_PLT_GOTREL_LO20, R_NDS32_PLT_GOTREL_LO20}, + {BFD_RELOC_NDS32_SDA12S2_DP, R_NDS32_SDA12S2_DP_RELA}, + {BFD_RELOC_NDS32_SDA12S2_SP, R_NDS32_SDA12S2_SP_RELA}, + {BFD_RELOC_NDS32_LO12S2_DP, R_NDS32_LO12S2_DP_RELA}, + {BFD_RELOC_NDS32_LO12S2_SP, R_NDS32_LO12S2_SP_RELA}, + {BFD_RELOC_NDS32_SDA16S3, R_NDS32_SDA16S3_RELA}, + {BFD_RELOC_NDS32_SDA17S2, R_NDS32_SDA17S2_RELA}, + {BFD_RELOC_NDS32_SDA18S1, R_NDS32_SDA18S1_RELA}, + {BFD_RELOC_NDS32_SDA19S0, R_NDS32_SDA19S0_RELA}, + {BFD_RELOC_NDS32_SDA_FP7U2_RELA, R_NDS32_SDA_FP7U2_RELA}, + {BFD_RELOC_NDS32_DWARF2_OP1, R_NDS32_DWARF2_OP1_RELA}, + {BFD_RELOC_NDS32_DWARF2_OP2, R_NDS32_DWARF2_OP2_RELA}, + {BFD_RELOC_NDS32_DWARF2_LEB, R_NDS32_DWARF2_LEB_RELA}, + {BFD_RELOC_NDS32_UPDATE_TA, R_NDS32_UPDATE_TA_RELA}, + {BFD_RELOC_NDS32_GOT_SUFF, R_NDS32_GOT_SUFF}, + {BFD_RELOC_NDS32_GOTOFF_SUFF, R_NDS32_GOTOFF_SUFF}, + {BFD_RELOC_NDS32_GOT15S2, R_NDS32_GOT15S2_RELA}, + {BFD_RELOC_NDS32_GOT17S2, R_NDS32_GOT17S2_RELA}, + {BFD_RELOC_NDS32_PTR, R_NDS32_PTR}, + {BFD_RELOC_NDS32_PTR_COUNT, R_NDS32_PTR_COUNT}, + {BFD_RELOC_NDS32_PLT_GOT_SUFF, R_NDS32_PLT_GOT_SUFF}, + {BFD_RELOC_NDS32_PTR_RESOLVED, R_NDS32_PTR_RESOLVED}, + {BFD_RELOC_NDS32_RELAX_ENTRY, R_NDS32_RELAX_ENTRY}, + {BFD_RELOC_NDS32_MULCALL_SUFF, R_NDS32_MULCALL_SUFF}, + {BFD_RELOC_NDS32_PLTBLOCK, R_NDS32_PLTBLOCK}, + {BFD_RELOC_NDS32_RELAX_REGION_BEGIN, R_NDS32_RELAX_REGION_BEGIN}, + {BFD_RELOC_NDS32_RELAX_REGION_END, R_NDS32_RELAX_REGION_END}, + {BFD_RELOC_NDS32_MINUEND, R_NDS32_MINUEND}, + {BFD_RELOC_NDS32_SUBTRAHEND, R_NDS32_SUBTRAHEND}, + + {BFD_RELOC_NDS32_DIFF8, R_NDS32_DIFF8}, + {BFD_RELOC_NDS32_DIFF16, R_NDS32_DIFF16}, + {BFD_RELOC_NDS32_DIFF32, R_NDS32_DIFF32}, + {BFD_RELOC_NDS32_DIFF_ULEB128, R_NDS32_DIFF_ULEB128}, + {BFD_RELOC_NDS32_25_ABS, R_NDS32_25_ABS_RELA}, + {BFD_RELOC_NDS32_DATA, R_NDS32_DATA}, + {BFD_RELOC_NDS32_TRAN, R_NDS32_TRAN}, + {BFD_RELOC_NDS32_17IFC_PCREL, R_NDS32_17IFC_PCREL_RELA}, + {BFD_RELOC_NDS32_10IFCU_PCREL, R_NDS32_10IFCU_PCREL_RELA}, +}; + +/* Patch tag. */ + +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 < ARRAY_SIZE (nds32_elf_howto_table); i++) + if (nds32_elf_howto_table[i].name != NULL + && strcasecmp (nds32_elf_howto_table[i].name, r_name) == 0) + return &nds32_elf_howto_table[i]; + + for (i = 0; i < ARRAY_SIZE (nds32_elf_relax_howto_table); i++) + if (nds32_elf_relax_howto_table[i].name != NULL + && strcasecmp (nds32_elf_relax_howto_table[i].name, r_name) == 0) + return &nds32_elf_relax_howto_table[i]; + + return NULL; +} + +static reloc_howto_type * +bfd_elf32_bfd_reloc_type_table_lookup (enum elf_nds32_reloc_type code) +{ + if (code < R_NDS32_RELAX_ENTRY) + { + BFD_ASSERT (code < ARRAY_SIZE (nds32_elf_howto_table)); + return &nds32_elf_howto_table[code]; + } + else + { + BFD_ASSERT ((size_t) (code - R_NDS32_RELAX_ENTRY) + < ARRAY_SIZE (nds32_elf_relax_howto_table)); + return &nds32_elf_relax_howto_table[code - R_NDS32_RELAX_ENTRY]; + } +} + +static reloc_howto_type * +bfd_elf32_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, + bfd_reloc_code_real_type code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE (nds32_reloc_map); i++) + { + if (nds32_reloc_map[i].bfd_reloc_val == code) + return bfd_elf32_bfd_reloc_type_table_lookup + (nds32_reloc_map[i].elf_reloc_val); + } + + return NULL; +} + +/* Set the howto pointer for an NDS32 ELF reloc. */ + +static void +nds32_info_to_howto_rel (bfd *abfd ATTRIBUTE_UNUSED, arelent *cache_ptr, + Elf_Internal_Rela *dst) +{ + enum elf_nds32_reloc_type r_type; + + r_type = ELF32_R_TYPE (dst->r_info); + BFD_ASSERT (ELF32_R_TYPE (dst->r_info) <= R_NDS32_GNU_VTENTRY); + cache_ptr->howto = bfd_elf32_bfd_reloc_type_table_lookup (r_type); +} + +static void +nds32_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED, arelent *cache_ptr, + Elf_Internal_Rela *dst) +{ + BFD_ASSERT ((ELF32_R_TYPE (dst->r_info) == R_NDS32_NONE) + || ((ELF32_R_TYPE (dst->r_info) > R_NDS32_GNU_VTENTRY) + && (ELF32_R_TYPE (dst->r_info) < R_NDS32_max))); + cache_ptr->howto = bfd_elf32_bfd_reloc_type_table_lookup (ELF32_R_TYPE (dst->r_info)); +} + +/* Support for core dump NOTE sections. + Reference to include/linux/elfcore.h in Linux. */ + +static bfd_boolean +nds32_elf_grok_prstatus (bfd *abfd, Elf_Internal_Note *note) +{ + int offset; + size_t size; + + switch (note->descsz) + { + case 0x114: + /* Linux/NDS32 32-bit, ABI1 */ + + /* pr_cursig */ + elf_tdata (abfd)->core->signal = bfd_get_16 (abfd, note->descdata + 12); + + /* pr_pid */ + elf_tdata (abfd)->core->pid = bfd_get_32 (abfd, note->descdata + 24); + + /* pr_reg */ + offset = 72; + size = 200; + break; + + case 0xfc: + /* Linux/NDS32 32-bit */ + + /* pr_cursig */ + elf_tdata (abfd)->core->signal = bfd_get_16 (abfd, note->descdata + 12); + + /* pr_pid */ + elf_tdata (abfd)->core->pid = bfd_get_32 (abfd, note->descdata + 24); + + /* pr_reg */ + offset = 72; + size = 176; + break; + + default: + return FALSE; + } + + /* Make a ".reg" section. */ + return _bfd_elfcore_make_pseudosection (abfd, ".reg", + size, note->descpos + offset); +} + +static bfd_boolean +nds32_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note) +{ + switch (note->descsz) + { + case 124: + /* Linux/NDS32 */ + + /* __kernel_uid_t, __kernel_gid_t are short on NDS32 platform. */ + elf_tdata (abfd)->core->program = + _bfd_elfcore_strndup (abfd, note->descdata + 28, 16); + elf_tdata (abfd)->core->command = + _bfd_elfcore_strndup (abfd, note->descdata + 44, 80); + + default: + return FALSE; + } + + /* 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; +} + +/* Hook called by the linker routine which adds symbols from an object + file. We must handle the special NDS32 section numbers here. + We also keep watching for whether we need to create the sdata special + linker sections. */ + +static bfd_boolean +nds32_elf_add_symbol_hook (bfd *abfd, + 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: + /* Common symbols less than the GP size are automatically + treated as SHN_MIPS_SCOMMON symbols. */ + if (sym->st_size > elf_gp_size (abfd) + || ELF_ST_TYPE (sym->st_info) == STT_TLS) + break; + + /* st_value is the alignemnt constraint. + That might be its actual size if it is an array or structure. */ + switch (sym->st_value) + { + case 1: + *secp = bfd_make_section_old_way (abfd, ".scommon_b"); + break; + case 2: + *secp = bfd_make_section_old_way (abfd, ".scommon_h"); + break; + case 4: + *secp = bfd_make_section_old_way (abfd, ".scommon_w"); + break; + case 8: + *secp = bfd_make_section_old_way (abfd, ".scommon_d"); + break; + default: + return TRUE; + } + + (*secp)->flags |= SEC_IS_COMMON; + *valp = sym->st_size; + break; + } + + return TRUE; +} + + +/* This function can figure out the best location for a base register to access + data relative to this base register + INPUT: + sda_d0: size of first DOUBLE WORD data section + sda_w0: size of first WORD data section + sda_h0: size of first HALF WORD data section + sda_b : size of BYTE data section + sda_hi: size of second HALF WORD data section + sda_w1: size of second WORD data section + sda_d1: size of second DOUBLE WORD data section + OUTPUT: + offset (always positive) from the beginning of sda_d0 if OK + a negative error value if fail + NOTE: + these 7 sections have to be located back to back if exist + a pass in 0 value for non-existing section */ + +/* Due to the interpretation of simm15 field of load/store depending on + data accessing size, the organization of base register relative data shall + like the following figure + ------------------------------------------- + | DOUBLE WORD sized data (range +/- 128K) + ------------------------------------------- + | WORD sized data (range +/- 64K) + ------------------------------------------- + | HALF WORD sized data (range +/- 32K) + ------------------------------------------- + | BYTE sized data (range +/- 16K) + ------------------------------------------- + | HALF WORD sized data (range +/- 32K) + ------------------------------------------- + | WORD sized data (range +/- 64K) + ------------------------------------------- + | DOUBLE WORD sized data (range +/- 128K) + ------------------------------------------- + Its base register shall be set to access these data freely. */ + +/* We have to figure out the SDA_BASE value, so that we can adjust the + symbol value correctly. We look up the symbol _SDA_BASE_ 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 asection *sda_rela_sec = NULL; + +#define SDA_SECTION_NUM 11 + +static bfd_reloc_status_type +nds32_elf_final_sda_base (bfd *output_bfd, struct bfd_link_info *info, + bfd_vma *psb, bfd_boolean add_symbol) +{ + int relax_fp_as_gp; + struct elf_nds32_link_hash_table *table; + struct bfd_link_hash_entry *h, *h2; + + h = bfd_link_hash_lookup (info->hash, "_SDA_BASE_", FALSE, FALSE, TRUE); + if (!h || (h->type != bfd_link_hash_defined && h->type != bfd_link_hash_defweak)) + { + asection *first = NULL, *final = NULL, *temp; + bfd_vma sda_base; + /* The first section must be 4-byte aligned to promise _SDA_BASE_ being + 4 byte-aligned. Therefore, it has to set the first section ".data" + 4 byte-aligned. */ + static const char sec_name[SDA_SECTION_NUM][10] = + { + ".data", ".got", ".sdata_d", ".sdata_w", ".sdata_h", ".sdata_b", + ".sbss_b", ".sbss_h", ".sbss_w", ".sbss_d", ".bss" + }; + size_t i = 0; + + if (output_bfd->sections == NULL) + { + *psb = elf_gp (output_bfd); + return bfd_reloc_ok; + } + + /* Get the first and final section. */ + while (i < sizeof (sec_name) / 10) + { + temp = bfd_get_section_by_name (output_bfd, sec_name[i]); + if (temp && !first && (temp->size != 0 || temp->rawsize != 0)) + first = temp; + if (temp && (temp->size != 0 || temp->rawsize != 0)) + final = temp; + i++; + } + + if (first && final) + { + /* The middle of data region. */ + sda_base = (final->vma + final->rawsize + first->vma) / 2; + + /* Find the section sda_base located. */ + i = 0; + while (i < sizeof (sec_name) / 10) + { + final = bfd_get_section_by_name (output_bfd, sec_name[i]); + if (final && (final->size != 0 || final->rawsize != 0) + && sda_base >= final->vma) + { + first = final; + i++; + } + else + break; + } + } + else + { + /* There is not any data section in output bfd, and set _SDA_BASE_ in + first output section. */ + first = output_bfd->sections; + while (first && first->size == 0 && first->rawsize == 0) + first = first->next; + if (!first) + { + *psb = elf_gp (output_bfd); + return bfd_reloc_ok; + } + sda_base = first->vma; + } + + sda_base -= first->vma; + sda_base = sda_base & (~7); + + if (!_bfd_generic_link_add_one_symbol + (info, output_bfd, "_SDA_BASE_", BSF_GLOBAL | BSF_WEAK, first, + (bfd_vma) sda_base, (const char *) NULL, FALSE, + get_elf_backend_data (output_bfd)->collect, &h)) + return FALSE; + + sda_rela_sec = first; + + table = nds32_elf_hash_table (info); + relax_fp_as_gp = table->relax_fp_as_gp; + if (relax_fp_as_gp) + { + h2 = bfd_link_hash_lookup (info->hash, FP_BASE_NAME, + FALSE, FALSE, FALSE); + /* Define a weak FP_BASE_NAME here to prevent the undefined symbol. + And set FP equal to SDA_BASE to do relaxation for + la $fp, _FP_BASE_. */ + if (!_bfd_generic_link_add_one_symbol + (info, output_bfd, FP_BASE_NAME, BSF_GLOBAL | BSF_WEAK, + first, (bfd_vma) sda_base, (const char *) NULL, + FALSE, get_elf_backend_data (output_bfd)->collect, &h2)) + return FALSE; + } + } + + if (add_symbol == TRUE) + { + if (h) + { + /* Now set gp. */ + elf_gp (output_bfd) = (h->u.def.value + + h->u.def.section->output_section->vma + + h->u.def.section->output_offset); + } + else + { + (*_bfd_error_handler) (_("error: Can't find symbol: _SDA_BASE_.")); + return bfd_reloc_dangerous; + } + } + + *psb = h->u.def.value + h->u.def.section->output_section->vma + + h->u.def.section->output_offset; + return bfd_reloc_ok; +} + + +/* Return size of a PLT entry. */ +#define elf_nds32_sizeof_plt(info) PLT_ENTRY_SIZE + + +/* Create an entry in an nds32 ELF linker hash table. */ + +static struct bfd_hash_entry * +nds32_elf_link_hash_newfunc (struct bfd_hash_entry *entry, + struct bfd_hash_table *table, + const char *string) +{ + struct elf_nds32_link_hash_entry *ret; + + ret = (struct elf_nds32_link_hash_entry *) entry; + + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (ret == NULL) + ret = (struct elf_nds32_link_hash_entry *) + bfd_hash_allocate (table, sizeof (struct elf_nds32_link_hash_entry)); + + if (ret == NULL) + return (struct bfd_hash_entry *) ret; + + /* Call the allocation method of the superclass. */ + ret = (struct elf_nds32_link_hash_entry *) + _bfd_elf_link_hash_newfunc ((struct bfd_hash_entry *) ret, table, string); + + if (ret != NULL) + { + struct elf_nds32_link_hash_entry *eh; + + eh = (struct elf_nds32_link_hash_entry *) ret; + eh->dyn_relocs = NULL; + } + + return (struct bfd_hash_entry *) ret; +} + +/* Create an nds32 ELF linker hash table. */ + +static struct bfd_link_hash_table * +nds32_elf_link_hash_table_create (bfd *abfd) +{ + struct elf_nds32_link_hash_table *ret; + + bfd_size_type amt = sizeof (struct elf_nds32_link_hash_table); + + ret = (struct elf_nds32_link_hash_table *) bfd_zmalloc (amt); + if (ret == NULL) + return NULL; + + /* patch tag. */ + if (!_bfd_elf_link_hash_table_init (&ret->root, abfd, + nds32_elf_link_hash_newfunc, + sizeof (struct elf_nds32_link_hash_entry), + NDS32_ELF_DATA)) + { + free (ret); + return NULL; + } + + ret->sgot = NULL; + ret->sgotplt = NULL; + ret->srelgot = NULL; + ret->splt = NULL; + ret->srelplt = NULL; + ret->sdynbss = NULL; + ret->srelbss = NULL; + ret->sym_ld_script = NULL; + ret->ex9_export_file = NULL; + ret->ex9_import_file = NULL; + + return &ret->root.root; +} + +/* Create .got, .gotplt, and .rela.got sections in DYNOBJ, and set up + shortcuts to them in our hash table. */ + +static bfd_boolean +create_got_section (bfd *dynobj, struct bfd_link_info *info) +{ + struct elf_nds32_link_hash_table *htab; + + if (!_bfd_elf_create_got_section (dynobj, info)) + return FALSE; + + htab = nds32_elf_hash_table (info); + htab->sgot = bfd_get_section_by_name (dynobj, ".got"); + htab->sgotplt = bfd_get_section_by_name (dynobj, ".got.plt"); + if (!htab->sgot || !htab->sgotplt) + abort (); + + /* _bfd_elf_create_got_section will create it for us. */ + htab->srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); + if (htab->srelgot == NULL + || !bfd_set_section_flags (dynobj, htab->srelgot, + (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS + | SEC_IN_MEMORY | SEC_LINKER_CREATED + | SEC_READONLY)) + || !bfd_set_section_alignment (dynobj, htab->srelgot, 2)) + return FALSE; + + return TRUE; +} + +/* Create dynamic sections when linking against a dynamic object. */ + +static bfd_boolean +nds32_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info) +{ + struct elf_nds32_link_hash_table *htab; + flagword flags, pltflags; + register asection *s; + const struct elf_backend_data *bed; + int ptralign = 2; /* 32-bit */ + + bed = get_elf_backend_data (abfd); + + htab = nds32_elf_hash_table (info); + + /* We need to create .plt, .rel[a].plt, .got, .got.plt, .dynbss, and + .rel[a].bss sections. */ + + flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY + | SEC_LINKER_CREATED); + + pltflags = flags; + pltflags |= SEC_CODE; + if (bed->plt_not_loaded) + pltflags &= ~(SEC_LOAD | SEC_HAS_CONTENTS); + if (bed->plt_readonly) + pltflags |= SEC_READONLY; + + s = bfd_make_section (abfd, ".plt"); + htab->splt = s; + if (s == NULL + || !bfd_set_section_flags (abfd, s, pltflags) + || !bfd_set_section_alignment (abfd, s, bed->plt_alignment)) + return FALSE; + + if (bed->want_plt_sym) + { + /* Define the symbol _PROCEDURE_LINKAGE_TABLE_ at the start of the + .plt section. */ + struct bfd_link_hash_entry *bh = NULL; + struct elf_link_hash_entry *h; + + if (!(_bfd_generic_link_add_one_symbol + (info, abfd, "_PROCEDURE_LINKAGE_TABLE_", BSF_GLOBAL, s, + (bfd_vma) 0, (const char *) NULL, FALSE, + get_elf_backend_data (abfd)->collect, &bh))) + return FALSE; + + h = (struct elf_link_hash_entry *) bh; + h->def_regular = 1; + h->type = STT_OBJECT; + + if (info->shared && !bfd_elf_link_record_dynamic_symbol (info, h)) + return FALSE; + } + + s = bfd_make_section (abfd, + bed->default_use_rela_p ? ".rela.plt" : ".rel.plt"); + htab->srelplt = s; + if (s == NULL + || !bfd_set_section_flags (abfd, s, flags | SEC_READONLY) + || !bfd_set_section_alignment (abfd, s, ptralign)) + return FALSE; + + if (htab->sgot == NULL && !create_got_section (abfd, info)) + return FALSE; + + { + const char *secname; + char *relname; + flagword secflags; + asection *sec; + + for (sec = abfd->sections; sec; sec = sec->next) + { + secflags = bfd_get_section_flags (abfd, sec); + if ((secflags & (SEC_DATA | SEC_LINKER_CREATED)) + || ((secflags & SEC_HAS_CONTENTS) != SEC_HAS_CONTENTS)) + continue; + secname = bfd_get_section_name (abfd, sec); + relname = (char *) bfd_malloc ((bfd_size_type) strlen (secname) + 6); + strcpy (relname, ".rela"); + strcat (relname, secname); + if (bfd_get_section_by_name (abfd, secname)) + continue; + s = bfd_make_section (abfd, relname); + if (s == NULL + || !bfd_set_section_flags (abfd, s, flags | SEC_READONLY) + || !bfd_set_section_alignment (abfd, s, ptralign)) + return FALSE; + } + } + + if (bed->want_dynbss) + { + /* The .dynbss section is a place to put symbols which are defined + by dynamic objects, are referenced by regular objects, and are + not functions. We must allocate space for them in the process + image and use a R_*_COPY reloc to tell the dynamic linker to + initialize them at run time. The linker script puts the .dynbss + section into the .bss section of the final image. */ + s = bfd_make_section (abfd, ".dynbss"); + htab->sdynbss = s; + if (s == NULL + || !bfd_set_section_flags (abfd, s, SEC_ALLOC | SEC_LINKER_CREATED)) + return FALSE; + /* The .rel[a].bss section holds copy relocs. This section is not + normally needed. We need to create it here, though, so that the + linker will map it to an output section. We can't just create it + only if we need it, because we will not know whether we need it + until we have seen all the input files, and the first time the + main linker code calls BFD after examining all the input files + (size_dynamic_sections) the input sections have already been + mapped to the output sections. If the section turns out not to + be needed, we can discard it later. We will never need this + section when generating a shared object, since they do not use + copy relocs. */ + if (!info->shared) + { + s = bfd_make_section (abfd, (bed->default_use_rela_p + ? ".rela.bss" : ".rel.bss")); + htab->srelbss = s; + if (s == NULL + || !bfd_set_section_flags (abfd, s, flags | SEC_READONLY) + || !bfd_set_section_alignment (abfd, s, ptralign)) + return FALSE; + } + } + + return TRUE; +} + +/* Copy the extra info we tack onto an elf_link_hash_entry. */ +static void +nds32_elf_copy_indirect_symbol (struct bfd_link_info *info, + struct elf_link_hash_entry *dir, + struct elf_link_hash_entry *ind) +{ + struct elf_nds32_link_hash_entry *edir, *eind; + + edir = (struct elf_nds32_link_hash_entry *) dir; + eind = (struct elf_nds32_link_hash_entry *) ind; + + if (eind->dyn_relocs != NULL) + { + if (edir->dyn_relocs != NULL) + { + struct elf_nds32_dyn_relocs **pp; + struct elf_nds32_dyn_relocs *p; + + if (ind->root.type == bfd_link_hash_indirect) + abort (); + + /* Add reloc counts against the weak sym to the strong sym + list. Merge any entries against the same section. */ + for (pp = &eind->dyn_relocs; (p = *pp) != NULL;) + { + struct elf_nds32_dyn_relocs *q; + + for (q = edir->dyn_relocs; q != NULL; q = q->next) + if (q->sec == p->sec) + { + q->pc_count += p->pc_count; + q->count += p->count; + *pp = p->next; + break; + } + if (q == NULL) + pp = &p->next; + } + *pp = edir->dyn_relocs; + } + + edir->dyn_relocs = eind->dyn_relocs; + eind->dyn_relocs = NULL; + } + + _bfd_elf_link_hash_copy_indirect (info, dir, ind); +} + + +/* Adjust a symbol defined by a dynamic object and referenced by a + regular object. The current definition is in some section of the + dynamic object, but we're not including those sections. We have to + change the definition to something the rest of the link can + understand. */ + +static bfd_boolean +nds32_elf_adjust_dynamic_symbol (struct bfd_link_info *info, + struct elf_link_hash_entry *h) +{ + struct elf_nds32_link_hash_table *htab; + struct elf_nds32_link_hash_entry *eh; + struct elf_nds32_dyn_relocs *p; + bfd *dynobj; + asection *s; + unsigned int power_of_two; + + dynobj = elf_hash_table (info)->dynobj; + + /* 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))); + + + /* If this is a function, put it in the procedure linkage table. We + will fill in the contents of the procedure linkage table later, + when we know the address of the .got section. */ + if (h->type == STT_FUNC || h->needs_plt) + { + if (!info->shared + && !h->def_dynamic + && !h->ref_dynamic + && h->root.type != bfd_link_hash_undefweak + && h->root.type != bfd_link_hash_undefined) + { + /* This case can occur if we saw a PLT reloc in an input + file, but the symbol was never referred to by a dynamic + object. In such a case, we don't actually need to build + a procedure linkage table, and we can just do a PCREL + reloc instead. */ + h->plt.offset = (bfd_vma) - 1; + h->needs_plt = 0; + } + + return TRUE; + } + else + h->plt.offset = (bfd_vma) - 1; + + /* If this is a weak symbol, and there is a real definition, the + processor independent code will have arranged for us to see the + real definition first, and we can just use the same value. */ + if (h->u.weakdef != NULL) + { + BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined + || h->u.weakdef->root.type == bfd_link_hash_defweak); + h->root.u.def.section = h->u.weakdef->root.u.def.section; + h->root.u.def.value = h->u.weakdef->root.u.def.value; + return TRUE; + } + + /* This is a reference to a symbol defined by a dynamic object which + is not a function. */ + + /* If we are creating a shared library, we must presume that the + only references to the symbol are via the global offset table. + For such cases we need not do anything here; the relocations will + be handled correctly by relocate_section. */ + if (info->shared) + return TRUE; + + /* If there are no references to this symbol that do not use the + GOT, we don't need to generate a copy reloc. */ + if (!h->non_got_ref) + return TRUE; + + /* If -z nocopyreloc was given, we won't generate them either. */ + if (info->nocopyreloc) + { + h->non_got_ref = 0; + return TRUE; + } + + eh = (struct elf_nds32_link_hash_entry *) h; + for (p = eh->dyn_relocs; p != NULL; p = p->next) + { + s = p->sec->output_section; + if (s != NULL && (s->flags & (SEC_READONLY | SEC_HAS_CONTENTS)) != 0) + break; + } + + /* If we didn't find any dynamic relocs in sections which needs the + copy reloc, then we'll be keeping the dynamic relocs and avoiding + the copy reloc. */ + if (p == NULL) + { + h->non_got_ref = 0; + return TRUE; + } + + /* We must allocate the symbol in our .dynbss section, which will + become part of the .bss section of the executable. There will be + an entry for this symbol in the .dynsym section. The dynamic + object will contain position independent code, so all references + from the dynamic object to this symbol will go through the global + offset table. The dynamic linker will use the .dynsym entry to + determine the address it must put in the global offset table, so + both the dynamic object and the regular object will refer to the + same memory location for the variable. */ + + htab = nds32_elf_hash_table (info); + s = htab->sdynbss; + BFD_ASSERT (s != NULL); + + /* We must generate a R_NDS32_COPY reloc to tell the dynamic linker + to copy the initial value out of the dynamic object and into the + runtime process image. We need to remember the offset into the + .rela.bss section we are going to use. */ + if ((h->root.u.def.section->flags & SEC_ALLOC) != 0) + { + asection *srel; + + srel = htab->srelbss; + BFD_ASSERT (srel != NULL); + srel->size += sizeof (Elf32_External_Rela); + h->needs_copy = 1; + } + + /* We need to figure out the alignment required for this symbol. I + have no idea how ELF linkers handle this. */ + power_of_two = bfd_log2 (h->size); + if (power_of_two > 3) + power_of_two = 3; + + /* Apply the required alignment. */ + s->size = BFD_ALIGN (s->size, (bfd_size_type) (1 << power_of_two)); + if (power_of_two > bfd_get_section_alignment (dynobj, s)) + { + if (!bfd_set_section_alignment (dynobj, s, power_of_two)) + return FALSE; + } + + /* Define the symbol as being at this point in the section. */ + h->root.u.def.section = s; + h->root.u.def.value = s->size; + + /* Increment the section size to make room for the symbol. */ + s->size += h->size; + + return TRUE; +} + +/* Allocate space in .plt, .got and associated reloc sections for + dynamic relocs. */ + +static bfd_boolean +allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) +{ + struct bfd_link_info *info; + struct elf_nds32_link_hash_table *htab; + struct elf_nds32_link_hash_entry *eh; + struct elf_nds32_dyn_relocs *p; + + if (h->root.type == bfd_link_hash_indirect) + return TRUE; + + if (h->root.type == bfd_link_hash_warning) + /* When warning symbols are created, they **replace** the "real" + entry in the hash table, thus we never get to see the real + symbol in a hash traversal. So look at it now. */ + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + info = (struct bfd_link_info *) inf; + htab = nds32_elf_hash_table (info); + + eh = (struct elf_nds32_link_hash_entry *) h; + + if (htab->root.dynamic_sections_created && h->plt.refcount > 0) + { + /* Make sure this symbol is output as a dynamic symbol. + Undefined weak syms won't yet be marked as dynamic. */ + if (h->dynindx == -1 && !h->forced_local) + { + if (!bfd_elf_link_record_dynamic_symbol (info, h)) + return FALSE; + } + + if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, info->shared, h)) + { + asection *s = htab->splt; + + /* If this is the first .plt entry, make room for the special + first entry. */ + if (s->size == 0) + s->size += PLT_ENTRY_SIZE; + + h->plt.offset = s->size; + + /* If this symbol is not defined in a regular file, and we are + not generating a shared library, then set the symbol to this + location in the .plt. This is required to make function + pointers compare as equal between the normal executable and + the shared library. */ + if (!info->shared && !h->def_regular) + { + h->root.u.def.section = s; + h->root.u.def.value = h->plt.offset; + } + + /* Make room for this entry. */ + s->size += PLT_ENTRY_SIZE; + + /* We also need to make an entry in the .got.plt section, which + will be placed in the .got section by the linker script. */ + htab->sgotplt->size += 4; + + /* We also need to make an entry in the .rel.plt section. */ + htab->srelplt->size += sizeof (Elf32_External_Rela); + } + else + { + h->plt.offset = (bfd_vma) - 1; + h->needs_plt = 0; + } + } + else + { + h->plt.offset = (bfd_vma) - 1; + h->needs_plt = 0; + } + + if (h->got.refcount > 0) + { + asection *s; + bfd_boolean dyn; + + /* Make sure this symbol is output as a dynamic symbol. + Undefined weak syms won't yet be marked as dynamic. */ + if (h->dynindx == -1 && !h->forced_local) + { + if (!bfd_elf_link_record_dynamic_symbol (info, h)) + return FALSE; + } + + s = htab->sgot; + + h->got.offset = s->size; + s->size += 4; + dyn = htab->root.dynamic_sections_created; + if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)) + htab->srelgot->size += sizeof (Elf32_External_Rela); + } + else + h->got.offset = (bfd_vma) - 1; + + if (eh->dyn_relocs == NULL) + return TRUE; + + /* In the shared -Bsymbolic case, discard space allocated for + dynamic pc-relative relocs against symbols which turn out to be + defined in regular objects. For the normal shared case, discard + space for pc-relative relocs that have become local due to symbol + visibility changes. */ + + if (info->shared) + { + if (h->def_regular && (h->forced_local || info->symbolic)) + { + struct elf_nds32_dyn_relocs **pp; + + for (pp = &eh->dyn_relocs; (p = *pp) != NULL;) + { + p->count -= p->pc_count; + p->pc_count = 0; + if (p->count == 0) + *pp = p->next; + else + pp = &p->next; + } + } + } + else + { + /* For the non-shared case, discard space for relocs against + symbols which turn out to need copy relocs or are not dynamic. */ + + if (!h->non_got_ref + && ((h->def_dynamic + && !h->def_regular) + || (htab->root.dynamic_sections_created + && (h->root.type == bfd_link_hash_undefweak + || h->root.type == bfd_link_hash_undefined)))) + { + /* Make sure this symbol is output as a dynamic symbol. + Undefined weak syms won't yet be marked as dynamic. */ + if (h->dynindx == -1 && !h->forced_local) + { + if (!bfd_elf_link_record_dynamic_symbol (info, h)) + return FALSE; + } + + /* If that succeeded, we know we'll be keeping all the + relocs. */ + if (h->dynindx != -1) + goto keep; + } + + eh->dyn_relocs = NULL; + + keep:; + } + + /* Finally, allocate space. */ + for (p = eh->dyn_relocs; p != NULL; p = p->next) + { + asection *sreloc = elf_section_data (p->sec)->sreloc; + sreloc->size += p->count * sizeof (Elf32_External_Rela); + } + + return TRUE; +} + +/* Find any dynamic relocs that apply to read-only sections. */ + +static bfd_boolean +readonly_dynrelocs (struct elf_link_hash_entry *h, void *inf) +{ + struct elf_nds32_link_hash_entry *eh; + struct elf_nds32_dyn_relocs *p; + + if (h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + eh = (struct elf_nds32_link_hash_entry *) h; + for (p = eh->dyn_relocs; p != NULL; p = p->next) + { + asection *s = p->sec->output_section; + + if (s != NULL && (s->flags & SEC_READONLY) != 0) + { + struct bfd_link_info *info = (struct bfd_link_info *) inf; + + info->flags |= DF_TEXTREL; + + /* Not an error, just cut short the traversal. */ + return FALSE; + } + } + return TRUE; +} + +/* Set the sizes of the dynamic sections. */ + +static bfd_boolean +nds32_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, + struct bfd_link_info *info) +{ + struct elf_nds32_link_hash_table *htab; + bfd *dynobj; + asection *s; + bfd_boolean relocs; + bfd *ibfd; + + htab = nds32_elf_hash_table (info); + dynobj = htab->root.dynobj; + BFD_ASSERT (dynobj != NULL); + + if (htab->root.dynamic_sections_created) + { + /* Set the contents of the .interp section to the interpreter. */ + if (!info->shared) + { + s = bfd_get_section_by_name (dynobj, ".interp"); + BFD_ASSERT (s != NULL); + s->size = sizeof ELF_DYNAMIC_INTERPRETER; + s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER; + } + } + + /* Set up .got offsets for local syms, and space for local dynamic + relocs. */ + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) + { + bfd_signed_vma *local_got; + bfd_signed_vma *end_local_got; + bfd_size_type locsymcount; + Elf_Internal_Shdr *symtab_hdr; + asection *srel; + + if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour) + continue; + + for (s = ibfd->sections; s != NULL; s = s->next) + { + struct elf_nds32_dyn_relocs *p; + + for (p = ((struct elf_nds32_dyn_relocs *) + elf_section_data (s)->local_dynrel); + p != NULL; p = p->next) + { + if (!bfd_is_abs_section (p->sec) + && bfd_is_abs_section (p->sec->output_section)) + { + /* Input section has been discarded, either because + it is a copy of a linkonce section or due to + linker script /DISCARD/, so we'll be discarding + the relocs too. */ + } + else if (p->count != 0) + { + srel = elf_section_data (p->sec)->sreloc; + srel->size += p->count * sizeof (Elf32_External_Rela); + if ((p->sec->output_section->flags & SEC_READONLY) != 0) + info->flags |= DF_TEXTREL; + } + } + } + + local_got = elf_local_got_refcounts (ibfd); + if (!local_got) + continue; + + symtab_hdr = &elf_tdata (ibfd)->symtab_hdr; + locsymcount = symtab_hdr->sh_info; + end_local_got = local_got + locsymcount; + s = htab->sgot; + srel = htab->srelgot; + for (; local_got < end_local_got; ++local_got) + { + if (*local_got > 0) + { + *local_got = s->size; + s->size += 4; + if (info->shared) + srel->size += sizeof (Elf32_External_Rela); + } + else + *local_got = (bfd_vma) - 1; + } + } + + /* Allocate global sym .plt and .got entries, and space for global + sym dynamic relocs. */ + elf_link_hash_traverse (&htab->root, allocate_dynrelocs, (void *) info); + + /* We now have determined the sizes of the various dynamic sections. + Allocate memory for them. */ + relocs = FALSE; + for (s = dynobj->sections; s != NULL; s = s->next) + { + if ((s->flags & SEC_LINKER_CREATED) == 0) + continue; + + if (s == htab->splt) + { + /* Strip this section if we don't need it; see the + comment below. */ + } + else if (s == htab->sgot) + { + got_size += s->size; + } + else if (s == htab->sgotplt) + { + got_size += s->size; + } + else if (strncmp (bfd_get_section_name (dynobj, s), ".rela", 5) == 0) + { + if (s->size != 0 && s != htab->srelplt) + relocs = TRUE; + + /* We use the reloc_count field as a counter if we need + to copy relocs into the output file. */ + s->reloc_count = 0; + } + else + { + /* It's not one of our sections, so don't allocate space. */ + continue; + } + + if (s->size == 0) + { + /* If we don't need this section, strip it from the + output file. This is mostly to handle .rela.bss and + .rela.plt. We must create both sections in + create_dynamic_sections, because they must be created + before the linker maps input sections to output + sections. The linker does that before + adjust_dynamic_symbol is called, and it is that + function which decides whether anything needs to go + into these sections. */ + s->flags |= SEC_EXCLUDE; + continue; + } + + /* Allocate memory for the section contents. We use bfd_zalloc + here in case unused entries are not reclaimed before the + section's contents are written out. This should not happen, + but this way if it does, we get a R_NDS32_NONE reloc instead + of garbage. */ + s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size); + if (s->contents == NULL) + return FALSE; + } + + + if (htab->root.dynamic_sections_created) + { + /* Add some entries to the .dynamic section. We fill in the + values later, in nds32_elf_finish_dynamic_sections, but we + must add the entries now so that we get the correct size for + the .dynamic section. The DT_DEBUG entry is filled in by the + dynamic linker and used by the debugger. */ +#define add_dynamic_entry(TAG, VAL) \ + _bfd_elf_add_dynamic_entry (info, TAG, VAL) + + if (!info->shared) + { + if (!add_dynamic_entry (DT_DEBUG, 0)) + return FALSE; + } + + if (htab->splt->size != 0) + { + if (!add_dynamic_entry (DT_PLTGOT, 0) + || !add_dynamic_entry (DT_PLTRELSZ, 0) + || !add_dynamic_entry (DT_PLTREL, DT_RELA) + || !add_dynamic_entry (DT_JMPREL, 0)) + return FALSE; + } + + if (relocs) + { + if (!add_dynamic_entry (DT_RELA, 0) + || !add_dynamic_entry (DT_RELASZ, 0) + || !add_dynamic_entry (DT_RELAENT, sizeof (Elf32_External_Rela))) + return FALSE; + + /* If any dynamic relocs apply to a read-only section, + then we need a DT_TEXTREL entry. */ + if ((info->flags & DF_TEXTREL) == 0) + elf_link_hash_traverse (&htab->root, readonly_dynrelocs, + (void *) info); + + if ((info->flags & DF_TEXTREL) != 0) + { + if (!add_dynamic_entry (DT_TEXTREL, 0)) + return FALSE; + } + } + } +#undef add_dynamic_entry + + return TRUE; +} + +static bfd_reloc_status_type +nds32_relocate_contents (reloc_howto_type *howto, bfd *input_bfd, + bfd_vma relocation, bfd_byte *location) +{ + int size; + bfd_vma x = 0; + bfd_reloc_status_type flag; + unsigned int rightshift = howto->rightshift; + unsigned int bitpos = howto->bitpos; + + /* If the size is negative, negate RELOCATION. This isn't very + general. */ + if (howto->size < 0) + relocation = -relocation; + + /* Get the value we are going to relocate. */ + size = bfd_get_reloc_size (howto); + switch (size) + { + default: + case 0: + case 1: + case 8: + abort (); + break; + case 2: + x = bfd_getb16 (location); + break; + case 4: + x = bfd_getb32 (location); + break; + } + + /* Check for overflow. FIXME: We may drop bits during the addition + which we don't check for. We must either check at every single + operation, which would be tedious, or we must do the computations + in a type larger than bfd_vma, which would be inefficient. */ + flag = bfd_reloc_ok; + if (howto->complain_on_overflow != complain_overflow_dont) + { + bfd_vma addrmask, fieldmask, signmask, ss; + bfd_vma a, b, sum; + + /* Get the values to be added together. For signed and unsigned + relocations, we assume that all values should be truncated to + the size of an address. For bitfields, all the bits matter. + See also bfd_check_overflow. */ + fieldmask = N_ONES (howto->bitsize); + signmask = ~fieldmask; + addrmask = N_ONES (bfd_arch_bits_per_address (input_bfd)) | fieldmask; + a = (relocation & addrmask) >> rightshift; + b = (x & howto->src_mask & addrmask) >> bitpos; + + switch (howto->complain_on_overflow) + { + case complain_overflow_signed: + /* If any sign bits are set, all sign bits must be set. + That is, A must be a valid negative address after + shifting. */ + signmask = ~(fieldmask >> 1); + /* Fall through. */ + + case complain_overflow_bitfield: + /* Much like the signed check, but for a field one bit + wider. We allow a bitfield to represent numbers in the + range -2**n to 2**n-1, where n is the number of bits in the + field. Note that when bfd_vma is 32 bits, a 32-bit reloc + can't overflow, which is exactly what we want. */ + ss = a & signmask; + if (ss != 0 && ss != ((addrmask >> rightshift) & signmask)) + flag = bfd_reloc_overflow; + + /* We only need this next bit of code if the sign bit of B + is below the sign bit of A. This would only happen if + SRC_MASK had fewer bits than BITSIZE. Note that if + SRC_MASK has more bits than BITSIZE, we can get into + trouble; we would need to verify that B is in range, as + we do for A above. */ + ss = ((~howto->src_mask) >> 1) & howto->src_mask; + ss >>= bitpos; + + /* Set all the bits above the sign bit. */ + b = (b ^ ss) - ss; + + /* Now we can do the addition. */ + sum = a + b; + + /* See if the result has the correct sign. Bits above the + sign bit are junk now; ignore them. If the sum is + positive, make sure we did not have all negative inputs; + if the sum is negative, make sure we did not have all + positive inputs. The test below looks only at the sign + bits, and it really just + SIGN (A) == SIGN (B) && SIGN (A) != SIGN (SUM) + + We mask with addrmask here to explicitly allow an address + wrap-around. The Linux kernel relies on it, and it is + the only way to write assembler code which can run when + loaded at a location 0x80000000 away from the location at + which it is linked. */ + if (((~(a ^ b)) & (a ^ sum)) & signmask & addrmask) + flag = bfd_reloc_overflow; + + break; + + case complain_overflow_unsigned: + /* Checking for an unsigned overflow is relatively easy: + trim the addresses and add, and trim the result as well. + Overflow is normally indicated when the result does not + fit in the field. However, we also need to consider the + case when, e.g., fieldmask is 0x7fffffff or smaller, an + input is 0x80000000, and bfd_vma is only 32 bits; then we + will get sum == 0, but there is an overflow, since the + inputs did not fit in the field. Instead of doing a + separate test, we can check for this by or-ing in the + operands when testing for the sum overflowing its final + field. */ + sum = (a + b) & addrmask; + if ((a | b | sum) & signmask) + flag = bfd_reloc_overflow; + break; + + default: + abort (); + } + } + + /* Put RELOCATION in the right bits. */ + relocation >>= (bfd_vma) rightshift; + relocation <<= (bfd_vma) bitpos; + + /* Add RELOCATION to the right bits of X. */ + /* FIXME : 090616 + Because the relaxation may generate duplicate relocation at one address, + an addition to immediate in the instruction may cause the relocation added + several times. + This bug should be fixed in assembler, but a check is also needed here. */ + if (howto->partial_inplace) + x = ((x & ~howto->dst_mask) + | (((x & howto->src_mask) + relocation) & howto->dst_mask)); + else + x = ((x & ~howto->dst_mask) | ((relocation) & howto->dst_mask)); + + + /* Put the relocated value back in the object file. */ + switch (size) + { + default: + case 0: + case 1: + case 8: + abort (); + break; + case 2: + bfd_putb16 (x, location); + break; + case 4: + bfd_putb32 (x, location); + break; + } + + return flag; +} + +static bfd_reloc_status_type +nds32_elf_final_link_relocate (reloc_howto_type *howto, bfd *input_bfd, + asection *input_section, bfd_byte *contents, + bfd_vma address, bfd_vma value, bfd_vma addend) +{ + bfd_vma relocation; + + /* Sanity check the address. */ + if (address > bfd_get_section_limit (input_bfd, input_section)) + return bfd_reloc_outofrange; + + /* This function assumes that we are dealing with a basic relocation + against a symbol. We want to compute the value of the symbol to + relocate to. This is just VALUE, the value of the symbol, plus + ADDEND, any addend associated with the reloc. */ + relocation = value + addend; + + /* If the relocation is PC relative, we want to set RELOCATION to + the distance between the symbol (currently in RELOCATION) and the + location we are relocating. Some targets (e.g., i386-aout) + arrange for the contents of the section to be the negative of the + offset of the location within the section; for such targets + pcrel_offset is FALSE. Other targets (e.g., m88kbcs or ELF) + simply leave the contents of the section as zero; for such + targets pcrel_offset is TRUE. If pcrel_offset is FALSE we do not + need to subtract out the offset of the location within the + section (which is just ADDRESS). */ + if (howto->pc_relative) + { + relocation -= (input_section->output_section->vma + + input_section->output_offset); + if (howto->pcrel_offset) + relocation -= address; + } + + return nds32_relocate_contents (howto, input_bfd, relocation, + contents + address); +} + +static bfd_boolean +nds32_elf_output_symbol_hook (struct bfd_link_info *info, + const char *name, + Elf_Internal_Sym *elfsym ATTRIBUTE_UNUSED, + asection *input_sec, + struct elf_link_hash_entry *h ATTRIBUTE_UNUSED) +{ + const char *source; + FILE *sym_ld_script = NULL; + struct elf_nds32_link_hash_table *table; + + table = nds32_elf_hash_table (info); + sym_ld_script = table->sym_ld_script; + if (!sym_ld_script) + return TRUE; + + if (!h || !name || *name == '\0') + return TRUE; + + if (input_sec->flags & SEC_EXCLUDE) + return TRUE; + + if (!check_start_export_sym) + { + fprintf (sym_ld_script, "SECTIONS\n{\n"); + check_start_export_sym = 1; + } + + if (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + { + if (!h->root.u.def.section->output_section) + return TRUE; + + if (bfd_is_const_section (input_sec)) + source = input_sec->name; + else + source = input_sec->owner->filename; + + fprintf (sym_ld_script, "\t%s = 0x%08lx;\t /* %s */\n", + h->root.root.string, + (long) (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset), source); + } + + return TRUE; +} + +/* Relocate an NDS32/D ELF section. + There is some attempt to make this function usable for many architectures, + both for RELA and REL type relocs, if only to serve as a learning tool. + + The RELOCATE_SECTION function is called by the new ELF backend linker + to handle the relocations for a section. + + The relocs are always passed as Rela structures; if the section + actually uses Rel structures, the r_addend field will always be + zero. + + This function is responsible for adjust the section contents as + necessary, and (if using Rela relocs and generating a + relocatable output file) adjusting the reloc addend as + necessary. + + This function does not have to worry about setting the reloc + address or the reloc symbol index. + + LOCAL_SYMS is a pointer to the swapped in local symbols. + + LOCAL_SECTIONS is an array giving the section in the input file + corresponding to the st_shndx field of each local symbol. + + The global hash table entry for the global symbols can be found + via elf_sym_hashes (input_bfd). + + When generating relocatable output, this function must handle + STB_LOCAL/STT_SECTION symbols specially. The output symbol is + going to be the section symbol corresponding to the output + section, which means that the addend must be adjusted + accordingly. */ + +static bfd_boolean +nds32_elf_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, + 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_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes; + Elf_Internal_Rela *rel, *relend; + bfd_boolean ret = TRUE; /* Assume success. */ + int align = 0; + bfd_reloc_status_type r; + const char *errmsg = NULL; + bfd_vma gp; + struct elf_nds32_link_hash_table *htab; + bfd *dynobj; + bfd_vma *local_got_offsets; + asection *sgot, *splt, *sreloc; + bfd_vma high_address; + struct elf_nds32_link_hash_table *table; + int eliminate_gc_relocs; + bfd_vma fpbase_addr; + + symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (input_bfd); + htab = nds32_elf_hash_table (info); + high_address = bfd_get_section_limit (input_bfd, input_section); + + dynobj = htab->root.dynobj; + local_got_offsets = elf_local_got_offsets (input_bfd); + + sgot = htab->sgot; + splt = htab->splt; + sreloc = NULL; + + rel = relocs; + relend = relocs + input_section->reloc_count; + + table = nds32_elf_hash_table (info); + eliminate_gc_relocs = table->eliminate_gc_relocs; + /* By this time, we can adjust the value of _SDA_BASE_. */ + if ((!info->relocatable)) + { + is_SDA_BASE_set = 1; + r = nds32_elf_final_sda_base (output_bfd, info, &gp, TRUE); + if (r != bfd_reloc_ok) + return FALSE; + } + + /* Use gp as fp to prevent truncated fit. Because in relaxation time + the fp value is set as gp, and it has be reverted for instruction + setting fp. */ + fpbase_addr = elf_gp (output_bfd); + + for (rel = relocs; rel < relend; rel++) + { + enum elf_nds32_reloc_type r_type; + reloc_howto_type *howto = NULL; + unsigned long r_symndx; + struct elf_link_hash_entry *h = NULL; + Elf_Internal_Sym *sym = NULL; + asection *sec; + bfd_vma relocation; + + /* We can't modify r_addend here as elf_link_input_bfd has an assert to + ensure it's zero (we use REL relocs, not RELA). Therefore this + should be assigning zero to `addend', but for clarity we use + `r_addend'. */ + + bfd_vma addend = rel->r_addend; + bfd_vma offset = rel->r_offset; + + r_type = ELF32_R_TYPE (rel->r_info); + if (r_type >= R_NDS32_max) + { + (*_bfd_error_handler) (_("%B: error: unknown relocation type %d."), + input_bfd, r_type); + bfd_set_error (bfd_error_bad_value); + ret = FALSE; + continue; + } + + if (r_type == R_NDS32_GNU_VTENTRY + || r_type == R_NDS32_GNU_VTINHERIT + || r_type == R_NDS32_NONE + || r_type == R_NDS32_RELA_GNU_VTENTRY + || r_type == R_NDS32_RELA_GNU_VTINHERIT + || (r_type >= R_NDS32_INSN16 && r_type <= R_NDS32_25_FIXED_RELA) + || r_type == R_NDS32_DATA + || r_type == R_NDS32_TRAN) + continue; + + /* If we enter the fp-as-gp region. Resolve the address of best fp-base. */ + if (ELF32_R_TYPE (rel->r_info) == R_NDS32_RELAX_REGION_BEGIN + && (rel->r_addend & R_NDS32_RELAX_REGION_OMIT_FP_FLAG)) + { + int dist; + + /* Distance to relocation of best fp-base is encoded in R_SYM. */ + dist = rel->r_addend >> 16; + fpbase_addr = calculate_memory_address (input_bfd, rel + dist, + local_syms, symtab_hdr); + } + else if (ELF32_R_TYPE (rel->r_info) == R_NDS32_RELAX_REGION_END + && (rel->r_addend & R_NDS32_RELAX_REGION_OMIT_FP_FLAG)) + { + fpbase_addr = elf_gp (output_bfd); + } + + if (((r_type >= R_NDS32_DWARF2_OP1_RELA + && r_type <= R_NDS32_DWARF2_LEB_RELA) + || r_type >= R_NDS32_RELAX_ENTRY) && !info->relocatable) + continue; + + howto = bfd_elf32_bfd_reloc_type_table_lookup (r_type); + r_symndx = ELF32_R_SYM (rel->r_info); + + /* This is a final link. */ + sym = NULL; + sec = NULL; + h = NULL; + + if (r_symndx < symtab_hdr->sh_info) + { + /* Local symbol. */ + sym = local_syms + r_symndx; + sec = local_sections[r_symndx]; + + relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel); + addend = rel->r_addend; + } + else + { + /* External symbol. */ + bfd_boolean warned, ignored, unresolved_reloc; + int symndx = r_symndx - symtab_hdr->sh_info; + + RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel, + r_symndx, symtab_hdr, sym_hashes, h, sec, + relocation, unresolved_reloc, warned, + ignored); + + /* la $fp, _FP_BASE_ is per-function (region). + Handle it specially. */ + switch ((int) r_type) + { + case R_NDS32_SDA19S0_RELA: + case R_NDS32_SDA15S0_RELA: + case R_NDS32_20_RELA: + if (strcmp (elf_sym_hashes (input_bfd)[symndx]->root.root.string, + FP_BASE_NAME) == 0) + { + relocation = fpbase_addr; + break; + } + } + + } + + if (info->relocatable) + { + /* This is a relocatable link. We don't have to change + anything, unless the reloc is against a section symbol, + in which case we have to adjust according to where the + section symbol winds up in the output section. */ + if (sym != NULL && ELF_ST_TYPE (sym->st_info) == STT_SECTION) + rel->r_addend += sec->output_offset + sym->st_value; + + continue; + } + + /* Sanity check the address. */ + if (offset > high_address) + { + r = bfd_reloc_outofrange; + goto check_reloc; + } + + if ((r_type >= R_NDS32_DWARF2_OP1_RELA + && r_type <= R_NDS32_DWARF2_LEB_RELA) + || r_type >= R_NDS32_RELAX_ENTRY) + continue; + + switch ((int) r_type) + { + case R_NDS32_GOTOFF: + /* Relocation is relative to the start of the global offset + table (for ld24 rx, #uimm24), e.g. access at label+addend + + ld24 rx. #label@GOTOFF + addend + sub rx, r12. */ + case R_NDS32_GOTOFF_HI20: + case R_NDS32_GOTOFF_LO12: + case R_NDS32_GOTOFF_LO15: + case R_NDS32_GOTOFF_LO19: + BFD_ASSERT (sgot != NULL); + + relocation -= elf_gp (output_bfd); + break; + + case R_NDS32_9_PLTREL: + case R_NDS32_25_PLTREL: + /* Relocation is to the entry for this symbol in the + procedure linkage table. */ + + /* The native assembler will generate a 25_PLTREL reloc + for a local symbol if you assemble a call from one + section to another when using -K pic. */ + if (h == NULL) + break; + + if (h->forced_local) + break; + + /* We didn't make a PLT entry for this symbol. This + happens when statically linking PIC code, or when + using -Bsymbolic. */ + if (h->plt.offset == (bfd_vma) - 1) + break; + + relocation = (splt->output_section->vma + + splt->output_offset + h->plt.offset); + break; + + case R_NDS32_PLT_GOTREL_HI20: + case R_NDS32_PLT_GOTREL_LO12: + case R_NDS32_PLT_GOTREL_LO15: + case R_NDS32_PLT_GOTREL_LO19: + case R_NDS32_PLT_GOTREL_LO20: + if (h == NULL || h->forced_local || h->plt.offset == (bfd_vma) - 1) + { + /* We didn't make a PLT entry for this symbol. This + happens when statically linking PIC code, or when + using -Bsymbolic. */ + relocation -= elf_gp (output_bfd); + break; + } + + relocation = (splt->output_section->vma + + splt->output_offset + h->plt.offset); + + relocation -= elf_gp (output_bfd); + break; + + case R_NDS32_PLTREL_HI20: + case R_NDS32_PLTREL_LO12: + + /* Relocation is to the entry for this symbol in the + procedure linkage table. */ + + /* The native assembler will generate a 25_PLTREL reloc + for a local symbol if you assemble a call from one + section to another when using -K pic. */ + if (h == NULL) + break; + + if (h->forced_local) + break; + + if (h->plt.offset == (bfd_vma) - 1) + /* We didn't make a PLT entry for this symbol. This + happens when statically linking PIC code, or when + using -Bsymbolic. */ + break; + + if (splt == NULL) + break; + + relocation = (splt->output_section->vma + + splt->output_offset + + h->plt.offset + 4) + - (input_section->output_section->vma + + input_section->output_offset + + rel->r_offset); + + break; + + case R_NDS32_GOTPC20: + /* .got(_GLOBAL_OFFSET_TABLE_) - pc relocation + ld24 rx,#_GLOBAL_OFFSET_TABLE_ */ + relocation = elf_gp (output_bfd); + break; + + case R_NDS32_GOTPC_HI20: + case R_NDS32_GOTPC_LO12: + { + /* .got(_GLOBAL_OFFSET_TABLE_) - pc relocation + bl .+4 + seth rx,#high(_GLOBAL_OFFSET_TABLE_) + or3 rx,rx,#low(_GLOBAL_OFFSET_TABLE_ +4) + or + bl .+4 + seth rx,#shigh(_GLOBAL_OFFSET_TABLE_) + add3 rx,rx,#low(_GLOBAL_OFFSET_TABLE_ +4) + */ + relocation = elf_gp (output_bfd); + relocation -= (input_section->output_section->vma + + input_section->output_offset + rel->r_offset); + break; + } + + case R_NDS32_GOT20: + /* Fall through. */ + case R_NDS32_GOT_HI20: + case R_NDS32_GOT_LO12: + case R_NDS32_GOT_LO15: + case R_NDS32_GOT_LO19: + /* Relocation is to the entry for this symbol in the global + offset table. */ + BFD_ASSERT (sgot != NULL); + + if (h != NULL) + { + bfd_boolean dyn; + bfd_vma off; + + off = h->got.offset; + BFD_ASSERT (off != (bfd_vma) - 1); + dyn = htab->root.dynamic_sections_created; + if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) + || (info->shared + && (info->symbolic + || h->dynindx == -1 + || h->forced_local) && h->def_regular)) + { + /* This is actually a static link, or it is a + -Bsymbolic link and the symbol is defined + locally, or the symbol was forced to be local + because of a version file. We must initialize + this entry in the global offset table. Since the + offset must always be a multiple of 4, we use the + least significant bit to record whether we have + initialized it already. + + When doing a dynamic link, we create a .rela.got + relocation entry to initialize the value. This + is done in the finish_dynamic_symbol routine. */ + if ((off & 1) != 0) + off &= ~1; + else + { + bfd_put_32 (output_bfd, relocation, sgot->contents + off); + h->got.offset |= 1; + } + } + relocation = sgot->output_section->vma + sgot->output_offset + off + - elf_gp (output_bfd); + } + else + { + bfd_vma off; + bfd_byte *loc; + + BFD_ASSERT (local_got_offsets != NULL + && local_got_offsets[r_symndx] != (bfd_vma) - 1); + + off = local_got_offsets[r_symndx]; + + /* The offset must always be a multiple of 4. We use + the least significant bit to record whether we have + already processed this entry. */ + if ((off & 1) != 0) + off &= ~1; + else + { + bfd_put_32 (output_bfd, relocation, sgot->contents + off); + + if (info->shared) + { + asection *srelgot; + Elf_Internal_Rela outrel; + + /* We need to generate a R_NDS32_RELATIVE reloc + for the dynamic linker. */ + srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); + BFD_ASSERT (srelgot != NULL); + + outrel.r_offset = (elf_gp (output_bfd) + + sgot->output_offset + off); + outrel.r_info = ELF32_R_INFO (0, R_NDS32_RELATIVE); + outrel.r_addend = relocation; + loc = srelgot->contents; + loc += + srelgot->reloc_count * sizeof (Elf32_External_Rela); + bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); + ++srelgot->reloc_count; + } + local_got_offsets[r_symndx] |= 1; + } + relocation = sgot->output_section->vma + sgot->output_offset + off + - elf_gp (output_bfd); + } + + break; + + case R_NDS32_16_RELA: + case R_NDS32_20_RELA: + case R_NDS32_5_RELA: + case R_NDS32_32_RELA: + case R_NDS32_9_PCREL_RELA: + case R_NDS32_WORD_9_PCREL_RELA: + case R_NDS32_10_UPCREL_RELA: + case R_NDS32_15_PCREL_RELA: + case R_NDS32_17_PCREL_RELA: + case R_NDS32_25_PCREL_RELA: + case R_NDS32_HI20_RELA: + case R_NDS32_LO12S3_RELA: + case R_NDS32_LO12S2_RELA: + case R_NDS32_LO12S2_DP_RELA: + case R_NDS32_LO12S2_SP_RELA: + case R_NDS32_LO12S1_RELA: + case R_NDS32_LO12S0_RELA: + case R_NDS32_LO12S0_ORI_RELA: + if (info->shared && r_symndx != 0 + && (input_section->flags & SEC_ALLOC) != 0 + && (eliminate_gc_relocs == 0 + || (sec && (sec->flags & SEC_EXCLUDE) == 0)) + && ((r_type != R_NDS32_9_PCREL_RELA + && r_type != R_NDS32_WORD_9_PCREL_RELA + && r_type != R_NDS32_10_UPCREL_RELA + && r_type != R_NDS32_15_PCREL_RELA + && r_type != R_NDS32_17_PCREL_RELA + && r_type != R_NDS32_25_PCREL_RELA + && !(r_type == R_NDS32_32_RELA + && strcmp (input_section->name, ".eh_frame") == 0)) + || (h != NULL && h->dynindx != -1 + && (!info->symbolic || !h->def_regular)))) + { + Elf_Internal_Rela outrel; + bfd_boolean skip, relocate; + bfd_byte *loc; + + /* When generating a shared object, these relocations + are copied into the output file to be resolved at run + time. */ + + if (sreloc == NULL) + { + const char *name; + + name = bfd_elf_string_from_elf_section + (input_bfd, elf_elfheader (input_bfd)->e_shstrndx, + elf_section_data (input_section)->rela.hdr->sh_name); + if (name == NULL) + return FALSE; + + BFD_ASSERT (strncmp (name, ".rela", 5) == 0 + && strcmp (bfd_get_section_name (input_bfd, + input_section), + name + 5) == 0); + + sreloc = bfd_get_section_by_name (dynobj, name); + BFD_ASSERT (sreloc != NULL); + } + + skip = FALSE; + relocate = FALSE; + + outrel.r_offset = _bfd_elf_section_offset (output_bfd, + info, + input_section, + rel->r_offset); + if (outrel.r_offset == (bfd_vma) - 1) + skip = TRUE; + else if (outrel.r_offset == (bfd_vma) - 2) + skip = TRUE, relocate = TRUE; + outrel.r_offset += (input_section->output_section->vma + + input_section->output_offset); + + if (skip) + memset (&outrel, 0, sizeof outrel); + else if (r_type == R_NDS32_17_PCREL_RELA + || r_type == R_NDS32_15_PCREL_RELA + || r_type == R_NDS32_25_PCREL_RELA) + { + BFD_ASSERT (h != NULL && h->dynindx != -1); + outrel.r_info = ELF32_R_INFO (h->dynindx, r_type); + outrel.r_addend = rel->r_addend; + } + else + { + /* h->dynindx may be -1 if this symbol was marked to + become local. */ + if (h == NULL + || ((info->symbolic || h->dynindx == -1) + && h->def_regular)) + { + relocate = TRUE; + outrel.r_info = ELF32_R_INFO (0, R_NDS32_RELATIVE); + outrel.r_addend = relocation + rel->r_addend; + } + else + { + BFD_ASSERT (h->dynindx != -1); + outrel.r_info = ELF32_R_INFO (h->dynindx, r_type); + outrel.r_addend = rel->r_addend; + } + } + + loc = sreloc->contents; + loc += sreloc->reloc_count * sizeof (Elf32_External_Rela); + bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); + ++sreloc->reloc_count; + + /* If this reloc is against an external symbol, we do + not want to fiddle with the addend. Otherwise, we + need to include the symbol value so that it becomes + an addend for the dynamic reloc. */ + if (!relocate) + continue; + } + break; + + case R_NDS32_25_ABS_RELA: + if (info->shared) + { + (*_bfd_error_handler) + (_("%s: warning: cannot deal R_NDS32_25_ABS_RELA in shared mode."), + bfd_get_filename (input_bfd)); + return FALSE; + } + break; + + case R_NDS32_9_PCREL: + r = nds32_elf_do_9_pcrel_reloc (input_bfd, howto, input_section, + contents, offset, + sec, relocation, addend); + goto check_reloc; + + case R_NDS32_HI20: + { + Elf_Internal_Rela *lorel; + + /* We allow an arbitrary number of HI20 relocs before the + LO12 reloc. This permits gcc to emit the HI and LO relocs + itself. */ + for (lorel = rel + 1; + (lorel < relend + && ELF32_R_TYPE (lorel->r_info) == R_NDS32_HI20); lorel++) + continue; + if (lorel < relend + && (ELF32_R_TYPE (lorel->r_info) == R_NDS32_LO12S3 + || ELF32_R_TYPE (lorel->r_info) == R_NDS32_LO12S2 + || ELF32_R_TYPE (lorel->r_info) == R_NDS32_LO12S1 + || ELF32_R_TYPE (lorel->r_info) == R_NDS32_LO12S0)) + { + nds32_elf_relocate_hi20 (input_bfd, r_type, rel, lorel, + contents, relocation + addend); + r = bfd_reloc_ok; + } + else + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, offset, relocation, addend); + } + + goto check_reloc; + + case R_NDS32_GOT17S2_RELA: + case R_NDS32_GOT15S2_RELA: + { + bfd_vma off; + + BFD_ASSERT (sgot != NULL); + + if (h != NULL) + { + bfd_boolean dyn; + + off = h->got.offset; + BFD_ASSERT (off != (bfd_vma) - 1); + + dyn = htab->root.dynamic_sections_created; + if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL + (dyn, info->shared, h) || (info->shared + && (info->symbolic + || h->dynindx == -1 + || h->forced_local) + && h->def_regular)) + { + /* This is actually a static link, or it is a + -Bsymbolic link and the symbol is defined + locally, or the symbol was forced to be local + because of a version file. We must initialize + this entry in the global offset table. Since the + offset must always be a multiple of 4, we use the + least significant bit to record whether we have + initialized it already. + + When doing a dynamic link, we create a .rela.got + relocation entry to initialize the value. This + is done in the finish_dynamic_symbol routine. */ + if ((off & 1) != 0) + off &= ~1; + else + { + bfd_put_32 (output_bfd, relocation, + sgot->contents + off); + h->got.offset |= 1; + } + } + } + else + { + bfd_byte *loc; + + BFD_ASSERT (local_got_offsets != NULL + && local_got_offsets[r_symndx] != (bfd_vma) - 1); + + off = local_got_offsets[r_symndx]; + + /* The offset must always be a multiple of 4. We use + the least significant bit to record whether we have + already processed this entry. */ + if ((off & 1) != 0) + off &= ~1; + else + { + bfd_put_32 (output_bfd, relocation, sgot->contents + off); + + if (info->shared) + { + asection *srelgot; + Elf_Internal_Rela outrel; + + /* We need to generate a R_NDS32_RELATIVE reloc + for the dynamic linker. */ + srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); + BFD_ASSERT (srelgot != NULL); + + outrel.r_offset = (elf_gp (output_bfd) + + sgot->output_offset + off); + outrel.r_info = ELF32_R_INFO (0, R_NDS32_RELATIVE); + outrel.r_addend = relocation; + loc = srelgot->contents; + loc += + srelgot->reloc_count * sizeof (Elf32_External_Rela); + bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); + ++srelgot->reloc_count; + } + local_got_offsets[r_symndx] |= 1; + } + } + relocation = sgot->output_section->vma + sgot->output_offset + off + - elf_gp (output_bfd); + } + if (relocation & align) + { + /* Incorrect alignment. */ + (*_bfd_error_handler) + (_("%B: warning: unaligned access to GOT entry."), input_bfd); + ret = FALSE; + r = bfd_reloc_dangerous; + goto check_reloc; + } + break; + + case R_NDS32_SDA16S3_RELA: + case R_NDS32_SDA15S3_RELA: + case R_NDS32_SDA15S3: + align = 0x7; + goto handle_sda; + + case R_NDS32_SDA17S2_RELA: + case R_NDS32_SDA15S2_RELA: + case R_NDS32_SDA12S2_SP_RELA: + case R_NDS32_SDA12S2_DP_RELA: + case R_NDS32_SDA15S2: + case R_NDS32_SDA_FP7U2_RELA: + align = 0x3; + goto handle_sda; + + case R_NDS32_SDA18S1_RELA: + case R_NDS32_SDA15S1_RELA: + case R_NDS32_SDA15S1: + align = 0x1; + goto handle_sda; + + case R_NDS32_SDA19S0_RELA: + case R_NDS32_SDA15S0_RELA: + case R_NDS32_SDA15S0: + { + align = 0x0; +handle_sda: + BFD_ASSERT (sec != NULL); + + /* If the symbol is in the abs section, the out_bfd will be null. + This happens when the relocation has a symbol@GOTOFF. */ + r = nds32_elf_final_sda_base (output_bfd, info, &gp, FALSE); + if (r != bfd_reloc_ok) + { + (*_bfd_error_handler) + (_("%B: warning: relocate SDA_BASE failed."), input_bfd); + ret = FALSE; + goto check_reloc; + } + + /* At this point `relocation' contains the object's + address. */ + if (r_type == R_NDS32_SDA_FP7U2_RELA) + { + relocation -= fpbase_addr; + } + else + relocation -= gp; + /* Now it contains the offset from _SDA_BASE_. */ + + /* Make sure alignment is correct. */ + + if (relocation & align) + { + /* Incorrect alignment. */ + (*_bfd_error_handler) + (_("%B(%A): warning: unaligned small data access of type %d."), + input_bfd, input_section, r_type); + ret = FALSE; + goto check_reloc; + } + } + + break; + case R_NDS32_17IFC_PCREL_RELA: + case R_NDS32_10IFCU_PCREL_RELA: + /* do nothing */ + break; + + /* DON'T fall through. */ + + default: + /* OLD_NDS32_RELOC. */ + + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, offset, relocation, addend); + goto check_reloc; + } + + switch ((int) r_type) + { + case R_NDS32_20_RELA: + case R_NDS32_5_RELA: + case R_NDS32_9_PCREL_RELA: + case R_NDS32_WORD_9_PCREL_RELA: + case R_NDS32_10_UPCREL_RELA: + case R_NDS32_15_PCREL_RELA: + case R_NDS32_17_PCREL_RELA: + case R_NDS32_25_PCREL_RELA: + case R_NDS32_25_ABS_RELA: + case R_NDS32_HI20_RELA: + case R_NDS32_LO12S3_RELA: + case R_NDS32_LO12S2_RELA: + case R_NDS32_LO12S2_DP_RELA: + case R_NDS32_LO12S2_SP_RELA: + case R_NDS32_LO12S1_RELA: + case R_NDS32_LO12S0_RELA: + case R_NDS32_LO12S0_ORI_RELA: + case R_NDS32_SDA16S3_RELA: + case R_NDS32_SDA17S2_RELA: + case R_NDS32_SDA18S1_RELA: + case R_NDS32_SDA19S0_RELA: + case R_NDS32_SDA15S3_RELA: + case R_NDS32_SDA15S2_RELA: + case R_NDS32_SDA12S2_DP_RELA: + case R_NDS32_SDA12S2_SP_RELA: + case R_NDS32_SDA15S1_RELA: + case R_NDS32_SDA15S0_RELA: + case R_NDS32_SDA_FP7U2_RELA: + case R_NDS32_9_PLTREL: + case R_NDS32_25_PLTREL: + case R_NDS32_GOT20: + case R_NDS32_GOT_HI20: + case R_NDS32_GOT_LO12: + case R_NDS32_GOT_LO15: + case R_NDS32_GOT_LO19: + case R_NDS32_GOT15S2_RELA: + case R_NDS32_GOT17S2_RELA: + case R_NDS32_GOTPC20: + case R_NDS32_GOTPC_HI20: + case R_NDS32_GOTPC_LO12: + case R_NDS32_GOTOFF: + case R_NDS32_GOTOFF_HI20: + case R_NDS32_GOTOFF_LO12: + case R_NDS32_GOTOFF_LO15: + case R_NDS32_GOTOFF_LO19: + case R_NDS32_PLTREL_HI20: + case R_NDS32_PLTREL_LO12: + case R_NDS32_PLT_GOTREL_HI20: + case R_NDS32_PLT_GOTREL_LO12: + case R_NDS32_PLT_GOTREL_LO15: + case R_NDS32_PLT_GOTREL_LO19: + case R_NDS32_PLT_GOTREL_LO20: + case R_NDS32_17IFC_PCREL_RELA: + case R_NDS32_10IFCU_PCREL_RELA: + /* Instruction related relocs must handle endian properly. */ + /* NOTE: PIC IS NOT HANDLE YET; DO IT LATER */ + r = nds32_elf_final_link_relocate (howto, input_bfd, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + break; + + default: + /* All other relocs can use default handler. */ + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + break; + } + +check_reloc: + + if (r != bfd_reloc_ok) + { + /* FIXME: This should be generic enough to go in a utility. */ + const char *name; + + if (h != NULL) + name = h->root.root.string; + else + { + name = bfd_elf_string_from_elf_section + (input_bfd, symtab_hdr->sh_link, sym->st_name); + if (name == NULL || *name == '\0') + name = bfd_section_name (input_bfd, sec); + } + + if (errmsg != NULL) + goto common_error; + + switch (r) + { + case bfd_reloc_overflow: + if (!((*info->callbacks->reloc_overflow) + (info, (h ? &h->root : NULL), name, howto->name, + (bfd_vma) 0, input_bfd, input_section, offset))) + return FALSE; + break; + + case bfd_reloc_undefined: + if (!((*info->callbacks->undefined_symbol) + (info, name, input_bfd, input_section, offset, TRUE))) + return FALSE; + break; + + case bfd_reloc_outofrange: + errmsg = _("internal error: out of range error"); + goto common_error; + + case bfd_reloc_notsupported: + errmsg = _("internal error: unsupported relocation error"); + goto common_error; + + case bfd_reloc_dangerous: + errmsg = _("internal error: dangerous error"); + goto common_error; + + default: + errmsg = _("internal error: unknown error"); + /* Fall through. */ + +common_error: + if (!((*info->callbacks->warning) + (info, errmsg, name, input_bfd, input_section, offset))) + return FALSE; + break; + } + } + } + + return ret; +} + +/* Finish up dynamic symbol handling. We set the contents of various + dynamic sections here. */ + +static bfd_boolean +nds32_elf_finish_dynamic_symbol (bfd *output_bfd, struct bfd_link_info *info, + struct elf_link_hash_entry *h, Elf_Internal_Sym *sym) +{ + struct elf_nds32_link_hash_table *htab; + bfd_byte *loc; + + htab = nds32_elf_hash_table (info); + + if (h->plt.offset != (bfd_vma) - 1) + { + asection *splt; + asection *sgot; + asection *srela; + + bfd_vma plt_index; + bfd_vma got_offset; + bfd_vma local_plt_offset; + Elf_Internal_Rela rela; + + /* This symbol has an entry in the procedure linkage table. Set + it up. */ + + BFD_ASSERT (h->dynindx != -1); + + splt = htab->splt; + sgot = htab->sgotplt; + srela = htab->srelplt; + BFD_ASSERT (splt != NULL && sgot != NULL && srela != NULL); + + /* Get the index in the procedure linkage table which + corresponds to this symbol. This is the index of this symbol + in all the symbols for which we are making plt entries. The + first entry in the procedure linkage table is reserved. */ + plt_index = h->plt.offset / PLT_ENTRY_SIZE - 1; + + /* Get the offset into the .got table of the entry that + corresponds to this function. Each .got entry is 4 bytes. + The first three are reserved. */ + got_offset = (plt_index + 3) * 4; + + /* Fill in the entry in the procedure linkage table. */ + if (!info->shared) + { + unsigned long insn; + + insn = PLT_ENTRY_WORD0 + (((sgot->output_section->vma + + sgot->output_offset + got_offset) >> 12) + & 0xfffff); + bfd_putb32 (insn, splt->contents + h->plt.offset); + + insn = PLT_ENTRY_WORD1 + (((sgot->output_section->vma + + sgot->output_offset + got_offset) & 0x0fff) + >> 2); + bfd_putb32 (insn, splt->contents + h->plt.offset + 4); + + insn = PLT_ENTRY_WORD2; + bfd_putb32 (insn, splt->contents + h->plt.offset + 8); + + insn = PLT_ENTRY_WORD3 + (plt_index & 0x7ffff); + bfd_putb32 (insn, splt->contents + h->plt.offset + 12); + + insn = PLT_ENTRY_WORD4 + + (((unsigned int) ((-(h->plt.offset + 16)) >> 1)) & 0xffffff); + bfd_putb32 (insn, splt->contents + h->plt.offset + 16); + local_plt_offset = 12; + } + else + { + /* sda_base must be set at this time. */ + unsigned long insn; + long offset; + + /* FIXME, sda_base is 65536, it will damage opcode. */ + /* insn = PLT_PIC_ENTRY_WORD0 + (((got_offset - sda_base) >> 2) & 0x7fff); */ + offset = sgot->output_section->vma + sgot->output_offset + got_offset + - elf_gp (output_bfd); + insn = PLT_PIC_ENTRY_WORD0 + ((offset >> 12) & 0xfffff); + bfd_putb32 (insn, splt->contents + h->plt.offset); + + insn = PLT_PIC_ENTRY_WORD1 + (offset & 0xfff); + bfd_putb32 (insn, splt->contents + h->plt.offset + 4); + + insn = PLT_PIC_ENTRY_WORD2; + bfd_putb32 (insn, splt->contents + h->plt.offset + 8); + + insn = PLT_PIC_ENTRY_WORD3; + bfd_putb32 (insn, splt->contents + h->plt.offset + 12); + + insn = PLT_PIC_ENTRY_WORD4 + (plt_index & 0x7fffff); + bfd_putb32 (insn, splt->contents + h->plt.offset + 16); + + insn = PLT_PIC_ENTRY_WORD5 + + (((unsigned int) ((-(h->plt.offset + 20)) >> 1)) & 0xffffff); + bfd_putb32 (insn, splt->contents + h->plt.offset + 20); + + local_plt_offset = 16; + } + + /* Fill in the entry in the global offset table, + so it will fall through to the next instruction for the first time. */ + bfd_put_32 (output_bfd, + (splt->output_section->vma + splt->output_offset + + h->plt.offset + local_plt_offset), + sgot->contents + got_offset); + + /* Fill in the entry in the .rela.plt section. */ + rela.r_offset = (sgot->output_section->vma + + sgot->output_offset + got_offset); + rela.r_info = ELF32_R_INFO (h->dynindx, R_NDS32_JMP_SLOT); + rela.r_addend = 0; + loc = srela->contents; + loc += plt_index * sizeof (Elf32_External_Rela); + bfd_elf32_swap_reloca_out (output_bfd, &rela, loc); + + if (!h->def_regular) + { + /* Mark the symbol as undefined, rather than as defined in + the .plt section. Leave the value alone. */ + sym->st_shndx = SHN_UNDEF; + if (!h->ref_regular_nonweak) + sym->st_value = 0; + } + } + + if (h->got.offset != (bfd_vma) - 1) + { + asection *sgot; + asection *srela; + Elf_Internal_Rela rela; + + /* This symbol has an entry in the global offset table. + Set it up. */ + + sgot = htab->sgot; + srela = htab->srelgot; + BFD_ASSERT (sgot != NULL && srela != NULL); + + rela.r_offset = (sgot->output_section->vma + + sgot->output_offset + (h->got.offset & ~1)); + + /* If this is a -Bsymbolic link, and the symbol is defined + locally, we just want to emit a RELATIVE reloc. Likewise if + the symbol was forced to be local because of a version file. + The entry in the global offset table will already have been + initialized in the relocate_section function. */ + if (info->shared + && (info->symbolic + || h->dynindx == -1 || h->forced_local) && h->def_regular) + { + rela.r_info = ELF32_R_INFO (0, R_NDS32_RELATIVE); + rela.r_addend = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + } + else + { + BFD_ASSERT ((h->got.offset & 1) == 0); + bfd_put_32 (output_bfd, (bfd_vma) 0, + sgot->contents + h->got.offset); + rela.r_info = ELF32_R_INFO (h->dynindx, R_NDS32_GLOB_DAT); + rela.r_addend = 0; + } + + loc = srela->contents; + loc += srela->reloc_count * sizeof (Elf32_External_Rela); + bfd_elf32_swap_reloca_out (output_bfd, &rela, loc); + ++srela->reloc_count; + } + + if (h->needs_copy) + { + asection *s; + Elf_Internal_Rela rela; + + /* This symbols needs a copy reloc. Set it up. */ + + BFD_ASSERT (h->dynindx != -1 + && (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak)); + + s = bfd_get_section_by_name (h->root.u.def.section->owner, ".rela.bss"); + BFD_ASSERT (s != NULL); + + rela.r_offset = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + rela.r_info = ELF32_R_INFO (h->dynindx, R_NDS32_COPY); + rela.r_addend = 0; + loc = s->contents; + loc += s->reloc_count * sizeof (Elf32_External_Rela); + bfd_elf32_swap_reloca_out (output_bfd, &rela, loc); + ++s->reloc_count; + } + + /* Mark some specially defined symbols as absolute. */ + if (strcmp (h->root.root.string, "_DYNAMIC") == 0 + || strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0) + sym->st_shndx = SHN_ABS; + + return TRUE; +} + + +/* Finish up the dynamic sections. */ + +static bfd_boolean +nds32_elf_finish_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info) +{ + struct elf_nds32_link_hash_table *htab; + bfd *dynobj; + asection *sdyn; + asection *sgot; + + htab = nds32_elf_hash_table (info); + dynobj = htab->root.dynobj; + + sgot = htab->sgotplt; + sdyn = bfd_get_section_by_name (dynobj, ".dynamic"); + + if (htab->root.dynamic_sections_created) + { + asection *splt; + Elf32_External_Dyn *dyncon, *dynconend; + + BFD_ASSERT (sgot != NULL && sdyn != NULL); + + dyncon = (Elf32_External_Dyn *) sdyn->contents; + dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->size); + + for (; dyncon < dynconend; dyncon++) + { + Elf_Internal_Dyn dyn; + asection *s; + + bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn); + + switch (dyn.d_tag) + { + default: + break; + + case DT_PLTGOT: + /* name = ".got"; */ + s = htab->sgot->output_section; + goto get_vma; + case DT_JMPREL: + s = htab->srelplt->output_section; + get_vma: + BFD_ASSERT (s != NULL); + dyn.d_un.d_ptr = s->vma; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_PLTRELSZ: + s = htab->srelplt->output_section; + BFD_ASSERT (s != NULL); + dyn.d_un.d_val = s->size; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_RELASZ: + /* My reading of the SVR4 ABI indicates that the + procedure linkage table relocs (DT_JMPREL) should be + included in the overall relocs (DT_RELA). This is + what Solaris does. However, UnixWare can not handle + that case. Therefore, we override the DT_RELASZ entry + here to make it not include the JMPREL relocs. Since + the linker script arranges for .rela.plt to follow all + other relocation sections, we don't have to worry + about changing the DT_RELA entry. */ + if (htab->srelplt != NULL) + { + s = htab->srelplt->output_section; + dyn.d_un.d_val -= s->size; + } + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + } + } + + /* Fill in the first entry in the procedure linkage table. */ + splt = htab->splt; + if (splt && splt->size > 0) + { + if (info->shared) + { + unsigned long insn; + long offset; + + /* FIXME, sda_base is 65536, it will damage opcode. */ + /* insn = PLT_PIC_ENTRY_WORD0 + (((got_offset - sda_base) >> 2) & 0x7fff); */ + offset = sgot->output_section->vma + sgot->output_offset + 4 + - elf_gp (output_bfd); + insn = PLT0_PIC_ENTRY_WORD0 | ((offset >> 12) & 0xfffff); + bfd_putb32 (insn, splt->contents); + + /* insn = PLT0_PIC_ENTRY_WORD0 | (((8 - sda_base) >> 2) & 0x7fff) ; */ + /* here has a typo? */ + insn = PLT0_PIC_ENTRY_WORD1 | (offset & 0xfff); + bfd_putb32 (insn, splt->contents + 4); + + insn = PLT0_PIC_ENTRY_WORD2; + bfd_putb32 (insn, splt->contents + 8); + + insn = PLT0_PIC_ENTRY_WORD3; + bfd_putb32 (insn, splt->contents + 12); + + insn = PLT0_PIC_ENTRY_WORD4; + bfd_putb32 (insn, splt->contents + 16); + + insn = PLT0_PIC_ENTRY_WORD5; + bfd_putb32 (insn, splt->contents + 20); + } + else + { + unsigned long insn; + unsigned long addr; + + /* addr = .got + 4 */ + addr = sgot->output_section->vma + sgot->output_offset + 4; + insn = PLT0_ENTRY_WORD0 | ((addr >> 12) & 0xfffff); + bfd_putb32 (insn, splt->contents); + + insn = PLT0_ENTRY_WORD1 | (addr & 0x0fff); + bfd_putb32 (insn, splt->contents + 4); + + insn = PLT0_ENTRY_WORD2; + bfd_putb32 (insn, splt->contents + 8); + + insn = PLT0_ENTRY_WORD3; + bfd_putb32 (insn, splt->contents + 12); + + insn = PLT0_ENTRY_WORD4; + bfd_putb32 (insn, splt->contents + 16); + } + + elf_section_data (splt->output_section)->this_hdr.sh_entsize = + PLT_ENTRY_SIZE; + } + } + + /* Fill in the first three entries in the global offset table. */ + if (sgot && sgot->size > 0) + { + if (sdyn == NULL) + bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents); + else + bfd_put_32 (output_bfd, + sdyn->output_section->vma + sdyn->output_offset, + sgot->contents); + bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + 4); + bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + 8); + + elf_section_data (sgot->output_section)->this_hdr.sh_entsize = 4; + } + + return TRUE; +} + + +/* Set the right machine number. */ + +static bfd_boolean +nds32_elf_object_p (bfd *abfd) +{ + static unsigned int cur_arch = 0; + + if (E_N1_ARCH != (elf_elfheader (abfd)->e_flags & EF_NDS_ARCH)) + { + /* E_N1_ARCH is a wild card, so it is set only when no others exist. */ + cur_arch = (elf_elfheader (abfd)->e_flags & EF_NDS_ARCH); + } + + switch (cur_arch) + { + default: + case E_N1_ARCH: + bfd_default_set_arch_mach (abfd, bfd_arch_nds32, bfd_mach_n1); + break; + case E_N1H_ARCH: + bfd_default_set_arch_mach (abfd, bfd_arch_nds32, bfd_mach_n1h); + break; + case E_NDS_ARCH_STAR_V2_0: + bfd_default_set_arch_mach (abfd, bfd_arch_nds32, bfd_mach_n1h_v2); + break; + case E_NDS_ARCH_STAR_V3_0: + bfd_default_set_arch_mach (abfd, bfd_arch_nds32, bfd_mach_n1h_v3); + break; + case E_NDS_ARCH_STAR_V3_M: + bfd_default_set_arch_mach (abfd, bfd_arch_nds32, bfd_mach_n1h_v3m); + break; + } + + return TRUE; +} + +/* Store the machine number in the flags field. */ + +static void +nds32_elf_final_write_processing (bfd *abfd, + bfd_boolean linker ATTRIBUTE_UNUSED) +{ + unsigned long val; + static unsigned int cur_mach = 0; + + if (bfd_mach_n1 != bfd_get_mach (abfd)) + { + cur_mach = bfd_get_mach (abfd); + } + + switch (cur_mach) + { + case bfd_mach_n1: + /* Only happen when object is empty, since the case is abandon. */ + val = E_N1_ARCH; + val |= E_NDS_ABI_AABI; + val |= E_NDS32_ELF_VER_1_4; + break; + case bfd_mach_n1h: + val = E_N1H_ARCH; + break; + case bfd_mach_n1h_v2: + val = E_NDS_ARCH_STAR_V2_0; + break; + case bfd_mach_n1h_v3: + val = E_NDS_ARCH_STAR_V3_0; + break; + case bfd_mach_n1h_v3m: + val = E_NDS_ARCH_STAR_V3_M; + break; + default: + val = 0; + break; + } + + elf_elfheader (abfd)->e_flags &= ~EF_NDS_ARCH; + elf_elfheader (abfd)->e_flags |= val; +} + +/* Function to keep NDS32 specific file flags. */ + +static bfd_boolean +nds32_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; +} + +static unsigned int +convert_e_flags (unsigned int e_flags, unsigned int arch) +{ + if ((e_flags & EF_NDS_ARCH) == E_NDS_ARCH_STAR_V0_9) + { + /* From 0.9 to 1.0. */ + e_flags = (e_flags & (~EF_NDS_ARCH)) | E_NDS_ARCH_STAR_V1_0; + + /* Invert E_NDS32_HAS_NO_MAC_INST. */ + e_flags ^= E_NDS32_HAS_NO_MAC_INST; + if (arch == E_NDS_ARCH_STAR_V1_0) + { + /* Done. */ + return e_flags; + } + } + + /* From 1.0 to 2.0. */ + e_flags = (e_flags & (~EF_NDS_ARCH)) | E_NDS_ARCH_STAR_V2_0; + + /* Clear E_NDS32_HAS_MFUSR_PC_INST. */ + e_flags &= ~E_NDS32_HAS_MFUSR_PC_INST; + + /* Invert E_NDS32_HAS_NO_MAC_INST. */ + e_flags ^= E_NDS32_HAS_NO_MAC_INST; + return e_flags; +} + +static bfd_boolean +nds32_check_vec_size (bfd *ibfd) +{ + static unsigned int nds32_vec_size = 0; + + asection *sec_t = NULL; + bfd_byte *contents = NULL; + + sec_t = bfd_get_section_by_name (ibfd, ".nds32_e_flags"); + + if (sec_t && sec_t->size >= 4) + { + /* Get vec_size in file. */ + unsigned int flag_t; + + nds32_get_section_contents (ibfd, sec_t, &contents); + flag_t = bfd_get_32 (ibfd, contents); + + /* The value could only be 4 or 16. */ + + if (!nds32_vec_size) + /* Set if not set yet. */ + nds32_vec_size = (flag_t & 0x3); + else if (nds32_vec_size != (flag_t & 0x3)) + { + (*_bfd_error_handler) (_("%B: ISR vector size mismatch" + " with previous modules, previous %u-byte, current %u-byte"), + ibfd, + nds32_vec_size == 1 ? 4 : nds32_vec_size == 2 ? 16 : 0xffffffff, + (flag_t & 0x3) == 1 ? 4 : (flag_t & 0x3) == 2 ? 16 : 0xffffffff); + return FALSE; + } + else + /* Only keep the first vec_size section. */ + sec_t->flags |= SEC_EXCLUDE; + } + + return TRUE; +} + +/* Merge backend specific data from an object file to the output + object file when linking. */ + +static bfd_boolean +nds32_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd) +{ + flagword out_flags; + flagword in_flags; + flagword out_16regs; + flagword in_no_mac; + flagword out_no_mac; + flagword in_16regs; + flagword out_version; + flagword in_version; + flagword out_fpu_config; + flagword in_fpu_config; + + /* TODO: Revise to use object-attributes instead. */ + if (!nds32_check_vec_size (ibfd)) + return FALSE; + + if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour + || bfd_get_flavour (obfd) != bfd_target_elf_flavour) + return TRUE; + + if (bfd_little_endian (ibfd) != bfd_little_endian (obfd)) + { + (*_bfd_error_handler) + (_("%B: warning: Endian mismatch with previous modules."), ibfd); + + bfd_set_error (bfd_error_bad_value); + return FALSE; + } + + in_version = elf_elfheader (ibfd)->e_flags & EF_NDS32_ELF_VERSION; + if (in_version == E_NDS32_ELF_VER_1_2) + { + (*_bfd_error_handler) + (_("%B: warning: Older version of object file encountered, " + "Please recompile with current tool chain."), ibfd); + } + + /* We may need to merge V1 and V2 arch object files to V2. */ + if ((elf_elfheader (ibfd)->e_flags & EF_NDS_ARCH) + != (elf_elfheader (obfd)->e_flags & EF_NDS_ARCH)) + { + /* Need to convert version. */ + if ((elf_elfheader (ibfd)->e_flags & EF_NDS_ARCH) + == E_NDS_ARCH_STAR_RESERVED) + { + elf_elfheader (obfd)->e_flags = elf_elfheader (ibfd)->e_flags; + } + else if ((elf_elfheader (obfd)->e_flags & EF_NDS_ARCH) == E_NDS_ARCH_STAR_V0_9 + || (elf_elfheader (ibfd)->e_flags & EF_NDS_ARCH) + > (elf_elfheader (obfd)->e_flags & EF_NDS_ARCH)) + { + elf_elfheader (obfd)->e_flags = + convert_e_flags (elf_elfheader (obfd)->e_flags, + (elf_elfheader (ibfd)->e_flags & EF_NDS_ARCH)); + } + else + { + elf_elfheader (ibfd)->e_flags = + convert_e_flags (elf_elfheader (ibfd)->e_flags, + (elf_elfheader (obfd)->e_flags & EF_NDS_ARCH)); + } + } + + /* Extract some flags. */ + in_flags = elf_elfheader (ibfd)->e_flags + & (~(E_NDS32_HAS_REDUCED_REGS | EF_NDS32_ELF_VERSION + | E_NDS32_HAS_NO_MAC_INST | E_NDS32_FPU_REG_CONF)); + + /* The following flags need special treatment. */ + in_16regs = elf_elfheader (ibfd)->e_flags & E_NDS32_HAS_REDUCED_REGS; + in_no_mac = elf_elfheader (ibfd)->e_flags & E_NDS32_HAS_NO_MAC_INST; + in_fpu_config = elf_elfheader (ibfd)->e_flags & E_NDS32_FPU_REG_CONF; + + /* Extract some flags. */ + out_flags = elf_elfheader (obfd)->e_flags + & (~(E_NDS32_HAS_REDUCED_REGS | EF_NDS32_ELF_VERSION + | E_NDS32_HAS_NO_MAC_INST | E_NDS32_FPU_REG_CONF)); + + /* The following flags need special treatment. */ + out_16regs = elf_elfheader (obfd)->e_flags & E_NDS32_HAS_REDUCED_REGS; + out_no_mac = elf_elfheader (obfd)->e_flags & E_NDS32_HAS_NO_MAC_INST; + out_fpu_config = elf_elfheader (obfd)->e_flags & E_NDS32_FPU_REG_CONF; + out_version = elf_elfheader (obfd)->e_flags & EF_NDS32_ELF_VERSION; + if (!elf_flags_init (obfd)) + { + /* If the input is the default architecture then do not + bother setting the flags for the output architecture, + instead allow future merges to do this. If no future + merges ever set these flags then they will retain their + unitialised values, which surprise surprise, correspond + to the default values. */ + if (bfd_get_arch_info (ibfd)->the_default) + return TRUE; + + elf_flags_init (obfd) = TRUE; + elf_elfheader (obfd)->e_flags = elf_elfheader (ibfd)->e_flags; + + if (bfd_get_arch (obfd) == bfd_get_arch (ibfd) + && bfd_get_arch_info (obfd)->the_default) + { + return bfd_set_arch_mach (obfd, bfd_get_arch (ibfd), + bfd_get_mach (ibfd)); + } + + return TRUE; + } + + /* Check flag compatibility. */ + if ((in_flags & EF_NDS_ABI) != (out_flags & EF_NDS_ABI)) + { + (*_bfd_error_handler) + (_("%B: error: ABI mismatch with previous modules."), ibfd); + + bfd_set_error (bfd_error_bad_value); + return FALSE; + } + + if ((in_flags & EF_NDS_ARCH) != (out_flags & EF_NDS_ARCH)) + { + if (((in_flags & EF_NDS_ARCH) != E_N1_ARCH)) + { + (*_bfd_error_handler) + (_("%B: error: Instruction set mismatch with previous modules."), ibfd); + + bfd_set_error (bfd_error_bad_value); + return FALSE; + } + } + + /* When linking with V1.2 and V1.3 objects together the output is V1.2. + and perf ext1 and DIV are mergerd to perf ext1. */ + if (in_version == E_NDS32_ELF_VER_1_2 || out_version == E_NDS32_ELF_VER_1_2) + { + elf_elfheader (obfd)->e_flags = + (in_flags & (~(E_NDS32_HAS_EXT_INST | E_NDS32_HAS_DIV_INST))) + | (out_flags & (~(E_NDS32_HAS_EXT_INST | E_NDS32_HAS_DIV_INST))) + | (((in_flags & (E_NDS32_HAS_EXT_INST | E_NDS32_HAS_DIV_INST))) + ? E_NDS32_HAS_EXT_INST : 0) + | (((out_flags & (E_NDS32_HAS_EXT_INST | E_NDS32_HAS_DIV_INST))) + ? E_NDS32_HAS_EXT_INST : 0) + | (in_16regs & out_16regs) | (in_no_mac & out_no_mac) + | ((in_version > out_version) ? out_version : in_version); + } + else + { + if (in_version != out_version) + (*_bfd_error_handler) (_("%B: warning: Incompatible elf-versions %s and %s."), + ibfd, nds32_elfver_strtab[out_version], + nds32_elfver_strtab[in_version]); + + elf_elfheader (obfd)->e_flags = in_flags | out_flags + | (in_16regs & out_16regs) | (in_no_mac & out_no_mac) + | (in_fpu_config > out_fpu_config ? in_fpu_config : out_fpu_config) + | (in_version > out_version ? out_version : in_version); + } + + return TRUE; +} + +/* Display the flags field. */ + +static bfd_boolean +nds32_elf_print_private_bfd_data (bfd *abfd, void *ptr) +{ + FILE *file = (FILE *) ptr; + + BFD_ASSERT (abfd != NULL && ptr != NULL); + + _bfd_elf_print_private_bfd_data (abfd, ptr); + + fprintf (file, _("private flags = %lx"), elf_elfheader (abfd)->e_flags); + + switch (elf_elfheader (abfd)->e_flags & EF_NDS_ARCH) + { + default: + case E_N1_ARCH: + fprintf (file, _(": n1 instructions")); + break; + case E_N1H_ARCH: + fprintf (file, _(": n1h instructions")); + break; + } + + fputc ('\n', file); + + return TRUE; +} + +static unsigned int +nds32_elf_action_discarded (asection *sec) +{ + + if (strncmp + (".gcc_except_table", sec->name, sizeof (".gcc_except_table") - 1) == 0) + return 0; + + return _bfd_elf_default_action_discarded (sec); +} + +static asection * +nds32_elf_gc_mark_hook (asection *sec, struct bfd_link_info *info, + Elf_Internal_Rela *rel, struct elf_link_hash_entry *h, + Elf_Internal_Sym *sym) +{ + if (h != NULL) + switch (ELF32_R_TYPE (rel->r_info)) + { + case R_NDS32_GNU_VTINHERIT: + case R_NDS32_GNU_VTENTRY: + case R_NDS32_RELA_GNU_VTINHERIT: + case R_NDS32_RELA_GNU_VTENTRY: + return NULL; + } + + return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym); +} + +static bfd_boolean +nds32_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info, asection *sec, + const Elf_Internal_Rela *relocs) +{ + /* Update the got entry reference counts for the section being removed. */ + Elf_Internal_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes; + bfd_signed_vma *local_got_refcounts; + const Elf_Internal_Rela *rel, *relend; + + elf_section_data (sec)->local_dynrel = NULL; + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (abfd); + local_got_refcounts = elf_local_got_refcounts (abfd); + + relend = relocs + sec->reloc_count; + for (rel = relocs; rel < relend; rel++) + { + unsigned long r_symndx; + struct elf_link_hash_entry *h = NULL; + + r_symndx = ELF32_R_SYM (rel->r_info); + if (r_symndx >= symtab_hdr->sh_info) + { + /* External symbol. */ + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + while (h->root.type == bfd_link_hash_indirect + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + } + + switch (ELF32_R_TYPE (rel->r_info)) + { + case R_NDS32_GOT_HI20: + case R_NDS32_GOT_LO12: + case R_NDS32_GOT_LO15: + case R_NDS32_GOT_LO19: + case R_NDS32_GOT17S2_RELA: + case R_NDS32_GOT15S2_RELA: + case R_NDS32_GOTOFF: + case R_NDS32_GOTOFF_HI20: + case R_NDS32_GOTOFF_LO12: + case R_NDS32_GOTOFF_LO15: + case R_NDS32_GOTOFF_LO19: + case R_NDS32_GOT20: + case R_NDS32_GOTPC_HI20: + case R_NDS32_GOTPC_LO12: + case R_NDS32_GOTPC20: + if (h != NULL) + { + if (h->got.refcount > 0) + h->got.refcount--; + } + else + { + if (local_got_refcounts && local_got_refcounts[r_symndx] > 0) + local_got_refcounts[r_symndx]--; + } + break; + + case R_NDS32_16_RELA: + case R_NDS32_20_RELA: + case R_NDS32_5_RELA: + case R_NDS32_32_RELA: + case R_NDS32_HI20_RELA: + case R_NDS32_LO12S3_RELA: + case R_NDS32_LO12S2_RELA: + case R_NDS32_LO12S2_DP_RELA: + case R_NDS32_LO12S2_SP_RELA: + case R_NDS32_LO12S1_RELA: + case R_NDS32_LO12S0_RELA: + case R_NDS32_LO12S0_ORI_RELA: + case R_NDS32_SDA16S3_RELA: + case R_NDS32_SDA17S2_RELA: + case R_NDS32_SDA18S1_RELA: + case R_NDS32_SDA19S0_RELA: + case R_NDS32_SDA15S3_RELA: + case R_NDS32_SDA15S2_RELA: + case R_NDS32_SDA12S2_DP_RELA: + case R_NDS32_SDA12S2_SP_RELA: + case R_NDS32_SDA15S1_RELA: + case R_NDS32_SDA15S0_RELA: + case R_NDS32_SDA_FP7U2_RELA: + case R_NDS32_15_PCREL_RELA: + case R_NDS32_17_PCREL_RELA: + case R_NDS32_25_PCREL_RELA: + if (h != NULL) + { + struct elf_nds32_link_hash_entry *eh; + struct elf_nds32_dyn_relocs **pp; + struct elf_nds32_dyn_relocs *p; + + if (!info->shared && h->plt.refcount > 0) + h->plt.refcount -= 1; + + eh = (struct elf_nds32_link_hash_entry *) h; + + for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next) + if (p->sec == sec) + { + if (ELF32_R_TYPE (rel->r_info) == R_NDS32_15_PCREL_RELA + || ELF32_R_TYPE (rel->r_info) == R_NDS32_17_PCREL_RELA + || ELF32_R_TYPE (rel->r_info) == R_NDS32_25_PCREL_RELA) + p->pc_count -= 1; + p->count -= 1; + if (p->count == 0) + *pp = p->next; + break; + } + } + break; + + case R_NDS32_9_PLTREL: + case R_NDS32_25_PLTREL: + if (h != NULL) + { + if (h->plt.refcount > 0) + h->plt.refcount--; + } + break; + + default: + break; + } + } + + return TRUE; +} + +/* Look through the relocs for a section during the first phase. + Since we don't do .gots or .plts, we just need to consider the + virtual table relocs for gc. */ + +static bfd_boolean +nds32_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, + asection *sec, const Elf_Internal_Rela *relocs) +{ + Elf_Internal_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes, **sym_hashes_end; + const Elf_Internal_Rela *rel; + const Elf_Internal_Rela *rel_end; + struct elf_nds32_link_hash_table *htab; + bfd *dynobj; + asection *sreloc = NULL; + + if (info->relocatable) + return TRUE; + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (abfd); + sym_hashes_end = + sym_hashes + symtab_hdr->sh_size / sizeof (Elf32_External_Sym); + if (!elf_bad_symtab (abfd)) + sym_hashes_end -= symtab_hdr->sh_info; + + htab = nds32_elf_hash_table (info); + dynobj = htab->root.dynobj; + + rel_end = relocs + sec->reloc_count; + for (rel = relocs; rel < rel_end; rel++) + { + enum elf_nds32_reloc_type r_type; + struct elf_link_hash_entry *h; + unsigned long r_symndx; + + r_symndx = ELF32_R_SYM (rel->r_info); + r_type = ELF32_R_TYPE (rel->r_info); + if (r_symndx < symtab_hdr->sh_info) + h = NULL; + else + { + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + while (h->root.type == bfd_link_hash_indirect + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + } + + /* Some relocs require a global offset table. */ + if (htab->sgot == NULL) + { + switch (r_type) + { + case R_NDS32_GOT_HI20: + case R_NDS32_GOT_LO12: + case R_NDS32_GOT_LO15: + case R_NDS32_GOT_LO19: + case R_NDS32_GOT17S2_RELA: + case R_NDS32_GOT15S2_RELA: + case R_NDS32_GOTOFF: + case R_NDS32_GOTOFF_HI20: + case R_NDS32_GOTOFF_LO12: + case R_NDS32_GOTOFF_LO15: + case R_NDS32_GOTOFF_LO19: + case R_NDS32_GOTPC20: + case R_NDS32_GOTPC_HI20: + case R_NDS32_GOTPC_LO12: + case R_NDS32_GOT20: + if (dynobj == NULL) + htab->root.dynobj = dynobj = abfd; + if (!create_got_section (dynobj, info)) + return FALSE; + break; + + default: + break; + } + } + + switch ((int) r_type) + { + case R_NDS32_GOT_HI20: + case R_NDS32_GOT_LO12: + case R_NDS32_GOT_LO15: + case R_NDS32_GOT_LO19: + case R_NDS32_GOT20: + if (h != NULL) + h->got.refcount += 1; + else + { + bfd_signed_vma *local_got_refcounts; + + /* This is a global offset table entry for a local + symbol. */ + local_got_refcounts = elf_local_got_refcounts (abfd); + if (local_got_refcounts == NULL) + { + bfd_size_type size; + + size = symtab_hdr->sh_info; + size *= sizeof (bfd_signed_vma); + local_got_refcounts = (bfd_signed_vma *) bfd_zalloc (abfd, size); + if (local_got_refcounts == NULL) + return FALSE; + elf_local_got_refcounts (abfd) = local_got_refcounts; + } + local_got_refcounts[r_symndx] += 1; + } + break; + + case R_NDS32_9_PLTREL: + case R_NDS32_25_PLTREL: + case R_NDS32_PLTREL_HI20: + case R_NDS32_PLTREL_LO12: + case R_NDS32_PLT_GOTREL_HI20: + case R_NDS32_PLT_GOTREL_LO12: + case R_NDS32_PLT_GOTREL_LO15: + case R_NDS32_PLT_GOTREL_LO19: + case R_NDS32_PLT_GOTREL_LO20: + + /* This symbol requires a procedure linkage table entry. We + actually build the entry in adjust_dynamic_symbol, + because this might be a case of linking PIC code without + linking in any dynamic objects, in which case we don't + need to generate a procedure linkage table after all. */ + + /* If this is a local symbol, we resolve it directly without + creating a procedure linkage table entry. */ + if (h == NULL) + continue; + + if (h->forced_local) + break; + + h->needs_plt = 1; + h->plt.refcount += 1; + break; + + case R_NDS32_16_RELA: + case R_NDS32_20_RELA: + case R_NDS32_5_RELA: + case R_NDS32_32_RELA: + case R_NDS32_HI20_RELA: + case R_NDS32_LO12S3_RELA: + case R_NDS32_LO12S2_RELA: + case R_NDS32_LO12S2_DP_RELA: + case R_NDS32_LO12S2_SP_RELA: + case R_NDS32_LO12S1_RELA: + case R_NDS32_LO12S0_RELA: + case R_NDS32_LO12S0_ORI_RELA: + case R_NDS32_SDA16S3_RELA: + case R_NDS32_SDA17S2_RELA: + case R_NDS32_SDA18S1_RELA: + case R_NDS32_SDA19S0_RELA: + case R_NDS32_SDA15S3_RELA: + case R_NDS32_SDA15S2_RELA: + case R_NDS32_SDA12S2_DP_RELA: + case R_NDS32_SDA12S2_SP_RELA: + case R_NDS32_SDA15S1_RELA: + case R_NDS32_SDA15S0_RELA: + case R_NDS32_SDA_FP7U2_RELA: + case R_NDS32_15_PCREL_RELA: + case R_NDS32_17_PCREL_RELA: + case R_NDS32_25_PCREL_RELA: + + if (h != NULL && !info->shared) + { + h->non_got_ref = 1; + h->plt.refcount += 1; + } + + /* If we are creating a shared library, and this is a reloc against + a global symbol, or a non PC relative reloc against a local + symbol, then we need to copy the reloc into the shared library. + However, if we are linking with -Bsymbolic, we do not need to + copy a reloc against a global symbol which is defined in an + object we are including in the link (i.e., DEF_REGULAR is set). + At this point we have not seen all the input files, so it is + possible that DEF_REGULAR is not set now but will be set later + (it is never cleared). We account for that possibility below by + storing information in the dyn_relocs field of the hash table + entry. A similar situation occurs when creating shared libraries + and symbol visibility changes render the symbol local. + + If on the other hand, we are creating an executable, we may need + to keep relocations for symbols satisfied by a dynamic library + if we manage to avoid copy relocs for the symbol. */ + if ((info->shared + && (sec->flags & SEC_ALLOC) != 0 + && ((r_type != R_NDS32_25_PCREL_RELA + && r_type != R_NDS32_15_PCREL_RELA + && r_type != R_NDS32_17_PCREL_RELA + && !(r_type == R_NDS32_32_RELA + && strcmp (sec->name, ".eh_frame") == 0)) + || (h != NULL + && (!info->symbolic + || h->root.type == bfd_link_hash_defweak + || !h->def_regular)))) + || (!info->shared + && (sec->flags & SEC_ALLOC) != 0 + && h != NULL + && (h->root.type == bfd_link_hash_defweak + || !h->def_regular))) + { + struct elf_nds32_dyn_relocs *p; + struct elf_nds32_dyn_relocs **head; + + if (dynobj == NULL) + htab->root.dynobj = dynobj = abfd; + + /* When creating a shared object, we must copy these + relocs into the output file. We create a reloc + section in dynobj and make room for the reloc. */ + if (sreloc == NULL) + { + const char *name; + + name = bfd_elf_string_from_elf_section + (abfd, elf_elfheader (abfd)->e_shstrndx, + elf_section_data (sec)->rela.hdr->sh_name); + if (name == NULL) + return FALSE; + + BFD_ASSERT (strncmp (name, ".rela", 5) == 0 + && strcmp (bfd_get_section_name (abfd, sec), + name + 5) == 0); + + sreloc = bfd_get_section_by_name (dynobj, name); + if (sreloc == NULL) + { + flagword flags; + + sreloc = bfd_make_section (dynobj, name); + flags = (SEC_HAS_CONTENTS | SEC_READONLY + | SEC_IN_MEMORY | SEC_LINKER_CREATED); + if ((sec->flags & SEC_ALLOC) != 0) + flags |= SEC_ALLOC | SEC_LOAD; + if (sreloc == NULL + || !bfd_set_section_flags (dynobj, sreloc, flags) + || !bfd_set_section_alignment (dynobj, sreloc, 2)) + return FALSE; + + elf_section_type (sreloc) = SHT_RELA; + } + elf_section_data (sec)->sreloc = sreloc; + } + + /* If this is a global symbol, we count the number of + relocations we need for this symbol. */ + if (h != NULL) + head = &((struct elf_nds32_link_hash_entry *) h)->dyn_relocs; + else + { + asection *s; + + Elf_Internal_Sym *isym; + isym = bfd_sym_from_r_symndx (&htab->sym_cache, abfd, r_symndx); + if (isym == NULL) + return FALSE; + + /* Track dynamic relocs needed for local syms too. */ + s = bfd_section_from_elf_index (abfd, isym->st_shndx); + if (s == NULL) + return FALSE; + + head = ((struct elf_nds32_dyn_relocs **) + &elf_section_data (s)->local_dynrel); + } + + p = *head; + if (p == NULL || p->sec != sec) + { + bfd_size_type amt = sizeof (*p); + p = (struct elf_nds32_dyn_relocs *) bfd_alloc (dynobj, amt); + if (p == NULL) + return FALSE; + p->next = *head; + *head = p; + p->sec = sec; + p->count = 0; + p->pc_count = 0; + } + + p->count += 1; + if (ELF32_R_TYPE (rel->r_info) == R_NDS32_25_PCREL_RELA + || ELF32_R_TYPE (rel->r_info) == R_NDS32_15_PCREL_RELA + || ELF32_R_TYPE (rel->r_info) == R_NDS32_17_PCREL_RELA) + p->pc_count += 1; + } + break; + + /* This relocation describes the C++ object vtable hierarchy. + Reconstruct it for later use during GC. */ + case R_NDS32_RELA_GNU_VTINHERIT: + case R_NDS32_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_NDS32_GNU_VTENTRY: + if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_offset)) + return FALSE; + break; + case R_NDS32_RELA_GNU_VTENTRY: + if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend)) + return FALSE; + break; + } + } + + return TRUE; +} + +/* Write VAL in uleb128 format to P, returning a pointer to the + following byte. + This code is copied from elf-attr.c. */ + +static bfd_byte * +write_uleb128 (bfd_byte *p, unsigned int val) +{ + bfd_byte c; + do + { + c = val & 0x7f; + val >>= 7; + if (val) + c |= 0x80; + *(p++) = c; + } + while (val); + return p; +} + +static bfd_signed_vma +calculate_offset (bfd *abfd, asection *sec, Elf_Internal_Rela *irel, + Elf_Internal_Sym *isymbuf, Elf_Internal_Shdr *symtab_hdr, + int *pic_ext_target) +{ + bfd_signed_vma foff; + bfd_vma symval, addend; + asection *sym_sec; + + /* Get the value of the symbol referred to by the reloc. */ + if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info) + { + Elf_Internal_Sym *isym; + + /* A local symbol. */ + isym = isymbuf + ELF32_R_SYM (irel->r_info); + + if (isym->st_shndx == SHN_UNDEF) + sym_sec = bfd_und_section_ptr; + else if (isym->st_shndx == SHN_ABS) + sym_sec = bfd_abs_section_ptr; + else if (isym->st_shndx == SHN_COMMON) + sym_sec = bfd_com_section_ptr; + else + sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx); + symval = isym->st_value + sym_sec->output_section->vma + + sym_sec->output_offset; + } + else + { + unsigned long indx; + struct elf_link_hash_entry *h; + bfd *owner; + + /* An external symbol. */ + indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info; + h = elf_sym_hashes (abfd)[indx]; + BFD_ASSERT (h != NULL); + + if (h->root.type != bfd_link_hash_defined + && h->root.type != bfd_link_hash_defweak) + /* This appears to be a reference to an undefined + symbol. Just ignore it--it will be caught by the + regular reloc processing. */ + return 0; + owner = h->root.u.def.section->owner; + if (owner && (elf_elfheader (owner)->e_flags & E_NDS32_HAS_PIC)) + *pic_ext_target = 1; + + if (h->root.u.def.section->flags & SEC_MERGE) + { + sym_sec = h->root.u.def.section; + symval = _bfd_merged_section_offset (abfd, &sym_sec, + elf_section_data (sym_sec)->sec_info, + h->root.u.def.value); + symval = symval + sym_sec->output_section->vma + + sym_sec->output_offset; + } + else + symval = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + } + + addend = irel->r_addend; + + foff = (symval + addend + - (irel->r_offset + sec->output_section->vma + sec->output_offset)); + return foff; +} + +static bfd_vma +calculate_plt_memory_address (bfd *abfd, struct bfd_link_info *link_info, + Elf_Internal_Sym *isymbuf, + Elf_Internal_Rela *irel, + Elf_Internal_Shdr *symtab_hdr) +{ + bfd_vma symval; + + if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info) + { + Elf_Internal_Sym *isym; + asection *sym_sec; + /* A local symbol. */ + isym = isymbuf + ELF32_R_SYM (irel->r_info); + + if (isym->st_shndx == SHN_UNDEF) + sym_sec = bfd_und_section_ptr; + else if (isym->st_shndx == SHN_ABS) + sym_sec = bfd_abs_section_ptr; + else if (isym->st_shndx == SHN_COMMON) + sym_sec = bfd_com_section_ptr; + else + sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx); + symval = isym->st_value + sym_sec->output_section->vma + + sym_sec->output_offset; + } + else + { + unsigned long indx; + struct elf_link_hash_entry *h; + struct elf_nds32_link_hash_table *htab; + asection *splt; + + /* An external symbol. */ + indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info; + h = elf_sym_hashes (abfd)[indx]; + BFD_ASSERT (h != NULL); + htab = nds32_elf_hash_table (link_info); + splt = htab->splt; + + 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; + + if (h->plt.offset == (bfd_vma) - 1) + { + if (h->root.type != bfd_link_hash_defined + && h->root.type != bfd_link_hash_defweak) + /* This appears to be a reference to an undefined + * symbol. Just ignore it--it will be caught by the + * regular reloc processing. */ + return 0; + symval = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + } + else + symval = splt->output_section->vma + h->plt.offset; + } + + return symval; +} + +static bfd_signed_vma +calculate_plt_offset (bfd *abfd, asection *sec, struct bfd_link_info *link_info, + Elf_Internal_Sym *isymbuf, Elf_Internal_Rela *irel, + Elf_Internal_Shdr *symtab_hdr) +{ + bfd_vma foff; + if ((foff = calculate_plt_memory_address (abfd, link_info, isymbuf, irel, + symtab_hdr)) == 0) + return 0; + else + return foff - (irel->r_offset + + sec->output_section->vma + sec->output_offset); +} + +/* Convert a 32-bit instruction to 16-bit one. + INSN is the input 32-bit instruction, INSN16 is the output 16-bit + instruction. If INSN_TYPE is not NULL, it the CGEN instruction + type of INSN16. Return 1 if successful. */ + +static int +nds32_convert_32_to_16_alu1 (bfd *abfd, uint32_t insn, uint16_t *pinsn16, + int *pinsn_type) +{ + uint16_t insn16 = 0; + int insn_type; + unsigned long mach = bfd_get_mach (abfd); + + if (N32_SH5 (insn) != 0) + return 0; + + switch (N32_SUB5 (insn)) + { + case N32_ALU1_ADD_SLLI: + case N32_ALU1_ADD_SRLI: + if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn) && N32_IS_RB3 (insn)) + { + insn16 = N16_TYPE333 (ADD333, N32_RT5 (insn), N32_RA5 (insn), + N32_RB5 (insn)); + insn_type = NDS32_INSN_ADD333; + } + else if (N32_IS_RT4 (insn)) + { + if (N32_RT5 (insn) == N32_RA5 (insn)) + insn16 = N16_TYPE45 (ADD45, N32_RT54 (insn), N32_RB5 (insn)); + else if (N32_RT5 (insn) == N32_RB5 (insn)) + insn16 = N16_TYPE45 (ADD45, N32_RT54 (insn), N32_RA5 (insn)); + insn_type = NDS32_INSN_ADD45; + } + break; + + case N32_ALU1_SUB_SLLI: + case N32_ALU1_SUB_SRLI: + if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn) && N32_IS_RB3 (insn)) + { + insn16 = N16_TYPE333 (SUB333, N32_RT5 (insn), N32_RA5 (insn), + N32_RB5 (insn)); + insn_type = NDS32_INSN_SUB333; + } + else if (N32_IS_RT4 (insn) && N32_RT5 (insn) == N32_RA5 (insn)) + { + insn16 = N16_TYPE45 (SUB45, N32_RT54 (insn), N32_RB5 (insn)); + insn_type = NDS32_INSN_SUB45; + } + break; + + case N32_ALU1_AND_SLLI: + case N32_ALU1_AND_SRLI: + /* and $rt, $rt, $rb -> and33 for v3, v3m. */ + if (mach >= MACH_V3 && N32_IS_RT3 (insn) && N32_IS_RA3 (insn) + && N32_IS_RB3 (insn)) + { + if (N32_RT5 (insn) == N32_RA5 (insn)) + insn16 = N16_MISC33 (AND33, N32_RT5 (insn), N32_RB5 (insn)); + else if (N32_RT5 (insn) == N32_RB5 (insn)) + insn16 = N16_MISC33 (AND33, N32_RT5 (insn), N32_RA5 (insn)); + if (insn16) + insn_type = NDS32_INSN_AND33; + } + break; + + case N32_ALU1_XOR_SLLI: + case N32_ALU1_XOR_SRLI: + /* xor $rt, $rt, $rb -> xor33 for v3, v3m. */ + if (mach >= MACH_V3 && N32_IS_RT3 (insn) && N32_IS_RA3 (insn) + && N32_IS_RB3 (insn)) + { + if (N32_RT5 (insn) == N32_RA5 (insn)) + insn16 = N16_MISC33 (XOR33, N32_RT5 (insn), N32_RB5 (insn)); + else if (N32_RT5 (insn) == N32_RB5 (insn)) + insn16 = N16_MISC33 (XOR33, N32_RT5 (insn), N32_RA5 (insn)); + if (insn16) + insn_type = NDS32_INSN_XOR33; + } + break; + + case N32_ALU1_OR_SLLI: + case N32_ALU1_OR_SRLI: + /* or $rt, $rt, $rb -> or33 for v3, v3m. */ + if (mach >= MACH_V3 && N32_IS_RT3 (insn) && N32_IS_RA3 (insn) + && N32_IS_RB3 (insn)) + { + if (N32_RT5 (insn) == N32_RA5 (insn)) + insn16 = N16_MISC33 (OR33, N32_RT5 (insn), N32_RB5 (insn)); + else if (N32_RT5 (insn) == N32_RB5 (insn)) + insn16 = N16_MISC33 (OR33, N32_RT5 (insn), N32_RA5 (insn)); + if (insn16) + insn_type = NDS32_INSN_OR33; + } + break; + case N32_ALU1_NOR: + /* nor $rt, $ra, $ra -> not33 for v3, v3m. */ + if (mach >= MACH_V3 && N32_IS_RT3 (insn) && N32_IS_RB3 (insn) + && N32_RA5 (insn) == N32_RB5 (insn)) + { + insn16 = N16_MISC33 (NOT33, N32_RT5 (insn), N32_RA5 (insn)); + insn_type = NDS32_INSN_NOT33; + } + break; + case N32_ALU1_SRAI: + if (N32_IS_RT4 (insn) && N32_RT5 (insn) == N32_RA5 (insn)) + { + insn16 = N16_TYPE45 (SRAI45, N32_RT54 (insn), N32_UB5 (insn)); + insn_type = NDS32_INSN_SRAI45; + } + break; + + case N32_ALU1_SRLI: + if (N32_IS_RT4 (insn) && N32_RT5 (insn) == N32_RA5 (insn)) + { + insn16 = N16_TYPE45 (SRLI45, N32_RT54 (insn), N32_UB5 (insn)); + insn_type = NDS32_INSN_SRLI45; + } + break; + + case N32_ALU1_SLLI: + if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn) && N32_UB5 (insn) < 8) + { + insn16 = N16_TYPE333 (SLLI333, N32_RT5 (insn), N32_RA5 (insn), + N32_UB5 (insn)); + insn_type = NDS32_INSN_SLLI333; + } + break; + + case N32_ALU1_ZEH: + if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn)) + { + insn16 = N16_BFMI333 (ZEH33, N32_RT5 (insn), N32_RA5 (insn)); + insn_type = NDS32_INSN_ZEH33; + } + break; + + case N32_ALU1_SEB: + if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn)) + { + insn16 = N16_BFMI333 (SEB33, N32_RT5 (insn), N32_RA5 (insn)); + insn_type = NDS32_INSN_SEB33; + } + break; + + case N32_ALU1_SEH: + if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn)) + { + insn16 = N16_BFMI333 (SEH33, N32_RT5 (insn), N32_RA5 (insn)); + insn_type = NDS32_INSN_SEH33; + } + break; + + case N32_ALU1_SLT: + if (N32_RT5 (insn) == REG_R15 && N32_IS_RA4 (insn)) + { + /* Implicit r15. */ + insn16 = N16_TYPE45 (SLT45, N32_RA54 (insn), N32_RB5 (insn)); + insn_type = NDS32_INSN_SLT45; + } + break; + + case N32_ALU1_SLTS: + if (N32_RT5 (insn) == REG_R15 && N32_IS_RA4 (insn)) + { + /* Implicit r15. */ + insn16 = N16_TYPE45 (SLTS45, N32_RA54 (insn), N32_RB5 (insn)); + insn_type = NDS32_INSN_SLTS45; + } + break; + } + + if ((insn16 & 0x8000) == 0) + return 0; + + if (pinsn16) + *pinsn16 = insn16; + if (pinsn_type) + *pinsn_type = insn_type; + return 1; +} + +static int +nds32_convert_32_to_16_alu2 (bfd *abfd, uint32_t insn, uint16_t *pinsn16, + int *pinsn_type) +{ + uint16_t insn16 = 0; + int insn_type; + unsigned long mach = bfd_get_mach (abfd); + + /* TODO: bset, bclr, btgl, btst. */ + if (__GF (insn, 6, 4) != 0) + return 0; + + switch (N32_IMMU (insn, 6)) + { + case N32_ALU2_MUL: + if (mach >= MACH_V3 && N32_IS_RT3 (insn) && N32_IS_RA3 (insn) + && N32_IS_RB3 (insn)) + { + if (N32_RT5 (insn) == N32_RA5 (insn)) + insn16 = N16_MISC33 (MUL33, N32_RT5 (insn), N32_RB5 (insn)); + else if (N32_RT5 (insn) == N32_RB5 (insn)) + insn16 = N16_MISC33 (MUL33, N32_RT5 (insn), N32_RA5 (insn)); + if (insn16) + insn_type = NDS32_INSN_MUL33; + } + } + + if ((insn16 & 0x8000) == 0) + return 0; + + if (pinsn16) + *pinsn16 = insn16; + if (pinsn_type) + *pinsn_type = insn_type; + return 1; +} + +int +nds32_convert_32_to_16 (bfd *abfd, uint32_t insn, uint16_t *pinsn16, + int *pinsn_type) +{ + int op6; + uint16_t insn16 = 0; + int insn_type; + unsigned long mach = bfd_get_mach (abfd); + + /* Decode 32-bit instruction. */ + if (insn & 0x80000000) + { + /* Not 32-bit insn. */ + return 0; + } + + op6 = N32_OP6 (insn); + + /* Convert it to 16-bit instruction. */ + switch (op6) + { + case N32_OP6_MOVI: + if (IS_WITHIN_S (N32_IMM20S (insn), 5)) + { + insn16 = N16_TYPE55 (MOVI55, N32_RT5 (insn), N32_IMM20S (insn)); + insn_type = NDS32_INSN_MOVI55; + } + else if (mach >= MACH_V3 && N32_IMM20S (insn) >= 16 + && N32_IMM20S (insn) < 48 && N32_IS_RT4 (insn)) + { + insn16 = N16_TYPE45 (MOVPI45, N32_RT54 (insn), + N32_IMM20S (insn) - 16); + insn_type = NDS32_INSN_MOVPI45; + } + break; + + case N32_OP6_ADDI: + if (N32_IMM15S (insn) == 0) + { + /* Do not convert `addi $sp, $sp, 0' to `mov55 $sp, $sp', + because `mov55 $sp, $sp' is ifret16 in V3 ISA. */ + if (mach <= MACH_V2 + || N32_RT5 (insn) != REG_SP || N32_RA5 (insn) != REG_SP) + { + insn16 = N16_TYPE55 (MOV55, N32_RT5 (insn), N32_RA5 (insn)); + insn_type = NDS32_INSN_MOV55; + } + } + else if (N32_IMM15S (insn) > 0) + { + if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn) && N32_IMM15S (insn) < 8) + { + insn16 = N16_TYPE333 (ADDI333, N32_RT5 (insn), N32_RA5 (insn), + N32_IMM15S (insn)); + insn_type = NDS32_INSN_ADDI333; + } + else if (N32_IS_RT4 (insn) && N32_RT5 (insn) == N32_RA5 (insn) + && N32_IMM15S (insn) < 32) + { + insn16 = N16_TYPE45 (ADDI45, N32_RT54 (insn), N32_IMM15S (insn)); + insn_type = NDS32_INSN_ADDI45; + } + else if (mach >= MACH_V2 && N32_RT5 (insn) == REG_SP + && N32_RT5 (insn) == N32_RA5 (insn) + && N32_IMM15S (insn) < 512) + { + insn16 = N16_TYPE10 (ADDI10S, N32_IMM15S (insn)); + insn_type = NDS32_INSN_ADDI10_SP; + } + else if (mach >= MACH_V3 && N32_IS_RT3 (insn) + && N32_RA5 (insn) == REG_SP && N32_IMM15S (insn) < 256 + && (N32_IMM15S (insn) % 4 == 0)) + { + insn16 = N16_TYPE36 (ADDRI36_SP, N32_RT5 (insn), + N32_IMM15S (insn) >> 2); + insn_type = NDS32_INSN_ADDRI36_SP; + } + } + else + { + /* Less than 0. */ + if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn) && N32_IMM15S (insn) > -8) + { + insn16 = N16_TYPE333 (SUBI333, N32_RT5 (insn), N32_RA5 (insn), + 0 - N32_IMM15S (insn)); + insn_type = NDS32_INSN_SUBI333; + } + else if (N32_IS_RT4 (insn) && N32_RT5 (insn) == N32_RA5 (insn) + && N32_IMM15S (insn) > -32) + { + insn16 = N16_TYPE45 (SUBI45, N32_RT54 (insn), 0 - N32_IMM15S (insn)); + insn_type = NDS32_INSN_SUBI45; + } + else if (mach >= MACH_V2 && N32_RT5 (insn) == REG_SP + && N32_RT5 (insn) == N32_RA5 (insn) + && N32_IMM15S (insn) >= -512) + { + insn16 = N16_TYPE10 (ADDI10S, N32_IMM15S (insn)); + insn_type = NDS32_INSN_ADDI10_SP; + } + } + break; + + case N32_OP6_ORI: + if (N32_IMM15S (insn) == 0) + { + /* Do not convert `ori $sp, $sp, 0' to `mov55 $sp, $sp', + because `mov55 $sp, $sp' is ifret16 in V3 ISA. */ + if (mach <= MACH_V2 + || N32_RT5 (insn) != REG_SP || N32_RA5 (insn) != REG_SP) + { + insn16 = N16_TYPE55 (MOV55, N32_RT5 (insn), N32_RA5 (insn)); + insn_type = NDS32_INSN_MOV55; + } + } + break; + + case N32_OP6_SUBRI: + if (mach >= MACH_V3 && N32_IS_RT3 (insn) + && N32_IS_RA3 (insn) && N32_IMM15S (insn) == 0) + { + insn16 = N16_MISC33 (NEG33, N32_RT5 (insn), N32_RA5 (insn)); + insn_type = NDS32_INSN_NEG33; + } + break; + + case N32_OP6_ANDI: + if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn)) + { + if (N32_IMM15U (insn) == 1) + { + insn16 = N16_BFMI333 (XLSB33, N32_RT5 (insn), N32_RA5 (insn)); + insn_type = NDS32_INSN_XLSB33; + } + else if (N32_IMM15U (insn) == 0x7ff) + { + insn16 = N16_BFMI333 (X11B33, N32_RT5 (insn), N32_RA5 (insn)); + insn_type = NDS32_INSN_X11B33; + } + else if (N32_IMM15U (insn) == 0xff) + { + insn16 = N16_BFMI333 (ZEB33, N32_RT5 (insn), N32_RA5 (insn)); + insn_type = NDS32_INSN_ZEB33; + } + else if (mach >= MACH_V3 && N32_RT5 (insn) == N32_RA5 (insn) + && N32_IMM15U (insn) < 256) + { + int imm15u = N32_IMM15U (insn); + + if (__builtin_popcount (imm15u) == 1) + { + /* BMSKI33 */ + int imm3u = __builtin_ctz (imm15u); + + insn16 = N16_BFMI333 (BMSKI33, N32_RT5 (insn), imm3u); + insn_type = NDS32_INSN_BMSKI33; + } + else if (imm15u != 0 && __builtin_popcount (imm15u + 1) == 1) + { + /* FEXTI33 */ + int imm3u = __builtin_ctz (imm15u + 1) - 1; + + insn16 = N16_BFMI333 (FEXTI33, N32_RT5 (insn), imm3u); + insn_type = NDS32_INSN_FEXTI33; + } + } + } + break; + + case N32_OP6_SLTI: + if (N32_RT5 (insn) == REG_R15 && N32_IS_RA4 (insn) + && IS_WITHIN_U (N32_IMM15S (insn), 5)) + { + insn16 = N16_TYPE45 (SLTI45, N32_RA54 (insn), N32_IMM15S (insn)); + insn_type = NDS32_INSN_SLTI45; + } + break; + + case N32_OP6_SLTSI: + if (N32_RT5 (insn) == REG_R15 && N32_IS_RA4 (insn) + && IS_WITHIN_U (N32_IMM15S (insn), 5)) + { + insn16 = N16_TYPE45 (SLTSI45, N32_RA54 (insn), N32_IMM15S (insn)); + insn_type = NDS32_INSN_SLTSI45; + } + break; + + case N32_OP6_LWI: + if (N32_IS_RT4 (insn) && N32_IMM15S (insn) == 0) + { + insn16 = N16_TYPE45 (LWI450, N32_RT54 (insn), N32_RA5 (insn)); + insn_type = NDS32_INSN_LWI450; + } + else if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn) + && IS_WITHIN_U (N32_IMM15S (insn), 3)) + { + insn16 = N16_TYPE333 (LWI333, N32_RT5 (insn), N32_RA5 (insn), + N32_IMM15S (insn)); + insn_type = NDS32_INSN_LWI333; + } + else if (N32_IS_RT3 (insn) && N32_RA5 (insn) == REG_FP + && IS_WITHIN_U (N32_IMM15S (insn), 7)) + { + insn16 = N16_TYPE37 (XWI37, N32_RT5 (insn), 0, N32_IMM15S (insn)); + insn_type = NDS32_INSN_LWI37; + } + else if (mach >= MACH_V2 && N32_IS_RT3 (insn) && N32_RA5 (insn) == REG_SP + && IS_WITHIN_U (N32_IMM15S (insn), 7)) + { + insn16 = N16_TYPE37 (XWI37SP, N32_RT5 (insn), 0, N32_IMM15S (insn)); + insn_type = NDS32_INSN_LWI37_SP; + } + else if (mach >= MACH_V2 && N32_IS_RT4 (insn) && N32_RA5 (insn) == REG_R8 + && -32 <= N32_IMM15S (insn) && N32_IMM15S (insn) < 0) + { + insn16 = N16_TYPE45 (LWI45_FE, N32_RT54 (insn), N32_IMM15S (insn) + 32); + insn_type = NDS32_INSN_LWI45_FE; + } + break; + + case N32_OP6_SWI: + if (N32_IS_RT4 (insn) && N32_IMM15S (insn) == 0) + { + insn16 = N16_TYPE45 (SWI450, N32_RT54 (insn), N32_RA5 (insn)); + insn_type = NDS32_INSN_SWI450; + } + else if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn) + && IS_WITHIN_U (N32_IMM15S (insn), 3)) + { + insn16 = N16_TYPE333 (SWI333, N32_RT5 (insn), N32_RA5 (insn), N32_IMM15S (insn)); + insn_type = NDS32_INSN_SWI333; + } + else if (N32_IS_RT3 (insn) && N32_RA5 (insn) == REG_FP + && IS_WITHIN_U (N32_IMM15S (insn), 7)) + { + insn16 = N16_TYPE37 (XWI37, N32_RT5 (insn), 1, N32_IMM15S (insn)); + insn_type = NDS32_INSN_SWI37; + } + else if (mach >= MACH_V2 && N32_IS_RT3 (insn) && N32_RA5 (insn) == REG_SP + && IS_WITHIN_U (N32_IMM15S (insn), 7)) + { + insn16 = N16_TYPE37 (XWI37SP, N32_RT5 (insn), 1, N32_IMM15S (insn)); + insn_type = NDS32_INSN_SWI37_SP; + } + break; + + case N32_OP6_LWI_BI: + if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn) + && IS_WITHIN_U (N32_IMM15S (insn), 3)) + { + insn16 = N16_TYPE333 (LWI333_BI, N32_RT5 (insn), N32_RA5 (insn), + N32_IMM15S (insn)); + insn_type = NDS32_INSN_LWI333_BI; + } + break; + + case N32_OP6_SWI_BI: + if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn) + && IS_WITHIN_U (N32_IMM15S (insn), 3)) + { + insn16 = N16_TYPE333 (SWI333_BI, N32_RT5 (insn), N32_RA5 (insn), + N32_IMM15S (insn)); + insn_type = NDS32_INSN_SWI333_BI; + } + break; + + case N32_OP6_LHI: + if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn) + && IS_WITHIN_U (N32_IMM15S (insn), 3)) + { + insn16 = N16_TYPE333 (LHI333, N32_RT5 (insn), N32_RA5 (insn), + N32_IMM15S (insn)); + insn_type = NDS32_INSN_LHI333; + } + break; + + case N32_OP6_SHI: + if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn) + && IS_WITHIN_U (N32_IMM15S (insn), 3)) + { + insn16 = N16_TYPE333 (SHI333, N32_RT5 (insn), N32_RA5 (insn), + N32_IMM15S (insn)); + insn_type = NDS32_INSN_SHI333; + } + break; + + case N32_OP6_LBI: + if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn) + && IS_WITHIN_U (N32_IMM15S (insn), 3)) + { + insn16 = N16_TYPE333 (LBI333, N32_RT5 (insn), N32_RA5 (insn), + N32_IMM15S (insn)); + insn_type = NDS32_INSN_LBI333; + } + break; + + case N32_OP6_SBI: + if (N32_IS_RT3 (insn) && N32_IS_RA3 (insn) + && IS_WITHIN_U (N32_IMM15S (insn), 3)) + { + insn16 = N16_TYPE333 (SBI333, N32_RT5 (insn), N32_RA5 (insn), + N32_IMM15S (insn)); + insn_type = NDS32_INSN_SBI333; + } + break; + + case N32_OP6_ALU1: + return nds32_convert_32_to_16_alu1 (abfd, insn, pinsn16, pinsn_type); + + case N32_OP6_ALU2: + return nds32_convert_32_to_16_alu2 (abfd, insn, pinsn16, pinsn_type); + + case N32_OP6_BR1: + if (!IS_WITHIN_S (N32_IMM14S (insn), 8)) + goto done; + + if ((insn & __BIT (14)) == 0) + { + /* N32_BR1_BEQ */ + if (N32_IS_RT3 (insn) && N32_RA5 (insn) == REG_R5 + && N32_RT5 (insn) != REG_R5) + insn16 = N16_TYPE38 (BEQS38, N32_RT5 (insn), N32_IMM14S (insn)); + else if (N32_IS_RA3 (insn) && N32_RT5 (insn) == REG_R5 + && N32_RA5 (insn) != REG_R5) + insn16 = N16_TYPE38 (BEQS38, N32_RA5 (insn), N32_IMM14S (insn)); + insn_type = NDS32_INSN_BEQS38; + break; + } + else + { + /* N32_BR1_BNE */ + if (N32_IS_RT3 (insn) && N32_RA5 (insn) == REG_R5 + && N32_RT5 (insn) != REG_R5) + insn16 = N16_TYPE38 (BNES38, N32_RT5 (insn), N32_IMM14S (insn)); + else if (N32_IS_RA3 (insn) && N32_RT5 (insn) == REG_R5 + && N32_RA5 (insn) != REG_R5) + insn16 = N16_TYPE38 (BNES38, N32_RA5 (insn), N32_IMM14S (insn)); + insn_type = NDS32_INSN_BNES38; + break; + } + break; + + case N32_OP6_BR2: + switch (N32_BR2_SUB (insn)) + { + case N32_BR2_BEQZ: + if (N32_IS_RT3 (insn) && IS_WITHIN_S (N32_IMM16S (insn), 8)) + { + insn16 = N16_TYPE38 (BEQZ38, N32_RT5 (insn), N32_IMM16S (insn)); + insn_type = NDS32_INSN_BEQZ38; + } + else if (N32_RT5 (insn) == REG_R15 && IS_WITHIN_S (N32_IMM16S (insn), 8)) + { + insn16 = N16_TYPE8 (BEQZS8, N32_IMM16S (insn)); + insn_type = NDS32_INSN_BEQZS8; + } + break; + + case N32_BR2_BNEZ: + if (N32_IS_RT3 (insn) && IS_WITHIN_S (N32_IMM16S (insn), 8)) + { + insn16 = N16_TYPE38 (BNEZ38, N32_RT5 (insn), N32_IMM16S (insn)); + insn_type = NDS32_INSN_BNEZ38; + } + else if (N32_RT5 (insn) == REG_R15 && IS_WITHIN_S (N32_IMM16S (insn), 8)) + { + insn16 = N16_TYPE8 (BNEZS8, N32_IMM16S (insn)); + insn_type = NDS32_INSN_BNEZS8; + } + break; + + case N32_BR2_IFCALL: + if (IS_WITHIN_U (N32_IMM16S (insn), 9)) + { + insn16 = N16_TYPE9 (IFCALL9, N32_IMM16S (insn)); + insn_type = NDS32_INSN_IFCALL9; + } + break; + } + break; + + case N32_OP6_JI: + if ((insn & __BIT (24)) == 0) + { + /* N32_JI_J */ + if (IS_WITHIN_S (N32_IMM24S (insn), 8)) + { + insn16 = N16_TYPE8 (J8, N32_IMM24S (insn)); + insn_type = NDS32_INSN_J8; + } + } + break; + + case N32_OP6_JREG: + if (__GF (insn, 8, 2) != 0) + goto done; + + switch (N32_IMMU (insn, 5)) + { + case N32_JREG_JR: + if (N32_JREG_HINT (insn) == 0) + { + /* jr */ + insn16 = N16_TYPE5 (JR5, N32_RB5 (insn)); + insn_type = NDS32_INSN_JR5; + } + else if (N32_JREG_HINT (insn) == 1) + { + /* ret */ + insn16 = N16_TYPE5 (RET5, N32_RB5 (insn)); + insn_type = NDS32_INSN_RET5; + } + else if (N32_JREG_HINT (insn) == 3) + { + /* ifret = mov55 $sp, $sp */ + insn16 = N16_TYPE55 (MOV55, REG_SP, REG_SP); + insn_type = NDS32_INSN_IFRET; + } + break; + + case N32_JREG_JRAL: + /* It's convertible when return rt5 is $lp and address + translation is kept. */ + if (N32_RT5 (insn) == REG_LP && N32_JREG_HINT (insn) == 0) + { + insn16 = N16_TYPE5 (JRAL5, N32_RB5 (insn)); + insn_type = NDS32_INSN_JRAL5; + } + break; + } + break; + + case N32_OP6_MISC: + if (N32_SUB5 (insn) == N32_MISC_BREAK && N32_SWID (insn) < 32) + { + /* For v3, swid above 31 are used for ex9.it. */ + insn16 = N16_TYPE5 (BREAK16, N32_SWID (insn)); + insn_type = NDS32_INSN_BREAK16; + } + break; + + default: + /* This instruction has no 16-bit variant. */ + goto done; + } + +done: + /* Bit-15 of insn16 should be set for a valid instruction. */ + if ((insn16 & 0x8000) == 0) + return 0; + + if (pinsn16) + *pinsn16 = insn16; + if (pinsn_type) + *pinsn_type = insn_type; + return 1; +} + +static int +special_convert_32_to_16 (unsigned long insn, uint16_t *pinsn16, + Elf_Internal_Rela *reloc) +{ + uint16_t insn16 = 0; + + if ((reloc->r_addend & R_NDS32_INSN16_FP7U2_FLAG) == 0 + || (ELF32_R_TYPE (reloc->r_info) != R_NDS32_INSN16)) + return 0; + + if (!N32_IS_RT3 (insn)) + return 0; + + switch (N32_OP6 (insn)) + { + case N32_OP6_LWI: + if (N32_RA5 (insn) == REG_GP && IS_WITHIN_U (N32_IMM15S (insn), 7)) + insn16 = N16_TYPE37 (XWI37, N32_RT5 (insn), 0, N32_IMM15S (insn)); + break; + case N32_OP6_SWI: + if (N32_RA5 (insn) == REG_GP && IS_WITHIN_U (N32_IMM15S (insn), 7)) + insn16 = N16_TYPE37 (XWI37, N32_RT5 (insn), 1, N32_IMM15S (insn)); + break; + case N32_OP6_HWGP: + if (!IS_WITHIN_U (N32_IMM17S (insn), 7)) + break; + + if (__GF (insn, 17, 3) == 6) + insn16 = N16_TYPE37 (XWI37, N32_RT5 (insn), 0, N32_IMM17S (insn)); + else if (__GF (insn, 17, 3) == 7) + insn16 = N16_TYPE37 (XWI37, N32_RT5 (insn), 1, N32_IMM17S (insn)); + break; + } + + if ((insn16 & 0x8000) == 0) + return 0; + + *pinsn16 = insn16; + return 1; +} + +/* Convert a 16-bit instruction to 32-bit one. + INSN16 it the input and PINSN it the point to output. + Return non-zero on successful. Otherwise 0 is returned. */ + +int +nds32_convert_16_to_32 (bfd *abfd, uint16_t insn16, uint32_t *pinsn) +{ + uint32_t insn = 0xffffffff; + unsigned long mach = bfd_get_mach (abfd); + + /* NOTE: push25, pop25 and movd44 do not have 32-bit variants. */ + + switch (__GF (insn16, 9, 6)) + { + case 0x4: /* add45 */ + insn = N32_ALU1 (ADD, N16_RT4 (insn16), N16_RT4 (insn16), N16_RA5 (insn16)); + goto done; + case 0x5: /* sub45 */ + insn = N32_ALU1 (SUB, N16_RT4 (insn16), N16_RT4 (insn16), N16_RA5 (insn16)); + goto done; + case 0x6: /* addi45 */ + insn = N32_TYPE2 (ADDI, N16_RT4 (insn16), N16_RT4 (insn16), N16_IMM5U (insn16)); + goto done; + case 0x7: /* subi45 */ + insn = N32_TYPE2 (ADDI, N16_RT4 (insn16), N16_RT4 (insn16), -N16_IMM5U (insn16)); + goto done; + case 0x8: /* srai45 */ + insn = N32_ALU1 (SRAI, N16_RT4 (insn16), N16_RT4 (insn16), N16_IMM5U (insn16)); + goto done; + case 0x9: /* srli45 */ + insn = N32_ALU1 (SRLI, N16_RT4 (insn16), N16_RT4 (insn16), N16_IMM5U (insn16)); + goto done; + + case 0xa: /* slli333 */ + insn = N32_ALU1 (SLLI, N16_RT3 (insn16), N16_RA3 (insn16), N16_IMM3U (insn16)); + goto done; + case 0xc: /* add333 */ + insn = N32_ALU1 (ADD, N16_RT3 (insn16), N16_RA3 (insn16), N16_RB3 (insn16)); + goto done; + case 0xd: /* sub333 */ + insn = N32_ALU1 (SUB, N16_RT3 (insn16), N16_RA3 (insn16), N16_RB3 (insn16)); + goto done; + case 0xe: /* addi333 */ + insn = N32_TYPE2 (ADDI, N16_RT3 (insn16), N16_RA3 (insn16), N16_IMM3U (insn16)); + goto done; + case 0xf: /* subi333 */ + insn = N32_TYPE2 (ADDI, N16_RT3 (insn16), N16_RA3 (insn16), -N16_IMM3U (insn16)); + goto done; + + case 0x10: /* lwi333 */ + insn = N32_TYPE2 (LWI, N16_RT3 (insn16), N16_RA3 (insn16), N16_IMM3U (insn16)); + goto done; + case 0x12: /* lhi333 */ + insn = N32_TYPE2 (LHI, N16_RT3 (insn16), N16_RA3 (insn16), N16_IMM3U (insn16)); + goto done; + case 0x13: /* lbi333 */ + insn = N32_TYPE2 (LBI, N16_RT3 (insn16), N16_RA3 (insn16), N16_IMM3U (insn16)); + goto done; + case 0x11: /* lwi333.bi */ + insn = N32_TYPE2 (LWI_BI, N16_RT3 (insn16), N16_RA3 (insn16), N16_IMM3U (insn16)); + goto done; + case 0x14: /* swi333 */ + insn = N32_TYPE2 (SWI, N16_RT3 (insn16), N16_RA3 (insn16), N16_IMM3U (insn16)); + goto done; + case 0x16: /* shi333 */ + insn = N32_TYPE2 (SHI, N16_RT3 (insn16), N16_RA3 (insn16), N16_IMM3U (insn16)); + goto done; + case 0x17: /* sbi333 */ + insn = N32_TYPE2 (SBI, N16_RT3 (insn16), N16_RA3 (insn16), N16_IMM3U (insn16)); + goto done; + case 0x15: /* swi333.bi */ + insn = N32_TYPE2 (SWI_BI, N16_RT3 (insn16), N16_RA3 (insn16), N16_IMM3U (insn16)); + goto done; + + case 0x18: /* addri36.sp */ + insn = N32_TYPE2 (ADDI, REG_SP, N16_RT3 (insn16), N16_IMM6U (insn16) << 2); + goto done; + + case 0x19: /* lwi45.fe */ + insn = N32_TYPE2 (LWI, N16_RT4 (insn16), REG_R8, (32 - N16_IMM5U (insn16)) << 2); + goto done; + case 0x1a: /* lwi450 */ + insn = N32_TYPE2 (LWI, N16_RT4 (insn16), N16_RA5 (insn16), 0); + goto done; + case 0x1b: /* swi450 */ + insn = N32_TYPE2 (SWI, N16_RT4 (insn16), N16_RA5 (insn16), 0); + goto done; + + /* These are r15 implied instructions. */ + case 0x30: /* slts45 */ + insn = N32_ALU1 (SLTS, REG_TA, N16_RT4 (insn16), N16_RA5 (insn16)); + goto done; + case 0x31: /* slt45 */ + insn = N32_ALU1 (SLT, REG_TA, N16_RT4 (insn16), N16_RA5 (insn16)); + goto done; + case 0x32: /* sltsi45 */ + insn = N32_TYPE2 (SLTSI, REG_TA, N16_RT4 (insn16), N16_IMM5U (insn16)); + goto done; + case 0x33: /* slti45 */ + insn = N32_TYPE2 (SLTI, REG_TA, N16_RT4 (insn16), N16_IMM5U (insn16)); + goto done; + case 0x34: /* beqzs8, bnezs8 */ + if (insn16 & __BIT (8)) + insn = N32_BR2 (BNEZ, REG_TA, N16_IMM8S (insn16)); + else + insn = N32_BR2 (BEQZ, REG_TA, N16_IMM8S (insn16)); + goto done; + + case 0x35: /* break16, ex9.it */ + /* Only consider range of v3 break16. */ + insn = N32_TYPE0 (MISC, (N16_IMM5U (insn16) << 5) | N32_MISC_BREAK); + goto done; + + case 0x3c: /* ifcall9 */ + insn = N32_BR2 (IFCALL, 0, N16_IMM9U (insn16)); + goto done; + case 0x3d: /* movpi45 */ + insn = N32_TYPE1 (MOVI, N16_RT4 (insn16), N16_IMM5U (insn16) + 16); + goto done; + + case 0x3f: /* MISC33 */ + switch (insn & 0x7) + { + case 2: /* neg33 */ + insn = N32_TYPE2 (SUBRI, N16_RT3 (insn16), N16_RA3 (insn16), 0); + break; + case 3: /* not33 */ + insn = N32_ALU1 (NOR, N16_RT3 (insn16), N16_RA3 (insn16), N16_RA3 (insn16)); + break; + case 4: /* mul33 */ + insn = N32_ALU2 (MUL, N16_RT3 (insn16), N16_RT3 (insn16), N16_RA3 (insn16)); + break; + case 5: /* xor33 */ + insn = N32_ALU1 (XOR, N16_RT3 (insn16), N16_RT3 (insn16), N16_RA3 (insn16)); + break; + case 6: /* and33 */ + insn = N32_ALU1 (AND, N16_RT3 (insn16), N16_RT3 (insn16), N16_RA3 (insn16)); + break; + case 7: /* or33 */ + insn = N32_ALU1 (OR, N16_RT3 (insn16), N16_RT3 (insn16), N16_RA3 (insn16)); + break; + } + goto done; + + case 0xb: /* ... */ + switch (insn16 & 0x7) + { + case 0: /* zeb33 */ + insn = N32_TYPE2 (ANDI, N16_RT3 (insn16), N16_RA3 (insn16), 0xff); + break; + case 1: /* zeh33 */ + insn = N32_ALU1 (ZEH, N16_RT3 (insn16), N16_RA3 (insn16), 0); + break; + case 2: /* seb33 */ + insn = N32_ALU1 (SEB, N16_RT3 (insn16), N16_RA3 (insn16), 0); + break; + case 3: /* seh33 */ + insn = N32_ALU1 (SEH, N16_RT3 (insn16), N16_RA3 (insn16), 0); + break; + case 4: /* xlsb33 */ + insn = N32_TYPE2 (ANDI, N16_RT3 (insn16), N16_RA3 (insn16), 1); + break; + case 5: /* x11b33 */ + insn = N32_TYPE2 (ANDI, N16_RT3 (insn16), N16_RA3 (insn16), 0x7ff); + break; + case 6: /* bmski33 */ + insn = N32_TYPE2 (ANDI, N16_RT3 (insn16), N16_RT3 (insn16), + 1 << N16_IMM3U (insn16)); + break; + case 7: /* fexti33 */ + insn = N32_TYPE2 (ANDI, N16_RT3 (insn16), N16_RT3 (insn16), + (1 << (N16_IMM3U (insn16) + 1)) - 1); + break; + } + goto done; + } + + switch (__GF (insn16, 10, 5)) + { + case 0x0: /* mov55 or ifret16 */ + if (mach >= MACH_V3 && N16_RT5 (insn16) == REG_SP + && N16_RT5 (insn16) == N16_RA5 (insn16)) + insn = N32_JREG (JR, 0, 0, 0, 3); + else + insn = N32_TYPE2 (ADDI, N16_RT5 (insn16), N16_RA5 (insn16), 0); + goto done; + case 0x1: /* movi55 */ + insn = N32_TYPE1 (MOVI, N16_RT5 (insn16), N16_IMM5S (insn16)); + goto done; + case 0x1b: /* addi10s (V2) */ + insn = N32_TYPE2 (ADDI, REG_SP, REG_SP, N16_IMM10S (insn16)); + goto done; + } + + switch (__GF (insn16, 11, 4)) + { + case 0x7: /* lwi37.fp/swi37.fp */ + if (insn16 & __BIT (7)) /* swi37.fp */ + insn = N32_TYPE2 (SWI, N16_RT38 (insn16), REG_FP, N16_IMM7U (insn16)); + else /* lwi37.fp */ + insn = N32_TYPE2 (LWI, N16_RT38 (insn16), REG_FP, N16_IMM7U (insn16)); + goto done; + case 0x8: /* beqz38 */ + insn = N32_BR2 (BEQZ, N16_RT38 (insn16), N16_IMM8S (insn16)); + goto done; + case 0x9: /* bnez38 */ + insn = N32_BR2 (BNEZ, N16_RT38 (insn16), N16_IMM8S (insn16)); + goto done; + case 0xa: /* beqs38/j8, implied r5 */ + if (N16_RT38 (insn16) == 5) + insn = N32_JI (J, N16_IMM8S (insn16)); + else + insn = N32_BR1 (BEQ, N16_RT38 (insn16), REG_R5, N16_IMM8S (insn16)); + goto done; + case 0xb: /* bnes38 and others */ + if (N16_RT38 (insn16) == 5) + { + switch (__GF (insn16, 5, 3)) + { + case 0: /* jr5 */ + insn = N32_JREG (JR, 0, N16_RA5 (insn16), 0, 0); + break; + case 4: /* ret5 */ + insn = N32_JREG (JR, 0, N16_RA5 (insn16), 0, 1); + break; + case 1: /* jral5 */ + insn = N32_JREG (JRAL, REG_LP, N16_RA5 (insn16), 0, 0); + break; + case 2: /* ex9.it imm5 */ + /* ex9.it had no 32-bit variantl. */ + break; + case 5: /* add5.pc */ + /* add5.pc had no 32-bit variantl. */ + break; + } + } + else /* bnes38 */ + insn = N32_BR1 (BNE, N16_RT38 (insn16), REG_R5, N16_IMM8S (insn16)); + goto done; + case 0xe: /* lwi37/swi37 */ + if (insn16 & (1 << 7)) /* swi37.sp */ + insn = N32_TYPE2 (SWI, N16_RT38 (insn16), REG_SP, N16_IMM7U (insn16)); + else /* lwi37.sp */ + insn = N32_TYPE2 (LWI, N16_RT38 (insn16), REG_SP, N16_IMM7U (insn16)); + goto done; + } + +done: + if (insn & 0x80000000) + return 0; + + if (pinsn) + *pinsn = insn; + return 1; +} + +static bfd_boolean +is_sda_access_insn (unsigned long insn) +{ + switch (N32_OP6 (insn)) + { + case N32_OP6_LWI: + case N32_OP6_LHI: + case N32_OP6_LHSI: + case N32_OP6_LBI: + case N32_OP6_LBSI: + case N32_OP6_SWI: + case N32_OP6_SHI: + case N32_OP6_SBI: + case N32_OP6_LWC: + case N32_OP6_LDC: + case N32_OP6_SWC: + case N32_OP6_SDC: + return TRUE; + default: + ; + } + return FALSE; +} + +static unsigned long +turn_insn_to_sda_access (uint32_t insn, bfd_signed_vma type, uint32_t *pinsn) +{ + uint32_t oinsn = 0; + + switch (type) + { + case R_NDS32_GOT_LO12: + case R_NDS32_GOTOFF_LO12: + case R_NDS32_PLTREL_LO12: + case R_NDS32_PLT_GOTREL_LO12: + case R_NDS32_LO12S0_RELA: + switch (N32_OP6 (insn)) + { + case N32_OP6_LBI: + /* lbi.gp */ + oinsn = N32_TYPE1 (LBGP, N32_RT5 (insn), 0); + break; + case N32_OP6_LBSI: + /* lbsi.gp */ + oinsn = N32_TYPE1 (LBGP, N32_RT5 (insn), __BIT (19)); + break; + case N32_OP6_SBI: + /* sbi.gp */ + oinsn = N32_TYPE1 (SBGP, N32_RT5 (insn), 0); + break; + case N32_OP6_ORI: + /* addi.gp */ + oinsn = N32_TYPE1 (SBGP, N32_RT5 (insn), __BIT (19)); + break; + } + break; + + case R_NDS32_LO12S1_RELA: + switch (N32_OP6 (insn)) + { + case N32_OP6_LHI: + /* lhi.gp */ + oinsn = N32_TYPE1 (HWGP, N32_RT5 (insn), 0); + break; + case N32_OP6_LHSI: + /* lhsi.gp */ + oinsn = N32_TYPE1 (HWGP, N32_RT5 (insn), __BIT (18)); + break; + case N32_OP6_SHI: + /* shi.gp */ + oinsn = N32_TYPE1 (HWGP, N32_RT5 (insn), __BIT (19)); + break; + } + break; + + case R_NDS32_LO12S2_RELA: + switch (N32_OP6 (insn)) + { + case N32_OP6_LWI: + /* lwi.gp */ + oinsn = N32_TYPE1 (HWGP, N32_RT5 (insn), __MF (6, 17, 3)); + break; + case N32_OP6_SWI: + /* swi.gp */ + oinsn = N32_TYPE1 (HWGP, N32_RT5 (insn), __MF (7, 17, 3)); + break; + } + break; + + case R_NDS32_LO12S2_DP_RELA: + case R_NDS32_LO12S2_SP_RELA: + oinsn = (insn & 0x7ff07000) | (REG_GP << 15); + break; + } + + if (oinsn) + *pinsn = oinsn; + + return oinsn != 0; +} + +/* Linker hasn't found the correct merge section for non-section symbol + in relax time, this work is left to the function elf_link_input_bfd(). + So for non-section symbol, _bfd_merged_section_offset is also needed + to find the correct symbol address. */ + +static bfd_vma +nds32_elf_rela_local_sym (bfd *abfd, Elf_Internal_Sym *sym, + asection **psec, Elf_Internal_Rela *rel) +{ + asection *sec = *psec; + bfd_vma relocation; + + relocation = (sec->output_section->vma + + sec->output_offset + sym->st_value); + if ((sec->flags & SEC_MERGE) && sec->sec_info_type == SEC_INFO_TYPE_MERGE) + { + if (ELF_ST_TYPE (sym->st_info) == STT_SECTION) + rel->r_addend = + _bfd_merged_section_offset (abfd, psec, + elf_section_data (sec)->sec_info, + sym->st_value + rel->r_addend); + else + rel->r_addend = + _bfd_merged_section_offset (abfd, psec, + elf_section_data (sec)->sec_info, + sym->st_value) + rel->r_addend; + + if (sec != *psec) + { + /* If we have changed the section, and our original section is + marked with SEC_EXCLUDE, it means that the original + SEC_MERGE section has been completely subsumed in some + other SEC_MERGE section. In this case, we need to leave + some info around for --emit-relocs. */ + if ((sec->flags & SEC_EXCLUDE) != 0) + sec->kept_section = *psec; + sec = *psec; + } + rel->r_addend -= relocation; + rel->r_addend += sec->output_section->vma + sec->output_offset; + } + return relocation; +} + +static bfd_vma +calculate_memory_address (bfd *abfd, Elf_Internal_Rela *irel, + Elf_Internal_Sym *isymbuf, + Elf_Internal_Shdr *symtab_hdr) +{ + bfd_signed_vma foff; + bfd_vma symval, addend; + Elf_Internal_Rela irel_fn; + Elf_Internal_Sym *isym; + asection *sym_sec; + + /* Get the value of the symbol referred to by the reloc. */ + if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info) + { + /* A local symbol. */ + isym = isymbuf + ELF32_R_SYM (irel->r_info); + + if (isym->st_shndx == SHN_UNDEF) + sym_sec = bfd_und_section_ptr; + else if (isym->st_shndx == SHN_ABS) + sym_sec = bfd_abs_section_ptr; + else if (isym->st_shndx == SHN_COMMON) + sym_sec = bfd_com_section_ptr; + else + sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx); + memcpy (&irel_fn, irel, sizeof (Elf_Internal_Rela)); + symval = nds32_elf_rela_local_sym (abfd, isym, &sym_sec, &irel_fn); + addend = irel_fn.r_addend; + } + else + { + unsigned long indx; + struct elf_link_hash_entry *h; + + /* An external symbol. */ + indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info; + h = elf_sym_hashes (abfd)[indx]; + BFD_ASSERT (h != NULL); + + 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; + + if (h->root.type != bfd_link_hash_defined + && h->root.type != bfd_link_hash_defweak) + /* This appears to be a reference to an undefined + symbol. Just ignore it--it will be caught by the + regular reloc processing. */ + return 0; + + if (h->root.u.def.section->flags & SEC_MERGE) + { + sym_sec = h->root.u.def.section; + symval = _bfd_merged_section_offset (abfd, &sym_sec, elf_section_data + (sym_sec)->sec_info, h->root.u.def.value); + symval = symval + sym_sec->output_section->vma + + sym_sec->output_offset; + } + else + symval = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + addend = irel->r_addend; + } + + foff = symval + addend; + + return foff; +} + +static bfd_vma +calculate_got_memory_address (bfd *abfd, struct bfd_link_info *link_info, + Elf_Internal_Rela *irel, + Elf_Internal_Shdr *symtab_hdr) +{ + int symndx; + bfd_vma *local_got_offsets; + /* Get the value of the symbol referred to by the reloc. */ + struct elf_link_hash_entry *h; + struct elf_nds32_link_hash_table *htab = nds32_elf_hash_table (link_info); + + /* An external symbol. */ + symndx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info; + h = elf_sym_hashes (abfd)[symndx]; + 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; + + if (symndx >= 0) + { + BFD_ASSERT (h != NULL); + return htab->sgot->output_section->vma + htab->sgot->output_offset + + h->got.offset; + } + else + { + local_got_offsets = elf_local_got_offsets (abfd); + BFD_ASSERT (local_got_offsets != NULL); + return htab->sgot->output_section->vma + htab->sgot->output_offset + + local_got_offsets[ELF32_R_SYM (irel->r_info)]; + } + + /* The _GLOBAL_OFFSET_TABLE_ may be undefweak(or should be?). */ + /* The check of h->root.type is passed. */ +} + +static int +is_16bit_NOP (bfd *abfd ATTRIBUTE_UNUSED, + asection *sec, Elf_Internal_Rela *rel) +{ + bfd_byte *contents; + unsigned short insn16; + + if (!(rel->r_addend & R_NDS32_INSN16_CONVERT_FLAG)) + return FALSE; + contents = elf_section_data (sec)->this_hdr.contents; + insn16 = bfd_getb16 (contents + rel->r_offset); + if (insn16 == NDS32_NOP16) + return TRUE; + return FALSE; +} + +/* It checks whether the instruction could be converted to + 16-bit form and returns the converted one. + + `internal_relocs' is supposed to be sorted. */ + +static int +is_convert_32_to_16 (bfd *abfd, asection *sec, + Elf_Internal_Rela *reloc, + Elf_Internal_Rela *internal_relocs, + Elf_Internal_Rela *irelend, + uint16_t *insn16) +{ +#define NORMAL_32_TO_16 (1 << 0) +#define SPECIAL_32_TO_16 (1 << 1) + bfd_byte *contents = NULL; + bfd_signed_vma off; + bfd_vma mem_addr; + uint32_t insn = 0; + Elf_Internal_Rela *pc_rel; + int pic_ext_target = 0; + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Sym *isymbuf = NULL; + int convert_type; + bfd_vma offset; + + if (reloc->r_offset + 4 > sec->size) + return FALSE; + + offset = reloc->r_offset; + + if (!nds32_get_section_contents (abfd, sec, &contents)) + return FALSE; + insn = bfd_getb32 (contents + offset); + + if (nds32_convert_32_to_16 (abfd, insn, insn16, NULL)) + convert_type = NORMAL_32_TO_16; + else if (special_convert_32_to_16 (insn, insn16, reloc)) + convert_type = SPECIAL_32_TO_16; + else + return FALSE; + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + if (!nds32_get_local_syms (abfd, sec, &isymbuf)) + return FALSE; + + /* Find the first relocation of the same relocation-type, + so we iteratie them forward. */ + pc_rel = reloc; + while ((pc_rel - 1) > internal_relocs && pc_rel[-1].r_offset == offset) + pc_rel--; + + for (; pc_rel < irelend && pc_rel->r_offset == offset; pc_rel++) + { + if (ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_15_PCREL_RELA + || ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_17_PCREL_RELA + || ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_25_PCREL_RELA + || ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_25_PLTREL) + { + off = calculate_offset (abfd, sec, pc_rel, isymbuf, symtab_hdr, + &pic_ext_target); + if (off > 0xff || off < -0x100 || off == 0) + return FALSE; + break; + } + else if (ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_20_RELA) + { + /* movi => movi55 */ + mem_addr = calculate_memory_address (abfd, pc_rel, isymbuf, symtab_hdr); + /* mem_addr is unsigned, but the value should be between [-16, 15]. */ + if ((mem_addr + 0x10) >> 5) + return FALSE; + break; + } + else if ((ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_SDA15S2_RELA + || ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_SDA17S2_RELA) + && (reloc->r_addend & R_NDS32_INSN16_FP7U2_FLAG) + && convert_type == SPECIAL_32_TO_16) + { + /* fp-as-gp + We've selected a best fp-base for this access, so we can + always resolve it anyway. Do nothing. */ + break; + } + else if ((ELF32_R_TYPE (pc_rel->r_info) > R_NDS32_NONE + && (ELF32_R_TYPE (pc_rel->r_info) < R_NDS32_RELA_GNU_VTINHERIT)) + || ((ELF32_R_TYPE (pc_rel->r_info) > R_NDS32_RELA_GNU_VTENTRY) + && (ELF32_R_TYPE (pc_rel->r_info) < R_NDS32_INSN16)) + || ((ELF32_R_TYPE (pc_rel->r_info) > R_NDS32_LOADSTORE) + && (ELF32_R_TYPE (pc_rel->r_info) < R_NDS32_DWARF2_OP1_RELA))) + { + /* Prevent unresolved addi instruction translate to addi45 or addi333. */ + return FALSE; + } + } + + return TRUE; +} + +static void +nds32_elf_write_16 (bfd *abfd ATTRIBUTE_UNUSED, bfd_byte *contents, + Elf_Internal_Rela *reloc, + Elf_Internal_Rela *internal_relocs, + Elf_Internal_Rela *irelend, + unsigned short insn16) +{ + Elf_Internal_Rela *pc_rel; + bfd_vma offset; + + offset = reloc->r_offset; + bfd_putb16 (insn16, contents + offset); + /* Find the first relocation of the same relocation-type, + so we iteratie them forward. */ + pc_rel = reloc; + while ((pc_rel - 1) > internal_relocs && pc_rel[-1].r_offset == offset) + pc_rel--; + + for (; pc_rel < irelend && pc_rel->r_offset == offset; pc_rel++) + { + if (ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_15_PCREL_RELA + || ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_17_PCREL_RELA + || ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_25_PCREL_RELA) + { + pc_rel->r_info = + ELF32_R_INFO (ELF32_R_SYM (pc_rel->r_info), R_NDS32_9_PCREL_RELA); + } + else if (ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_25_PLTREL) + pc_rel->r_info = + ELF32_R_INFO (ELF32_R_SYM (pc_rel->r_info), R_NDS32_9_PLTREL); + else if (ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_20_RELA) + pc_rel->r_info = + ELF32_R_INFO (ELF32_R_SYM (pc_rel->r_info), R_NDS32_5_RELA); + else if (ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_SDA15S2_RELA + || ELF32_R_TYPE (pc_rel->r_info) == R_NDS32_SDA17S2_RELA) + pc_rel->r_info = + ELF32_R_INFO (ELF32_R_SYM (pc_rel->r_info), R_NDS32_SDA_FP7U2_RELA); + } +} + +/* Find a relocation of type specified by `reloc_type' + of the same r_offset with reloc. + If not found, return irelend. + + Assuming relocations are sorted by r_offset, + we find the relocation from `reloc' backward untill relocs, + or find it from `reloc' forward untill irelend. */ + +static Elf_Internal_Rela * +find_relocs_at_address (Elf_Internal_Rela *reloc, + Elf_Internal_Rela *relocs, + Elf_Internal_Rela *irelend, + enum elf_nds32_reloc_type reloc_type) +{ + Elf_Internal_Rela *rel_t; + + /* Find backward. */ + for (rel_t = reloc; + rel_t >= relocs && rel_t->r_offset == reloc->r_offset; + rel_t--) + if (ELF32_R_TYPE (rel_t->r_info) == reloc_type) + return rel_t; + + /* We didn't find it backward. Try find it forward. */ + for (rel_t = reloc; + rel_t < irelend && rel_t->r_offset == reloc->r_offset; + rel_t++) + if (ELF32_R_TYPE (rel_t->r_info) == reloc_type) + return rel_t; + + return irelend; +} + +/* Find a relocation of specified type and offset. + `reloc' is just a refence point to find a relocation at specified offset. + If not found, return irelend. + + Assuming relocations are sorted by r_offset, + we find the relocation from `reloc' backward untill relocs, + or find it from `reloc' forward untill irelend. */ + +static Elf_Internal_Rela * +find_relocs_at_address_addr (Elf_Internal_Rela *reloc, + Elf_Internal_Rela *relocs, + Elf_Internal_Rela *irelend, + unsigned char reloc_type, + bfd_vma offset_p) +{ + Elf_Internal_Rela *rel_t = NULL; + + /* First, we try to find a relocation of offset `offset_p', + and then we use find_relocs_at_address to find specific type. */ + + if (reloc->r_offset > offset_p) + { + /* Find backward. */ + for (rel_t = reloc; + rel_t >= relocs && rel_t->r_offset > offset_p; rel_t--) + /* Do nothing. */; + } + else if (reloc->r_offset < offset_p) + { + /* Find forward. */ + for (rel_t = reloc; + rel_t < irelend && rel_t->r_offset < offset_p; rel_t++) + /* Do nothing. */; + } + else + rel_t = reloc; + + /* Not found? */ + if (rel_t < relocs || rel_t == irelend || rel_t->r_offset != offset_p) + return irelend; + + return find_relocs_at_address (rel_t, relocs, irelend, reloc_type); +} + +static bfd_boolean +nds32_elf_check_dup_relocs (Elf_Internal_Rela *reloc, + Elf_Internal_Rela *internal_relocs, + Elf_Internal_Rela *irelend, + unsigned char reloc_type) +{ + Elf_Internal_Rela *rel_t; + + for (rel_t = reloc; + rel_t >= internal_relocs && rel_t->r_offset == reloc->r_offset; + rel_t--) + if (ELF32_R_TYPE (rel_t->r_info) == reloc_type) + { + if (ELF32_R_SYM (rel_t->r_info) == ELF32_R_SYM (reloc->r_info) + && rel_t->r_addend == reloc->r_addend) + continue; + return TRUE; + } + + for (rel_t = reloc; rel_t < irelend && rel_t->r_offset == reloc->r_offset; + rel_t++) + if (ELF32_R_TYPE (rel_t->r_info) == reloc_type) + { + if (ELF32_R_SYM (rel_t->r_info) == ELF32_R_SYM (reloc->r_info) + && rel_t->r_addend == reloc->r_addend) + continue; + return TRUE; + } + + return FALSE; +} + +typedef struct nds32_elf_blank nds32_elf_blank_t; +struct nds32_elf_blank +{ + /* Where the blank begins. */ + bfd_vma offset; + /* The size of the blank. */ + bfd_vma size; + /* The accumulative size before this blank. */ + bfd_vma total_size; + nds32_elf_blank_t *next; + nds32_elf_blank_t *prev; +}; + +static nds32_elf_blank_t *blank_free_list = NULL; + +static nds32_elf_blank_t * +create_nds32_elf_blank (bfd_vma offset_p, bfd_vma size_p) +{ + nds32_elf_blank_t *blank_t; + + if (blank_free_list) + { + blank_t = blank_free_list; + blank_free_list = blank_free_list->next; + } + else + blank_t = bfd_malloc (sizeof (nds32_elf_blank_t)); + + if (blank_t == NULL) + return NULL; + + blank_t->offset = offset_p; + blank_t->size = size_p; + blank_t->total_size = 0; + blank_t->next = NULL; + blank_t->prev = NULL; + + return blank_t; +} + +static void +remove_nds32_elf_blank (nds32_elf_blank_t *blank_p) +{ + if (blank_free_list) + { + blank_free_list->prev = blank_p; + blank_p->next = blank_free_list; + } + else + blank_p->next = NULL; + + blank_p->prev = NULL; + blank_free_list = blank_p; +} + +static void +clean_nds32_elf_blank (void) +{ + nds32_elf_blank_t *blank_t; + + while (blank_free_list) + { + blank_t = blank_free_list; + blank_free_list = blank_free_list->next; + free (blank_t); + } +} + +static nds32_elf_blank_t * +search_nds32_elf_blank (nds32_elf_blank_t *blank_p, bfd_vma addr) +{ + nds32_elf_blank_t *blank_t; + + if (!blank_p) + return NULL; + blank_t = blank_p; + + while (blank_t && addr < blank_t->offset) + blank_t = blank_t->prev; + while (blank_t && blank_t->next && addr >= blank_t->next->offset) + blank_t = blank_t->next; + + return blank_t; +} + +static bfd_vma +get_nds32_elf_blank_total (nds32_elf_blank_t **blank_p, bfd_vma addr, + int overwrite) +{ + nds32_elf_blank_t *blank_t; + + blank_t = search_nds32_elf_blank (*blank_p, addr); + if (!blank_t) + return 0; + + if (overwrite) + *blank_p = blank_t; + + if (addr < blank_t->offset + blank_t->size) + return blank_t->total_size + (addr - blank_t->offset); + else + return blank_t->total_size + blank_t->size; +} + +static bfd_boolean +insert_nds32_elf_blank (nds32_elf_blank_t **blank_p, bfd_vma addr, bfd_vma len) +{ + nds32_elf_blank_t *blank_t, *blank_t2; + + if (!*blank_p) + { + *blank_p = create_nds32_elf_blank (addr, len); + return *blank_p ? TRUE : FALSE; + } + + blank_t = search_nds32_elf_blank (*blank_p, addr); + + if (blank_t == NULL) + { + blank_t = create_nds32_elf_blank (addr, len); + if (!blank_t) + return FALSE; + while ((*blank_p)->prev != NULL) + *blank_p = (*blank_p)->prev; + blank_t->next = *blank_p; + (*blank_p)->prev = blank_t; + (*blank_p) = blank_t; + return TRUE; + } + + if (addr < blank_t->offset + blank_t->size) + { + if (addr > blank_t->offset + blank_t->size) + blank_t->size = addr - blank_t->offset; + } + else + { + blank_t2 = create_nds32_elf_blank (addr, len); + if (!blank_t2) + return FALSE; + if (blank_t->next) + { + blank_t->next->prev = blank_t2; + blank_t2->next = blank_t->next; + } + blank_t2->prev = blank_t; + blank_t->next = blank_t2; + *blank_p = blank_t2; + } + + return TRUE; +} + +static bfd_boolean +insert_nds32_elf_blank_recalc_total (nds32_elf_blank_t **blank_p, bfd_vma addr, + bfd_vma len) +{ + nds32_elf_blank_t *blank_t; + + if (!insert_nds32_elf_blank (blank_p, addr, len)) + return FALSE; + + blank_t = *blank_p; + + if (!blank_t->prev) + { + blank_t->total_size = 0; + blank_t = blank_t->next; + } + + while (blank_t) + { + blank_t->total_size = blank_t->prev->total_size + blank_t->prev->size; + blank_t = blank_t->next; + } + + return TRUE; +} + +static void +calc_nds32_blank_total (nds32_elf_blank_t *blank_p) +{ + nds32_elf_blank_t *blank_t; + bfd_vma total_size = 0; + + if (!blank_p) + return; + + blank_t = blank_p; + while (blank_t->prev) + blank_t = blank_t->prev; + while (blank_t) + { + blank_t->total_size = total_size; + total_size += blank_t->size; + blank_t = blank_t->next; + } +} + +static bfd_boolean +nds32_elf_relax_delete_blanks (bfd *abfd, asection *sec, + nds32_elf_blank_t *blank_p) +{ + Elf_Internal_Shdr *symtab_hdr; /* Symbol table header of this bfd. */ + Elf_Internal_Sym *isym = NULL; /* Symbol table of this bfd. */ + Elf_Internal_Sym *isymend; /* Symbol entry iterator. */ + unsigned int sec_shndx; /* The section the be relaxed. */ + bfd_byte *contents; /* Contents data of iterating section. */ + Elf_Internal_Rela *internal_relocs; + Elf_Internal_Rela *irel; + Elf_Internal_Rela *irelend; + struct elf_link_hash_entry **sym_hashes; + struct elf_link_hash_entry **end_hashes; + unsigned int symcount; + asection *sect; + nds32_elf_blank_t *blank_t; + nds32_elf_blank_t *blank_t2; + nds32_elf_blank_t *blank_head; + + blank_head = blank_t = blank_p; + while (blank_head->prev != NULL) + blank_head = blank_head->prev; + while (blank_t->next != NULL) + blank_t = blank_t->next; + + if (blank_t->offset + blank_t->size <= sec->size) + { + blank_t->next = create_nds32_elf_blank (sec->size + 4, 0); + blank_t->next->prev = blank_t; + } + if (blank_head->offset > 0) + { + blank_head->prev = create_nds32_elf_blank (0, 0); + blank_head->prev->next = blank_head; + blank_head = blank_head->prev; + } + + sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec); + + /* The deletion must stop at the next ALIGN reloc for an alignment + power larger than the number of bytes we are deleting. */ + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + if (!nds32_get_local_syms (abfd, sec, &isym)) + return FALSE; + + if (isym == NULL) + { + isym = bfd_elf_get_elf_syms (abfd, symtab_hdr, + symtab_hdr->sh_info, 0, NULL, NULL, NULL); + symtab_hdr->contents = (bfd_byte *) isym; + } + + if (isym == NULL || symtab_hdr->sh_info == 0) + return FALSE; + + blank_t = blank_head; + calc_nds32_blank_total (blank_head); + + for (sect = abfd->sections; sect != NULL; sect = sect->next) + { + /* Adjust all the relocs. */ + + /* Relocations MUST be kept in memory, because relaxation adjust them. */ + internal_relocs = _bfd_elf_link_read_relocs (abfd, sect, NULL, NULL, + TRUE /* keep_memory */); + irelend = internal_relocs + sect->reloc_count; + + blank_t = blank_head; + blank_t2 = blank_head; + + if (!(sect->flags & SEC_RELOC)) + continue; + + nds32_get_section_contents (abfd, sect, &contents); + + for (irel = internal_relocs; irel < irelend; irel++) + { + bfd_vma raddr; + + if (ELF32_R_TYPE (irel->r_info) >= R_NDS32_DIFF8 + && ELF32_R_TYPE (irel->r_info) <= R_NDS32_DIFF32 + && isym[ELF32_R_SYM (irel->r_info)].st_shndx == sec_shndx) + { + unsigned long val = 0; + unsigned long before, between; + + switch (ELF32_R_TYPE (irel->r_info)) + { + case R_NDS32_DIFF8: + val = bfd_get_8 (abfd, contents + irel->r_offset); + break; + case R_NDS32_DIFF16: + val = bfd_get_16 (abfd, contents + irel->r_offset); + break; + case R_NDS32_DIFF32: + val = bfd_get_32 (abfd, contents + irel->r_offset); + break; + default: + BFD_ASSERT (0); + } + + /* DIFF value + 0 |encoded in location| + |------------|-------------------|--------- + sym+off(addend) + -- before ---| ***************** + --------------------- between ---| + + We only care how much data are relax between DIFF, marked as ***. */ + + before = get_nds32_elf_blank_total (&blank_t, irel->r_addend, 0); + between = get_nds32_elf_blank_total (&blank_t, irel->r_addend + val, 0); + if (between == before) + goto done_adjust_diff; + + switch (ELF32_R_TYPE (irel->r_info)) + { + case R_NDS32_DIFF8: + bfd_put_8 (abfd, val - (between - before), contents + irel->r_offset); + break; + case R_NDS32_DIFF16: + bfd_put_16 (abfd, val - (between - before), contents + irel->r_offset); + break; + case R_NDS32_DIFF32: + bfd_put_32 (abfd, val - (between - before), contents + irel->r_offset); + break; + } + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_DIFF_ULEB128 + && isym[ELF32_R_SYM (irel->r_info)].st_shndx == sec_shndx) + { + bfd_vma val = 0; + unsigned int len = 0; + unsigned long before, between; + bfd_byte *endp, *p; + + val = read_unsigned_leb128 (abfd, contents + irel->r_offset, &len); + + before = get_nds32_elf_blank_total (&blank_t, irel->r_addend, 0); + between = get_nds32_elf_blank_total (&blank_t, irel->r_addend + val, 0); + if (between == before) + goto done_adjust_diff; + + p = contents + irel->r_offset; + endp = p + len -1; + memset (p, 0x80, len); + *(endp) = 0; + p = write_uleb128 (p, val - (between - before)) - 1; + if (p < endp) + *p |= 0x80; + } +done_adjust_diff: + + if (sec == sect) + { + raddr = irel->r_offset; + irel->r_offset -= get_nds32_elf_blank_total (&blank_t2, irel->r_offset, 1); + + if (ELF32_R_TYPE (irel->r_info) == R_NDS32_NONE) + continue; + if (blank_t2 && blank_t2->next + && (blank_t2->offset > raddr || blank_t2->next->offset <= raddr)) + (*_bfd_error_handler) (_("%B: %s\n"), abfd, + "Error: search_nds32_elf_blank reports wrong node"); + + /* Mark reloc in deleted portion as NONE. + For some relocs like R_NDS32_LABEL that doesn't modify the + content in the section. R_NDS32_LABEL doesn't belong to the + instruction in the section, so we should preserve it. */ + if (raddr >= blank_t2->offset + && raddr < blank_t2->offset + blank_t2->size + && ELF32_R_TYPE (irel->r_info) != R_NDS32_LABEL + && ELF32_R_TYPE (irel->r_info) != R_NDS32_RELAX_REGION_BEGIN + && ELF32_R_TYPE (irel->r_info) != R_NDS32_RELAX_REGION_END + && ELF32_R_TYPE (irel->r_info) != R_NDS32_RELAX_ENTRY + && ELF32_R_TYPE (irel->r_info) != R_NDS32_SUBTRAHEND + && ELF32_R_TYPE (irel->r_info) != R_NDS32_MINUEND) + { + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + R_NDS32_NONE); + continue; + } + } + + if (ELF32_R_TYPE (irel->r_info) == R_NDS32_NONE + || ELF32_R_TYPE (irel->r_info) == R_NDS32_LABEL + || ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_ENTRY) + continue; + + if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info + && isym[ELF32_R_SYM (irel->r_info)].st_shndx == sec_shndx + && ELF_ST_TYPE (isym[ELF32_R_SYM (irel->r_info)].st_info) == STT_SECTION) + { + if (irel->r_addend <= sec->size) + irel->r_addend -= + get_nds32_elf_blank_total (&blank_t, irel->r_addend, 1); + } + } + } + + /* Adjust the local symbols defined in this section. */ + blank_t = blank_head; + for (isymend = isym + symtab_hdr->sh_info; isym < isymend; isym++) + { + if (isym->st_shndx == sec_shndx) + { + if (isym->st_value <= sec->size) + { + bfd_vma ahead; + bfd_vma orig_addr = isym->st_value; + + ahead = get_nds32_elf_blank_total (&blank_t, isym->st_value, 1); + isym->st_value -= ahead; + + /* Adjust function size. */ + if (ELF32_ST_TYPE (isym->st_info) == STT_FUNC && isym->st_size > 0) + isym->st_size -= get_nds32_elf_blank_total + (&blank_t, orig_addr + isym->st_size, 0) - ahead; + } + } + } + + /* Now adjust the global symbols defined in this section. */ + symcount = (symtab_hdr->sh_size / sizeof (Elf32_External_Sym) + - symtab_hdr->sh_info); + sym_hashes = elf_sym_hashes (abfd); + end_hashes = sym_hashes + symcount; + blank_t = blank_head; + for (; sym_hashes < end_hashes; sym_hashes++) + { + struct elf_link_hash_entry *sym_hash = *sym_hashes; + + if ((sym_hash->root.type == bfd_link_hash_defined + || sym_hash->root.type == bfd_link_hash_defweak) + && sym_hash->root.u.def.section == sec) + { + if (sym_hash->root.u.def.value <= sec->size) + { + bfd_vma ahead; + bfd_vma orig_addr = sym_hash->root.u.def.value; + + ahead = get_nds32_elf_blank_total (&blank_t, sym_hash->root.u.def.value, 1); + sym_hash->root.u.def.value -= ahead; + + /* Adjust function size. */ + if (sym_hash->type == STT_FUNC) + sym_hash->size -= get_nds32_elf_blank_total + (&blank_t, orig_addr + sym_hash->size, 0) - ahead; + + } + } + } + + contents = elf_section_data (sec)->this_hdr.contents; + blank_t = blank_head; + while (blank_t->next) + { + /* Actually delete the bytes. */ + + /* If current blank is the last blank overlap with current section, + go to finish process. */ + if (sec->size <= (blank_t->next->offset)) + break; + + memmove (contents + blank_t->offset - blank_t->total_size, + contents + blank_t->offset + blank_t->size, + blank_t->next->offset - (blank_t->offset + blank_t->size)); + + blank_t = blank_t->next; + } + + if (sec->size > (blank_t->offset + blank_t->size)) + { + /* There are remaining code between blank and section boundary. + Move the remaining code to appropriate location. */ + memmove (contents + blank_t->offset - blank_t->total_size, + contents + blank_t->offset + blank_t->size, + sec->size - (blank_t->offset + blank_t->size)); + sec->size -= blank_t->total_size + blank_t->size; + } + else + /* This blank is not entirely included in the section, + reduce the section size by only part of the blank size. */ + sec->size -= blank_t->total_size + (sec->size - blank_t->offset); + + while (blank_head) + { + blank_t = blank_head; + blank_head = blank_head->next; + remove_nds32_elf_blank (blank_t); + } + + return TRUE; +} + +/* Get the contents of a section. */ + +static int +nds32_get_section_contents (bfd *abfd, asection *sec, bfd_byte **contents_p) +{ + /* Get the section contents. */ + if (elf_section_data (sec)->this_hdr.contents != NULL) + *contents_p = elf_section_data (sec)->this_hdr.contents; + else + { + if (!bfd_malloc_and_get_section (abfd, sec, contents_p)) + return FALSE; + elf_section_data (sec)->this_hdr.contents = *contents_p; + } + + return TRUE; +} + +/* Get the contents of the internal symbol of abfd. */ + +static int +nds32_get_local_syms (bfd *abfd, asection *sec ATTRIBUTE_UNUSED, + Elf_Internal_Sym **isymbuf_p) +{ + Elf_Internal_Shdr *symtab_hdr; + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + + /* Read this BFD's local symbols if we haven't done so already. */ + if (*isymbuf_p == NULL && symtab_hdr->sh_info != 0) + { + *isymbuf_p = (Elf_Internal_Sym *) symtab_hdr->contents; + if (*isymbuf_p == NULL) + { + *isymbuf_p = bfd_elf_get_elf_syms (abfd, symtab_hdr, + symtab_hdr->sh_info, 0, + NULL, NULL, NULL); + if (*isymbuf_p == NULL) + return FALSE; + } + } + symtab_hdr->contents = (bfd_byte *) (*isymbuf_p); + + return TRUE; +} + +/* Range of small data. */ +static bfd_vma sdata_range[5][2]; +static bfd_vma const sdata_init_range[5] = { 0x2000, 0x4000, 0x8000, 0x10000, 0x40000 }; + +static int +nds32_elf_insn_size (bfd *abfd ATTRIBUTE_UNUSED, + bfd_byte *contents, bfd_vma addr) +{ + unsigned long insn = bfd_getb32 (contents + addr); + + if (insn & 0x80000000) + return 2; + + return 4; +} + +/* Set the gp relax range. We have to measure the safe range + to do gp relaxation. */ + +static void +relax_range_measurement (bfd *abfd) +{ + asection *sec_f, *sec_b; + /* For upper bound. */ + bfd_vma maxpgsz = get_elf_backend_data (abfd)->maxpagesize; + bfd_vma align; + bfd_vma init_range; + static int decide_relax_range = 0; + int i; + + if (decide_relax_range) + return; + decide_relax_range = 1; + + if (sda_rela_sec == NULL) + { + /* Since there is no data sections, we assume the range is page size. */ + for (i = 0; i < 5; i++) + { + sdata_range[i][0] = sdata_init_range[i] - 0x1000; + sdata_range[i][1] = sdata_init_range[i] - 0x1000; + } + return; + } + + /* Get the biggest alignment power after the gp located section. */ + sec_f = sda_rela_sec->output_section; + sec_b = sec_f->next; + align = 0; + while (sec_b != NULL) + { + if ((unsigned)(1 << sec_b->alignment_power) > align) + align = (1 << sec_b->alignment_power); + sec_b = sec_b->next; + } + + /* I guess we can not determine the section before + gp located section, so we assume the align is max page size. */ + for (i = 0; i < 5; i++) + { + init_range = sdata_init_range[i]; + sdata_range[i][1] = init_range - align; + BFD_ASSERT (sdata_range[i][1] <= sdata_init_range[i]); + sdata_range[i][0] = init_range - maxpgsz; + BFD_ASSERT (sdata_range[i][0] <= sdata_init_range[i]); + } +} + +/* These are macros used to check flags encoded in r_addend. + They are only used by nds32_elf_relax_section (). */ +#define GET_SEQ_LEN(addend) ((addend) & 0x000000ff) +#define IS_1ST_CONVERT(addend) ((addend) & 0x80000000) +#define IS_OPTIMIZE(addend) ((addend) & 0x40000000) +#define IS_16BIT_ON(addend) ((addend) & 0x20000000) + +static bfd_boolean +nds32_elf_relax_section (bfd *abfd, asection *sec, + struct bfd_link_info *link_info, bfd_boolean *again) +{ + nds32_elf_blank_t *relax_blank_list = NULL; + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Rela *internal_relocs; + Elf_Internal_Rela *irel; + Elf_Internal_Rela *irelend; + Elf_Internal_Sym *isymbuf = NULL; + bfd_byte *contents = NULL; + bfd_boolean result = TRUE; + int optimize = 0; + int optimize_for_space ATTRIBUTE_UNUSED = 0; + int optimize_for_space_no_align ATTRIBUTE_UNUSED = 0; + int insn_opt = 0; + int i; + uint32_t insn; + uint16_t insn16; + bfd_vma local_sda; + + /* Target dependnet option. */ + struct elf_nds32_link_hash_table *table; + int load_store_relax; + int relax_round; + + relax_blank_list = NULL; + + *again = FALSE; + + /* Nothing to do for + * relocatable link or + * non-relocatable section or + * non-code section or + * empty content or + * no reloc entry. */ + if (link_info->relocatable + || (sec->flags & SEC_RELOC) == 0 + || (sec->flags & SEC_EXCLUDE) == 1 + || (sec->flags & SEC_CODE) == 0 + || sec->size == 0) + return TRUE; + + /* 09.12.11 Workaround. */ + /* We have to adjust align for R_NDS32_LABEL if needed. + The adjust approach only can fix 2-byte align once. */ + if (sec->alignment_power > 2) + { + (*_bfd_error_handler) + (_("%B(%A): warning: relax is suppressed for sections " + "of alignment %d-bytes > 4-byte."), + abfd, sec, sec->alignment_power); + return TRUE; + } + + /* The optimization type to do. */ + + table = nds32_elf_hash_table (link_info); + relax_round = table->relax_round; + switch (relax_round) + { + case NDS32_RELAX_JUMP_IFC_ROUND: + /* Here is the entrance of ifc jump relaxation. */ + if (!nds32_elf_ifc_calc (link_info, abfd, sec)) + return FALSE; + return TRUE; + + case NDS32_RELAX_EX9_BUILD_ROUND: + /* Here is the entrance of ex9 relaxation. There are two pass of + ex9 relaxation. The one is to traverse all instructions and build + the hash table. The other one is to compare instructions and replace + it by ex9.it. */ + if (!nds32_elf_ex9_build_hash_table (abfd, sec, link_info)) + return FALSE; + return TRUE; + + case NDS32_RELAX_EX9_REPLACE_ROUND: + if (!nds32_elf_ex9_replace_instruction (link_info, abfd, sec)) + return FALSE; + return TRUE; + + default: + if (sec->reloc_count == 0) + return TRUE; + break; + } + + /* The begining of general relaxation. */ + + if (is_SDA_BASE_set == 0) + { + bfd_vma gp; + is_SDA_BASE_set = 1; + nds32_elf_final_sda_base (sec->output_section->owner, link_info, &gp, FALSE); + relax_range_measurement (abfd); + } + + if (is_ITB_BASE_set == 0) + { + /* Set the _ITB_BASE_. */ + if (!nds32_elf_ex9_itb_base (link_info)) + { + (*_bfd_error_handler) (_("%B: error: Cannot set _ITB_BASE_"), abfd); + bfd_set_error (bfd_error_bad_value); + } + } + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + /* Relocations MUST be kept in memory, because relaxation adjust them. */ + internal_relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL, + TRUE /* keep_memory */); + if (internal_relocs == NULL) + goto error_return; + + irelend = internal_relocs + sec->reloc_count; + irel = + find_relocs_at_address (internal_relocs, internal_relocs, irelend, + R_NDS32_RELAX_ENTRY); + /* If 31th bit of addend of R_NDS32_RELAX_ENTRY is set, + this section is already relaxed. */ + if (irel == irelend) + return TRUE; + + if (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_ENTRY) + { + if (irel->r_addend & R_NDS32_RELAX_ENTRY_DISABLE_RELAX_FLAG) + return TRUE; + + if (irel->r_addend & R_NDS32_RELAX_ENTRY_OPTIMIZE_FLAG) + optimize = 1; + + if (irel->r_addend & R_NDS32_RELAX_ENTRY_OPTIMIZE_FOR_SPACE_FLAG) + optimize_for_space = 1; + } + + relax_active = 1; + load_store_relax = table->load_store_relax; + + /* Get symbol table and section content. */ + if (!nds32_get_section_contents (abfd, sec, &contents) + || !nds32_get_local_syms (abfd, sec, &isymbuf)) + goto error_return; + + /* Do relax loop only when finalize is not done. + Take care of relaxable relocs except INSN16. */ + for (irel = internal_relocs; irel < irelend; irel++) + { + bfd_vma laddr; + unsigned long comp_insn = 0; + unsigned short comp_insn16 = 0; + unsigned long i_mask = 0xffffffff; + int seq_len; /* Original length of instruction sequence. */ + int insn_len = 0; /* Final length of instruction sequence. */ + int convertible; /* 1st insn convertible. */ + int insn16_on; /* 16-bit on/off. */ + Elf_Internal_Rela *hi_irelfn = NULL; + Elf_Internal_Rela *lo_irelfn = NULL; + Elf_Internal_Rela *i1_irelfn = NULL; + Elf_Internal_Rela *i2_irelfn = NULL; + Elf_Internal_Rela *cond_irelfn = NULL; + int i1_offset = 0; + int i2_offset = 0; + bfd_signed_vma foff; + unsigned long reloc = R_NDS32_NONE; + int hi_off; + int insn_off; + int pic_ext_target = 0; + bfd_vma access_addr = 0; + bfd_vma range_l = 0, range_h = 0; /* Upper/lower bound. */ + + insn = 0; + insn16 = 0; + if (ELF32_R_TYPE (irel->r_info) == R_NDS32_LABEL + && (irel->r_addend & 0x1f) >= 2) + optimize = 1; + + /* Relocation Types + R_NDS32_LONGCALL1 53 + R_NDS32_LONGCALL2 54 + R_NDS32_LONGCALL3 55 + R_NDS32_LONGJUMP1 56 + R_NDS32_LONGJUMP2 57 + R_NDS32_LONGJUMP3 58 + R_NDS32_LOADSTORE 59 */ + if (ELF32_R_TYPE (irel->r_info) >= R_NDS32_LONGCALL1 + && ELF32_R_TYPE (irel->r_info) <= R_NDS32_LOADSTORE) + { + seq_len = GET_SEQ_LEN (irel->r_addend); + insn_opt = IS_OPTIMIZE (irel->r_addend); + convertible = IS_1ST_CONVERT (irel->r_addend); + insn16_on = IS_16BIT_ON (irel->r_addend); + laddr = irel->r_offset; + } + /* Relocation Types + R_NDS32_LO12S0_RELA 30 + R_NDS32_LO12S1_RELA 29 + R_NDS32_LO12S2_RELA 28 + R_NDS32_LO12S2_SP_RELA 71 + R_NDS32_LO12S2_DP_RELA 70 + R_NDS32_GOT_LO12 46 + R_NDS32_GOTOFF_LO12 50 + R_NDS32_PLTREL_LO12 65 + R_NDS32_PLT_GOTREL_LO12 67 + R_NDS32_GOT_SUFF 193 + R_NDS32_GOTOFF_SUFF 194 + R_NDS32_PLT_GOT_SUFF 195 + R_NDS32_MULCALL_SUFF 196 + R_NDS32_PTR 197 */ + else if ((ELF32_R_TYPE (irel->r_info) <= R_NDS32_LO12S0_RELA + && ELF32_R_TYPE (irel->r_info) >= R_NDS32_LO12S2_RELA) + || ELF32_R_TYPE (irel->r_info) == R_NDS32_LO12S2_SP_RELA + || ELF32_R_TYPE (irel->r_info) == R_NDS32_LO12S2_DP_RELA + || ELF32_R_TYPE (irel->r_info) == R_NDS32_GOT_LO12 + || ELF32_R_TYPE (irel->r_info) == R_NDS32_GOTOFF_LO12 + || ELF32_R_TYPE (irel->r_info) == R_NDS32_PLTREL_LO12 + || ELF32_R_TYPE (irel->r_info) == R_NDS32_PLT_GOTREL_LO12 + || (ELF32_R_TYPE (irel->r_info) >= R_NDS32_GOT_SUFF + && ELF32_R_TYPE (irel->r_info) <= R_NDS32_PTR) + || ELF32_R_TYPE (irel->r_info) == R_NDS32_PLTBLOCK + || ELF32_R_TYPE (irel->r_info) == R_NDS32_17IFC_PCREL_RELA) + { + seq_len = 0; + insn_opt = IS_OPTIMIZE (irel->r_addend) > 0; + convertible = 0; + insn16_on = 0; + laddr = irel->r_offset; + } + else + continue; + + insn_len = seq_len; + + if (laddr + seq_len > (bfd_vma) sec->size) + { + char *s = NULL; + int pass_check = 0; + + if (ELF32_R_TYPE (irel->r_info) >= R_NDS32_LONGCALL1 + && ELF32_R_TYPE (irel->r_info) <= R_NDS32_LONGJUMP3) + { + for (i1_irelfn = irel; + i1_irelfn < irelend && i1_irelfn->r_offset < (laddr + seq_len - 4); + i1_irelfn++) + ; + + for (; + i1_irelfn < irelend && i1_irelfn->r_offset == (laddr + seq_len - 4); + i1_irelfn++) + if (ELF32_R_TYPE (i1_irelfn->r_info) == R_NDS32_INSN16) + { + pass_check = 1; + break; + } + i1_irelfn = NULL; + } + + if (pass_check == 0) + { + reloc_howto_type *howto = + bfd_elf32_bfd_reloc_type_table_lookup (ELF32_R_TYPE + (irel->r_info)); + s = howto->name; + + (*_bfd_error_handler) + ("%B: warning: %s points to unrecognized insns at 0x%lx.", + abfd, s, (long) irel->r_offset); + + continue; + } + } + + if (ELF32_R_TYPE (irel->r_info) == R_NDS32_LONGCALL1) + { + /* There are 3 variations for LONGCALL1 + case 4-4-2; 16-bit on, optimize off or optimize for space + sethi ta, hi20(symbol) ; LONGCALL1/HI20 + ori ta, ta, lo12(symbol) ; LO12S0 + jral5 ta ; + + case 4-4-4; 16-bit off, optimize don't care + sethi ta, hi20(symbol) ; LONGCALL1/HI20 + ori ta, ta, lo12(symbol) ; LO12S0 + jral ta ; + + case 4-4-4; 16-bit on, optimize for speed + sethi ta, hi20(symbol) ; LONGCALL1/HI20 + ori ta, ta, lo12(symbol) ; LO12S0 + jral ta ; (INSN16) + Check code for -mlong-calls output. */ + + /* Get the reloc for the address from which the register is + being loaded. This reloc will tell us which function is + actually being called. */ + hi_irelfn = find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_HI20_RELA, laddr); + lo_irelfn = find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_LO12S0_ORI_RELA, + laddr + 4); + i1_offset = 8; + + if (hi_irelfn == irelend || lo_irelfn == irelend) + { + hi_irelfn = find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_20_RELA, laddr); + i1_offset = 4; + if (hi_irelfn == irelend) + { + (*_bfd_error_handler) + ("%B: warning: R_NDS32_LONGCALL1 points to unrecognized reloc at 0x%lx.", + abfd, (long) irel->r_offset); + continue; + } + } + + i1_irelfn = find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_INSN16, + laddr + i1_offset); + + /* Get the value of the symbol referred to by the reloc. */ + foff = calculate_offset (abfd, sec, hi_irelfn, isymbuf, symtab_hdr, + &pic_ext_target); + + /* This condition only happened when symbol is undefined. */ + if (pic_ext_target || foff == 0) + continue; + if (foff < -0x1000000 || foff >= 0x1000000) + { + continue; + } + + /* Relax to + jal symbol ; 25_PCREL */ + /* For simplicity of coding, we are going to modify the section + contents, the section relocs, and the BFD symbol table. We + must tell the rest of the code not to free up this + information. It would be possible to instead create a table + of changes which have to be made, as is done in coff-mips.c; + that would be more work, but would require less memory when + the linker is run. */ + + /* Replace the long call with a jal. */ + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), + R_NDS32_25_PCREL_RELA); + irel->r_addend = hi_irelfn->r_addend; + + /* We don't resolve this here but resolve it in relocate_section. */ + insn = INSN_JAL; + + bfd_putb32 (insn, contents + irel->r_offset); + hi_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), R_NDS32_NONE); + lo_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), R_NDS32_NONE); + insn_len = 4; + if (i1_irelfn != irelend) + { + if (!insn_opt + && (i1_irelfn->r_addend & R_NDS32_INSN16_CONVERT_FLAG)) + { + /* The instruction pointed by R_NDS32_INSN16 is already + turned into 16-bit instruction, so the total length of + this sequence is decreased by 2. */ + seq_len = seq_len - 2; + } + i1_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (i1_irelfn->r_info), R_NDS32_NONE); + } + if (seq_len & 0x2) + { + insn16 = NDS32_NOP16; + bfd_putb16 (insn16, contents + irel->r_offset + insn_len); + lo_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), + R_NDS32_INSN16); + lo_irelfn->r_addend = R_NDS32_INSN16_CONVERT_FLAG; + insn_len += 2; + } + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_LONGCALL2) + { + /* bltz rt, $1 ; LONGCALL2 + jal symbol ; 25_FIXED + $1: */ + /* Get the reloc for the address from which the register is + being loaded. This reloc will tell us which function is + actually being called. */ + i1_irelfn = + find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_25_PCREL_RELA, laddr + 4); + + if (i1_irelfn == irelend) + { + (*_bfd_error_handler) + ("%B: warning: R_NDS32_LONGCALL2 points to unrecognized reloc at 0x%lx.", + abfd, (long) irel->r_offset); + + continue; + } + + insn = bfd_getb32 (contents + laddr); + + /* Get the value of the symbol referred to by the reloc. */ + foff = + calculate_offset (abfd, sec, i1_irelfn, isymbuf, symtab_hdr, + &pic_ext_target); + if (foff == 0) + continue; + if (foff < -0x10000 - 4 || foff >= 0x10000 - 4) + /* After all that work, we can't shorten this function call. */ + continue; + + /* Relax to bgezal rt, label ; 17_PCREL + or bltzal rt, label ; 17_PCREL */ + + /* Convert to complimentary conditional call. */ + insn &= 0xffff0000; + insn ^= 0x90000; + + /* For simplicity of coding, we are going to modify the section + contents, the section relocs, and the BFD symbol table. We + must tell the rest of the code not to free up this + information. It would be possible to instead create a table + of changes which have to be made, as is done in coff-mips.c; + that would be more work, but would require less memory when + the linker is run. */ + + /* Replace the long call with a bgezal. */ + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (i1_irelfn->r_info), + R_NDS32_17_PCREL_RELA); + + bfd_putb32 (insn, contents + irel->r_offset); + + i1_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (i1_irelfn->r_info), R_NDS32_NONE); + cond_irelfn = + find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_17_PCREL_RELA, laddr); + if (cond_irelfn != irelend) + { + cond_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (i1_irelfn->r_info), + R_NDS32_17_PCREL_RELA); + cond_irelfn->r_addend = i1_irelfn->r_addend; + } + insn_len = 4; + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_LONGCALL3) + { + /* There are 3 variations for LONGCALL3 + case 4-4-4-2; 16-bit on, optimize off or optimize for space + bltz rt, $1 ; LONGCALL3 + sethi ta, hi20(symbol) ; HI20 + ori ta, ta, lo12(symbol) ; LO12S0 + jral5 ta ; + $1 + + case 4-4-4-4; 16-bit off, optimize don't care + bltz rt, $1 ; LONGCALL3 + sethi ta, hi20(symbol) ; HI20 + ori ta, ta, lo12(symbol) ; LO12S0 + jral ta ; + $1 + + case 4-4-4-4; 16-bit on, optimize for speed + bltz rt, $1 ; LONGCALL3 + sethi ta, hi20(symbol) ; HI20 + ori ta, ta, lo12(symbol) ; LO12S0 + jral ta ; (INSN16) + $1 */ + + /* Get the reloc for the address from which the register is + being loaded. This reloc will tell us which function is + actually being called. */ + hi_irelfn = + find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_HI20_RELA, laddr + 4); + lo_irelfn = + find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_LO12S0_ORI_RELA, laddr + 8); + i2_offset = 12; + + if (hi_irelfn == irelend || lo_irelfn == irelend) + { + i2_offset = 8; + hi_irelfn = + find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_20_RELA, laddr + 4); + + if (hi_irelfn == irelend) + { + (*_bfd_error_handler) + ("%B: warning: R_NDS32_LONGCALL3 points to unrecognized reloc at 0x%lx.", + abfd, (long) irel->r_offset); + continue; + } + } + + i2_irelfn = + find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_INSN16, laddr + i2_offset); + + /* Get the value of the symbol referred to by the reloc. */ + foff = + calculate_offset (abfd, sec, hi_irelfn, isymbuf, symtab_hdr, + &pic_ext_target); + if (pic_ext_target || foff == 0) + continue; + if (foff < -0x1000000 || foff >= 0x1000000) + continue; + + insn = bfd_getb32 (contents + laddr); + if (foff >= -0x10000 - 4 && foff < 0x10000 - 4) + { + /* Relax to bgezal rt, label ; 17_PCREL + or bltzal rt, label ; 17_PCREL */ + + /* Convert to complimentary conditional call. */ + insn &= 0xffff0000; + insn ^= 0x90000; + bfd_putb32 (insn, contents + irel->r_offset); + + insn_len = 4; + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), + R_NDS32_NONE); + hi_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), R_NDS32_NONE); + lo_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), R_NDS32_NONE); + if (i2_irelfn != irelend) + { + if (!insn_opt + && (i2_irelfn->r_addend & R_NDS32_INSN16_CONVERT_FLAG)) + { + /* The instruction pointed by R_NDS32_INSN16 is already + turned into 16-bit instruction, so the total length + of this sequence is decreased by 2. */ + seq_len = seq_len - 2; + } + i2_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (i2_irelfn->r_info), + R_NDS32_NONE); + } + cond_irelfn = + find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_17_PCREL_RELA, laddr); + if (cond_irelfn != irelend) + { + cond_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), + R_NDS32_17_PCREL_RELA); + cond_irelfn->r_addend = hi_irelfn->r_addend; + } + + if (seq_len & 0x2) + { + insn16 = NDS32_NOP16; + bfd_putb16 (insn16, contents + irel->r_offset + insn_len); + hi_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), + R_NDS32_INSN16); + hi_irelfn->r_addend = R_NDS32_INSN16_CONVERT_FLAG; + insn_len += 2; + } + } + else + { + /* Relax to the following instruction sequence + bltz rt, $1 ; LONGCALL2 + jal symbol ; 25_PCREL + $1 + */ + insn = (insn & 0xffff0000) | 4; + bfd_putb32 (insn, contents + irel->r_offset); + /* This relax is incorrect. Review, fix and test it. + Check 6a726f0f for the oringnal code. */ + BFD_ASSERT (0); + + bfd_putb32 (insn, contents + irel->r_offset + 4); + insn_len = 8; + hi_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), + R_NDS32_25_PCREL_RELA); + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_LONGCALL2); + + lo_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), R_NDS32_NONE); + if (i2_irelfn != irelend) + { + i2_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (i2_irelfn->r_info), + R_NDS32_NONE); + } + if (seq_len & 0x2) + { + insn16 = NDS32_NOP16; + bfd_putb16 (insn16, contents + irel->r_offset + insn_len); + lo_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), + R_NDS32_INSN16); + lo_irelfn->r_addend = R_NDS32_INSN16_CONVERT_FLAG; + insn_len += 2; + } + } + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_LONGJUMP1) + { + /* There are 3 variations for LONGJUMP1 + case 4-4-2; 16-bit bit on, optimize off or optimize for space + sethi ta, hi20(symbol) ; LONGJUMP1/HI20 + ori ta, ta, lo12(symbol) ; LO12S0 + jr5 ta ; + + case 4-4-4; 16-bit off, optimize don't care + sethi ta, hi20(symbol) ; LONGJUMP1/HI20 + ori ta, ta, lo12(symbol) ; LO12S0 + jr ta ; + + case 4-4-4; 16-bit on, optimize for speed + sethi ta, hi20(symbol) ; LONGJUMP1/HI20 + ori ta, ta, lo12(symbol) ; LO12S0 + jr ta ; INSN16 */ + + /* Get the reloc for the address from which the register is + being loaded. This reloc will tell us which function is + actually being called. */ + hi_irelfn = + find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_HI20_RELA, laddr); + lo_irelfn = + find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_LO12S0_ORI_RELA, laddr + 4); + i1_offset = 8; + + if (hi_irelfn == irelend || lo_irelfn == irelend) + { + hi_irelfn = + find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_20_RELA, laddr); + i1_offset = 4; + + if (hi_irelfn == irelend) + { + (*_bfd_error_handler) + ("%B: warning: R_NDS32_LONGJUMP1 points to unrecognized reloc at 0x%lx.", + abfd, (long) irel->r_offset); + + continue; + } + } + + i1_irelfn = + find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_INSN16, laddr + i1_offset); + + /* Get the value of the symbol referred to by the reloc. */ + foff = + calculate_offset (abfd, sec, hi_irelfn, isymbuf, symtab_hdr, + &pic_ext_target); + if (pic_ext_target || foff == 0) + continue; + + if (foff >= -0x1000000 && foff < 0x1000000) + { + /* j label */ + if (!insn_opt && insn16_on && foff >= -0x100 && foff < 0x100 + && (seq_len & 0x2)) + { + /* 16-bit on, but not optimized for speed. */ + reloc = R_NDS32_9_PCREL_RELA; + insn16 = INSN_J8; + bfd_putb16 (insn16, contents + irel->r_offset); + insn_len = 2; + } + else + { + reloc = R_NDS32_25_PCREL_RELA; + insn = INSN_J; + bfd_putb32 (insn, contents + irel->r_offset); + insn_len = 4; + } + } + else + { + continue; + } + + /* For simplicity of coding, we are going to modify the section + contents, the section relocs, and the BFD symbol table. We + must tell the rest of the code not to free up this + information. It would be possible to instead create a table + of changes which have to be made, as is done in coff-mips.c; + that would be more work, but would require less memory when + the linker is run. */ + + if (insn == 4) + { + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_INSN16); + irel->r_addend = 0; + } + else + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE); + + hi_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), reloc); + lo_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), R_NDS32_NONE); + if (i1_irelfn != irelend) + { + if (!insn_opt + && (i1_irelfn->r_addend & R_NDS32_INSN16_CONVERT_FLAG)) + { + /* The instruction pointed by R_NDS32_INSN16 is already + turned into 16-bit instruction, so the total length + of this sequence is decreased by 2. */ + seq_len = seq_len - 2; + i1_irelfn->r_addend = 0; + } + i1_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (i1_irelfn->r_info), R_NDS32_NONE); + } + + if ((seq_len & 0x2) && ((insn_len & 2) == 0)) + { + insn16 = NDS32_NOP16; + bfd_putb16 (insn16, contents + irel->r_offset + insn_len); + lo_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), + R_NDS32_INSN16); + lo_irelfn->r_addend = R_NDS32_INSN16_CONVERT_FLAG; + insn_len += 2; + } + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_LONGJUMP2) + { + /* There are 3 variations for LONGJUMP2 + case 2-4; 1st insn convertible, 16-bit on, optimize off or optimize for space + bnes38 rt, ra, $1 ; LONGJUMP2 + j label ; 25_PCREL + $1: + + case 4-4; 1st insn not convertible + bne rt, ra, $1 ; LONGJUMP2 + j label ; 25_PCREL + $1: + + case 4-4; 1st insn convertible, 16-bit on, optimize for speed + bne rt, ra, $1 ; LONGJUMP2/INSN16 + j label ; 25_PCREL + $1: */ + + /* Get the reloc for the address from which the register is + being loaded. This reloc will tell us which function is + actually being called. */ + enum elf_nds32_reloc_type checked_types[] = + { R_NDS32_15_PCREL_RELA, R_NDS32_9_PCREL_RELA }; + hi_off = (seq_len == 6) ? 2 : 4; + i2_irelfn = + find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_25_PCREL_RELA, + laddr + hi_off); + + for (i = 0; i < 2; i++) + { + cond_irelfn = + find_relocs_at_address_addr (irel, internal_relocs, irelend, + checked_types[i], laddr); + if (cond_irelfn != irelend) + break; + } + if (i2_irelfn == irelend) + { + (*_bfd_error_handler) + ("%B: warning: R_NDS32_LONGJUMP2 points to unrecognized reloc at 0x%lx.", + abfd, (long) irel->r_offset); + + continue; + } + + i1_irelfn = + find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_INSN16, laddr); + + if (i1_irelfn != irelend && !insn_opt + && (i1_irelfn->r_addend & R_NDS32_INSN16_CONVERT_FLAG)) + { + /* The instruction pointed by R_NDS32_INSN16 is already turned + into 16-bit instruction, so the total length of this sequence + is decreased by 2. */ + seq_len = seq_len - 2; + } + + if (seq_len == 8) + { + /* possible cases + 1. range is outside of +/-256 bytes + 2. optimize is on with INSN16 + 3. optimize is off */ + insn_off = 4; + insn = bfd_getb32 (contents + laddr); + if (!insn16_on) + { + /* 16-bit is off, can't convert to 16-bit. */ + comp_insn16 = 0; + } + else if (N32_OP6 (insn) == N32_OP6_BR1) + { + /* beqs label ; 15_PCREL (INSN16) */ + comp_insn = (insn ^ 0x4000) & 0xffffc000; + i_mask = 0xffffc000; + if (N32_IS_RT3 (insn) && N32_RA5 (insn) == REG_R5) + { + /* Insn can be contracted to 16-bit. */ + comp_insn16 = + (insn & 0x4000) ? INSN_BNES38 : INSN_BEQS38; + comp_insn16 |= (N32_RT5 (insn) & 0x7) << 8; + } + else + { + /* No conversion. */ + comp_insn16 = 0; + } + } + else + { + comp_insn = (insn ^ 0x10000) & 0xffffc000; + i_mask = 0xffff0000; + if (N32_BR2_SUB (insn) == N32_BR2_BEQZ + || N32_BR2_SUB (insn) == N32_BR2_BNEZ) + { + if (N32_IS_RT3 (insn)) + { + /* Insn can be contracted to 16-bit. */ + comp_insn16 = + (insn & 0x10000) ? INSN_BNEZ38 : INSN_BEQZ38; + comp_insn16 |= (N32_RT5 (insn) & 0x7) << 8; + } + else if (N32_RT5 (insn) == REG_R15) + { + /* Insn can be contracted to 16-bit. */ + comp_insn16 = + (insn & 0x10000) ? INSN_BNES38 : INSN_BEQS38; + } + else + { + /* No conversion. */ + comp_insn16 = 0; + } + } + else + { + /* No conversion. */ + comp_insn16 = 0; + } + } + } + else + { + /* First instruction is 16-bit. */ + insn_off = 2; + insn16 = bfd_getb16 (contents + laddr); + switch ((insn16 & 0xf000) >> 12) + { + case 0xc: + /* beqz38 or bnez38 */ + comp_insn = (insn16 & 0x0800) ? INSN_BNEZ : INSN_BEQZ; + comp_insn |= ((insn16 & 0x0700) >> 8) << 20; + comp_insn16 = (insn16 ^ 0x0800) & 0xff00; + insn = (insn16 & 0x0800) ? INSN_BEQZ : INSN_BNEZ; + insn |= ((insn16 & 0x0700) >> 8) << 20; + i_mask = 0xffff0000; + break; + + case 0xd: + /* beqs38 or bnes38 */ + comp_insn = (insn16 & 0x0800) ? INSN_BNE : INSN_BEQ; + comp_insn |= (((insn16 & 0x0700) >> 8) << 20) + | (REG_R5 << 15); + comp_insn16 = (insn16 ^ 0x0800) & 0xff00; + insn = (insn16 & 0x0800) ? INSN_BEQ : INSN_BNE; + insn |= (((insn16 & 0x0700) >> 8) << 20) | (REG_R5 << 15); + i_mask = 0xffffc000; + break; + + case 0xe: + /* beqzS8 or bnezS8 */ + comp_insn = (insn16 & 0x0100) ? INSN_BNEZ : INSN_BEQZ; + comp_insn |= REG_R15 << 20; + comp_insn16 = (insn16 ^ 0x0100) & 0xff00; + insn = (insn16 & 0x0100) ? INSN_BEQZ : INSN_BNEZ; + insn |= REG_R15 << 20; + i_mask = 0xffff0000; + break; + + default: + comp_insn16 = 0; + insn = 0; + break; + } + } + + /* Get the value of the symbol referred to by the reloc. */ + foff = + calculate_offset (abfd, sec, i2_irelfn, isymbuf, symtab_hdr, + &pic_ext_target); + if (pic_ext_target || foff == 0) + continue; + + if (comp_insn16 + && foff >= -0x100 - insn_off && foff < 0x100 - insn_off) + { + if (insn_opt || seq_len == 8) + { + /* Don't convert it to 16-bit now, keep this as relaxable for + ``label reloc; INSN16''. */ + + /* Save comp_insn32 to buffer. */ + insn = comp_insn; + bfd_putb32 (insn, contents + irel->r_offset); + insn_len = 4; + reloc = (N32_OP6 (comp_insn) == N32_OP6_BR1) ? + R_NDS32_15_PCREL_RELA : R_NDS32_17_PCREL_RELA; + + if (cond_irelfn != irelend) + { + cond_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (cond_irelfn->r_info), + R_NDS32_INSN16); + cond_irelfn->r_addend = 0; + } + } + else + { + /* Not optimize for speed; convert sequence to 16-bit. */ + + /* Save comp_insn16 to buffer. */ + insn16 = comp_insn16; + bfd_putb16 (insn16, contents + irel->r_offset); + insn_len = 2; + reloc = R_NDS32_9_PCREL_RELA; + } + + /* Change relocs. */ + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (i2_irelfn->r_info), reloc); + irel->r_addend = i2_irelfn->r_addend; + + i2_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (i2_irelfn->r_info), + R_NDS32_NONE); + } + else if (N32_OP6 (insn) == N32_OP6_BR1 + && (foff >= -0x4000 - insn_off && foff < 0x4000 - insn_off)) + { + /* beqs label ; 15_PCREL */ + insn = comp_insn; + bfd_putb32 (insn, contents + irel->r_offset); + insn_len = 4; + + /* Change relocs. */ + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (i2_irelfn->r_info), + R_NDS32_15_PCREL_RELA); + irel->r_addend = i2_irelfn->r_addend; + if (i1_irelfn != irelend) + i1_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (i1_irelfn->r_info), + R_NDS32_NONE); + + if (seq_len & 0x2) + { + insn16 = NDS32_NOP16; + bfd_putb16 (insn16, contents + irel->r_offset + insn_len); + i2_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (i2_irelfn->r_info), + R_NDS32_INSN16); + i2_irelfn->r_addend = R_NDS32_INSN16_CONVERT_FLAG; + insn_len += 2; + } + } + else if (N32_OP6 (insn) == N32_OP6_BR2 && foff >= -0x10000 && foff < 0x10000) + { + /* beqz label ; 17_PCREL */ + insn = comp_insn; + bfd_putb32 (insn, contents + irel->r_offset); + insn_len = 4; + + /* Change relocs. */ + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (i2_irelfn->r_info), + R_NDS32_17_PCREL_RELA); + irel->r_addend = i2_irelfn->r_addend; + if (i1_irelfn != irelend) + i1_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (i1_irelfn->r_info), + R_NDS32_NONE); + if (seq_len & 0x2) + { + insn16 = NDS32_NOP16; + bfd_putb16 (insn16, contents + irel->r_offset + insn_len); + i2_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (i2_irelfn->r_info), + R_NDS32_INSN16); + i2_irelfn->r_addend = R_NDS32_INSN16_CONVERT_FLAG; + insn_len += 2; + } + } + else + continue; + + if (cond_irelfn != irelend) + cond_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (cond_irelfn->r_info), + R_NDS32_NONE); + + + /* For simplicity of coding, we are going to modify the section + contents, the section relocs, and the BFD symbol table. We + must tell the rest of the code not to free up this + information. It would be possible to instead create a table + of changes which have to be made, as is done in coff-mips.c; + that would be more work, but would require less memory when + the linker is run. */ + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_LONGJUMP3) + { + int reloc_off = 0, cond_removed = 0; + /* Get the reloc for the address from which the register is + being loaded. This reloc will tell us which function is + actually being called. */ + enum elf_nds32_reloc_type checked_types[] = + { R_NDS32_15_PCREL_RELA, R_NDS32_9_PCREL_RELA }; + + /* There are 5 variations for LONGJUMP3 + case 1: 2-4-4-2; 1st insn convertible, 16-bit on, + optimize off or optimize for space + bnes38 rt, ra, $1 ; LONGJUMP3 + sethi ta, hi20(symbol) ; HI20 + ori ta, ta, lo12(symbol) ; LO12S0 + jr5 ta ; + $1: ; + + case 2: 2-4-4-2; 1st insn convertible, 16-bit on, optimize for speed + bnes38 rt, ra, $1 ; LONGJUMP3 + sethi ta, hi20(symbol) ; HI20 + ori ta, ta, lo12(symbol) ; LO12S0 + jr5 ta ; + $1: ; LABEL + + case 3: 4-4-4-2; 1st insn not convertible, 16-bit on, + optimize off or optimize for space + bne rt, ra, $1 ; LONGJUMP3 + sethi ta, hi20(symbol) ; HI20 + ori ta, ta, lo12(symbol) ; LO12S0 + jr5 ta ; + $1: ; + + case 4: 4-4-4-4; 1st insn don't care, 16-bit off, optimize don't care + 16-bit off if no INSN16 + bne rt, ra, $1 ; LONGJUMP3 + sethi ta, hi20(symbol) ; HI20 + ori ta, ta, lo12(symbol) ; LO12S0 + jr ta ; + $1: ; + + case 5: 4-4-4-4; 1st insn not convertible, 16-bit on, optimize for speed + 16-bit off if no INSN16 + bne rt, ra, $1 ; LONGJUMP3 + sethi ta, hi20(symbol) ; HI20 + ori ta, ta, lo12(symbol) ; LO12S0 + jr ta ; INSN16 + $1: ; LABEL + */ + + if (convertible) + { + hi_off = 2; + if (insn_opt) + reloc_off = 2; + } + else + { + hi_off = 4; + } + + hi_irelfn = find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_HI20_RELA, + laddr + hi_off); + lo_irelfn = find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_LO12S0_ORI_RELA, + laddr + hi_off + 4); + i2_offset = 8; + + if (hi_irelfn == irelend || lo_irelfn == irelend) + { + hi_irelfn = find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_20_RELA, + laddr + hi_off); + i2_offset = 4; + + if (hi_irelfn == irelend) + { + (*_bfd_error_handler) + ("%B: warning: R_NDS32_LONGJUMP3 points to unrecognized reloc at 0x%lx.", + abfd, (long) irel->r_offset); + continue; + } + } + + i2_irelfn = + find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_INSN16, + laddr + hi_off + i2_offset); + + /* Get the value of the symbol referred to by the reloc. */ + foff = + calculate_offset (abfd, sec, hi_irelfn, isymbuf, symtab_hdr, + &pic_ext_target); + if (pic_ext_target || foff == 0) + continue; + + /* Set offset adjustment value. */ + /* Check instruction type and set complimentary instruction. */ + if (hi_off == 2) + { + /* First instruction is 16-bit. */ + insn_off = 2; + insn16 = bfd_getb16 (contents + laddr); + switch ((insn16 & 0xf000) >> 12) + { + case 0xc: + /* beqz38 or bnez38 */ + comp_insn = (insn16 & 0x0800) ? INSN_BNEZ : INSN_BEQZ; + comp_insn |= ((insn16 & 0x0700) >> 8) << 20; + comp_insn16 = (insn16 ^ 0x0800) & 0xff00; + insn = (insn16 & 0x0800) ? INSN_BEQZ : INSN_BNEZ; + insn |= ((insn16 & 0x0700) >> 8) << 20; + i_mask = 0xffff0000; + break; + + case 0xd: + /* beqs38 or bnes38 */ + comp_insn = (insn16 & 0x0800) ? INSN_BNE : INSN_BEQ; + comp_insn |= (((insn16 & 0x0700) >> 8) << 20) + | (REG_R5 << 15); + comp_insn16 = (insn16 ^ 0x0800) & 0xff00; + insn = (insn16 & 0x0800) ? INSN_BEQ : INSN_BNE; + insn |= (((insn16 & 0x0700) >> 8) << 20) | (REG_R5 << 15); + i_mask = 0xffffc000; + break; + + case 0xe: + /* beqzS8 or bnezS8 */ + comp_insn = (insn16 & 0x0100) ? INSN_BNEZ : INSN_BEQZ; + comp_insn |= REG_R15 << 20; + comp_insn16 = (insn16 ^ 0x0100) & 0xff00; + insn = (insn16 & 0x0100) ? INSN_BEQZ : INSN_BNEZ; + insn |= REG_R15 << 20; + i_mask = 0xffff0000; + break; + } + } + else + { + /* First instruction is 32-bit. */ + insn_off = 4; + insn = bfd_getb32 (contents + laddr); + if (!insn16_on) + { + /* 16-bit is off */ + comp_insn16 = 0; + } + else if (N32_OP6 (insn) == N32_OP6_BR1) + { + /* +/-16K range */ + comp_insn = insn ^ 0x4000; + i_mask = 0xffffc000; + if (N32_IS_RT3 (insn) && N32_RA5 (insn) == REG_R5) + { + /* This instruction can turn to 16-bit. */ + comp_insn16 = + (insn & 0x4000) ? INSN_BNES38 : INSN_BEQS38; + comp_insn16 |= (N32_RT5 (insn) & 0x7) << 8; + } + else + { + /* no conversion */ + comp_insn16 = 0; + } + } + else + { + /* +/-64K range */ + comp_insn = insn ^ 0x10000; + i_mask = 0xffff0000; + if (N32_BR2_SUB (insn) == N32_BR2_BEQZ + || N32_BR2_SUB (insn) == N32_BR2_BNEZ) + { + if (N32_IS_RT3 (insn)) + { + /* This instruction can turn to 16-bit. */ + comp_insn16 = + (insn & 0x10000) ? INSN_BNEZ38 : INSN_BEQZ38; + comp_insn16 |= (N32_RT5 (insn) & 0x7) << 8; + } + else if (N32_RT5 (insn) == REG_R15) + { + /* This instruction can turn to 16-bit. */ + comp_insn16 = + (insn & 0x10000) ? INSN_BNEZS8 : INSN_BEQZS8; + } + else + { + /* No conversion. */ + comp_insn16 = 0; + } + } + else + { + /* No conversion. */ + comp_insn16 = 0; + } + } + } + + if (foff < -0x1000000 && foff >= 0x1000000) + continue; + + if (i2_irelfn != irelend) + { + if (insn_opt == 0 + && (i2_irelfn->r_addend & R_NDS32_INSN16_CONVERT_FLAG)) + { + /* The instruction pointed by R_NDS32_INSN16 is already + turned into 16-bit instruction, so the total length + of this sequence is decreased by 2. */ + seq_len = seq_len - 2; + i2_irelfn->r_addend = 0; + } + } + + /* For simplicity of coding, we are going to modify the section + contents, the section relocs, and the BFD symbol table. We + must tell the rest of the code not to free up this + information. It would be possible to instead create a table + of changes which have to be made, as is done in coff-mips.c; + that would be more work, but would require less memory when + the linker is run. */ + + if (comp_insn16 + && foff >= -0x100 - insn_off && foff < 0x100 - insn_off) + { + if (insn_opt || (seq_len & 0x2) == 0) + { + /* Don't convert it to 16-bit now, keep this as relaxable + for ``label reloc; INSN1a''6. */ + /* Save comp_insn32 to buffer. */ + insn = comp_insn; + bfd_putb32 (insn, contents + irel->r_offset); + insn_len = 4; + reloc = (N32_OP6 (comp_insn) == N32_OP6_BR1) ? + R_NDS32_15_PCREL_RELA : R_NDS32_17_PCREL_RELA; + + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), + R_NDS32_INSN16); + } + else + { + /* Not optimize for speed; convert sequence to 16-bit. */ + /* Save comp_insn16 to buffer. */ + insn16 = comp_insn16; + bfd_putb16 (insn16, contents + irel->r_offset); + insn_len = 2; + reloc = R_NDS32_9_PCREL_RELA; + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), + R_NDS32_NONE); + } + + /* Change relocs. */ + for (i = 0; i < 2; i++) + { + cond_irelfn = + find_relocs_at_address_addr (irel, internal_relocs, + irelend, checked_types[i], + laddr); + + if (cond_irelfn != irelend) + { + cond_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), reloc); + cond_irelfn->r_addend = hi_irelfn->r_addend; + } + } + hi_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), R_NDS32_NONE); + lo_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), R_NDS32_NONE); + cond_removed = 1; + } + else if (N32_OP6 (insn) == N32_OP6_BR1 + && foff >= -0x4000 - insn_off && foff < 0x4000 - insn_off) + { + /* Relax to `beq label ; 15_PCREL'. */ + + /* Save comp_insn to buffer. */ + insn = comp_insn; + bfd_putb32 (insn, contents + irel->r_offset); + insn_len = 4; + reloc = R_NDS32_15_PCREL_RELA; + + /* Change relocs. */ + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE); + hi_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), R_NDS32_NONE); + lo_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), R_NDS32_NONE); + if (seq_len & 0x2) + { + insn16 = NDS32_NOP16; + bfd_putb16 (insn16, contents + irel->r_offset + insn_len); + hi_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), + R_NDS32_INSN16); + hi_irelfn->r_addend = R_NDS32_INSN16_CONVERT_FLAG; + if (hi_off == 2) + hi_irelfn->r_offset += 2; + insn_len += 2; + } + cond_removed = 1; + } + else if (N32_OP6 (insn) == N32_OP6_BR2 + && foff >= -0x10000 - insn_off + && foff < 0x10000 - insn_off) + { + /* Relax to `beqz label ; 17_PCREL'. */ + + /* Save comp_insn to buffer. */ + insn = comp_insn; + bfd_putb32 (insn, contents + irel->r_offset); + insn_len = 4; + reloc = R_NDS32_17_PCREL_RELA; + + /* Change relocs. */ + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE); + hi_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), R_NDS32_NONE); + lo_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), R_NDS32_NONE); + if (seq_len & 0x2) + { + insn16 = NDS32_NOP16; + bfd_putb16 (insn16, contents + irel->r_offset + insn_len); + lo_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), + R_NDS32_INSN16); + lo_irelfn->r_addend = R_NDS32_INSN16_CONVERT_FLAG; + if (hi_off == 2) + hi_irelfn->r_offset += 2; + insn_len += 2; + } + cond_removed = 1; + } + else if (foff >= -0x1000000 - reloc_off + && foff < 0x1000000 - reloc_off) + { + /* Relax to one of the following 3 variations + + case 2-4; 1st insn convertible, 16-bit on, optimize off or optimize for space + bnes38 rt, $1 ; LONGJUMP2 + j label ; 25_PCREL + $1 + + case 4-4; 1st insn not convertible, others don't care + bne rt, ra, $1 ; LONGJUMP2 + j label ; 25_PCREL + $1 + + case 4-4; 1st insn convertible, 16-bit on, optimize for speed + bne rt, ra, $1 ; LONGJUMP2/INSN16 + j label ; 25_PCREL + $1 + */ + + /* Offset for first instruction. */ + + if (hi_off == 2) + { + /* First instruction is 16-bit. */ + if (hi_irelfn != irelend) + { + /* INSN16 exists so this is optimized for speed. */ + /* Convert this instruction to 32-bit for label alignment. */ + insn = (insn & i_mask) | 4; + bfd_putb32 (insn, contents + irel->r_offset); + insn_len = 8; + hi_irelfn->r_offset += 2; + } + else + { + /* Not optimized for speed. */ + insn16 = (insn16 & 0xff00) | 3; + bfd_putb16 (insn16, contents + irel->r_offset); + insn_len = 6; + } + } + else + { + /* First instruction is 32-bit. */ + insn = (insn & i_mask) | 4; + bfd_putb32 (insn, contents + irel->r_offset); + insn_len = 8; + } + + /* Use j label as second instruction. */ + insn = INSN_J; + bfd_putb32 (insn, contents + irel->r_offset); + + /* Change relocs. */ + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_LONGJUMP2); + hi_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), + R_NDS32_25_PCREL_RELA); + lo_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), R_NDS32_NONE); + if (((seq_len ^ insn_len) & 0x2) != 0x2) + { + insn16 = NDS32_NOP16; + bfd_putb16 (insn16, contents + irel->r_offset + insn_len); + lo_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), + R_NDS32_INSN16); + lo_irelfn->r_addend = R_NDS32_INSN16_CONVERT_FLAG; + lo_irelfn->r_offset = hi_irelfn->r_offset + 4; + insn_len += 2; + } + } + + if (cond_removed) + { + for (i = 0; i < 2; i++) + { + cond_irelfn = + find_relocs_at_address_addr (irel, internal_relocs, + irelend, checked_types[i], + laddr); + + if (cond_irelfn != irelend) + break; + } + if (cond_irelfn != irelend) + { + cond_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), reloc); + cond_irelfn->r_addend = hi_irelfn->r_addend; + } + } + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_LOADSTORE) + { + int eliminate_sethi = 0, ls_range_type; + enum elf_nds32_reloc_type checked_types[] = + { R_NDS32_HI20_RELA, R_NDS32_GOT_HI20, + R_NDS32_GOTPC_HI20, R_NDS32_GOTOFF_HI20, + R_NDS32_PLTREL_HI20, R_NDS32_PLT_GOTREL_HI20 + }; + + insn_len = seq_len; + + for (i = 0; i < 6; i++) + { + hi_irelfn = find_relocs_at_address_addr (irel, internal_relocs, irelend, + checked_types[i], laddr); + if (hi_irelfn != irelend) + break; + } + + if (hi_irelfn == irelend) + { + (*_bfd_error_handler) + ("%B: warning: R_NDS32_LOADSTORE points to unrecognized reloc at 0x%lx.", + abfd, (long) irel->r_offset); + continue; + } + + ls_range_type = (irel->r_addend >> 8) & 0x3f; + + nds32_elf_final_sda_base (sec->output_section->owner, link_info, + &local_sda, FALSE); + switch (ELF32_R_TYPE (hi_irelfn->r_info)) + { + case R_NDS32_HI20_RELA: + insn = bfd_getb32 (contents + laddr); + access_addr = + calculate_memory_address (abfd, hi_irelfn, isymbuf, + symtab_hdr); + + if ((ls_range_type & 0x3f) == 0x20) + { + if ((access_addr < 0x7f000)) + { + eliminate_sethi = 1; + break; + } + else + { + /* This is avoid to relax symbol address which is fixed + relocations. Ex: _stack. */ + struct elf_link_hash_entry *h; + int indx; + indx = ELF32_R_SYM (hi_irelfn->r_info) - symtab_hdr->sh_info; + if (indx >= 0) + { + h = elf_sym_hashes (abfd)[indx]; + if (h && bfd_is_abs_section (h->root.u.def.section)) + break; + } + } + } + + if (!load_store_relax) + continue; + + if (((insn >> 20) & 0x1f) == REG_GP) + break; + + if (ls_range_type & 0x8 || ls_range_type & 0x10) + { + range_l = sdata_range[0][0]; + range_h = sdata_range[0][1]; + } + else + { + range_l = sdata_range[4][0]; + range_h = sdata_range[4][1]; + } + break; + + case R_NDS32_GOT_HI20: + access_addr = + calculate_got_memory_address (abfd, link_info, hi_irelfn, + symtab_hdr); + + /* If this symbol is not in .got, the return value will be -1. + Since the gp value is set to SDA_BASE but not GLOBAL_OFFSET_TABLE, + a negative offset is allowed. */ + if ((bfd_signed_vma) (access_addr - local_sda) < 0x7f000 + && (bfd_signed_vma) (access_addr - local_sda) >= -0x7f000) + eliminate_sethi = 1; + break; + + case R_NDS32_PLT_GOTREL_HI20: + access_addr = + calculate_plt_memory_address (abfd, link_info, isymbuf, + hi_irelfn, symtab_hdr); + + if ((bfd_signed_vma) (access_addr - local_sda) < 0x7f000 + && (bfd_signed_vma) (access_addr - local_sda) >= -0x7f000) + eliminate_sethi = 1; + break; + + case R_NDS32_GOTOFF_HI20: + access_addr = + calculate_memory_address (abfd, hi_irelfn, isymbuf, + symtab_hdr); + + if ((bfd_signed_vma) (access_addr - local_sda) < 0x7f000 + && (bfd_signed_vma) (access_addr - local_sda) >= -0x7f000) + eliminate_sethi = 1; + break; + + case R_NDS32_GOTPC_HI20: + for (i1_irelfn = irel; + i1_irelfn->r_offset <= irel->r_offset + 4 + && i1_irelfn < irelend; i1_irelfn++) + if (ELF32_R_TYPE (i1_irelfn->r_info) == R_NDS32_GOTPC_LO12) + break; + if (i1_irelfn == irelend + || i1_irelfn->r_offset != irel->r_offset + 4) + continue; + + access_addr = sec->output_section->vma + sec->output_offset + + irel->r_offset; + if ((bfd_signed_vma) (local_sda - access_addr) < 0x7f000 + && (bfd_signed_vma) (local_sda - access_addr) >= -0x7f000) + { + /* Turn into MOVI. */ + insn = bfd_getb32 (contents + laddr + 4); + if (((insn & 0x1f00000) >> 20) != REG_GP) + continue; + + hi_irelfn->r_addend = ((int) hi_irelfn->r_addend) < -4 + ? (hi_irelfn->r_addend + 4) : (hi_irelfn->r_addend); + hi_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), + R_NDS32_GOTPC20); + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE); + i1_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (i1_irelfn->r_info), + R_NDS32_NONE); + insn = N32_TYPE1 (MOVI, N32_RT5 (insn), 0); + bfd_putb32 (insn, contents + laddr); + insn_len = 4; + seq_len = 8; + } + break; + + default: + continue; + } + if (eliminate_sethi == 1 + || (local_sda <= access_addr && (access_addr - local_sda) < range_h) + || (local_sda > access_addr && (local_sda - access_addr) <= range_l)) + { + hi_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), R_NDS32_NONE); + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE); + insn_len = 0; + } + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_17IFC_PCREL_RELA) + { + foff = calculate_offset (abfd, sec, irel, isymbuf, symtab_hdr, + &pic_ext_target); + if (pic_ext_target || foff == 0) + continue; + if (foff < 1022 && foff >= 0) + { + reloc = R_NDS32_10IFCU_PCREL_RELA; + insn16 = INSN_IFCALL9; + bfd_putb16 (insn16, contents + irel->r_offset); + insn_len = 2; + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_10IFCU_PCREL_RELA); + *again = TRUE; + + i2_irelfn = find_relocs_at_address (irel, internal_relocs, + irelend, R_NDS32_INSN16); + if (i2_irelfn < irelend) + { + insn16 = NDS32_NOP16; + bfd_putb16 (insn16, contents + irel->r_offset + 2); + i2_irelfn->r_addend = R_NDS32_INSN16_CONVERT_FLAG; + i2_irelfn->r_offset += 2; + insn_len += 2; + } + else + { + ((*_bfd_error_handler) + ("%s: 0x%lx: warning: R_NDS32_17IFC points to unrecognized reloc at 0x%lx", + bfd_get_filename (abfd), (long) irel->r_offset)); + } + } + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_LO12S0_RELA + || ELF32_R_TYPE (irel->r_info) == R_NDS32_LO12S1_RELA + || ELF32_R_TYPE (irel->r_info) == R_NDS32_LO12S2_DP_RELA + || ELF32_R_TYPE (irel->r_info) == R_NDS32_LO12S2_SP_RELA + || ELF32_R_TYPE (irel->r_info) == R_NDS32_LO12S2_RELA) + { + nds32_elf_final_sda_base (sec->output_section->owner, link_info, + &local_sda, FALSE); + + insn = bfd_getb32 (contents + laddr); + + if (!is_sda_access_insn (insn) + && N32_OP6 (insn) != N32_OP6_ORI) + continue; + + access_addr = + calculate_memory_address (abfd, irel, isymbuf, symtab_hdr); + insn_len = seq_len = 4; + + /* This is avoid to relax symbol address which is fixed + relocations. Ex: _stack. */ + if (N32_OP6 (insn) == N32_OP6_ORI && access_addr >= 0x7f000) + { + struct elf_link_hash_entry *h; + int indx; + indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info; + if (indx >= 0) + { + h = elf_sym_hashes (abfd)[indx]; + if (h && bfd_is_abs_section (h->root.u.def.section)) + continue; + } + } + + if (N32_OP6 (insn) == N32_OP6_ORI && access_addr < 0x7f000) + { + reloc = R_NDS32_20_RELA; + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), reloc); + insn = N32_TYPE1 (MOVI, N32_RT5 (insn), 0); + bfd_putb32 (insn, contents + laddr); + } + else if (load_store_relax) + { + range_l = sdata_range[4][0]; + range_h = sdata_range[4][1]; + switch (ELF32_R_TYPE (irel->r_info)) + { + case R_NDS32_LO12S0_RELA: + reloc = R_NDS32_SDA19S0_RELA; + break; + case R_NDS32_LO12S1_RELA: + reloc = R_NDS32_SDA18S1_RELA; + break; + case R_NDS32_LO12S2_RELA: + reloc = R_NDS32_SDA17S2_RELA; + break; + case R_NDS32_LO12S2_DP_RELA: + range_l = sdata_range[0][0]; + range_h = sdata_range[0][1]; + reloc = R_NDS32_SDA12S2_DP_RELA; + break; + case R_NDS32_LO12S2_SP_RELA: + range_l = sdata_range[0][0]; + range_h = sdata_range[0][1]; + reloc = R_NDS32_SDA12S2_SP_RELA; + break; + default: + break; + } + + /* There are range_h and range_l because linker has to promise + all sections move cross one page together. */ + if ((local_sda <= access_addr && (access_addr - local_sda) < range_h) + || (local_sda > access_addr && (local_sda - access_addr) <= range_l)) + { + if (N32_OP6 (insn) == N32_OP6_ORI && N32_RT5 (insn) == REG_GP) + { + /* Maybe we should add R_NDS32_INSN16 reloc type here + or manually do some optimization. sethi can't be + eliminated when updating $gp so the relative ori + needs to be preserved. */ + continue; + } + if (!turn_insn_to_sda_access (insn, ELF32_R_TYPE (irel->r_info), + &insn)) + continue; + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), reloc); + bfd_putb32 (insn, contents + laddr); + } + } + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_GOT_LO12 + || ELF32_R_TYPE (irel->r_info) == R_NDS32_GOTOFF_LO12 + || ELF32_R_TYPE (irel->r_info) == R_NDS32_PLTREL_LO12 + || ELF32_R_TYPE (irel->r_info) == R_NDS32_PLT_GOTREL_LO12) + { + nds32_elf_final_sda_base (sec->output_section->owner, link_info, + &local_sda, FALSE); + + insn = bfd_getb32 (contents + laddr); + + if (N32_OP6 (insn) != N32_OP6_ORI) + continue; + + insn_len = seq_len = 4; + if (ELF32_R_TYPE (irel->r_info) == R_NDS32_GOT_LO12) + { + foff = calculate_got_memory_address (abfd, link_info, irel, + symtab_hdr) - local_sda; + reloc = R_NDS32_GOT20; + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_PLT_GOTREL_LO12) + { + foff = calculate_plt_memory_address (abfd, link_info, isymbuf, irel, + symtab_hdr) - local_sda; + reloc = R_NDS32_PLT_GOTREL_LO20; + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_GOTOFF_LO12) + { + foff = calculate_memory_address (abfd, irel, isymbuf, + symtab_hdr) - local_sda; + reloc = R_NDS32_GOTOFF; + } + else + continue; + + if ((foff < 0x7f000) && (foff >= -0x7f000)) + { + /* Turn into MOVI. */ + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), reloc); + insn = N32_TYPE1 (MOVI, N32_RT5 (insn), 0); + bfd_putb32 (insn, contents + laddr); + } + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_PTR) + { + i1_irelfn = + find_relocs_at_address_addr (irel, internal_relocs, irelend, + R_NDS32_PTR_RESOLVED, irel->r_addend); + + if (i1_irelfn == irelend) + { + (*_bfd_error_handler) + ("%B: warning: R_NDS32_PTR points to unrecognized reloc at 0x%lx.", + abfd, (long) irel->r_offset); + continue; + } + + if (i1_irelfn->r_addend & 1) + { + /* Pointed target is relaxed and no longer needs this void *, + change the type to NONE. */ + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE); + + i1_irelfn = + find_relocs_at_address (irel, internal_relocs, irelend, + R_NDS32_PTR_COUNT); + + if (i1_irelfn == irelend) + { + (*_bfd_error_handler) + ("%B: warning: no R_NDS32_PTR_COUNT coexist with R_NDS32_PTR at 0x%lx.", + abfd, (long) irel->r_offset); + continue; + } + + if (--i1_irelfn->r_addend > 0) + continue; + + /* If the PTR_COUNT is already 0, remove current instruction. */ + seq_len = nds32_elf_insn_size (abfd, contents, irel->r_offset); + insn_len = 0; + } + else + continue; + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_PLT_GOT_SUFF) + { + /* FIXME: It's a little trouble to turn JRAL5 to JAL since + we need additional space. It might be help if we could + borrow some space from instructions to be eliminated + such as sethi, ori, add. */ + + insn = bfd_getb32 (contents + laddr); + if (insn & 0x80000000) + continue; + + if (nds32_elf_check_dup_relocs + (irel, internal_relocs, irelend, R_NDS32_PLT_GOT_SUFF)) + continue; + + seq_len = insn_len = 4; + i1_irelfn = + find_relocs_at_address (irel, internal_relocs, irelend, + R_NDS32_PTR_RESOLVED); + + /* FIXIT 090606 + The boundary should be reduced since the .plt section hasn't + been created and the address of specific entry is still unknown + Maybe the range between the function call and the begin of the + .text section can be used to decide if the .plt is in the range + of function call. */ + + if (N32_OP6 (insn) == N32_OP6_ALU1 + && N32_SUB5 (insn) == N32_ALU1_ADD_SLLI + && N32_SH5 (insn) == 0) + { + /* Get the value of the symbol referred to by the reloc. */ + nds32_elf_final_sda_base (sec->output_section->owner, link_info, + &local_sda, FALSE); + foff = (bfd_signed_vma) (calculate_plt_memory_address + (abfd, link_info, isymbuf, irel, + symtab_hdr) - local_sda); + /* This condition only happened when symbol is undefined. */ + if (foff == 0) + continue; + + if (foff < -0x3f000 || foff >= 0x3f000) + continue; + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + R_NDS32_PLT_GOTREL_LO19); + /* addi.gp */ + insn = N32_TYPE1 (SBGP, N32_RT5 (insn), __BIT (19)); + } + else if (N32_OP6 (insn) == N32_OP6_JREG + && N32_SUB5 (insn) == N32_JREG_JRAL) + { + /* Get the value of the symbol referred to by the reloc. */ + foff = + calculate_plt_offset (abfd, sec, link_info, isymbuf, irel, + symtab_hdr); + /* This condition only happened when symbol is undefined. */ + if (foff == 0) + continue; + if (foff < -0x1000000 || foff >= 0x1000000) + continue; + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_25_PLTREL); + insn = INSN_JAL; + } + else + continue; + + bfd_putb32 (insn, contents + laddr); + if (i1_irelfn != irelend) + { + i1_irelfn->r_addend |= 1; + *again = TRUE; + } + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_GOT_SUFF) + { + + insn = bfd_getb32 (contents + laddr); + if (insn & 0x80000000) + continue; + + if (nds32_elf_check_dup_relocs + (irel, internal_relocs, irelend, R_NDS32_GOT_SUFF)) + continue; + + seq_len = insn_len = 4; + i1_irelfn = find_relocs_at_address (irel, internal_relocs, irelend, + R_NDS32_PTR_RESOLVED); + + foff = calculate_got_memory_address (abfd, link_info, irel, + symtab_hdr) - local_sda; + + if (foff < 0x3f000 && foff >= -0x3f000) + { + /* Turn LW to LWI.GP. Change relocation type to R_NDS32_GOT_REL. */ + insn = N32_TYPE1 (HWGP, N32_RT5 (insn), __MF (6, 17, 3)); + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_GOT17S2_RELA); + } + else + continue; + + bfd_putb32 (insn, contents + laddr); + if (i1_irelfn != irelend) + { + i1_irelfn->r_addend |= 1; + *again = TRUE; + } + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_GOTOFF_SUFF) + { + int opc_insn_gotoff; + + insn = bfd_getb32 (contents + laddr); + if (insn & 0x80000000) + continue; + + if (nds32_elf_check_dup_relocs + (irel, internal_relocs, irelend, R_NDS32_GOTOFF_SUFF)) + continue; + + seq_len = insn_len = 4; + + i1_irelfn = find_relocs_at_address (irel, internal_relocs, irelend, + R_NDS32_PTR_RESOLVED); + nds32_elf_final_sda_base (sec->output_section->owner, link_info, + &local_sda, FALSE); + access_addr = calculate_memory_address (abfd, irel, isymbuf, symtab_hdr); + foff = access_addr - local_sda; + + if (foff >= 0x3f000 || foff < -0x3f000) + continue; + + /* Concatenate opcode and sub-opcode for switch case. + It may be MEM or ALU1. */ + opc_insn_gotoff = (N32_OP6 (insn) << 8) | (insn & 0xff); + switch (opc_insn_gotoff) + { + case (N32_OP6_MEM << 8) | N32_MEM_LW: + /* 4-byte aligned. */ + insn = N32_TYPE1 (HWGP, N32_RT5 (insn), __MF (6, 17, 3)); + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_SDA17S2_RELA); + break; + case (N32_OP6_MEM << 8) | N32_MEM_SW: + insn = N32_TYPE1 (HWGP, N32_RT5 (insn), __MF (7, 17, 3)); + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_SDA17S2_RELA); + break; + case (N32_OP6_MEM << 8) | N32_MEM_LH: + /* 2-byte aligned. */ + insn = N32_TYPE1 (HWGP, N32_RT5 (insn), 0); + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_SDA18S1_RELA); + break; + case (N32_OP6_MEM << 8) | N32_MEM_LHS: + insn = N32_TYPE1 (HWGP, N32_RT5 (insn), __BIT (18)); + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_SDA18S1_RELA); + break; + case (N32_OP6_MEM << 8) | N32_MEM_SH: + insn = N32_TYPE1 (HWGP, N32_RT5 (insn), __BIT (19)); + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_SDA18S1_RELA); + break; + case (N32_OP6_MEM << 8) | N32_MEM_LB: + /* 1-byte aligned. */ + insn = N32_TYPE1 (LBGP, N32_RT5 (insn), 0); + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_SDA19S0_RELA); + break; + case (N32_OP6_MEM << 8) | N32_MEM_LBS: + insn = N32_TYPE1 (LBGP, N32_RT5 (insn), __BIT (19)); + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_SDA19S0_RELA); + break; + case (N32_OP6_MEM << 8) | N32_MEM_SB: + insn = N32_TYPE1 (SBGP, N32_RT5 (insn), 0); + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_SDA19S0_RELA); + break; + case (N32_OP6_ALU1 << 8) | N32_ALU1_ADD_SLLI: + if (N32_SH5 (insn) != 0) + continue; + insn = N32_TYPE1 (SBGP, N32_RT5 (insn), __BIT (19)); + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_SDA19S0_RELA); + break; + default: + continue; + } + + bfd_putb32 (insn, contents + laddr); + if (i1_irelfn != irelend) + { + i1_irelfn->r_addend |= 1; + *again = TRUE; + } + if ((i2_irelfn = + find_relocs_at_address (irel, internal_relocs, irelend, + R_NDS32_INSN16)) != irelend) + i2_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE); + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_MULCALL_SUFF) + { + /* The last bit of r_addend indicates its a two instruction block. */ + i1_irelfn = find_relocs_at_address (irel, internal_relocs, irelend, + R_NDS32_PTR_RESOLVED); + if ((i1_irelfn != irelend && (i1_irelfn->r_addend & 1)) + || (nds32_elf_insn_size (abfd, contents, irel->r_offset) != 4 + && !(i1_irelfn != irelend && (i1_irelfn->r_addend & 2)))) + continue; + + /* Get the value of the symbol referred to by the reloc. */ + foff = calculate_offset (abfd, sec, irel, isymbuf, symtab_hdr, + &pic_ext_target); + + /* This condition only happened when symbol is undefined. */ + if (pic_ext_target || foff == 0) + continue; + if (foff < -0x1000000 || foff >= 0x1000000) + continue; + + if (i1_irelfn != irelend && (i1_irelfn->r_addend & 2)) + { + seq_len = nds32_elf_insn_size (abfd, contents, irel->r_offset); + seq_len += nds32_elf_insn_size (abfd, contents, + irel->r_offset + seq_len); + } + else + seq_len = 4; + insn_len = 4; + + insn = INSN_JAL; + bfd_putb32 (insn, contents + laddr); + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_25_PCREL_RELA); + + if (i1_irelfn != irelend) + { + i1_irelfn->r_addend |= 1; + *again = TRUE; + } + while (i1_irelfn != irelend + && irel->r_offset == i1_irelfn->r_offset) + i1_irelfn++; + for (; + i1_irelfn != irelend + && i1_irelfn->r_offset < irel->r_offset + 4; i1_irelfn++) + i1_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (i1_irelfn->r_info), R_NDS32_NONE); + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_PLTBLOCK) + { + i1_irelfn = find_relocs_at_address (irel, internal_relocs, irelend, + R_NDS32_PLT_GOTREL_HI20); + + if (i1_irelfn == irelend) + { + (*_bfd_error_handler) + ("%B: warning: R_NDS32_PLTBLOCK points to unrecognized reloc at 0x%lx.", + abfd, (long) irel->r_offset); + continue; + } + + nds32_elf_final_sda_base (sec->output_section->owner, link_info, + &local_sda, FALSE); + foff = + calculate_plt_offset (abfd, sec, link_info, isymbuf, hi_irelfn, + symtab_hdr); + + if (foff < -0x1000000 || foff >= 0x1000000) + { + foff = (bfd_signed_vma) (calculate_plt_memory_address + (abfd, link_info, isymbuf, hi_irelfn, + symtab_hdr) - local_sda); + if (foff >= -0x4000 && foff < 0x4000) + { + /* addi $rt, $gp, lo15(Sym - SDA_BASE) + jral $rt */ + + /* TODO: We can use add.gp here, once ISA V1 is obsolete. */ + insn = N32_TYPE2 (ADDI, N32_RT5 (insn), REG_GP, 0); + bfd_putb32 (insn, contents + irel->r_offset + 8); + + i1_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), + R_NDS32_PLT_GOTREL_LO15); + i1_irelfn->r_addend = hi_irelfn->r_addend; + + seq_len = 8; + } + else if (foff >= -0x80000 && foff < 0x80000) + { + /* movi $rt, lo20(Sym - SDA_BASE) PLT_GOTREL_LO20 + add $rt, $gp, $rt INSN16 + jral $rt INSN16 */ + + for (i1_irelfn = irel; + i1_irelfn->r_offset < irel->r_offset + 4; i1_irelfn++) + ; + for (; i1_irelfn->r_offset < irel->r_offset + 8; i1_irelfn++) + if (ELF32_R_TYPE (i1_irelfn->r_info) != R_NDS32_PLT_GOTREL_LO12) + i2_irelfn = i1_irelfn; + else if (ELF32_R_TYPE (i1_irelfn->r_info) != R_NDS32_LABEL) + i1_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (i1_irelfn->r_info), + R_NDS32_NONE); + if (i2_irelfn) + { + insn = N32_TYPE1 (MOVI, N32_RT5 (insn), 0); + bfd_putb32 (insn, contents + irel->r_offset + 4); + i2_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (i2_irelfn->r_info), + R_NDS32_PLT_GOTREL_LO20); + } + seq_len = 4; + } + else + continue; + + } + else + { + /* jal Sym INSN16/25_PLTREL */ + for (i1_irelfn = irel; + i1_irelfn->r_offset < irel->r_offset + 12; i1_irelfn++) + ; + + i2_irelfn = i1_irelfn - 1; + i2_irelfn->r_offset = i1_irelfn->r_offset; + i2_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), + R_NDS32_25_PLTREL); + i2_irelfn->r_addend = hi_irelfn->r_addend; + insn = INSN_JAL; + bfd_putb32 (insn, contents + irel->r_offset + 12); + seq_len = 12; + } + + insn_len = 0; + } + else + continue; + + if (seq_len - insn_len > 0) + { + if (!insert_nds32_elf_blank + (&relax_blank_list, irel->r_offset + insn_len, + seq_len - insn_len)) + goto error_return; + *again = TRUE; + } + } + + calc_nds32_blank_total (relax_blank_list); + + if (table->relax_fp_as_gp) + { + if (!nds32_relax_fp_as_gp (link_info, abfd, sec, internal_relocs, + irelend, isymbuf)) + goto error_return; + + if (*again == FALSE) + { + if (!nds32_fag_remove_unused_fpbase (abfd, sec, internal_relocs, + irelend)) + goto error_return; + } + } + + if (*again == FALSE) + { + /* This code block is used to adjust 4-byte alignment by relax a pair + of instruction a time. + + It recognizes three types of relocations. + 1. R_NDS32_LABEL - a aligment. + 2. R_NDS32_INSN16 - relax a 32-bit instruction to 16-bit. + 3. is_16bit_NOP () - remove a 16-bit instruction. + + FIXME: It seems currently implementation only support 4-byte aligment. + We should handle any-aligment. */ + + Elf_Internal_Rela *insn_rel = NULL; + Elf_Internal_Rela *label_rel = NULL; + Elf_Internal_Rela *tmp_rel, tmp2_rel, *tmp3_rel = NULL; + + /* Checking for branch relaxation relies on the relocations to + be sorted on 'r_offset'. This is not guaranteed so we must sort. */ + nds32_insertion_sort (internal_relocs, sec->reloc_count, + sizeof (Elf_Internal_Rela), compar_reloc); + + nds32_elf_final_sda_base (sec->output_section->owner, link_info, + &local_sda, FALSE); + + /* Force R_NDS32_LABEL before R_NDS32_INSN16. */ + /* FIXME: Can we generate the right order in assembler? + So we don't have to swapping them here. */ + for (label_rel = internal_relocs, insn_rel = internal_relocs; + label_rel < irelend; label_rel++) + { + if (ELF32_R_TYPE (label_rel->r_info) != R_NDS32_LABEL) + continue; + + /* Find the first reloc has the same offset with label_rel. */ + while (insn_rel < irelend && insn_rel->r_offset < label_rel->r_offset) + insn_rel++; + + for (; + insn_rel < irelend && insn_rel->r_offset == label_rel->r_offset; + insn_rel++) + /* Check if there were R_NDS32_INSN16 and R_NDS32_LABEL at the same + address. */ + if (ELF32_R_TYPE (insn_rel->r_info) == R_NDS32_INSN16) + break; + + if (insn_rel < irelend && insn_rel->r_offset == label_rel->r_offset + && insn_rel < label_rel) + { + /* Swap the two reloc if the R_NDS32_INSN16 is before R_NDS32_LABEL. */ + memcpy (&tmp2_rel, insn_rel, sizeof (Elf_Internal_Rela)); + memcpy (insn_rel, label_rel, sizeof (Elf_Internal_Rela)); + memcpy (label_rel, &tmp2_rel, sizeof (Elf_Internal_Rela)); + } + } + label_rel = NULL; + insn_rel = NULL; + + /* If there were a sequence of R_NDS32_LABEL end up with .align 2 or higher, + remove other R_NDS32_LABEL with lower alignment. + If an R_NDS32_INSN16 in between R_NDS32_LABELs must be converted, + then the R_NDS32_LABEL sequence is broke. */ + for (tmp_rel = internal_relocs; tmp_rel < irelend; tmp_rel++) + { + if (ELF32_R_TYPE (tmp_rel->r_info) == R_NDS32_LABEL) + { + if (label_rel == NULL) + { + if (tmp_rel->r_addend < 2) + label_rel = tmp_rel; + continue; + } + else if (tmp_rel->r_addend > 1) + { + for (tmp3_rel = label_rel; tmp3_rel < tmp_rel; tmp3_rel++) + { + if (ELF32_R_TYPE (tmp3_rel->r_info) == R_NDS32_LABEL + && tmp3_rel->r_addend < 2) + tmp3_rel->r_info = ELF32_R_INFO (ELF32_R_SYM (tmp3_rel->r_info), R_NDS32_NONE); + } + label_rel = NULL; + } + } + else if (ELF32_R_TYPE (tmp_rel->r_info) == R_NDS32_INSN16) + { + if (label_rel + && label_rel->r_offset != tmp_rel->r_offset + && (is_convert_32_to_16 (abfd, sec, tmp_rel, internal_relocs, + irelend, &insn16) + || is_16bit_NOP (abfd, sec, tmp_rel))) + { + label_rel = NULL; + } + } + } + label_rel = NULL; + insn_rel = NULL; + + /* Optimized for speed and nothing has not been relaxed. + It's time to align labels. + We may convert a 16-bit instruction right before a label to + 32-bit, in order to align the label if necessary + all reloc entries has been sorted by r_offset. */ + for (irel = internal_relocs; irel < irelend; irel++) + { + if (ELF32_R_TYPE (irel->r_info) != R_NDS32_INSN16 + && ELF32_R_TYPE (irel->r_info) != R_NDS32_LABEL) + continue; + + /* Search for INSN16 reloc. */ + if (ELF32_R_TYPE (irel->r_info) == R_NDS32_INSN16) + { + if (label_rel) + { + /* Previous LABEL reloc exists. Try to resolve it. */ + if (label_rel->r_offset == irel->r_offset) + { + /* LABEL and INSN are at the same addr. */ + if ((irel->r_offset + - get_nds32_elf_blank_total (&relax_blank_list, + irel->r_offset, + 1)) & 0x02) + { + if (irel->r_addend > 1) + { + /* Force to relax. */ + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + R_NDS32_NONE); + if (is_convert_32_to_16 + (abfd, sec, irel, internal_relocs, irelend, + &insn16)) + { + nds32_elf_write_16 (abfd, contents, irel, + internal_relocs, irelend, + insn16); + + if (!insert_nds32_elf_blank_recalc_total + (&relax_blank_list, irel->r_offset + 2, + 2)) + goto error_return; + } + else if (is_16bit_NOP (abfd, sec, irel)) + { + if (!insert_nds32_elf_blank_recalc_total + (&relax_blank_list, irel->r_offset, 2)) + goto error_return; + } + } + else + { + if (is_convert_32_to_16 + (abfd, sec, irel, internal_relocs, irelend, + &insn16) + || is_16bit_NOP (abfd, sec, irel)) + insn_rel = irel; + } + label_rel = NULL; + continue; + } + else + { + /* Already aligned, reset LABEL and keep INSN16. */ + } + } + else + { + /* No INSN16 to relax, we don't want to insert 16-bit. */ + /* Nop here, just signal the algorithm is wrong. */ + } + label_rel = NULL; + } + /* A new INSN16 found, resize the old one. */ + else if (insn_rel) + { + if (!is_convert_32_to_16 + (abfd, sec, irel, internal_relocs, irelend, + &insn16) + && !is_16bit_NOP (abfd, sec, irel)) + { + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + R_NDS32_NONE); + continue; + } + /* Previous INSN16 reloc exists, reduce its size to 16-bit. */ + if (is_convert_32_to_16 + (abfd, sec, insn_rel, internal_relocs, irelend, + &insn16)) + { + nds32_elf_write_16 (abfd, contents, insn_rel, + internal_relocs, irelend, insn16); + + if (!insert_nds32_elf_blank_recalc_total + (&relax_blank_list, insn_rel->r_offset + 2, 2)) + goto error_return; + } + else if (is_16bit_NOP (abfd, sec, insn_rel)) + { + if (!insert_nds32_elf_blank_recalc_total + (&relax_blank_list, insn_rel->r_offset, 2)) + goto error_return; + } + insn_rel->r_info = + ELF32_R_INFO (ELF32_R_SYM (insn_rel->r_info), + R_NDS32_NONE); + insn_rel = NULL; + } + + if (is_convert_32_to_16 + (abfd, sec, irel, internal_relocs, irelend, &insn16) + || is_16bit_NOP (abfd, sec, irel)) + { + insn_rel = irel; + } + /* Save the new one for later use. */ + } + /* Search for label. */ + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_LABEL) + { + /* Label on 16-bit instruction, just reset this reloc. */ + insn16 = bfd_getb16 (contents + irel->r_offset); + if ((irel->r_addend & 0x1f) < 2 && (insn16 & 0x8000)) + { + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE); + continue; + } + + if (!optimize && (irel->r_addend & 0x1f) < 2) + { + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE); + continue; + } + + /* Try to align this label. */ + if (insn_rel) + { + int force_relax = 0; + + /* If current location is .align 2, we can't relax previous 32-bit inst. */ + /* Or the alignment constraint is broke. */ + if ((irel->r_addend & 0x1f) < 2) + { + /* Label_rel always seats before insn_rel after our sort. */ + + /* INSN16 and LABEL at different location. */ + /* Search for INSN16 at LABEL location. */ + for (tmp_rel = irel; + tmp_rel < irelend && tmp_rel->r_offset == irel->r_offset; + tmp_rel++) + { + if (ELF32_R_TYPE (tmp_rel->r_info) == R_NDS32_INSN16) + break; + } + + if (tmp_rel < irelend + && tmp_rel->r_offset == irel->r_offset) + { + if (ELF32_R_TYPE (tmp_rel->r_info) == R_NDS32_INSN16) + { + if (is_convert_32_to_16 + (abfd, sec, tmp_rel, internal_relocs, + irelend, &insn16) + || is_16bit_NOP (abfd, sec, tmp_rel)) + force_relax = 1; + } + } + } + + if ((irel->r_offset + - get_nds32_elf_blank_total (&relax_blank_list, + irel->r_offset, 1)) & 0x01) + { + /* Can't align on byte, BIG ERROR. */ + } + else + { + if (force_relax + || ((irel->r_offset + - get_nds32_elf_blank_total + (&relax_blank_list, irel->r_offset, 1)) + & 0x02) + || irel->r_addend == 1) + { + if (insn_rel != NULL) + { + /* Label not aligned. */ + /* Previous reloc exists, reduce its size to 16-bit. */ + if (is_convert_32_to_16 + (abfd, sec, insn_rel, internal_relocs, + irelend, &insn16)) + { + nds32_elf_write_16 (abfd, contents, insn_rel, + internal_relocs, irelend, + insn16); + + if (!insert_nds32_elf_blank_recalc_total + (&relax_blank_list, + insn_rel->r_offset + 2, 2)) + goto error_return; + } + else if (is_16bit_NOP (abfd, sec, insn_rel)) + { + if (!insert_nds32_elf_blank_recalc_total + (&relax_blank_list, insn_rel->r_offset, + 2)) + goto error_return; + } + else + { + goto error_return; + } + } + } + + if (force_relax) + { + label_rel = irel; + } + + /* INSN16 reloc is used. */ + insn_rel = NULL; + } + } + } + } + + if (insn_rel) + { + if (((sec->size - get_nds32_elf_blank_total (&relax_blank_list, sec->size, 0)) + - ((sec->size - get_nds32_elf_blank_total (&relax_blank_list, sec->size, 0)) + & (0xffffffff << sec->alignment_power)) == 2) + || optimize_for_space) + { + if (is_convert_32_to_16 + (abfd, sec, insn_rel, internal_relocs, irelend, + &insn16)) + { + nds32_elf_write_16 (abfd, contents, insn_rel, internal_relocs, + irelend, insn16); + if (!insert_nds32_elf_blank_recalc_total + (&relax_blank_list, insn_rel->r_offset + 2, 2)) + goto error_return; + insn_rel->r_info = ELF32_R_INFO (ELF32_R_SYM (insn_rel->r_info), + R_NDS32_NONE); + } + else if (is_16bit_NOP (abfd, sec, insn_rel)) + { + if (!insert_nds32_elf_blank_recalc_total + (&relax_blank_list, insn_rel->r_offset, 2)) + goto error_return; + insn_rel->r_info = ELF32_R_INFO (ELF32_R_SYM (insn_rel->r_info), + R_NDS32_NONE); + } + } + insn_rel = NULL; + } + } + /* It doesn't matter optimize_for_space_no_align anymore. + If object file is assembled with flag '-Os', + the we don't adjust jump-destination on 4-byte boundary. */ + + if (relax_blank_list) + { + nds32_elf_relax_delete_blanks (abfd, sec, relax_blank_list); + relax_blank_list = NULL; + } + + if (*again == FALSE) + { + /* Closing the section, so we don't relax it anymore. */ + bfd_vma sec_size_align; + Elf_Internal_Rela *tmp_rel; + + /* Pad to alignment boundary. Only handle current section alignment. */ + sec_size_align = (sec->size + (~((-1) << sec->alignment_power))) + & ((-1) << sec->alignment_power); + if ((sec_size_align - sec->size) & 0x2) + { + insn16 = NDS32_NOP16; + bfd_putb16 (insn16, contents + sec->size); + sec->size += 2; + } + + while (sec_size_align != sec->size) + { + insn = NDS32_NOP32; + bfd_putb32 (insn, contents + sec->size); + sec->size += 4; + } + + tmp_rel = find_relocs_at_address (internal_relocs, internal_relocs, irelend, + R_NDS32_RELAX_ENTRY); + if (tmp_rel != irelend) + tmp_rel->r_addend |= R_NDS32_RELAX_ENTRY_DISABLE_RELAX_FLAG; + + clean_nds32_elf_blank (); + } + +finish: + if (internal_relocs != NULL + && elf_section_data (sec)->relocs != internal_relocs) + free (internal_relocs); + + if (contents != NULL + && elf_section_data (sec)->this_hdr.contents != contents) + free (contents); + + if (isymbuf != NULL && symtab_hdr->contents != (bfd_byte *) isymbuf) + free (isymbuf); + + return result; + +error_return: + result = FALSE; + goto finish; +} + +static struct bfd_elf_special_section const nds32_elf_special_sections[] = +{ + {".sdata", 6, -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE}, + {".sbss", 5, -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE}, + {NULL, 0, 0, 0, 0} +}; + +static bfd_boolean +nds32_elf_output_arch_syms (bfd *output_bfd ATTRIBUTE_UNUSED, + struct bfd_link_info *info, + void *finfo ATTRIBUTE_UNUSED, + bfd_boolean (*func) (void *, const char *, + Elf_Internal_Sym *, + asection *, + struct elf_link_hash_entry *) + ATTRIBUTE_UNUSED) +{ + FILE *sym_ld_script = NULL; + struct elf_nds32_link_hash_table *table; + + table = nds32_elf_hash_table (info); + sym_ld_script = table->sym_ld_script; + + if (check_start_export_sym) + fprintf (sym_ld_script, "}\n"); + + return TRUE; +} + +static enum elf_reloc_type_class +nds32_elf_reloc_type_class (const struct bfd_link_info *info ATTRIBUTE_UNUSED, + const asection *rel_sec ATTRIBUTE_UNUSED, + const Elf_Internal_Rela *rela) +{ + switch ((int) ELF32_R_TYPE (rela->r_info)) + { + case R_NDS32_RELATIVE: + return reloc_class_relative; + case R_NDS32_JMP_SLOT: + return reloc_class_plt; + case R_NDS32_COPY: + return reloc_class_copy; + default: + return reloc_class_normal; + } +} + +/* Put target dependent option into info hash table. */ +void +bfd_elf32_nds32_set_target_option (struct bfd_link_info *link_info, + int relax_fp_as_gp, + int eliminate_gc_relocs, + FILE * sym_ld_script, int load_store_relax, + int target_optimize, int relax_status, + int relax_round, FILE * ex9_export_file, + FILE * ex9_import_file, + int update_ex9_table, int ex9_limit, + bfd_boolean ex9_loop_aware, + bfd_boolean ifc_loop_aware) +{ + struct elf_nds32_link_hash_table *table; + + table = nds32_elf_hash_table (link_info); + if (table == NULL) + return; + + table->relax_fp_as_gp = relax_fp_as_gp; + table->eliminate_gc_relocs = eliminate_gc_relocs; + table->sym_ld_script = sym_ld_script; + table ->load_store_relax = load_store_relax; + table->target_optimize = target_optimize; + table->relax_status = relax_status; + table->relax_round = relax_round; + table->ex9_export_file = ex9_export_file; + table->ex9_import_file = ex9_import_file; + table->update_ex9_table = update_ex9_table; + table->ex9_limit = ex9_limit; + table->ex9_loop_aware = ex9_loop_aware; + table->ifc_loop_aware = ifc_loop_aware; +} + +/* These functions and data-structures are used for fp-as-gp + optimization. */ + +#define FAG_THRESHOLD 3 /* At least 3 gp-access. */ +#define FAG_BUMPER 8 /* Leave some space to avoid aligment issues. */ +#define FAG_WINDOW (512 - FAG_BUMPER) /* lwi37.fp covers 512 bytes. */ + +/* An nds32_fag represent a gp-relative access. + We find best fp-base by using a sliding window + to find a base address which can cover most gp-access. */ +struct nds32_fag +{ + struct nds32_fag *next; /* NULL-teminated linked list. */ + bfd_vma addr; /* The address of this fag. */ + Elf_Internal_Rela **relas; /* The relocations associated with this fag. + It is used for applying FP7U2_FLAG. */ + int count; /* How many times this address is referred. + There should be exactly `count' relocations + in relas. */ + int relas_capcity; /* The buffer size of relas. + We use an array instead of linked-list, + and realloc is used to adjust buffer size. */ +}; + +static void +nds32_fag_init (struct nds32_fag *head) +{ + memset (head, 0, sizeof (struct nds32_fag)); +} + +static void +nds32_fag_verify (struct nds32_fag *head) +{ + struct nds32_fag *iter; + struct nds32_fag *prev; + + prev = NULL; + iter = head->next; + while (iter) + { + if (prev && prev->addr >= iter->addr) + puts ("Bug in fp-as-gp insertion."); + prev = iter; + iter = iter->next; + } +} + +/* Insert a fag in ascending order. + If a fag of the same address already exists, + they are chained by relas array. */ + +static void +nds32_fag_insert (struct nds32_fag *head, bfd_vma addr, + Elf_Internal_Rela * rel) +{ + struct nds32_fag *iter; + struct nds32_fag *new_fag; + const int INIT_RELAS_CAP = 4; + + for (iter = head; + iter->next && iter->next->addr <= addr; + iter = iter->next) + /* Find somewhere to insert. */ ; + + /* `iter' will be equal to `head' if the list is empty. */ + if (iter != head && iter->addr == addr) + { + /* The address exists in the list. + Insert `rel' into relocation list, relas. */ + + /* Check whether relas is big enough. */ + if (iter->count >= iter->relas_capcity) + { + iter->relas_capcity *= 2; + iter->relas = bfd_realloc + (iter->relas, iter->relas_capcity * sizeof (void *)); + } + iter->relas[iter->count++] = rel; + return; + } + + /* This is a new address. Create a fag node for it. */ + new_fag = bfd_malloc (sizeof (struct nds32_fag)); + memset (new_fag, 0, sizeof (*new_fag)); + new_fag->addr = addr; + new_fag->count = 1; + new_fag->next = iter->next; + new_fag->relas_capcity = INIT_RELAS_CAP; + new_fag->relas = (Elf_Internal_Rela **) + bfd_malloc (new_fag->relas_capcity * sizeof (void *)); + new_fag->relas[0] = rel; + iter->next = new_fag; + + nds32_fag_verify (head); +} + +static void +nds32_fag_free_list (struct nds32_fag *head) +{ + struct nds32_fag *iter; + + iter = head->next; + while (iter) + { + struct nds32_fag *tmp = iter; + iter = iter->next; + free (tmp->relas); + tmp->relas = NULL; + free (tmp); + } +} + +static bfd_boolean +nds32_fag_isempty (struct nds32_fag *head) +{ + return head->next == NULL; +} + +/* Find the best fp-base address. + The relocation associated with that address is returned, + so we can track the symbol instead of a fixed address. + + When relaxation, the address of an datum may change, + because a text section is shrinked, so the data section + moves forward. If the aligments of text and data section + are different, their distance may change too. + Therefore, tracking a fixed address is not appriate. */ + +static int +nds32_fag_find_base (struct nds32_fag *head, struct nds32_fag **bestpp) +{ + struct nds32_fag *base; /* First fag in the window. */ + struct nds32_fag *last; /* First fag outside the window. */ + int accu = 0; /* Usage accumulation. */ + struct nds32_fag *best; /* Best fag. */ + int baccu = 0; /* Best accumulation. */ + + /* Use first fag for initial, and find the last fag in the window. + + In each iteration, we could simply subtract previous fag + and accumulate following fags which are inside the window, + untill we each the end. */ + + if (nds32_fag_isempty (head)) + return 0; + + /* Initialize base. */ + base = head->next; + best = base; + for (last = base; + last && last->addr < base->addr + FAG_WINDOW; + last = last->next) + accu += last->count; + + baccu = accu; + + /* Record the best base in each iteration. */ + while (base->next) + { + accu -= base->count; + base = base->next; + /* Account fags in window. */ + for (/* Nothing. */; + last && last->addr < base->addr + FAG_WINDOW; + last = last->next) + accu += last->count; + + /* A better fp-base? */ + if (accu > baccu) + { + best = base; + baccu = accu; + } + } + + if (bestpp) + *bestpp = best; + return baccu; +} + +/* Apply R_NDS32_INSN16_FP7U2_FLAG on gp-relative accesses, + so we can convert it fo fp-relative access later. + `best_fag' is the best fp-base. Only those inside the window + of best_fag is applied the flag. */ + +static bfd_boolean +nds32_fag_mark_relax (struct bfd_link_info *link_info, + bfd *abfd, struct nds32_fag *best_fag, + Elf_Internal_Rela *internal_relocs, + Elf_Internal_Rela *irelend) +{ + struct nds32_fag *ifag; + bfd_vma best_fpbase, gp; + bfd *output_bfd; + + output_bfd = abfd->sections->output_section->owner; + nds32_elf_final_sda_base (output_bfd, link_info, &gp, FALSE); + best_fpbase = best_fag->addr; + + if (best_fpbase > gp + sdata_range[4][1] + || best_fpbase < gp - sdata_range[4][0]) + return FALSE; + + /* Mark these inside the window R_NDS32_INSN16_FP7U2_FLAG flag, + so we know they can be converted to lwi37.fp. */ + for (ifag = best_fag; + ifag && ifag->addr < best_fpbase + FAG_WINDOW; ifag = ifag->next) + { + int i; + + for (i = 0; i < ifag->count; i++) + { + Elf_Internal_Rela *insn16_rel; + Elf_Internal_Rela *fag_rel; + + fag_rel = ifag->relas[i]; + + /* Only if this is within the WINDOWS, FP7U2_FLAG + is applied. */ + + insn16_rel = find_relocs_at_address + (fag_rel, internal_relocs, irelend, R_NDS32_INSN16); + + if (insn16_rel != irelend) + insn16_rel->r_addend = R_NDS32_INSN16_FP7U2_FLAG; + } + } + return TRUE; +} + +/* This is the main function of fp-as-gp optimization. + It should be called by relax_section. */ + +static bfd_boolean +nds32_relax_fp_as_gp (struct bfd_link_info *link_info, + bfd *abfd, asection *sec, + Elf_Internal_Rela *internal_relocs, + Elf_Internal_Rela *irelend, + Elf_Internal_Sym *isymbuf) +{ + Elf_Internal_Rela *begin_rel = NULL; + Elf_Internal_Rela *irel; + struct nds32_fag fag_head; + Elf_Internal_Shdr *symtab_hdr; + bfd_byte *contents; + + /* FIXME: Can we bfd_elf_link_read_relocs for the relocs? */ + + /* Per-function fp-base selection. + 1. Create a list for all the gp-relative access. + 2. Base on those gp-relative address, + find a fp-base which can cover most access. + 3. Use the fp-base for fp-as-gp relaxation. + + NOTE: If fp-as-gp is not worth to do, (e.g., less than 3 times), + we should + 1. delete the `la $fp, _FP_BASE_' instruction and + 2. not convert lwi.gp to lwi37.fp. + + To delete the _FP_BASE_ instruction, we simply apply + R_NDS32_RELAX_REGION_NOT_OMIT_FP_FLAG flag in the r_addend to disable it. + + To suppress the conversion, we simply NOT to apply + R_NDS32_INSN16_FP7U2_FLAG flag. */ + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + + if (!nds32_get_section_contents (abfd, sec, &contents) + || !nds32_get_local_syms (abfd, sec, &isymbuf)) + return FALSE; + + /* Check whether it is worth for fp-as-gp optimization, + i.e., at least 3 gp-load. + + Set R_NDS32_RELAX_REGION_NOT_OMIT_FP_FLAG if we should NOT + apply this optimization. */ + + for (irel = internal_relocs; irel < irelend; irel++) + { + /* We recognize R_NDS32_RELAX_REGION_BEGIN/_END for the region. + One we enter the begin of the region, we track all the LW/ST + instructions, so when we leave the region, we try to find + the best fp-base address for those LW/ST instructions. */ + + if (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_REGION_BEGIN + && (irel->r_addend & R_NDS32_RELAX_REGION_OMIT_FP_FLAG)) + { + /* Begin of the region. */ + if (begin_rel) + (*_bfd_error_handler) (_("%B: Nested OMIT_FP in %A."), abfd, sec); + + begin_rel = irel; + nds32_fag_init (&fag_head); + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_REGION_END + && (irel->r_addend & R_NDS32_RELAX_REGION_OMIT_FP_FLAG)) + { + int accu; + struct nds32_fag *best_fag; + int dist; + + /* End of the region. + Check whether it is worth to do fp-as-gp. */ + + if (begin_rel == NULL) + { + (*_bfd_error_handler) (_("%B: Unmatched OMIT_FP in %A."), abfd, sec); + continue; + } + + accu = nds32_fag_find_base (&fag_head, &best_fag); + + /* Check if it is worth, and FP_BASE is near enough to SDA_BASE. */ + if (accu < FAG_THRESHOLD + || !nds32_fag_mark_relax (link_info, abfd, best_fag, + internal_relocs, irelend)) + { + /* Not worth to do fp-as-gp. */ + begin_rel->r_addend |= R_NDS32_RELAX_REGION_NOT_OMIT_FP_FLAG; + begin_rel->r_addend &= ~R_NDS32_RELAX_REGION_OMIT_FP_FLAG; + irel->r_addend |= R_NDS32_RELAX_REGION_NOT_OMIT_FP_FLAG; + irel->r_addend &= ~R_NDS32_RELAX_REGION_OMIT_FP_FLAG; + nds32_fag_free_list (&fag_head); + begin_rel = NULL; + continue; + } + + /* R_SYM of R_NDS32_RELAX_REGION_BEGIN is not used by assembler, + so we use it to record the distance to the reloction of best + fp-base. */ + dist = best_fag->relas[0] - begin_rel; + BFD_ASSERT (dist > 0 && dist < 0xffffff); + /* Use high 16 bits of addend to record the _FP_BASE_ matched + relocation. And get the base value when relocating. */ + begin_rel->r_addend |= dist << 16; + + nds32_fag_free_list (&fag_head); + begin_rel = NULL; + } + + if (begin_rel == NULL) + /* Skip if we are not in the region of fp-as-gp. */ + continue; + + if (ELF32_R_TYPE (irel->r_info) == R_NDS32_SDA15S2_RELA + || ELF32_R_TYPE (irel->r_info) == R_NDS32_SDA17S2_RELA) + { + bfd_vma addr; + uint32_t insn; + + /* A gp-relative access is found. Insert it to the fag-list. */ + + /* Rt is necessary an RT3, so it can be converted to lwi37.fp. */ + insn = bfd_getb32 (contents + irel->r_offset); + if (!N32_IS_RT3 (insn)) + continue; + + addr = calculate_memory_address (abfd, irel, isymbuf, symtab_hdr); + nds32_fag_insert (&fag_head, addr, irel); + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_SDA_FP7U2_RELA) + { + begin_rel = NULL; + } + } + + return TRUE; +} + +/* Remove unused `la $fp, _FD_BASE_' instruction. */ + +static bfd_boolean +nds32_fag_remove_unused_fpbase (bfd *abfd, asection *sec, + Elf_Internal_Rela *internal_relocs, + Elf_Internal_Rela *irelend) +{ + Elf_Internal_Rela *irel; + Elf_Internal_Shdr *symtab_hdr; + bfd_byte *contents = NULL; + nds32_elf_blank_t *relax_blank_list = NULL; + bfd_boolean result = TRUE; + bfd_boolean unused_region = FALSE; + + /* + NOTE: Disable fp-as-gp if we encounter ifcall relocations. + * R_NDS32_17IFC_PCREL_RELA + * R_NDS32_10IFCU_PCREL_RELA + + CASE?????????????? + */ + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + nds32_get_section_contents (abfd, sec, &contents); + + for (irel = internal_relocs; irel < irelend; irel++) + { + /* To remove unused fp-base, we simply find the REGION_NOT_OMIT_FP + we marked to in previous pass. + DO NOT scan relocations again, since we've alreadly decided it + and set the flag. */ + const char *syname; + int syndx; + uint32_t insn; + + if (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_REGION_BEGIN + && (irel->r_addend & R_NDS32_RELAX_REGION_NOT_OMIT_FP_FLAG)) + unused_region = TRUE; + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_REGION_END + && (irel->r_addend & R_NDS32_RELAX_REGION_NOT_OMIT_FP_FLAG)) + unused_region = FALSE; + + /* We're not in the region. */ + if (!unused_region) + continue; + + /* _FP_BASE_ must be a GLOBAL symbol. */ + syndx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info; + if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info) + continue; + + /* The symbol name must be _FP_BASE_. */ + syname = elf_sym_hashes (abfd)[syndx]->root.root.string; + if (strcmp (syname, FP_BASE_NAME) != 0) + continue; + + if (ELF32_R_TYPE (irel->r_info) == R_NDS32_SDA19S0_RELA) + { + /* addi.gp $fp, -256 */ + insn = bfd_getb32 (contents + irel->r_offset); + if (insn != INSN_ADDIGP_TO_FP) + continue; + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_SDA15S0_RELA) + { + /* addi $fp, $gp, -256 */ + insn = bfd_getb32 (contents + irel->r_offset); + if (insn != INSN_ADDI_GP_TO_FP) + continue; + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_20_RELA) + { + /* movi $fp, FP_BASE */ + insn = bfd_getb32 (contents + irel->r_offset); + if (insn != INSN_MOVI_TO_FP) + continue; + } + else + continue; + + /* We got here because a FP_BASE instruction is found. */ + if (!insert_nds32_elf_blank_recalc_total + (&relax_blank_list, irel->r_offset, 4)) + goto error_return; + } + +finish: + if (relax_blank_list) + { + nds32_elf_relax_delete_blanks (abfd, sec, relax_blank_list); + relax_blank_list = NULL; + } + return result; + +error_return: + result = FALSE; + goto finish; +} + +/* Link-time IFC relaxation. + In this optimization, we chains jump instructions + of the same destination with ifcall. */ + + +/* List to save jal and j relocation. */ +struct elf_nds32_ifc_symbol_entry +{ + asection *sec; + struct elf_link_hash_entry *h; + struct elf_nds32_ifc_irel_list *irel_head; + unsigned long insn; + int times; + int enable; /* Apply ifc. */ + int ex9_enable; /* Apply ifc after ex9. */ + struct elf_nds32_ifc_symbol_entry *next; +}; + +struct elf_nds32_ifc_irel_list +{ + Elf_Internal_Rela *irel; + asection *sec; + bfd_vma addr; + /* If this is set, then it is the last instruction for + ifc-chain, so it must be keep for the actual branching. */ + int keep; + struct elf_nds32_ifc_irel_list *next; +}; + +static struct elf_nds32_ifc_symbol_entry *ifc_symbol_head = NULL; + +/* Insert symbol of jal and j for ifc. */ + +static void +nds32_elf_ifc_insert_symbol (asection *sec, + struct elf_link_hash_entry *h, + Elf_Internal_Rela *irel, + unsigned long insn) +{ + struct elf_nds32_ifc_symbol_entry *ptr = ifc_symbol_head; + + /* Check there is target of existing entry the same as the new one. */ + while (ptr != NULL) + { + if (((h == NULL && ptr->sec == sec + && ELF32_R_SYM (ptr->irel_head->irel->r_info) == ELF32_R_SYM (irel->r_info) + && ptr->irel_head->irel->r_addend == irel->r_addend) + || h != NULL) + && ptr->h == h + && ptr->insn == insn) + { + /* The same target exist, so insert into list. */ + struct elf_nds32_ifc_irel_list *irel_list = ptr->irel_head; + + while (irel_list->next != NULL) + irel_list = irel_list->next; + irel_list->next = bfd_malloc (sizeof (struct elf_nds32_ifc_irel_list)); + irel_list = irel_list->next; + irel_list->irel = irel; + irel_list->keep = 1; + + if (h == NULL) + irel_list->sec = NULL; + else + irel_list->sec = sec; + irel_list->next = NULL; + return; + } + if (ptr->next == NULL) + break; + ptr = ptr->next; + } + + /* There is no same target entry, so build a new one. */ + if (ifc_symbol_head == NULL) + { + ifc_symbol_head = bfd_malloc (sizeof (struct elf_nds32_ifc_symbol_entry)); + ptr = ifc_symbol_head; + } + else + { + ptr->next = bfd_malloc (sizeof (struct elf_nds32_ifc_symbol_entry)); + ptr = ptr->next; + } + + ptr->h = h; + ptr->irel_head = bfd_malloc (sizeof (struct elf_nds32_ifc_irel_list)); + ptr->irel_head->irel = irel; + ptr->insn = insn; + ptr->irel_head->keep = 1; + + if (h == NULL) + { + /* Local symbols. */ + ptr->sec = sec; + ptr->irel_head->sec = NULL; + } + else + { + /* Global symbol. */ + ptr->sec = NULL; + ptr->irel_head->sec = sec; + } + + ptr->irel_head->next = NULL; + ptr->times = 0; + ptr->enable = 0; + ptr->ex9_enable = 0; + ptr->next = NULL; +} + +/* Gather all jal and j instructions. */ + +static bfd_boolean +nds32_elf_ifc_calc (struct bfd_link_info *info, + bfd *abfd, asection *sec) +{ + Elf_Internal_Rela *internal_relocs; + Elf_Internal_Rela *irelend; + Elf_Internal_Rela *irel; + Elf_Internal_Shdr *symtab_hdr; + bfd_byte *contents = NULL; + unsigned long insn, insn_with_reg; + unsigned long r_symndx; + struct elf_link_hash_entry *h; + struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (abfd); + struct elf_nds32_link_hash_table *table; + bfd_boolean ifc_loop_aware; + + internal_relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL, + TRUE /* keep_memory */); + irelend = internal_relocs + sec->reloc_count; + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + + /* Check if the object enable ifc. */ + irel = find_relocs_at_address (internal_relocs, internal_relocs, irelend, + R_NDS32_RELAX_ENTRY); + + if (irel == NULL + || irel >= irelend + || ELF32_R_TYPE (irel->r_info) != R_NDS32_RELAX_ENTRY + || (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_ENTRY + && !(irel->r_addend & R_NDS32_RELAX_ENTRY_IFC_FLAG))) + return TRUE; + + if (!nds32_get_section_contents (abfd, sec, &contents)) + return FALSE; + + table = nds32_elf_hash_table (info); + ifc_loop_aware = table->ifc_loop_aware; + while (irel != NULL && irel < irelend) + { + /* Traverse all relocation and gather all of them to build the list. */ + + if (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_REGION_BEGIN) + { + if (ifc_loop_aware == 1 + && (irel->r_addend & R_NDS32_RELAX_REGION_INNERMOST_LOOP_FLAG) != 0) + { + /* Check the region if loop or not. If it is true and + ifc-loop-aware is true, ignore the region till region end. */ + while (irel != NULL + && irel < irelend + && (ELF32_R_TYPE (irel->r_info) != R_NDS32_RELAX_REGION_END + || (irel->r_addend & R_NDS32_RELAX_REGION_INNERMOST_LOOP_FLAG) != 0)) + irel++; + } + } + + if (ELF32_R_TYPE (irel->r_info) == R_NDS32_25_PCREL_RELA) + { + insn = bfd_getb32 (contents + irel->r_offset); + nds32_elf_get_insn_with_reg (irel, insn, &insn_with_reg); + r_symndx = ELF32_R_SYM (irel->r_info); + if (r_symndx < symtab_hdr->sh_info) + { + /* Local symbol. */ + nds32_elf_ifc_insert_symbol (sec, NULL, irel, insn_with_reg); + } + else + { + /* External symbol. */ + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + nds32_elf_ifc_insert_symbol (sec, h, irel, insn_with_reg); + } + } + irel++; + } + return TRUE; +} + +/* Determine whether j and jal should be substituted. */ + +static void +nds32_elf_ifc_filter (struct bfd_link_info *info) +{ + struct elf_nds32_ifc_symbol_entry *ptr = ifc_symbol_head; + struct elf_nds32_ifc_irel_list *irel_ptr = NULL; + struct elf_nds32_ifc_irel_list *irel_keeper = NULL; + struct elf_nds32_link_hash_table *table; + int target_optimize; + bfd_vma address; + + table = nds32_elf_hash_table (info); + target_optimize = table->target_optimize; + while (ptr) + { + irel_ptr = ptr->irel_head; + if (ptr->h == NULL) + { + /* Local symbol. */ + irel_keeper = irel_ptr; + while (irel_ptr && irel_ptr->next) + { + /* Check there is jump target can be used. */ + if ((irel_ptr->next->irel->r_offset + - irel_keeper->irel->r_offset) > 1022) + irel_keeper = irel_ptr->next; + else + { + ptr->enable = 1; + irel_ptr->keep = 0; + } + irel_ptr = irel_ptr->next; + } + } + else + { + /* Global symbol. We have to get the absolute address + and decide whether to keep it or not.*/ + + while (irel_ptr) + { + address = (irel_ptr->irel->r_offset + + irel_ptr->sec->output_section->vma + + irel_ptr->sec->output_offset); + irel_ptr->addr = address; + irel_ptr = irel_ptr->next; + } + + irel_ptr = ptr->irel_head; + while (irel_ptr) + { + struct elf_nds32_ifc_irel_list *irel_dest = irel_ptr; + struct elf_nds32_ifc_irel_list *irel_temp = irel_ptr; + struct elf_nds32_ifc_irel_list *irel_ptr_prev = NULL; + struct elf_nds32_ifc_irel_list *irel_dest_prev = NULL; + + while (irel_temp->next) + { + if (irel_temp->next->addr < irel_dest->addr) + { + irel_dest_prev = irel_temp; + irel_dest = irel_temp->next; + } + irel_temp = irel_temp->next; + } + if (irel_dest != irel_ptr) + { + if (irel_ptr_prev) + irel_ptr_prev->next = irel_dest; + if (irel_dest_prev) + irel_dest_prev->next = irel_ptr; + irel_temp = irel_ptr->next; + irel_ptr->next = irel_dest->next; + irel_dest->next = irel_temp; + } + irel_ptr_prev = irel_ptr; + irel_ptr = irel_ptr->next; + } + + irel_ptr = ptr->irel_head; + irel_keeper = irel_ptr; + while (irel_ptr && irel_ptr->next) + { + if ((irel_ptr->next->addr - irel_keeper->addr) > 1022) + irel_keeper = irel_ptr->next; + else + { + ptr->enable = 1; + irel_ptr->keep = 0; + } + irel_ptr = irel_ptr->next; + } + } + + /* Ex9 enable. Reserve it for ex9. */ + if ((target_optimize & NDS32_RELAX_EX9_ON) + && ptr->irel_head != irel_keeper) + ptr->enable = 0; + ptr = ptr->next; + } +} + +/* Determine whether j and jal should be substituted after ex9 done. */ + +static void +nds32_elf_ifc_filter_after_ex9 (void) +{ + struct elf_nds32_ifc_symbol_entry *ptr = ifc_symbol_head; + struct elf_nds32_ifc_irel_list *irel_ptr = NULL; + + while (ptr) + { + if (ptr->enable == 0) + { + /* Check whether ifc is applied or not. */ + irel_ptr = ptr->irel_head; + ptr->ex9_enable = 1; + while (irel_ptr) + { + if (ELF32_R_TYPE (irel_ptr->irel->r_info) == R_NDS32_TRAN) + { + /* Ex9 already. */ + ptr->ex9_enable = 0; + break; + } + irel_ptr = irel_ptr->next; + } + } + ptr = ptr->next; + } +} + +/* Wrapper to do ifc relaxation. */ + +bfd_boolean +nds32_elf_ifc_finish (struct bfd_link_info *info) +{ + int relax_status; + struct elf_nds32_link_hash_table *table; + + table = nds32_elf_hash_table (info); + relax_status = table->relax_status; + + if (!(relax_status & NDS32_RELAX_JUMP_IFC_DONE)) + nds32_elf_ifc_filter (info); + else + nds32_elf_ifc_filter_after_ex9 (); + + if (!nds32_elf_ifc_replace (info)) + return FALSE; + + if (table) + table->relax_status |= NDS32_RELAX_JUMP_IFC_DONE; + return TRUE; +} + +/* Traverse the result of ifc filter and replace it with ifcall9. */ + +static bfd_boolean +nds32_elf_ifc_replace (struct bfd_link_info *info) +{ + struct elf_nds32_ifc_symbol_entry *ptr = ifc_symbol_head; + struct elf_nds32_ifc_irel_list *irel_ptr = NULL; + nds32_elf_blank_t *relax_blank_list = NULL; + bfd_byte *contents = NULL; + Elf_Internal_Rela *internal_relocs; + Elf_Internal_Rela *irel; + Elf_Internal_Rela *irelend; + unsigned short insn16 = INSN_IFCALL9; + struct elf_nds32_link_hash_table *table; + int relax_status; + + table = nds32_elf_hash_table (info); + relax_status = table->relax_status; + + while (ptr) + { + /* Traverse the ifc gather list, and replace the + filter entries by ifcall9. */ + if ((!(relax_status & NDS32_RELAX_JUMP_IFC_DONE) && ptr->enable == 1) + || ((relax_status & NDS32_RELAX_JUMP_IFC_DONE) && ptr->ex9_enable == 1)) + { + irel_ptr = ptr->irel_head; + if (ptr->h == NULL) + { + /* Local symbol. */ + internal_relocs = _bfd_elf_link_read_relocs + (ptr->sec->owner, ptr->sec, NULL, NULL, TRUE /* keep_memory */); + irelend = internal_relocs + ptr->sec->reloc_count; + + if (!nds32_get_section_contents (ptr->sec->owner, ptr->sec, &contents)) + return FALSE; + + while (irel_ptr) + { + if (irel_ptr->keep == 0 && irel_ptr->next) + { + /* The one can be replaced. We have to check whether + there is any alignment point in the region. */ + irel = irel_ptr->irel; + while (((irel_ptr->next->keep == 0 && irel < irel_ptr->next->irel) + || (irel_ptr->next->keep == 1 && irel < irelend)) + && !(ELF32_R_TYPE (irel->r_info) == R_NDS32_LABEL + && (irel->r_addend & 0x1f) == 2)) + irel++; + if (irel >= irelend + || !(ELF32_R_TYPE (irel->r_info) == R_NDS32_LABEL + && (irel->r_addend & 0x1f) == 2 + && ((irel->r_offset + - get_nds32_elf_blank_total + (&relax_blank_list, irel->r_offset, 1)) & 0x02) == 0)) + { + /* Replace by ifcall9. */ + bfd_putb16 (insn16, contents + irel_ptr->irel->r_offset); + if (!insert_nds32_elf_blank_recalc_total + (&relax_blank_list, irel_ptr->irel->r_offset + 2, 2)) + return FALSE; + irel_ptr->irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel_ptr->irel->r_info), R_NDS32_TRAN); + } + } + irel_ptr = irel_ptr->next; + } + + /* Delete the redundant code. */ + if (relax_blank_list) + { + nds32_elf_relax_delete_blanks (ptr->sec->owner, ptr->sec, + relax_blank_list); + relax_blank_list = NULL; + } + } + else + { + /* Global symbol. */ + while (irel_ptr) + { + if (irel_ptr->keep == 0 && irel_ptr->next) + { + /* The one can be replaced, and we have to check + whether there is any alignment point in the region. */ + internal_relocs = _bfd_elf_link_read_relocs + (irel_ptr->sec->owner, irel_ptr->sec, NULL, NULL, + TRUE /* keep_memory */); + irelend = internal_relocs + irel_ptr->sec->reloc_count; + if (!nds32_get_section_contents + (irel_ptr->sec->owner, irel_ptr->sec, &contents)) + return FALSE; + + irel = irel_ptr->irel; + while (((irel_ptr->sec == irel_ptr->next->sec + && irel_ptr->next->keep == 0 + && irel < irel_ptr->next->irel) + || ((irel_ptr->sec != irel_ptr->next->sec + || irel_ptr->next->keep == 1) + && irel < irelend)) + && !(ELF32_R_TYPE (irel->r_info) == R_NDS32_LABEL + && (irel->r_addend & 0x1f) == 2)) + irel++; + if (irel >= irelend + || !(ELF32_R_TYPE (irel->r_info) == R_NDS32_LABEL + && (irel->r_addend & 0x1f) == 2 + && ((irel->r_offset + - get_nds32_elf_blank_total (&relax_blank_list, + irel->r_offset, 1)) & 0x02) == 0)) + { + /* Replace by ifcall9. */ + bfd_putb16 (insn16, contents + irel_ptr->irel->r_offset); + if (!insert_nds32_elf_blank_recalc_total + (&relax_blank_list, irel_ptr->irel->r_offset + 2, 2)) + return FALSE; + + /* Delete the redundant code, and clear the relocation. */ + nds32_elf_relax_delete_blanks (irel_ptr->sec->owner, + irel_ptr->sec, + relax_blank_list); + irel_ptr->irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel_ptr->irel->r_info), R_NDS32_TRAN); + relax_blank_list = NULL; + } + } + + irel_ptr = irel_ptr->next; + } + } + } + ptr = ptr->next; + } + + return TRUE; +} + +/* Relocate ifcall. */ + +bfd_boolean +nds32_elf_ifc_reloc (void) +{ + struct elf_nds32_ifc_symbol_entry *ptr = ifc_symbol_head; + struct elf_nds32_ifc_irel_list *irel_ptr = NULL; + struct elf_nds32_ifc_irel_list *irel_keeper = NULL; + bfd_vma relocation, address; + unsigned short insn16; + + bfd_byte *contents = NULL; + + while (ptr) + { + if (ptr->enable == 1 || ptr->ex9_enable == 1) + { + /* Check the entry is enable ifcall. */ + irel_ptr = ptr->irel_head; + while (irel_ptr) + { + if (irel_ptr->keep == 1) + { + irel_keeper = irel_ptr; + break; + } + irel_ptr = irel_ptr->next; + } + + irel_ptr = ptr->irel_head; + if (ptr->h == NULL) + { + /* Local symbol. */ + if (!nds32_get_section_contents (ptr->sec->owner, ptr->sec, &contents)) + return FALSE; + + while (irel_ptr) + { + if (irel_ptr->keep == 0 + && ELF32_R_TYPE (irel_ptr->irel->r_info) == R_NDS32_TRAN) + { + relocation = irel_keeper->irel->r_offset; + relocation = relocation - irel_ptr->irel->r_offset; + while (irel_keeper && relocation > 1022) + { + irel_keeper = irel_keeper->next; + if (irel_keeper && irel_keeper->keep == 1) + { + relocation = irel_keeper->irel->r_offset; + relocation = relocation - irel_ptr->irel->r_offset; + } + } + if (relocation > 1022) + { + /* Double check. */ + irel_keeper = ptr->irel_head; + while (irel_keeper) + { + if (irel_keeper->keep == 1) + { + relocation = irel_keeper->irel->r_offset; + relocation = relocation - irel_ptr->irel->r_offset; + } + if (relocation <= 1022) + break; + irel_keeper = irel_keeper->next; + } + if (!irel_keeper) + return FALSE; + } + + insn16 = INSN_IFCALL9 | (relocation >> 1); + bfd_putb16 (insn16, contents + irel_ptr->irel->r_offset); + } + irel_ptr = irel_ptr->next; + } + } + else + { + /* Global symbol. */ + while (irel_ptr) + { + if (irel_ptr->keep == 0 + && ELF32_R_TYPE (irel_ptr->irel->r_info) == R_NDS32_TRAN) + { + relocation = (irel_keeper->irel->r_offset + + irel_keeper->sec->output_section->vma + + irel_keeper->sec->output_offset); + address = (irel_ptr->irel->r_offset + + irel_ptr->sec->output_section->vma + + irel_ptr->sec->output_offset); + relocation = relocation - address; + while (irel_keeper && relocation > 1022) + { + irel_keeper = irel_keeper->next; + if (irel_keeper && irel_keeper->keep ==1) + { + relocation = (irel_keeper->irel->r_offset + + irel_keeper->sec->output_section->vma + + irel_keeper->sec->output_offset); + relocation = relocation - address; + } + } + + if (relocation > 1022) + { + /* Double check. */ + irel_keeper = ptr->irel_head; + while (irel_keeper) + { + if (irel_keeper->keep == 1) + { + + relocation = (irel_keeper->irel->r_offset + + irel_keeper->sec->output_section->vma + + irel_keeper->sec->output_offset); + relocation = relocation - address; + } + if (relocation <= 1022) + break; + irel_keeper = irel_keeper->next; + } + if (!irel_keeper) + return FALSE; + } + if (!nds32_get_section_contents + (irel_ptr->sec->owner, irel_ptr->sec, &contents)) + return FALSE; + insn16 = INSN_IFCALL9 | (relocation >> 1); + bfd_putb16 (insn16, contents + irel_ptr->irel->r_offset); + } + irel_ptr =irel_ptr->next; + } + } + } + ptr = ptr->next; + } + + return TRUE; +} + +/* End of IFC relaxation. */ + +/* EX9 Instruction Table Relaxation. */ + +/* Global hash list. */ +struct elf_link_hash_entry_list +{ + struct elf_link_hash_entry *h; + struct elf_link_hash_entry_list *next; +}; + +/* Save different destination but same insn. */ +struct elf_link_hash_entry_mul_list +{ + /* Global symbol times. */ + int times; + /* Save relocation for each global symbol but useful?? */ + Elf_Internal_Rela *irel; + /* For sethi, two sethi may have the same high-part but different low-parts. */ + Elf_Internal_Rela rel_backup; + struct elf_link_hash_entry_list *h_list; + struct elf_link_hash_entry_mul_list *next; +}; + +/* Instruction hash table. */ +struct elf_nds32_code_hash_entry +{ + struct bfd_hash_entry root; + int times; + /* For insn that can use relocation or constant ex: sethi. */ + int const_insn; + asection *sec; + struct elf_link_hash_entry_mul_list *m_list; + /* Using r_addend. */ + Elf_Internal_Rela *irel; + /* Using r_info. */ + Elf_Internal_Rela rel_backup; +}; + +/* Instruction count list. */ +struct elf_nds32_insn_times_entry +{ + const char *string; + int times; + int order; + asection *sec; + struct elf_link_hash_entry_mul_list *m_list; + Elf_Internal_Rela *irel; + Elf_Internal_Rela rel_backup; + struct elf_nds32_insn_times_entry *next; +}; + +/* J and JAL symbol list. */ +struct elf_nds32_symbol_entry +{ + char *string; + unsigned long insn; + struct elf_nds32_symbol_entry *next; +}; + +/* Relocation list. */ +struct elf_nds32_irel_entry +{ + Elf_Internal_Rela *irel; + struct elf_nds32_irel_entry *next; +}; + +/* ex9.it insn need to be fixed. */ +struct elf_nds32_ex9_refix +{ + Elf_Internal_Rela *irel; + asection *sec; + struct elf_link_hash_entry *h; + int order; + struct elf_nds32_ex9_refix *next; +}; + +static struct bfd_hash_table ex9_code_table; +static struct elf_nds32_insn_times_entry *ex9_insn_head = NULL; +static struct elf_nds32_ex9_refix *ex9_refix_head = NULL; + +/* EX9 hash function. */ + +static struct bfd_hash_entry * +nds32_elf_code_hash_newfunc (struct bfd_hash_entry *entry, + struct bfd_hash_table *table, + const char *string) +{ + struct elf_nds32_code_hash_entry *ret; + + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (entry == NULL) + { + entry = (struct bfd_hash_entry *) + bfd_hash_allocate (table, sizeof (*ret)); + if (entry == NULL) + return entry; + } + + /* Call the allocation method of the superclass. */ + entry = bfd_hash_newfunc (entry, table, string); + if (entry == NULL) + return entry; + + ret = (struct elf_nds32_code_hash_entry*) entry; + ret->times = 0; + ret->const_insn = 0; + ret->m_list = NULL; + ret->sec = NULL; + ret->irel = NULL; + return &ret->root; +} + +/* Insert ex9 entry + this insert must be stable sorted by times. */ + +static void +nds32_elf_ex9_insert_entry (struct elf_nds32_insn_times_entry *ptr) +{ + struct elf_nds32_insn_times_entry *temp; + struct elf_nds32_insn_times_entry *temp2; + + if (ex9_insn_head == NULL) + { + ex9_insn_head = ptr; + ptr->next = NULL; + } + else + { + temp = ex9_insn_head; + temp2 = ex9_insn_head; + while (temp->next && + (temp->next->times >= ptr->times + || temp->times == -1)) + { + if (temp->times == -1) + temp2 = temp; + temp = temp->next; + } + if (ptr->times > temp->times && temp->times != -1) + { + ptr->next = temp; + if (temp2->times == -1) + temp2->next = ptr; + else + ex9_insn_head = ptr; + } + else if (temp->next == NULL) + { + temp->next = ptr; + ptr->next = NULL; + } + else + { + ptr->next = temp->next; + temp->next = ptr; + } + } +} + +/* Examine each insn times in hash table. + Handle multi-link hash entry. + + TODO: This function doesn't assign so much info since it is fake. */ + +static int +nds32_elf_examine_insn_times (struct elf_nds32_code_hash_entry *h) +{ + struct elf_nds32_insn_times_entry *ptr; + int times; + + if (h->m_list == NULL) + { + /* Local symbol insn or insn without relocation. */ + if (h->times < 3) + return TRUE; + + ptr = (struct elf_nds32_insn_times_entry *) + bfd_malloc (sizeof (struct elf_nds32_insn_times_entry)); + ptr->times = h->times; + ptr->string = h->root.string; + ptr->m_list = NULL; + ptr->sec = h->sec; + ptr->irel = h->irel; + ptr->rel_backup = h->rel_backup; + nds32_elf_ex9_insert_entry (ptr); + } + else + { + /* Global symbol insn. */ + /* Only sethi insn has multiple m_list. */ + struct elf_link_hash_entry_mul_list *m_list = h->m_list; + + times = 0; + while (m_list) + { + times += m_list->times; + m_list = m_list->next; + } + if (times >= 3) + { + m_list = h->m_list; + ptr = (struct elf_nds32_insn_times_entry *) + bfd_malloc (sizeof (struct elf_nds32_insn_times_entry)); + ptr->times = times; /* Use the total times. */ + ptr->string = h->root.string; + ptr->m_list = m_list; + ptr->sec = h->sec; + ptr->irel = m_list->irel; + ptr->rel_backup = m_list->rel_backup; + nds32_elf_ex9_insert_entry (ptr); + } + if (h->const_insn == 1) + { + /* sethi with constant value. */ + if (h->times < 3) + return TRUE; + + ptr = (struct elf_nds32_insn_times_entry *) + bfd_malloc (sizeof (struct elf_nds32_insn_times_entry)); + ptr->times = h->times; + ptr->string = h->root.string; + ptr->m_list = NULL; + ptr->sec = NULL; + ptr->irel = NULL; + ptr->rel_backup = h->rel_backup; + nds32_elf_ex9_insert_entry (ptr); + } + } + return TRUE; +} + +/* Count each insn times in hash table. + Handle multi-link hash entry. */ + +static int +nds32_elf_count_insn_times (struct elf_nds32_code_hash_entry *h) +{ + int reservation, times; + unsigned long relocation, min_relocation; + struct elf_nds32_insn_times_entry *ptr; + + if (h->m_list == NULL) + { + /* Local symbol insn or insn without relocation. */ + if (h->times < 3) + return TRUE; + ptr = (struct elf_nds32_insn_times_entry *) + bfd_malloc (sizeof (struct elf_nds32_insn_times_entry)); + ptr->times = h->times; + ptr->string = h->root.string; + ptr->m_list = NULL; + ptr->sec = h->sec; + ptr->irel = h->irel; + ptr->rel_backup = h->rel_backup; + nds32_elf_ex9_insert_entry (ptr); + } + else + { + /* Global symbol insn. */ + /* Only sethi insn has multiple m_list. */ + struct elf_link_hash_entry_mul_list *m_list = h->m_list; + + if (ELF32_R_TYPE (m_list->rel_backup.r_info) == R_NDS32_HI20_RELA + && m_list->next != NULL) + { + /* Sethi insn has different symbol or addend but has same hi20. */ + times = 0; + reservation = 1; + relocation = 0; + min_relocation = 0xffffffff; + while (m_list) + { + /* Get the minimum sethi address + and calculate how many entry the sethi-list have to use. */ + if ((m_list->h_list->h->root.type == bfd_link_hash_defined + || m_list->h_list->h->root.type == bfd_link_hash_defweak) + && (m_list->h_list->h->root.u.def.section != NULL + && m_list->h_list->h->root.u.def.section->output_section != NULL)) + { + relocation = (m_list->h_list->h->root.u.def.value + + m_list->h_list->h->root.u.def.section->output_section->vma + + m_list->h_list->h->root.u.def.section->output_offset); + relocation += m_list->irel->r_addend; + } + else + relocation = 0; + if (relocation < min_relocation) + min_relocation = relocation; + times += m_list->times; + m_list = m_list->next; + } + if (min_relocation < ex9_relax_size) + reservation = (min_relocation >> 12) + 1; + else + reservation = (min_relocation >> 12) + - ((min_relocation - ex9_relax_size) >> 12) + 1; + if (reservation < (times / 3)) + { + /* Efficient enough to use ex9. */ + int i; + + for (i = reservation ; i > 0; i--) + { + /* Allocate number of reservation ex9 entry. */ + ptr = (struct elf_nds32_insn_times_entry *) + bfd_malloc (sizeof (struct elf_nds32_insn_times_entry)); + ptr->times = h->m_list->times / reservation; + ptr->string = h->root.string; + ptr->m_list = h->m_list; + ptr->sec = h->sec; + ptr->irel = h->m_list->irel; + ptr->rel_backup = h->m_list->rel_backup; + nds32_elf_ex9_insert_entry (ptr); + } + } + } + else + { + /* Normal global symbol that means no different address symbol + using same ex9 entry. */ + if (m_list->times >= 3) + { + ptr = (struct elf_nds32_insn_times_entry *) + bfd_malloc (sizeof (struct elf_nds32_insn_times_entry)); + ptr->times = m_list->times; + ptr->string = h->root.string; + ptr->m_list = h->m_list; + ptr->sec = h->sec; + ptr->irel = h->m_list->irel; + ptr->rel_backup = h->m_list->rel_backup; + nds32_elf_ex9_insert_entry (ptr); + } + } + + if (h->const_insn == 1) + { + /* sethi with constant value. */ + if (h->times < 3) + return TRUE; + + ptr = (struct elf_nds32_insn_times_entry *) + bfd_malloc (sizeof (struct elf_nds32_insn_times_entry)); + ptr->times = h->times; + ptr->string = h->root.string; + ptr->m_list = NULL; + ptr->sec = NULL; + ptr->irel = NULL; + ptr->rel_backup = h->rel_backup; + nds32_elf_ex9_insert_entry (ptr); + } + } + + return TRUE; +} + +/* Hash table traverse function. */ + +static void +nds32_elf_code_hash_traverse (int (*func) (struct elf_nds32_code_hash_entry*)) +{ + unsigned int i; + + ex9_code_table.frozen = 1; + for (i = 0; i < ex9_code_table.size; i++) + { + struct bfd_hash_entry *p; + + for (p = ex9_code_table.table[i]; p != NULL; p = p->next) + if (!func ((struct elf_nds32_code_hash_entry *) p)) + goto out; + } +out: + ex9_code_table.frozen = 0; +} + + +/* Give order number to insn list. */ + +static void +nds32_elf_order_insn_times (struct bfd_link_info *info) +{ + struct elf_nds32_insn_times_entry *ex9_insn; + struct elf_nds32_insn_times_entry *temp; + struct elf_nds32_link_hash_table *table; + char *insn; + int ex9_limit; + int number = 0, total = 0; + struct bfd_link_hash_entry *bh; + +/* The max number of entries is 512. */ + ex9_insn = ex9_insn_head; + table = nds32_elf_hash_table (info); + ex9_limit = table->ex9_limit; + + /* Get the minimun one of ex9 list and limitation. */ + while (ex9_insn) + { + total++; + ex9_insn = ex9_insn->next; + } + total = MIN (total, ex9_limit); + + temp = bfd_malloc (sizeof (struct elf_nds32_insn_times_entry)); + temp->string = bfd_malloc (sizeof (char) * 10); + temp->times = 0; + temp->sec = NULL; + temp->m_list = NULL; + temp->irel = NULL; + temp->next = NULL; + /* Since the struct elf_nds32_insn_times_entry string is const char, + it has to allocate another space to write break 0xea. */ + insn = bfd_malloc (sizeof (char) * 10); + snprintf (insn, sizeof (char) * 10, "%08x", INSN_BREAK_EA); + temp->string = (const char *) insn; + + ex9_insn = ex9_insn_head; + + while (ex9_insn != NULL && number <= ex9_limit) + { + /* Save 234th entry for break 0xea, because trace32 need to use + break16 0xea. If the number of entry is less than 234, adjust + the address of _ITB_BASE_ backward. */ + if (total < 234) + { + ex9_insn->order = number + 234 - total; + if (!ex9_insn->next) + { + /* Link break 0xea entry into list. */ + ex9_insn->next = temp; + temp->next = NULL; + temp ->order = number + 235 - total; + ex9_insn = NULL; + break; + } + } + else + ex9_insn->order = number; + + number++; + + if (number == 234) + { + /* Link break 0xea entry into list. */ + temp->next = ex9_insn->next; + ex9_insn->next = temp; + temp->order = number; + number++; + ex9_insn = ex9_insn->next; + } + + if (number > ex9_limit) + { + temp = ex9_insn; + ex9_insn = ex9_insn->next; + temp->next = NULL; + break; + } + ex9_insn = ex9_insn->next; + } + + if (total < 234) + { + /* Adjust the address of _ITB_BASE_. */ + bh = bfd_link_hash_lookup (info->hash, "_ITB_BASE_", + FALSE, FALSE, FALSE); + if (bh) + bh->u.def.value = (total - 234) * 4; + } + + while (ex9_insn != NULL) + { + /* Free useless entry. */ + temp = ex9_insn; + ex9_insn = ex9_insn->next; + free (temp); + } +} + +/* Build .ex9.itable section. */ + +static void +nds32_elf_ex9_build_itable (struct bfd_link_info *link_info) +{ + asection *table_sec; + struct elf_nds32_insn_times_entry *ptr; + bfd *it_abfd; + int number = 0; + bfd_byte *contents = NULL; + + for (it_abfd = link_info->input_bfds; it_abfd != NULL; + it_abfd = it_abfd->link_next) + { + /* Find the section .ex9.itable, and put all entries into it. */ + table_sec = bfd_get_section_by_name (it_abfd, ".ex9.itable"); + if (table_sec != NULL) + { + if (!nds32_get_section_contents (it_abfd, table_sec, &contents)) + return; + + for (ptr = ex9_insn_head; ptr !=NULL ; ptr = ptr->next) + number++; + + table_sec->size = number * 4; + + if (number == 0) + { + /* There is no insntruction effective enough to convert to ex9. + Only add break 0xea into ex9 table. */ + table_sec->size = 4; + bfd_putb32 ((bfd_vma) INSN_BREAK_EA, (char *) contents); + return; + } + + elf_elfheader (link_info->output_bfd)->e_flags |= E_NDS32_HAS_EX9_INST; + number = 0; + for (ptr = ex9_insn_head; ptr !=NULL ; ptr = ptr->next) + { + long val; + + val = strtol (ptr->string, NULL, 16); + bfd_putb32 ((bfd_vma) val, (char *) contents + (number * 4)); + number++; + } + break; + } + } +} + +/* Get insn with regs according to relocation type. */ + +static void +nds32_elf_get_insn_with_reg (Elf_Internal_Rela *irel, + unsigned long insn, unsigned long *insn_with_reg) +{ + reloc_howto_type *howto = NULL; + + if (irel == NULL + || (ELF32_R_TYPE (irel->r_info) >= (int) ARRAY_SIZE (nds32_elf_howto_table) + && (ELF32_R_TYPE (irel->r_info) - R_NDS32_RELAX_ENTRY) + >= (int) ARRAY_SIZE (nds32_elf_relax_howto_table))) + { + *insn_with_reg = insn; + return; + } + + howto = bfd_elf32_bfd_reloc_type_table_lookup (ELF32_R_TYPE (irel->r_info)); + *insn_with_reg = insn & (0xffffffff ^ howto->dst_mask); +} + +/* Mask number of address bits according to relocation. */ + +static unsigned long +nds32_elf_irel_mask (Elf_Internal_Rela *irel) +{ + reloc_howto_type *howto = NULL; + + if (irel == NULL + || (ELF32_R_TYPE (irel->r_info) >= (int) ARRAY_SIZE (nds32_elf_howto_table) + && (ELF32_R_TYPE (irel->r_info) - R_NDS32_RELAX_ENTRY) + >= (int) ARRAY_SIZE (nds32_elf_relax_howto_table))) + return 0; + + howto = bfd_elf32_bfd_reloc_type_table_lookup (ELF32_R_TYPE (irel->r_info)); + return howto->dst_mask; +} + +static void +nds32_elf_insert_irel_entry (struct elf_nds32_irel_entry **irel_list, + struct elf_nds32_irel_entry *irel_ptr) +{ + if (*irel_list == NULL) + { + *irel_list = irel_ptr; + irel_ptr->next = NULL; + } + else + { + irel_ptr->next = *irel_list; + *irel_list = irel_ptr; + } +} + +static void +nds32_elf_ex9_insert_fix (asection * sec, Elf_Internal_Rela * irel, + struct elf_link_hash_entry *h, int order) +{ + struct elf_nds32_ex9_refix *ptr; + + ptr = bfd_malloc (sizeof (struct elf_nds32_ex9_refix)); + ptr->sec = sec; + ptr->irel = irel; + ptr->h = h; + ptr->order = order; + ptr->next = NULL; + + if (ex9_refix_head == NULL) + ex9_refix_head = ptr; + else + { + struct elf_nds32_ex9_refix *temp = ex9_refix_head; + + while (temp->next != NULL) + temp = temp->next; + temp->next = ptr; + } +} + +enum +{ + DATA_EXIST = 1, + CLEAN_PRE = 1 << 1, + PUSH_PRE = 1 << 2 +}; + +/* Check relocation type if supporting for ex9. */ + +static int +nds32_elf_ex9_relocation_check (struct bfd_link_info *info, + Elf_Internal_Rela **irel, + Elf_Internal_Rela *irelend, + nds32_elf_blank_t *relax_blank_list, + asection *sec, + long unsigned int *off, + bfd_byte *contents) +{ + /* Suppress ex9 if `.no_relax ex9' or inner loop. */ + bfd_boolean nested_ex9, nested_loop; + bfd_boolean ex9_loop_aware; + /* We use the highest 1 byte of result to record + how many bytes location counter has to move. */ + int result = 0; + Elf_Internal_Rela *irel_save = NULL; + struct elf_nds32_link_hash_table *table; + + table = nds32_elf_hash_table (info); + ex9_loop_aware = table->ex9_loop_aware; + + while ((*irel) != NULL && (*irel) < irelend && *off == (*irel)->r_offset) + { + switch (ELF32_R_TYPE ((*irel)->r_info)) + { + case R_NDS32_RELAX_REGION_BEGIN: + /* Ignore code block. */ + nested_ex9 = FALSE; + nested_loop = FALSE; + if (((*irel)->r_addend & R_NDS32_RELAX_REGION_NO_EX9_FLAG) + || (ex9_loop_aware + && ((*irel)->r_addend & R_NDS32_RELAX_REGION_INNERMOST_LOOP_FLAG))) + { + /* Check the region if loop or not. If it is true and + ex9-loop-aware is true, ignore the region till region end. */ + /* To save the status for in .no_relax ex9 region and + loop region to conform the block can do ex9 relaxation. */ + nested_ex9 = ((*irel)->r_addend & R_NDS32_RELAX_REGION_NO_EX9_FLAG); + nested_loop = (ex9_loop_aware + && ((*irel)->r_addend & R_NDS32_RELAX_REGION_INNERMOST_LOOP_FLAG)); + while ((*irel) && (*irel) < irelend && (nested_ex9 || nested_loop)) + { + (*irel)++; + if (ELF32_R_TYPE ((*irel)->r_info) == R_NDS32_RELAX_REGION_BEGIN) + { + /* There may be nested region. */ + if (((*irel)->r_addend & R_NDS32_RELAX_REGION_NO_EX9_FLAG) != 0) + nested_ex9 = TRUE; + else if (ex9_loop_aware + && ((*irel)->r_addend & R_NDS32_RELAX_REGION_INNERMOST_LOOP_FLAG)) + nested_loop = TRUE; + } + else if (ELF32_R_TYPE ((*irel)->r_info) == R_NDS32_RELAX_REGION_END) + { + /* The end of region. */ + if (((*irel)->r_addend & R_NDS32_RELAX_REGION_NO_EX9_FLAG) != 0) + nested_ex9 = FALSE; + else if (ex9_loop_aware + && ((*irel)->r_addend & R_NDS32_RELAX_REGION_INNERMOST_LOOP_FLAG)) + nested_loop = FALSE; + } + else if (relax_blank_list + && ELF32_R_TYPE ((*irel)->r_info) == R_NDS32_LABEL + && ((*irel)->r_addend & 0x1f) == 2) + { + /* Alignment exist in the region. */ + result |= CLEAN_PRE; + if (((*irel)->r_offset - + get_nds32_elf_blank_total (&relax_blank_list, + (*irel)->r_offset, 0)) & 0x02) + result |= PUSH_PRE; + } + } + if ((*irel) >= irelend) + *off = sec->size; + else + *off = (*irel)->r_offset; + + /* The final instruction in the region, regard this one as data to ignore it. */ + result |= DATA_EXIST; + return result; + } + break; + + case R_NDS32_LABEL: + if (relax_blank_list && ((*irel)->r_addend & 0x1f) == 2) + { + /* Check this point is align and decide to do ex9 or not. */ + result |= CLEAN_PRE; + if (((*irel)->r_offset - + get_nds32_elf_blank_total (&relax_blank_list, + (*irel)->r_offset, 0)) & 0x02) + result |= PUSH_PRE; + } + break; + case R_NDS32_32_RELA: + /* Data. */ + result |= (4 << 24); + result |= DATA_EXIST; + break; + case R_NDS32_16_RELA: + /* Data. */ + result |= (2 << 24); + result |= DATA_EXIST; + break; + case R_NDS32_DATA: + /* Data. */ + /* The least code alignment is 2. If the data is only one byte, + we have to shift one more byte. */ + if ((*irel)->r_addend == 1) + result |= ((*irel)->r_addend << 25) ; + else + result |= ((*irel)->r_addend << 24) ; + + result |= DATA_EXIST; + break; + + case R_NDS32_25_PCREL_RELA: + case R_NDS32_SDA16S3_RELA: + case R_NDS32_SDA15S3_RELA: + case R_NDS32_SDA15S3: + case R_NDS32_SDA17S2_RELA: + case R_NDS32_SDA15S2_RELA: + case R_NDS32_SDA12S2_SP_RELA: + case R_NDS32_SDA12S2_DP_RELA: + case R_NDS32_SDA15S2: + case R_NDS32_SDA18S1_RELA: + case R_NDS32_SDA15S1_RELA: + case R_NDS32_SDA15S1: + case R_NDS32_SDA19S0_RELA: + case R_NDS32_SDA15S0_RELA: + case R_NDS32_SDA15S0: + case R_NDS32_HI20_RELA: + case R_NDS32_LO12S0_ORI_RELA: + case R_NDS32_LO12S0_RELA: + case R_NDS32_LO12S1_RELA: + case R_NDS32_LO12S2_RELA: + /* These relocation is supported ex9 relaxation currently. */ + /* We have to save the relocation for using later, since we have + to check there is any alignment in the same address. */ + irel_save = *irel; + break; + default: + /* Not support relocations. */ + if (ELF32_R_TYPE ((*irel)->r_info) < ARRAY_SIZE (nds32_elf_howto_table) + && ELF32_R_TYPE ((*irel)->r_info) != R_NDS32_NONE) + { + /* Note: To optimize aggressively, it maybe can ignore R_NDS32_INSN16 here. + But we have to consider if there is any side-effect. */ + if (!(result & DATA_EXIST)) + { + /* We have to confirm there is no data relocation in the + same address. In general case, this won't happen. */ + /* We have to do ex9 conservative, for those relocation not + considerd we ignore instruction. */ + result |= DATA_EXIST; + if (*(contents + *off) & 0x80) + result |= (2 << 24); + else + result |= (4 << 24); + break; + } + } + } + if ((*irel) < irelend + && ((*irel) + 1) < irelend + && (*irel)->r_offset == ((*irel) + 1)->r_offset) + /* There are relocations pointing to the same address, we have to + check all of them. */ + (*irel)++; + else + { + if (irel_save) + *irel = irel_save; + return result; + } + } + return result; +} + +/* Replace input file instruction which is in ex9 itable. */ + +static bfd_boolean +nds32_elf_ex9_replace_instruction (struct bfd_link_info *info, bfd *abfd, asection *sec) +{ + struct elf_nds32_insn_times_entry *ex9_insn = ex9_insn_head; + bfd_byte *contents = NULL; + long unsigned int off; + unsigned short insn16, insn_ex9; + /* `pre_*' are used to track previous instruction that can use ex9.it. */ + unsigned int pre_off = -1; + unsigned short pre_insn16 = 0; + struct elf_nds32_irel_entry *pre_irel_ptr = NULL; + Elf_Internal_Rela *internal_relocs; + Elf_Internal_Rela *irel; + Elf_Internal_Rela *irelend; + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Sym *isym = NULL; + nds32_elf_blank_t *relax_blank_list = NULL; + unsigned long insn = 0; + unsigned long insn_with_reg = 0; + unsigned long it_insn; + unsigned long it_insn_with_reg; + unsigned long r_symndx; + asection *isec; + struct elf_nds32_irel_entry *irel_list = NULL; + struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (abfd); + int data_flag, do_replace, save_irel; + + /* Load section instructions, relocations, and symbol table. */ + if (!nds32_get_section_contents (abfd, sec, &contents) + || !nds32_get_local_syms (abfd, sec, &isym)) + return FALSE; + internal_relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL, + TRUE /* keep_memory */); + irelend = internal_relocs + sec->reloc_count; + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + + off = 0; + + /* Check if the object enable ex9. */ + irel = find_relocs_at_address (internal_relocs, internal_relocs, irelend, + R_NDS32_RELAX_ENTRY); + + /* Check this section trigger ex9 relaxation. */ + if (irel == NULL + || irel >= irelend + || ELF32_R_TYPE (irel->r_info) != R_NDS32_RELAX_ENTRY + || (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_ENTRY + && !(irel->r_addend & R_NDS32_RELAX_ENTRY_EX9_FLAG))) + return TRUE; + + irel = internal_relocs; + + /* Check alignment and fetch proper relocation. */ + while (off < sec->size) + { + struct elf_link_hash_entry *h = NULL; + struct elf_nds32_irel_entry *irel_ptr = NULL; + + /* Syn the instruction and the relocation. */ + while (irel != NULL && irel < irelend && irel->r_offset < off) + irel++; + + data_flag = nds32_elf_ex9_relocation_check (info, &irel, irelend, + relax_blank_list, sec, + &off, contents); + if (data_flag & PUSH_PRE) + { + if (pre_insn16 != 0) + { + /* Implement the ex9 relaxation. */ + bfd_putb16 (pre_insn16, contents + pre_off); + if (!insert_nds32_elf_blank_recalc_total + (&relax_blank_list, pre_off + 2, 2)) + return FALSE; + if (pre_irel_ptr != NULL) + nds32_elf_insert_irel_entry (&irel_list, + pre_irel_ptr); + } + } + + if (data_flag & CLEAN_PRE) + { + pre_off = 0; + pre_insn16 = 0; + pre_irel_ptr = NULL; + } + if (data_flag & DATA_EXIST) + { + /* We save the move offset in the highest byte. */ + off += (data_flag >> 24); + continue; + } + + if (*(contents + off) & 0x80) + { + /* 2-byte instruction. */ + off += 2; + continue; + } + + /* Load the instruction and its opcode with register for comparing. */ + ex9_insn = ex9_insn_head; + insn = bfd_getb32 (contents + off); + insn_with_reg = 0; + while (ex9_insn) + { + it_insn = strtol (ex9_insn->string, NULL, 16); + it_insn_with_reg = 0; + do_replace = 0; + save_irel = 0; + + if (irel != NULL && irel < irelend && irel->r_offset == off) + { + /* Insn with relocation. */ + nds32_elf_get_insn_with_reg (irel, insn, &insn_with_reg); + + if (ex9_insn->irel != NULL) + nds32_elf_get_insn_with_reg (ex9_insn->irel, it_insn, &it_insn_with_reg); + + if (ex9_insn->irel != NULL + && ELF32_R_TYPE (irel->r_info) == ELF32_R_TYPE (ex9_insn->irel->r_info) + && (insn_with_reg == it_insn_with_reg)) + { + /* Insn relocation and format is the same as table entry. */ + + if (ELF32_R_TYPE (irel->r_info) == R_NDS32_25_PCREL_RELA + || ELF32_R_TYPE (irel->r_info) == R_NDS32_LO12S0_ORI_RELA + || ELF32_R_TYPE (irel->r_info) == R_NDS32_LO12S0_RELA + || ELF32_R_TYPE (irel->r_info) == R_NDS32_LO12S1_RELA + || ELF32_R_TYPE (irel->r_info) == R_NDS32_LO12S2_RELA + || (ELF32_R_TYPE (irel->r_info) >= R_NDS32_SDA15S3 + && ELF32_R_TYPE (irel->r_info) <= R_NDS32_SDA15S0) + || (ELF32_R_TYPE (irel->r_info) >= R_NDS32_SDA15S3_RELA + && ELF32_R_TYPE (irel->r_info) <= R_NDS32_SDA15S0_RELA) + || (ELF32_R_TYPE (irel->r_info) >= R_NDS32_SDA12S2_DP_RELA + && ELF32_R_TYPE (irel->r_info) <= + R_NDS32_SDA12S2_SP_RELA) + || (ELF32_R_TYPE (irel->r_info) >= R_NDS32_SDA16S3_RELA + && ELF32_R_TYPE (irel->r_info) <= R_NDS32_SDA19S0_RELA)) + { + r_symndx = ELF32_R_SYM (irel->r_info); + if (r_symndx < symtab_hdr->sh_info) + { + /* Local symbol. */ + int shndx = isym[r_symndx].st_shndx; + + isec = elf_elfsections (abfd)[shndx]->bfd_section; + if (ex9_insn->sec == isec + && ex9_insn->irel->r_addend == irel->r_addend + && ex9_insn->irel->r_info == irel->r_info) + { + do_replace = 1; + save_irel = 1; + } + } + else + { + /* External symbol. */ + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + if (ex9_insn->m_list) + { + struct elf_link_hash_entry_list *h_list; + + h_list = ex9_insn->m_list->h_list; + while (h_list) + { + if (h == h_list->h + && ex9_insn->m_list->irel->r_addend == irel->r_addend) + { + do_replace = 1; + save_irel = 1; + break; + } + h_list = h_list->next; + } + } + } + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_HI20_RELA) + { + r_symndx = ELF32_R_SYM (irel->r_info); + if (r_symndx < symtab_hdr->sh_info) + { + /* Local symbols. Compare its base symbol and offset. */ + int shndx = isym[r_symndx].st_shndx; + + isec = elf_elfsections (abfd)[shndx]->bfd_section; + if (ex9_insn->sec == isec + && ex9_insn->irel->r_addend == irel->r_addend + && ex9_insn->irel->r_info == irel->r_info) + { + do_replace = 1; + save_irel = 1; + } + } + else + { + /* External symbol. */ + struct elf_link_hash_entry_mul_list *m_list; + + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + m_list = ex9_insn->m_list; + + while (m_list) + { + struct elf_link_hash_entry_list *h_list = m_list->h_list; + + while (h_list) + { + if (h == h_list->h + && m_list->irel->r_addend == irel->r_addend) + { + do_replace = 1; + save_irel = 1; + if (ex9_insn->next + && ex9_insn->m_list + && ex9_insn->m_list == ex9_insn->next->m_list) + { + /* sethi multiple entry must be fixed */ + nds32_elf_ex9_insert_fix (sec, irel, + h, ex9_insn->order); + } + break; + } + h_list = h_list->next; + } + m_list = m_list->next; + } + } + } + } + + /* Import table: Check the symbol hash table and the + jump target. Only R_NDS32_25_PCREL_RELA now. */ + else if (ex9_insn->times == -1 + && ELF32_R_TYPE (irel->r_info) == R_NDS32_25_PCREL_RELA) + { + nds32_elf_get_insn_with_reg (irel, it_insn, &it_insn_with_reg); + if (insn_with_reg == it_insn_with_reg) + { + char code[10]; + bfd_vma relocation; + + r_symndx = ELF32_R_SYM (irel->r_info); + if (r_symndx >= symtab_hdr->sh_info) + { + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + if ((h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + && (h->root.u.def.section != NULL + && h->root.u.def.section->output_section != NULL) + && h->root.u.def.section->gc_mark == 1 + && strcmp (h->root.u.def.section->name, + BFD_ABS_SECTION_NAME) == 0 + && h->root.u.def.value > sec->size) + { + relocation = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + relocation += irel->r_addend; + insn = insn_with_reg | ((relocation >> 1) & 0xffffff); + snprintf (code, sizeof (code), "%08lx", insn); + if (strcmp (code, ex9_insn->string) == 0) + { + do_replace = 1; + save_irel = 1; + } + } + } + } + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_REGION_BEGIN + || ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_REGION_END) + { + /* These relocations do not have to relocate contens, so it can + be regard as instruction without relocation. */ + if (insn == it_insn && ex9_insn->irel == NULL) + do_replace = 1; + } + } + else + { + /* Instruction without relocation, we only + have to compare their byte code. */ + if (insn == it_insn && ex9_insn->irel == NULL) + do_replace = 1; + } + + /* Insntruction match so replacing the code here. */ + if (do_replace == 1) + { + /* There are two formats of ex9 instruction. */ + if (ex9_insn->order < 32) + insn_ex9 = INSN_EX9_IT_2; + else + insn_ex9 = INSN_EX9_IT_1; + insn16 = insn_ex9 | ex9_insn->order; + + if (pre_insn16 != 0) + { + bfd_putb16 (pre_insn16, contents + pre_off); + if (!insert_nds32_elf_blank_recalc_total + (&relax_blank_list, pre_off + 2, 2)) + return FALSE; + if (pre_irel_ptr != NULL) + nds32_elf_insert_irel_entry (&irel_list, pre_irel_ptr); + } + pre_off = off; + pre_insn16 = insn16; + + if (save_irel) + { + /* For instuction with relocation do relax. */ + irel_ptr = (struct elf_nds32_irel_entry *) + bfd_malloc (sizeof (struct elf_nds32_irel_entry)); + irel_ptr->irel = irel; + irel_ptr->next = NULL; + pre_irel_ptr = irel_ptr; + } + else + pre_irel_ptr = NULL; + break; + } + ex9_insn = ex9_insn->next; + } + off += 4; + } + + if (pre_insn16 != 0) + { + /* Implement the ex9 relaxation. */ + bfd_putb16 (pre_insn16, contents + pre_off); + if (!insert_nds32_elf_blank_recalc_total + (&relax_blank_list, pre_off + 2, 2)) + return FALSE; + if (pre_irel_ptr != NULL) + nds32_elf_insert_irel_entry (&irel_list, pre_irel_ptr); + } + + /* Delete the redundant code. */ + if (relax_blank_list) + { + nds32_elf_relax_delete_blanks (abfd, sec, relax_blank_list); + relax_blank_list = NULL; + } + + /* Clear the relocation that is replaced by ex9. */ + while (irel_list) + { + struct elf_nds32_irel_entry *irel_ptr; + + irel_ptr = irel_list; + irel_list = irel_ptr->next; + irel_ptr->irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel_ptr->irel->r_info), R_NDS32_TRAN); + free (irel_ptr); + } + return TRUE; +} + +/* Initialize ex9 hash table. */ + +int +nds32_elf_ex9_init (void) +{ + if (!bfd_hash_table_init_n (&ex9_code_table, nds32_elf_code_hash_newfunc, + sizeof (struct elf_nds32_code_hash_entry), + 1023)) + { + (*_bfd_error_handler) (_("Linker: cannot init ex9 hash table error \n")); + return FALSE; + } + return TRUE; +} + +/* Predict how many bytes will be relaxed with ex9 and ifc. */ + +static void +nds32_elf_ex9_total_relax (struct bfd_link_info *info) +{ + struct elf_nds32_insn_times_entry *ex9_insn; + struct elf_nds32_insn_times_entry *temp; + int target_optimize; + struct elf_nds32_link_hash_table *table; + + if (ex9_insn_head == NULL) + return; + + table = nds32_elf_hash_table (info); + target_optimize = table->target_optimize; + ex9_insn = ex9_insn_head; + while (ex9_insn) + { + ex9_relax_size = ex9_insn->times * 2 + ex9_relax_size; + temp = ex9_insn; + ex9_insn = ex9_insn->next; + free (temp); + } + ex9_insn_head = NULL; + + if ((target_optimize & NDS32_RELAX_JUMP_IFC_ON)) + { + /* Examine ifc reduce size. */ + struct elf_nds32_ifc_symbol_entry *ifc_ent = ifc_symbol_head; + struct elf_nds32_ifc_irel_list *irel_ptr = NULL; + int size = 0; + + while (ifc_ent) + { + if (ifc_ent->enable == 0) + { + /* Not ifc yet. */ + irel_ptr = ifc_ent->irel_head; + while (irel_ptr) + { + size += 2; + irel_ptr = irel_ptr->next; + } + } + size -= 2; + ifc_ent = ifc_ent->next; + } + ex9_relax_size += size; + } +} + +/* Finish ex9 table. */ + +void +nds32_elf_ex9_finish (struct bfd_link_info *link_info) +{ + struct elf_nds32_link_hash_table *table; + + nds32_elf_code_hash_traverse (nds32_elf_examine_insn_times); + nds32_elf_order_insn_times (link_info); + nds32_elf_ex9_total_relax (link_info); + /* Traverse the hash table and count its times. */ + nds32_elf_code_hash_traverse (nds32_elf_count_insn_times); + nds32_elf_order_insn_times (link_info); + nds32_elf_ex9_build_itable (link_info); + table = nds32_elf_hash_table (link_info); + if (table) + table->relax_round = NDS32_RELAX_EX9_REPLACE_ROUND; +} + +/* Relocate the entries in ex9 table. */ + +static bfd_vma +nds32_elf_ex9_reloc_insn (struct elf_nds32_insn_times_entry *ptr, + struct bfd_link_info *link_info) +{ + Elf_Internal_Sym *isym = NULL; + bfd_vma relocation = -1; + + if (ptr->m_list != NULL) + { + /* Global symbol. */ + if ((ptr->m_list->h_list->h->root.type == bfd_link_hash_defined + || ptr->m_list->h_list->h->root.type == bfd_link_hash_defweak) + && (ptr->m_list->h_list->h->root.u.def.section != NULL + && ptr->m_list->h_list->h->root.u.def.section->output_section != NULL)) + { + + relocation = (ptr->m_list->h_list->h->root.u.def.value + + ptr->m_list->h_list->h->root.u.def.section->output_section->vma + + ptr->m_list->h_list->h->root.u.def.section->output_offset); + relocation += ptr->m_list->irel->r_addend; + } + else + relocation = 0; + } + else if (ptr->sec !=NULL) + { + /* Local symbol. */ + Elf_Internal_Sym sym; + asection *sec = NULL; + asection isec; + asection *isec_ptr = &isec; + Elf_Internal_Rela irel_backup = *(ptr->irel); + asection *sec_backup = ptr->sec; + bfd *abfd = ptr->sec->owner; + + if (!nds32_get_local_syms (abfd, sec, &isym)) + return FALSE; + isym = isym + ELF32_R_SYM (ptr->irel->r_info); + + sec = bfd_section_from_elf_index (abfd, isym->st_shndx); + if (sec != NULL) + *isec_ptr = *sec; + sym = *isym; + + /* The purpose is same as elf_link_input_bfd. */ + if (isec_ptr != NULL + && isec_ptr->sec_info_type == SEC_INFO_TYPE_MERGE + && ELF_ST_TYPE (isym->st_info) != STT_SECTION) + { + sym.st_value = + _bfd_merged_section_offset (ptr->sec->output_section->owner, &isec_ptr, + elf_section_data (isec_ptr)->sec_info, + isym->st_value); + } + relocation = _bfd_elf_rela_local_sym (link_info->output_bfd, &sym, + &ptr->sec, ptr->irel); + if (ptr->irel != NULL) + relocation += ptr->irel->r_addend; + + /* Restore origin value since there may be some insntructions that + could not be replaced with ex9.it. */ + *(ptr->irel) = irel_backup; + ptr->sec = sec_backup; + } + + return relocation; +} + +/* Import ex9 table and build list. */ + +void +nds32_elf_ex9_import_table (struct bfd_link_info *info) +{ + int count = 0, num = 1; + bfd_byte *contents; + unsigned long insn; + FILE *ex9_import_file; + int update_ex9_table; + struct elf_nds32_link_hash_table *table; + + table = nds32_elf_hash_table (info); + ex9_import_file = table->ex9_import_file; + + contents = bfd_malloc (sizeof (bfd_byte) * 4); + + /* Count the number of input file instructions. */ + while (!feof (ex9_import_file)) + { + fgetc (ex9_import_file); + count++; + } + count = count / 4; + rewind (ex9_import_file); + /* Read instructions from the input file and build the list. */ + while (count != 0) + { + char *code; + struct elf_nds32_insn_times_entry *ptr; + size_t nread; + + nread = fread (contents, sizeof (bfd_byte) * 4, 1, ex9_import_file); + if (nread < sizeof (bfd_byte) * 4) + { + (*_bfd_error_handler) ("Unexpected size of imported ex9 table."); + break; + } + insn = bfd_getb32 (contents); + code = bfd_malloc (sizeof (char) * 9); + snprintf (code, 9, "%08lx", insn); + ptr = bfd_malloc (sizeof (struct elf_nds32_insn_times_entry)); + ptr->string = code; + ptr->order = num; + ptr->times = -1; + ptr->sec = NULL; + ptr->m_list = NULL; + ptr->rel_backup.r_offset = 0; + ptr->rel_backup.r_info = 0; + ptr->rel_backup.r_addend = 0; + ptr->irel = NULL; + ptr->next = NULL; + nds32_elf_ex9_insert_entry (ptr); + count--; + num++; + } + + update_ex9_table = table->update_ex9_table; + if (update_ex9_table == 1) + { + /* It has to consider of sethi need to use multiple page + but it not be done yet. */ + nds32_elf_code_hash_traverse (nds32_elf_examine_insn_times); + nds32_elf_order_insn_times (info); + } +} + +/* Export ex9 table. */ + +static void +nds32_elf_ex9_export (struct bfd_link_info *info, + bfd_byte *contents, int size) +{ + FILE *ex9_export_file; + struct elf_nds32_link_hash_table *table; + + table = nds32_elf_hash_table (info); + ex9_export_file = table->ex9_export_file; + fwrite (contents, sizeof (bfd_byte), size, ex9_export_file); + fclose (ex9_export_file); +} + +/* Adjust relocations of J and JAL in ex9.itable. + Export ex9 table. */ + +void +nds32_elf_ex9_reloc_jmp (struct bfd_link_info *link_info) +{ + asection *table_sec = NULL; + struct elf_nds32_insn_times_entry *ex9_insn = ex9_insn_head; + struct elf_nds32_insn_times_entry *temp_ptr, *temp_ptr2; + bfd *it_abfd; + unsigned long insn, insn_with_reg, source_insn; + bfd_byte *contents = NULL, *source_contents = NULL; + int size = 0; + bfd_vma gp; + int shift, update_ex9_table, offset = 0; + reloc_howto_type *howto = NULL; + Elf_Internal_Rela rel_backup; + unsigned short insn_ex9; + struct elf_nds32_link_hash_table *table; + FILE *ex9_export_file, *ex9_import_file; + + table = nds32_elf_hash_table (link_info); + if (table) + table->relax_status |= NDS32_RELAX_EX9_DONE; + + + update_ex9_table = table->update_ex9_table; + /* Generated ex9.itable exactly. */ + if (update_ex9_table == 0) + { + for (it_abfd = link_info->input_bfds; it_abfd != NULL; + it_abfd = it_abfd->link_next) + { + table_sec = bfd_get_section_by_name (it_abfd, ".ex9.itable"); + if (table_sec != NULL) + break; + } + + if (table_sec != NULL) + { + bfd *output_bfd; + struct bfd_link_hash_entry *bh = NULL; + + output_bfd = table_sec->output_section->owner; + nds32_elf_final_sda_base (output_bfd, link_info, &gp, FALSE); + if (table_sec->size == 0) + return; + + if (!nds32_get_section_contents (it_abfd, table_sec, &contents)) + return; + /* Get the offset between _ITB_BASE_ and .ex9.itable. */ + bh = bfd_link_hash_lookup (link_info->hash, "_ITB_BASE_", + FALSE, FALSE, FALSE); + offset = bh->u.def.value; + } + } + else + { + /* Set gp. */ + bfd *output_bfd; + + output_bfd = link_info->input_bfds->sections->output_section->owner; + nds32_elf_final_sda_base (output_bfd, link_info, &gp, FALSE); + contents = bfd_malloc (sizeof (bfd_byte) * 2048); + } + + /* Relocate instruction. */ + while (ex9_insn) + { + bfd_vma relocation, min_relocation = 0xffffffff; + + insn = strtol (ex9_insn->string, NULL, 16); + insn_with_reg = 0; + if (ex9_insn->m_list != NULL || ex9_insn->sec != NULL) + { + if (ex9_insn->m_list) + rel_backup = ex9_insn->m_list->rel_backup; + else + rel_backup = ex9_insn->rel_backup; + + nds32_elf_get_insn_with_reg (&rel_backup, insn, &insn_with_reg); + howto = + bfd_elf32_bfd_reloc_type_table_lookup (ELF32_R_TYPE + (rel_backup.r_info)); + shift = howto->rightshift; + if (ELF32_R_TYPE (rel_backup.r_info) == R_NDS32_25_PCREL_RELA + || ELF32_R_TYPE (rel_backup.r_info) == R_NDS32_LO12S0_ORI_RELA + || ELF32_R_TYPE (rel_backup.r_info) == R_NDS32_LO12S0_RELA + || ELF32_R_TYPE (rel_backup.r_info) == R_NDS32_LO12S1_RELA + || ELF32_R_TYPE (rel_backup.r_info) == R_NDS32_LO12S2_RELA) + { + relocation = nds32_elf_ex9_reloc_insn (ex9_insn, link_info); + insn = + insn_with_reg | ((relocation >> shift) & + nds32_elf_irel_mask (&rel_backup)); + bfd_putb32 (insn, contents + (ex9_insn->order) * 4 + offset); + } + else if ((ELF32_R_TYPE (rel_backup.r_info) >= R_NDS32_SDA15S3 + && ELF32_R_TYPE (rel_backup.r_info) <= R_NDS32_SDA15S0) + || (ELF32_R_TYPE (rel_backup.r_info) >= R_NDS32_SDA15S3_RELA + && ELF32_R_TYPE (rel_backup.r_info) <= R_NDS32_SDA15S0_RELA) + || (ELF32_R_TYPE (rel_backup.r_info) >= R_NDS32_SDA12S2_DP_RELA + && ELF32_R_TYPE (rel_backup.r_info) <= R_NDS32_SDA12S2_SP_RELA) + || (ELF32_R_TYPE (rel_backup.r_info) >= R_NDS32_SDA16S3_RELA + && ELF32_R_TYPE (rel_backup.r_info) <= R_NDS32_SDA19S0_RELA)) + { + relocation = nds32_elf_ex9_reloc_insn (ex9_insn, link_info); + insn = + insn_with_reg | (((relocation - gp) >> shift) & + nds32_elf_irel_mask (&rel_backup)); + bfd_putb32 (insn, contents + (ex9_insn->order) * 4 + offset); + } + else if (ELF32_R_TYPE (rel_backup.r_info) == R_NDS32_HI20_RELA) + { + /* Sethi may be multiple entry for one insn. */ + if (ex9_insn->next && ((ex9_insn->m_list && ex9_insn->m_list == ex9_insn->next->m_list) + || (ex9_insn->m_list && ex9_insn->next->order == 234 + && ex9_insn->next->next + && ex9_insn->m_list == ex9_insn->next->next->m_list))) + { + struct elf_link_hash_entry_mul_list *m_list; + struct elf_nds32_ex9_refix *fix_ptr; + + temp_ptr = ex9_insn; + temp_ptr2 = ex9_insn; + m_list = ex9_insn->m_list; + while (m_list) + { + relocation = (m_list->h_list->h->root.u.def.value + + m_list->h_list->h->root.u.def.section->output_section->vma + + m_list->h_list->h->root.u.def.section->output_offset); + relocation += m_list->irel->r_addend; + + if (relocation < min_relocation) + min_relocation = relocation; + m_list = m_list->next; + } + relocation = min_relocation; + + /* Put insntruction into ex9 table. */ + insn = insn_with_reg + | ((relocation >> shift) & nds32_elf_irel_mask (&rel_backup)); + bfd_putb32 (insn, contents + (ex9_insn->order) * 4 + offset); + relocation = relocation + 0x1000; /* hi20 */ + + while (ex9_insn->next && ((ex9_insn->m_list && ex9_insn->m_list == ex9_insn->next->m_list) + || (ex9_insn->m_list && ex9_insn->next->order == 234 + && ex9_insn->next->next + && ex9_insn->m_list == ex9_insn->next->next->m_list))) + { + /* Multiple sethi. */ + ex9_insn = ex9_insn->next; + size += 4; + if (ex9_insn->order == 234) + { + ex9_insn = ex9_insn->next; + size += 4; + } + insn = + insn_with_reg | ((relocation >> shift) & + nds32_elf_irel_mask (&rel_backup)); + bfd_putb32 (insn, contents + (ex9_insn->order) * 4 + offset); + relocation = relocation + 0x1000; /* hi20 */ + } + + fix_ptr = ex9_refix_head; + while (fix_ptr) + { + /* Fix ex9 insn. */ + /* temp_ptr2 points to the head of multiple sethi. */ + temp_ptr = temp_ptr2; + while (fix_ptr->order != temp_ptr->order && fix_ptr->next) + { + fix_ptr = fix_ptr->next; + } + if (fix_ptr->order != temp_ptr->order) + break; + + /* Set source insn. */ + relocation = (fix_ptr->h->root.u.def.value + + fix_ptr->h->root.u.def.section->output_section->vma + + fix_ptr->h->root.u.def.section->output_offset); + relocation += fix_ptr->irel->r_addend; + /* sethi imm is imm20s. */ + source_insn = insn_with_reg | ((relocation >> shift) & 0xfffff); + + while (temp_ptr) + { + if (temp_ptr->order == 234) + { + temp_ptr = temp_ptr->next; + continue; + } + + /* Match entry and source code. */ + insn = bfd_getb32 (contents + (temp_ptr->order) * 4 + offset); + if (insn == source_insn) + { + /* Fix the ex9 insn. */ + if (temp_ptr->order != fix_ptr->order) + { + if (!nds32_get_section_contents + (fix_ptr->sec->owner, fix_ptr->sec, + &source_contents)) + (*_bfd_error_handler) + (_("Linker: error cannot fixed ex9 relocation \n")); + if (temp_ptr->order < 32) + insn_ex9 = INSN_EX9_IT_2; + else + insn_ex9 = INSN_EX9_IT_1; + insn_ex9 = insn_ex9 | temp_ptr->order; + bfd_putb16 (insn_ex9, source_contents + fix_ptr->irel->r_offset); + } + break; + } + else + { + if (!temp_ptr->next || temp_ptr->m_list != temp_ptr->next->m_list) + (*_bfd_error_handler) + (_("Linker: error cannot fixed ex9 relocation \n")); + else + temp_ptr = temp_ptr->next; + } + } + fix_ptr = fix_ptr->next; + } + } + else + { + relocation = nds32_elf_ex9_reloc_insn (ex9_insn, link_info); + insn = insn_with_reg + | ((relocation >> shift) & nds32_elf_irel_mask (&rel_backup)); + bfd_putb32 (insn, contents + (ex9_insn->order) * 4 + offset); + } + } + } + else + { + /* Insn without relocation does not have to be fixed + if need to update export table. */ + if (update_ex9_table == 1) + bfd_putb32 (insn, contents + (ex9_insn->order) * 4); + } + ex9_insn = ex9_insn->next; + size += 4; + } + + ex9_export_file = table->ex9_export_file; + if (ex9_export_file != NULL) + nds32_elf_ex9_export (link_info, contents + 4, table_sec->size - 4); + else if (update_ex9_table == 1) + { + ex9_import_file = table->ex9_import_file; + ex9_export_file = ex9_import_file; + rewind (ex9_export_file); + nds32_elf_ex9_export (link_info, contents + 4, size); + } +} + +/* Generate ex9 hash table. */ + +static bfd_boolean +nds32_elf_ex9_build_hash_table (bfd * abfd, asection * sec, + struct bfd_link_info *link_info) +{ + Elf_Internal_Rela *internal_relocs; + Elf_Internal_Rela *irelend; + Elf_Internal_Rela *irel; + Elf_Internal_Rela *jrel; + Elf_Internal_Rela rel_backup; + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Sym *isym = NULL; + asection *isec; + struct elf_link_hash_entry **sym_hashes; + bfd_byte *contents = NULL; + long unsigned int off = 0; + unsigned long r_symndx; + unsigned long insn; + unsigned long insn_with_reg; + struct elf_link_hash_entry *h; + int data_flag, shift, align; + bfd_vma relocation; + /* Suppress ex9 if `.no_relax ex9' or inner loop. */ + reloc_howto_type *howto = NULL; + + sym_hashes = elf_sym_hashes (abfd); + /* Load section instructions, relocations, and symbol table. */ + if (!nds32_get_section_contents (abfd, sec, &contents)) + return FALSE; + + internal_relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL, + TRUE /* keep_memory */); + irelend = internal_relocs + sec->reloc_count; + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + if (!nds32_get_local_syms (abfd, sec, &isym)) + return FALSE; + + /* Check the object if enable ex9. */ + irel = find_relocs_at_address (internal_relocs, internal_relocs, irelend, + R_NDS32_RELAX_ENTRY); + + /* Check this section trigger ex9 relaxation. */ + if (irel == NULL + || irel >= irelend + || ELF32_R_TYPE (irel->r_info) != R_NDS32_RELAX_ENTRY + || (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_ENTRY + && !(irel->r_addend & R_NDS32_RELAX_ENTRY_EX9_FLAG))) + return TRUE; + + irel = internal_relocs; + + /* Push each insn into hash table. */ + while (off < sec->size) + { + char code[10]; + struct elf_nds32_code_hash_entry *entry; + + while (irel != NULL && irel < irelend && irel->r_offset < off) + irel++; + + data_flag = nds32_elf_ex9_relocation_check (link_info, &irel, irelend, NULL, + sec, &off, contents); + if (data_flag & DATA_EXIST) + { + /* We save the move offset in the highest byte. */ + off += (data_flag >> 24); + continue; + } + + if (*(contents + off) & 0x80) + { + off += 2; + } + else + { + h = NULL; + isec = NULL; + jrel = NULL; + rel_backup.r_info = 0; + rel_backup.r_offset = 0; + rel_backup.r_addend = 0; + /* Load the instruction and its opcode with register for comparing. */ + insn = bfd_getb32 (contents + off); + insn_with_reg = 0; + if (irel != NULL && irel < irelend && irel->r_offset == off) + { + nds32_elf_get_insn_with_reg (irel, insn, &insn_with_reg); + howto = bfd_elf32_bfd_reloc_type_table_lookup (ELF32_R_TYPE (irel->r_info)); + shift = howto->rightshift; + align = (1 << shift) - 1; + if (ELF32_R_TYPE (irel->r_info) == R_NDS32_25_PCREL_RELA + || ELF32_R_TYPE (irel->r_info) == R_NDS32_HI20_RELA + || ELF32_R_TYPE (irel->r_info) == R_NDS32_LO12S0_ORI_RELA + || ELF32_R_TYPE (irel->r_info) == R_NDS32_LO12S0_RELA + || ELF32_R_TYPE (irel->r_info) == R_NDS32_LO12S1_RELA + || ELF32_R_TYPE (irel->r_info) == R_NDS32_LO12S2_RELA + ||(ELF32_R_TYPE (irel->r_info) >= R_NDS32_SDA15S3 + && ELF32_R_TYPE (irel->r_info) <= R_NDS32_SDA15S0) + || (ELF32_R_TYPE (irel->r_info) >= R_NDS32_SDA15S3_RELA + && ELF32_R_TYPE (irel->r_info) <= R_NDS32_SDA15S0_RELA) + || (ELF32_R_TYPE (irel->r_info) >= R_NDS32_SDA12S2_DP_RELA + && ELF32_R_TYPE (irel->r_info) <= R_NDS32_SDA12S2_SP_RELA) + || (ELF32_R_TYPE (irel->r_info) >= R_NDS32_SDA16S3_RELA + && ELF32_R_TYPE (irel->r_info) <= R_NDS32_SDA19S0_RELA)) + { + r_symndx = ELF32_R_SYM (irel->r_info); + jrel = irel; + rel_backup = *irel; + if (r_symndx < symtab_hdr->sh_info) + { + /* Local symbol. */ + int shndx = isym[r_symndx].st_shndx; + + bfd_vma st_value = (isym + r_symndx)->st_value; + isec = elf_elfsections (abfd)[shndx]->bfd_section; + relocation = (isec->output_section->vma + isec->output_offset + + st_value + irel->r_addend); + } + else + { + /* External symbol. */ + bfd_boolean warned ATTRIBUTE_UNUSED; + bfd_boolean ignored ATTRIBUTE_UNUSED; + bfd_boolean unresolved_reloc ATTRIBUTE_UNUSED; + asection *sym_sec; + + /* Maybe there is a better way to get h and relocation */ + RELOC_FOR_GLOBAL_SYMBOL (link_info, abfd, sec, irel, + r_symndx, symtab_hdr, sym_hashes, + h, sym_sec, relocation, + unresolved_reloc, warned, ignored); + relocation += irel->r_addend; + if (h->type != bfd_link_hash_defined + && h->type != bfd_link_hash_defweak) + { + off += 4; + continue; + } + } + + /* Check for gp relative instruction alignment. */ + if ((ELF32_R_TYPE (irel->r_info) >= R_NDS32_SDA15S3 + && ELF32_R_TYPE (irel->r_info) <= R_NDS32_SDA15S0) + || (ELF32_R_TYPE (irel->r_info) >= R_NDS32_SDA15S3_RELA + && ELF32_R_TYPE (irel->r_info) <= R_NDS32_SDA15S0_RELA) + || (ELF32_R_TYPE (irel->r_info) >= R_NDS32_SDA12S2_DP_RELA + && ELF32_R_TYPE (irel->r_info) <= R_NDS32_SDA12S2_SP_RELA) + || (ELF32_R_TYPE (irel->r_info) >= R_NDS32_SDA16S3_RELA + && ELF32_R_TYPE (irel->r_info) <= R_NDS32_SDA19S0_RELA)) + { + bfd_vma gp; + bfd *output_bfd = sec->output_section->owner; + bfd_reloc_status_type r; + + /* If the symbol is in the abs section, the out_bfd will be null. + This happens when the relocation has a symbol@GOTOFF. */ + r = nds32_elf_final_sda_base (output_bfd, link_info, &gp, FALSE); + if (r != bfd_reloc_ok) + { + off += 4; + continue; + } + + relocation -= gp; + + /* Make sure alignment is correct. */ + if (relocation & align) + { + /* Incorrect alignment. */ + (*_bfd_error_handler) + (_("%s: warning: unaligned small data access. " + "For entry: {%d, %d, %d}, addr = 0x%x, align = 0x%x."), + bfd_get_filename (abfd), irel->r_offset, + irel->r_info, irel->r_addend, relocation, align); + off += 4; + continue; + } + } + + insn = insn_with_reg + | ((relocation >> shift) & nds32_elf_irel_mask (irel)); + } + else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_REGION_BEGIN + || ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_REGION_END) + { + /* These relocations do not have to relocate contens, so it can + be regard as instruction without relocation. */ + } + else + { + off += 4; + continue; + } + } + + snprintf (code, sizeof (code), "%08lx", insn); + /* Copy "code". */ + entry = (struct elf_nds32_code_hash_entry*) + bfd_hash_lookup (&ex9_code_table, code, TRUE, TRUE); + if (entry == NULL) + { + (*_bfd_error_handler) + (_("%P%F: failed creating ex9.it %s hash table: %E\n"), code); + return FALSE; + } + if (h) + { + if (h->root.type == bfd_link_hash_undefined) + return TRUE; + /* Global symbol. */ + /* In order to do sethi with different symbol but same value. */ + if (entry->m_list == NULL) + { + struct elf_link_hash_entry_mul_list *m_list_new; + struct elf_link_hash_entry_list *h_list_new; + + m_list_new = (struct elf_link_hash_entry_mul_list *) + bfd_malloc (sizeof (struct elf_link_hash_entry_mul_list)); + h_list_new = (struct elf_link_hash_entry_list *) + bfd_malloc (sizeof (struct elf_link_hash_entry_list)); + entry->m_list = m_list_new; + m_list_new->h_list = h_list_new; + m_list_new->rel_backup = rel_backup; + m_list_new->times = 1; + m_list_new->irel = jrel; + m_list_new->next = NULL; + h_list_new->h = h; + h_list_new->next = NULL; + } + else + { + struct elf_link_hash_entry_mul_list *m_list = entry->m_list; + struct elf_link_hash_entry_list *h_list; + + while (m_list) + { + /* Build the different symbols that point to the same address. */ + h_list = m_list->h_list; + if (h_list->h->root.u.def.value == h->root.u.def.value + && h_list->h->root.u.def.section->output_section->vma + == h->root.u.def.section->output_section->vma + && h_list->h->root.u.def.section->output_offset + == h->root.u.def.section->output_offset + && m_list->rel_backup.r_addend == rel_backup.r_addend) + { + m_list->times++; + m_list->irel = jrel; + while (h_list->h != h && h_list->next) + h_list = h_list->next; + if (h_list->h != h) + { + struct elf_link_hash_entry_list *h_list_new; + + h_list_new = (struct elf_link_hash_entry_list *) + bfd_malloc (sizeof (struct elf_link_hash_entry_list)); + h_list->next = h_list_new; + h_list_new->h = h; + h_list_new->next = NULL; + } + break; + } + /* The sethi case may have different address but the + hi20 is the same. */ + else if (ELF32_R_TYPE (jrel->r_info) == R_NDS32_HI20_RELA + && m_list->next == NULL) + { + struct elf_link_hash_entry_mul_list *m_list_new; + struct elf_link_hash_entry_list *h_list_new; + + m_list_new = (struct elf_link_hash_entry_mul_list *) + bfd_malloc (sizeof (struct elf_link_hash_entry_mul_list)); + h_list_new = (struct elf_link_hash_entry_list *) + bfd_malloc (sizeof (struct elf_link_hash_entry_list)); + m_list->next = m_list_new; + m_list_new->h_list = h_list_new; + m_list_new->rel_backup = rel_backup; + m_list_new->times = 1; + m_list_new->irel = jrel; + m_list_new->next = NULL; + h_list_new->h = h; + h_list_new->next = NULL; + break; + } + m_list = m_list->next; + } + if (!m_list) + { + off += 4; + continue; + } + } + } + else + { + /* Local symbol and insn without relocation*/ + entry->times++; + entry->rel_backup = rel_backup; + } + + /* Use in sethi insn with constant and global symbol in same format. */ + if (!jrel) + entry->const_insn = 1; + else + entry->irel = jrel; + entry->sec = isec; + off += 4; + } + } + return TRUE; +} + +/* Set the _ITB_BASE, and point it to ex9 table. */ + +bfd_boolean +nds32_elf_ex9_itb_base (struct bfd_link_info *link_info) +{ + bfd *abfd; + asection *sec; + bfd *output_bfd = NULL; + struct bfd_link_hash_entry *bh = NULL; + int target_optimize; + struct elf_nds32_link_hash_table *table; + + if (is_ITB_BASE_set == 1) + return TRUE; + + is_ITB_BASE_set = 1; + + table = nds32_elf_hash_table (link_info); + target_optimize = table->target_optimize; + + for (abfd = link_info->input_bfds; abfd != NULL; + abfd = abfd->link_next) + { + sec = bfd_get_section_by_name (abfd, ".ex9.itable"); + if (sec != NULL) + { + output_bfd = sec->output_section->owner; + break; + } + } + if (output_bfd == NULL) + { + output_bfd = link_info->output_bfd; + if (output_bfd->sections == NULL) + return TRUE; + else + sec = bfd_abs_section_ptr; + } + bh = bfd_link_hash_lookup (link_info->hash, "_ITB_BASE_", + FALSE, FALSE, TRUE); + return (_bfd_generic_link_add_one_symbol + (link_info, output_bfd, "_ITB_BASE_", + BSF_GLOBAL | BSF_WEAK, sec, + /* We don't know its value yet, set it to 0. */ + (target_optimize & NDS32_RELAX_EX9_ON) ? 0 : (-234 * 4), + (const char *) NULL, FALSE, get_elf_backend_data + (output_bfd)->collect, &bh)); +} /* End EX9.IT */ + + +#define ELF_ARCH bfd_arch_nds32 +#define ELF_MACHINE_CODE EM_NDS32 +#define ELF_MAXPAGESIZE 0x1000 + +#define TARGET_BIG_SYM bfd_elf32_nds32be_vec +#define TARGET_BIG_NAME "elf32-nds32be" +#define TARGET_LITTLE_SYM bfd_elf32_nds32le_vec +#define TARGET_LITTLE_NAME "elf32-nds32le" + +#define elf_info_to_howto nds32_info_to_howto +#define elf_info_to_howto_rel nds32_info_to_howto_rel + +#define bfd_elf32_bfd_link_hash_table_create nds32_elf_link_hash_table_create +#define bfd_elf32_bfd_merge_private_bfd_data nds32_elf_merge_private_bfd_data +#define bfd_elf32_bfd_print_private_bfd_data nds32_elf_print_private_bfd_data +#define bfd_elf32_bfd_relax_section nds32_elf_relax_section +#define bfd_elf32_bfd_set_private_flags nds32_elf_set_private_flags + +#define elf_backend_action_discarded nds32_elf_action_discarded +#define elf_backend_add_symbol_hook nds32_elf_add_symbol_hook +#define elf_backend_check_relocs nds32_elf_check_relocs +#define elf_backend_adjust_dynamic_symbol nds32_elf_adjust_dynamic_symbol +#define elf_backend_create_dynamic_sections nds32_elf_create_dynamic_sections +#define elf_backend_finish_dynamic_sections nds32_elf_finish_dynamic_sections +#define elf_backend_finish_dynamic_symbol nds32_elf_finish_dynamic_symbol +#define elf_backend_size_dynamic_sections nds32_elf_size_dynamic_sections +#define elf_backend_relocate_section nds32_elf_relocate_section +#define elf_backend_gc_mark_hook nds32_elf_gc_mark_hook +#define elf_backend_gc_sweep_hook nds32_elf_gc_sweep_hook +#define elf_backend_grok_prstatus nds32_elf_grok_prstatus +#define elf_backend_grok_psinfo nds32_elf_grok_psinfo +#define elf_backend_reloc_type_class nds32_elf_reloc_type_class +#define elf_backend_copy_indirect_symbol nds32_elf_copy_indirect_symbol +#define elf_backend_link_output_symbol_hook nds32_elf_output_symbol_hook +#define elf_backend_output_arch_syms nds32_elf_output_arch_syms +#define elf_backend_object_p nds32_elf_object_p +#define elf_backend_final_write_processing nds32_elf_final_write_processing +#define elf_backend_special_sections nds32_elf_special_sections + +#define elf_backend_can_gc_sections 1 +#define elf_backend_can_refcount 1 +#define elf_backend_want_got_plt 1 +#define elf_backend_plt_readonly 1 +#define elf_backend_want_plt_sym 0 +#define elf_backend_got_header_size 12 +#define elf_backend_may_use_rel_p 1 +#define elf_backend_default_use_rela_p 1 +#define elf_backend_may_use_rela_p 1 + +#include "elf32-target.h" + +#undef ELF_MAXPAGESIZE +#define ELF_MAXPAGESIZE 0x2000 + +#undef TARGET_BIG_SYM +#define TARGET_BIG_SYM bfd_elf32_nds32belin_vec +#undef TARGET_BIG_NAME +#define TARGET_BIG_NAME "elf32-nds32be-linux" +#undef TARGET_LITTLE_SYM +#define TARGET_LITTLE_SYM bfd_elf32_nds32lelin_vec +#undef TARGET_LITTLE_NAME +#define TARGET_LITTLE_NAME "elf32-nds32le-linux" +#undef elf32_bed +#define elf32_bed elf32_nds32_lin_bed + +#include "elf32-target.h" diff --git a/bfd/elf32-nds32.h b/bfd/elf32-nds32.h new file mode 100644 index 0000000..7b97f30 --- /dev/null +++ b/bfd/elf32-nds32.h @@ -0,0 +1,147 @@ +/* NDS32-specific support for 32-bit ELF. + Copyright (C) 2012-2013 Free Software Foundation, Inc. + Contributed by Andes Technology Corporation. + + 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.*/ + +#ifndef ELF32_NDS32_H +#define ELF32_NDS32_H + +/* Relocation flags encoded in r_addend. */ + +/* Relocation flags for R_NDS32_ERLAX_ENTRY. */ + +/* Set if relax on this section is done or disabled. */ +#define R_NDS32_RELAX_ENTRY_DISABLE_RELAX_FLAG (1 << 31) +/* Optimize for performance. */ +#define R_NDS32_RELAX_ENTRY_OPTIMIZE_FLAG (1 << 30) +/* Optimize for size. Branch destination 4-byte adjustment + may be disabled. */ +#define R_NDS32_RELAX_ENTRY_OPTIMIZE_FOR_SPACE_FLAG (1 << 29) +/* To distinguish the assembly code generated by compiler + or written manually. */ +#define R_NDS32_RELAX_ENTRY_VERBATIM_FLAG (1 << 28) +/* EX9 and link-time IFC must be explicitly enabled, so we + won't mess up handcraft assembly code. */ +/* Enable EX9 optimization for this section. */ +#define R_NDS32_RELAX_ENTRY_EX9_FLAG (1 << 2) +/* Enable IFC optimization for this section. */ +#define R_NDS32_RELAX_ENTRY_IFC_FLAG (1 << 3) + + +/* Relocation flags for R_NDS32_INSN16. */ + +#define R_NDS32_INSN16_CONVERT_FLAG (1 << 0) +/* Convert a gp-relative access (e.g., lwi.gp) + to fp-as-gp access (lwi37.fp). + This value is used by linker internally only. + It's fine to change the vlaue. */ +#define R_NDS32_INSN16_FP7U2_FLAG (1 << 1) + +/* Relocation flags for R_NDS32_RELAX_REGION_OMIT_FP_START/END. */ + +/* OMIT_FP_FLAG marks the region for applying fp-as-gp + optimization. */ +#define R_NDS32_RELAX_REGION_OMIT_FP_FLAG (1 << 0) +/* NOT_OMIT_FP_FLAG is set if this region is not worth + for fp-as-gp. */ +#define R_NDS32_RELAX_REGION_NOT_OMIT_FP_FLAG (1 << 1) +/* Suppress EX9 optimization in the region. */ +#define R_NDS32_RELAX_REGION_NO_EX9_FLAG (1 << 2) +/* A Innermost loop region. Some optimizations is suppressed + in this region due to performance drop. */ +#define R_NDS32_RELAX_REGION_INNERMOST_LOOP_FLAG (1 << 4) + +/* Relax tag for nds32_elf_relax_section, we have to specify which + optimization do in this round. */ +enum +{ + NDS32_RELAX_NONE_ROUND = 0, + NDS32_RELAX_JUMP_IFC_ROUND = 1, + NDS32_RELAX_EX9_BUILD_ROUND, + NDS32_RELAX_EX9_REPLACE_ROUND + +}; + +/* Optimization status mask. */ +#define NDS32_RELAX_JUMP_IFC_DONE (1 << 0) +#define NDS32_RELAX_EX9_DONE (1 << 1) + +/* Optimization turn on mask. */ +#define NDS32_RELAX_JUMP_IFC_ON (1 << 0) +#define NDS32_RELAX_EX9_ON (1 << 1) + +/* The break 0xea defined for ex9 table to keep for trace32 to use 0xeaea. */ +#define INSN_BREAK_EA 0x64001d4a + +extern void nds32_insertion_sort + (void *, size_t, size_t, int (*) (const void *, const void *)); + +extern int nds32_elf_ex9_init (void); +extern void nds32_elf_ex9_reloc_jmp (struct bfd_link_info *); +extern void nds32_elf_ex9_finish (struct bfd_link_info *); +extern bfd_boolean nds32_elf_ex9_itb_base (struct bfd_link_info *); +extern void nds32_elf_ex9_import_table (struct bfd_link_info *); +extern bfd_boolean nds32_elf_ifc_reloc (void); +extern bfd_boolean nds32_elf_ifc_finish (struct bfd_link_info *); +extern int nds32_convert_32_to_16 (bfd *, uint32_t, uint16_t *, int *); +extern int nds32_convert_16_to_32 (bfd *, uint16_t, uint32_t *); +extern void bfd_elf32_nds32_set_target_option (struct bfd_link_info *, int, int, + FILE *, int, int, int, int, FILE *, FILE *, + int, int, bfd_boolean, bfd_boolean); + +#define nds32_elf_hash_table(info) \ + (elf_hash_table_id ((struct elf_link_hash_table *) ((info)->hash)) \ + == NDS32_ELF_DATA ? ((struct elf_nds32_link_hash_table *) ((info)->hash)) : NULL) + +/* Hash table structure for target nds32. There are some members to + save target options passed from nds32elf.em to bfd. */ + +struct elf_nds32_link_hash_table +{ + struct elf_link_hash_table root; + + /* Short-cuts to get to dynamic linker sections. */ + asection *sgot; + asection *sgotplt; + asection *srelgot; + asection *splt; + asection *srelplt; + asection *sdynbss; + asection *srelbss; + + /* Small local sym to section mapping cache. */ + struct sym_cache sym_cache; + + /* Target dependent options. */ + int relax_fp_as_gp; /* --mrelax-omit-fp */ + int eliminate_gc_relocs; /* --meliminate-gc-relocs */ + FILE *sym_ld_script; /* --mgen-symbol-ld-script=<file> */ + /* Disable if linking a dynamically linked executable. */ + int load_store_relax; + int target_optimize; /* Switch optimization. */ + int relax_status; /* Finished optimization. */ + int relax_round; /* Going optimization. */ + FILE *ex9_export_file; /* --mexport-ex9=<file> */ + FILE *ex9_import_file; /* --mimport-ex9=<file> */ + int update_ex9_table; /* --mupdate-ex9. */ + int ex9_limit; + bfd_boolean ex9_loop_aware; /* Ignore ex9 if inside a loop. */ + bfd_boolean ifc_loop_aware; /* Ignore ifc if inside a loop. */ +}; +#endif diff --git a/bfd/libbfd.h b/bfd/libbfd.h index 4aaecbf..87605b9 100644 --- a/bfd/libbfd.h +++ b/bfd/libbfd.h @@ -1746,6 +1746,100 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@", "BFD_RELOC_M32R_GOTPC_HI_ULO", "BFD_RELOC_M32R_GOTPC_HI_SLO", "BFD_RELOC_M32R_GOTPC_LO", + "BFD_RELOC_NDS32_20", + "BFD_RELOC_NDS32_9_PCREL", + "BFD_RELOC_NDS32_WORD_9_PCREL", + "BFD_RELOC_NDS32_15_PCREL", + "BFD_RELOC_NDS32_17_PCREL", + "BFD_RELOC_NDS32_25_PCREL", + "BFD_RELOC_NDS32_HI20", + "BFD_RELOC_NDS32_LO12S3", + "BFD_RELOC_NDS32_LO12S2", + "BFD_RELOC_NDS32_LO12S1", + "BFD_RELOC_NDS32_LO12S0", + "BFD_RELOC_NDS32_LO12S0_ORI", + "BFD_RELOC_NDS32_SDA15S3", + "BFD_RELOC_NDS32_SDA15S2", + "BFD_RELOC_NDS32_SDA15S1", + "BFD_RELOC_NDS32_SDA15S0", + "BFD_RELOC_NDS32_SDA16S3", + "BFD_RELOC_NDS32_SDA17S2", + "BFD_RELOC_NDS32_SDA18S1", + "BFD_RELOC_NDS32_SDA19S0", + "BFD_RELOC_NDS32_GOT20", + "BFD_RELOC_NDS32_9_PLTREL", + "BFD_RELOC_NDS32_25_PLTREL", + "BFD_RELOC_NDS32_COPY", + "BFD_RELOC_NDS32_GLOB_DAT", + "BFD_RELOC_NDS32_JMP_SLOT", + "BFD_RELOC_NDS32_RELATIVE", + "BFD_RELOC_NDS32_GOTOFF", + "BFD_RELOC_NDS32_GOTOFF_HI20", + "BFD_RELOC_NDS32_GOTOFF_LO12", + "BFD_RELOC_NDS32_GOTPC20", + "BFD_RELOC_NDS32_GOT_HI20", + "BFD_RELOC_NDS32_GOT_LO12", + "BFD_RELOC_NDS32_GOTPC_HI20", + "BFD_RELOC_NDS32_GOTPC_LO12", + "BFD_RELOC_NDS32_INSN16", + "BFD_RELOC_NDS32_LABEL", + "BFD_RELOC_NDS32_LONGCALL1", + "BFD_RELOC_NDS32_LONGCALL2", + "BFD_RELOC_NDS32_LONGCALL3", + "BFD_RELOC_NDS32_LONGJUMP1", + "BFD_RELOC_NDS32_LONGJUMP2", + "BFD_RELOC_NDS32_LONGJUMP3", + "BFD_RELOC_NDS32_LOADSTORE", + "BFD_RELOC_NDS32_9_FIXED", + "BFD_RELOC_NDS32_15_FIXED", + "BFD_RELOC_NDS32_17_FIXED", + "BFD_RELOC_NDS32_25_FIXED", + "BFD_RELOC_NDS32_PLTREL_HI20", + "BFD_RELOC_NDS32_PLTREL_LO12", + "BFD_RELOC_NDS32_PLT_GOTREL_HI20", + "BFD_RELOC_NDS32_PLT_GOTREL_LO12", + "BFD_RELOC_NDS32_SDA12S2_DP", + "BFD_RELOC_NDS32_SDA12S2_SP", + "BFD_RELOC_NDS32_LO12S2_DP", + "BFD_RELOC_NDS32_LO12S2_SP", + "BFD_RELOC_NDS32_DWARF2_OP1", + "BFD_RELOC_NDS32_DWARF2_OP2", + "BFD_RELOC_NDS32_DWARF2_LEB", + "BFD_RELOC_NDS32_UPDATE_TA", + "BFD_RELOC_NDS32_PLT_GOTREL_LO20", + "BFD_RELOC_NDS32_PLT_GOTREL_LO15", + "BFD_RELOC_NDS32_PLT_GOTREL_LO19", + "BFD_RELOC_NDS32_GOT_LO15", + "BFD_RELOC_NDS32_GOT_LO19", + "BFD_RELOC_NDS32_GOTOFF_LO15", + "BFD_RELOC_NDS32_GOTOFF_LO19", + "BFD_RELOC_NDS32_GOT15S2", + "BFD_RELOC_NDS32_GOT17S2", + "BFD_RELOC_NDS32_5", + "BFD_RELOC_NDS32_10_UPCREL", + "BFD_RELOC_NDS32_SDA_FP7U2_RELA", + "BFD_RELOC_NDS32_RELAX_ENTRY", + "BFD_RELOC_NDS32_GOT_SUFF", + "BFD_RELOC_NDS32_GOTOFF_SUFF", + "BFD_RELOC_NDS32_PLT_GOT_SUFF", + "BFD_RELOC_NDS32_MULCALL_SUFF", + "BFD_RELOC_NDS32_PTR", + "BFD_RELOC_NDS32_PTR_COUNT", + "BFD_RELOC_NDS32_PTR_RESOLVED", + "BFD_RELOC_NDS32_PLTBLOCK", + "BFD_RELOC_NDS32_RELAX_REGION_BEGIN", + "BFD_RELOC_NDS32_RELAX_REGION_END", + "BFD_RELOC_NDS32_MINUEND", + "BFD_RELOC_NDS32_SUBTRAHEND", + "BFD_RELOC_NDS32_DIFF8", + "BFD_RELOC_NDS32_DIFF16", + "BFD_RELOC_NDS32_DIFF32", + "BFD_RELOC_NDS32_DIFF_ULEB128", + "BFD_RELOC_NDS32_25_ABS", + "BFD_RELOC_NDS32_DATA", + "BFD_RELOC_NDS32_TRAN", + "BFD_RELOC_NDS32_17IFC_PCREL", + "BFD_RELOC_NDS32_10IFCU_PCREL", "BFD_RELOC_V850_9_PCREL", "BFD_RELOC_V850_22_PCREL", "BFD_RELOC_V850_SDA_16_16_OFFSET", diff --git a/bfd/reloc.c b/bfd/reloc.c index 77a04f8..0d191f1 100644 --- a/bfd/reloc.c +++ b/bfd/reloc.c @@ -3839,6 +3839,274 @@ ENUMDOC ENUM + BFD_RELOC_NDS32_20 +ENUMDOC + NDS32 relocs. + This is a 20 bit absolute address. +ENUM + BFD_RELOC_NDS32_9_PCREL +ENUMDOC + This is a 9-bit pc-relative reloc with the right 1 bit assumed to be 0. +ENUM + BFD_RELOC_NDS32_WORD_9_PCREL +ENUMDOC + This is a 9-bit pc-relative reloc with the right 1 bit assumed to be 0. +ENUM + BFD_RELOC_NDS32_15_PCREL +ENUMDOC + This is an 15-bit reloc with the right 1 bit assumed to be 0. +ENUM + BFD_RELOC_NDS32_17_PCREL +ENUMDOC + This is an 17-bit reloc with the right 1 bit assumed to be 0. +ENUM + BFD_RELOC_NDS32_25_PCREL +ENUMDOC + This is a 25-bit reloc with the right 1 bit assumed to be 0. +ENUM + BFD_RELOC_NDS32_HI20 +ENUMDOC + This is a 20-bit reloc containing the high 20 bits of an address + used with the lower 12 bits +ENUM + BFD_RELOC_NDS32_LO12S3 +ENUMDOC + This is a 12-bit reloc containing the lower 12 bits of an address + then shift right by 3. This is used with ldi,sdi... +ENUM + BFD_RELOC_NDS32_LO12S2 +ENUMDOC + This is a 12-bit reloc containing the lower 12 bits of an address + then shift left by 2. This is used with lwi,swi... +ENUM + BFD_RELOC_NDS32_LO12S1 +ENUMDOC + This is a 12-bit reloc containing the lower 12 bits of an address + then shift left by 1. This is used with lhi,shi... +ENUM + BFD_RELOC_NDS32_LO12S0 +ENUMDOC + This is a 12-bit reloc containing the lower 12 bits of an address + then shift left by 0. This is used with lbisbi... +ENUM + BFD_RELOC_NDS32_LO12S0_ORI +ENUMDOC + This is a 12-bit reloc containing the lower 12 bits of an address + then shift left by 0. This is only used with branch relaxations +ENUM + BFD_RELOC_NDS32_SDA15S3 +ENUMDOC + This is a 15-bit reloc containing the small data area 18-bit signed offset + and shift left by 3 for use in ldi, sdi... +ENUM + BFD_RELOC_NDS32_SDA15S2 +ENUMDOC + This is a 15-bit reloc containing the small data area 17-bit signed offset + and shift left by 2 for use in lwi, swi... +ENUM + BFD_RELOC_NDS32_SDA15S1 +ENUMDOC + This is a 15-bit reloc containing the small data area 16-bit signed offset + and shift left by 1 for use in lhi, shi... +ENUM + BFD_RELOC_NDS32_SDA15S0 +ENUMDOC + This is a 15-bit reloc containing the small data area 15-bit signed offset + and shift left by 0 for use in lbi, sbi... +ENUM + BFD_RELOC_NDS32_SDA16S3 +ENUMDOC + This is a 16-bit reloc containing the small data area 16-bit signed offset + and shift left by 3 +ENUM + BFD_RELOC_NDS32_SDA17S2 +ENUMDOC + This is a 17-bit reloc containing the small data area 17-bit signed offset + and shift left by 2 for use in lwi.gp, swi.gp... +ENUM + BFD_RELOC_NDS32_SDA18S1 +ENUMDOC + This is a 18-bit reloc containing the small data area 18-bit signed offset + and shift left by 1 for use in lhi.gp, shi.gp... +ENUM + BFD_RELOC_NDS32_SDA19S0 +ENUMDOC + This is a 19-bit reloc containing the small data area 19-bit signed offset + and shift left by 0 for use in lbi.gp, sbi.gp... +ENUM + BFD_RELOC_NDS32_GOT20 +ENUMX + BFD_RELOC_NDS32_9_PLTREL +ENUMX + BFD_RELOC_NDS32_25_PLTREL +ENUMX + BFD_RELOC_NDS32_COPY +ENUMX + BFD_RELOC_NDS32_GLOB_DAT +ENUMX + BFD_RELOC_NDS32_JMP_SLOT +ENUMX + BFD_RELOC_NDS32_RELATIVE +ENUMX + BFD_RELOC_NDS32_GOTOFF +ENUMX + BFD_RELOC_NDS32_GOTOFF_HI20 +ENUMX + BFD_RELOC_NDS32_GOTOFF_LO12 +ENUMX + BFD_RELOC_NDS32_GOTPC20 +ENUMX + BFD_RELOC_NDS32_GOT_HI20 +ENUMX + BFD_RELOC_NDS32_GOT_LO12 +ENUMX + BFD_RELOC_NDS32_GOTPC_HI20 +ENUMX + BFD_RELOC_NDS32_GOTPC_LO12 +ENUMDOC + for PIC +ENUM + BFD_RELOC_NDS32_INSN16 +ENUMX + BFD_RELOC_NDS32_LABEL +ENUMX + BFD_RELOC_NDS32_LONGCALL1 +ENUMX + BFD_RELOC_NDS32_LONGCALL2 +ENUMX + BFD_RELOC_NDS32_LONGCALL3 +ENUMX + BFD_RELOC_NDS32_LONGJUMP1 +ENUMX + BFD_RELOC_NDS32_LONGJUMP2 +ENUMX + BFD_RELOC_NDS32_LONGJUMP3 +ENUMX + BFD_RELOC_NDS32_LOADSTORE +ENUMX + BFD_RELOC_NDS32_9_FIXED +ENUMX + BFD_RELOC_NDS32_15_FIXED +ENUMX + BFD_RELOC_NDS32_17_FIXED +ENUMX + BFD_RELOC_NDS32_25_FIXED +ENUMDOC + for relax +ENUM + BFD_RELOC_NDS32_PLTREL_HI20 +ENUMX + BFD_RELOC_NDS32_PLTREL_LO12 +ENUMX + BFD_RELOC_NDS32_PLT_GOTREL_HI20 +ENUMX + BFD_RELOC_NDS32_PLT_GOTREL_LO12 +ENUMDOC + for PIC +ENUM + BFD_RELOC_NDS32_SDA12S2_DP +ENUMX + BFD_RELOC_NDS32_SDA12S2_SP +ENUMX + BFD_RELOC_NDS32_LO12S2_DP +ENUMX + BFD_RELOC_NDS32_LO12S2_SP +ENUMDOC + for floating point +ENUM + BFD_RELOC_NDS32_DWARF2_OP1 +ENUMX + BFD_RELOC_NDS32_DWARF2_OP2 +ENUMX + BFD_RELOC_NDS32_DWARF2_LEB +ENUMDOC + for dwarf2 debug_line. +ENUM + BFD_RELOC_NDS32_UPDATE_TA +ENUMDOC + for eliminate 16-bit instructions +ENUM + BFD_RELOC_NDS32_PLT_GOTREL_LO20 +ENUMX + BFD_RELOC_NDS32_PLT_GOTREL_LO15 +ENUMX + BFD_RELOC_NDS32_PLT_GOTREL_LO19 +ENUMX + BFD_RELOC_NDS32_GOT_LO15 +ENUMX + BFD_RELOC_NDS32_GOT_LO19 +ENUMX + BFD_RELOC_NDS32_GOTOFF_LO15 +ENUMX + BFD_RELOC_NDS32_GOTOFF_LO19 +ENUMX + BFD_RELOC_NDS32_GOT15S2 +ENUMX + BFD_RELOC_NDS32_GOT17S2 +ENUMDOC + for PIC object relaxation +ENUM + BFD_RELOC_NDS32_5 +ENUMDOC + NDS32 relocs. + This is a 5 bit absolute address. +ENUM + BFD_RELOC_NDS32_10_UPCREL +ENUMDOC + This is a 10-bit unsigned pc-relative reloc with the right 1 bit assumed to be 0. +ENUM + BFD_RELOC_NDS32_SDA_FP7U2_RELA +ENUMDOC + If fp were omitted, fp can used as another gp. +ENUM + BFD_RELOC_NDS32_RELAX_ENTRY +ENUMX + BFD_RELOC_NDS32_GOT_SUFF +ENUMX + BFD_RELOC_NDS32_GOTOFF_SUFF +ENUMX + BFD_RELOC_NDS32_PLT_GOT_SUFF +ENUMX + BFD_RELOC_NDS32_MULCALL_SUFF +ENUMX + BFD_RELOC_NDS32_PTR +ENUMX + BFD_RELOC_NDS32_PTR_COUNT +ENUMX + BFD_RELOC_NDS32_PTR_RESOLVED +ENUMX + BFD_RELOC_NDS32_PLTBLOCK +ENUMX + BFD_RELOC_NDS32_RELAX_REGION_BEGIN +ENUMX + BFD_RELOC_NDS32_RELAX_REGION_END +ENUMX + BFD_RELOC_NDS32_MINUEND +ENUMX + BFD_RELOC_NDS32_SUBTRAHEND +ENUMX + BFD_RELOC_NDS32_DIFF8 +ENUMX + BFD_RELOC_NDS32_DIFF16 +ENUMX + BFD_RELOC_NDS32_DIFF32 +ENUMX + BFD_RELOC_NDS32_DIFF_ULEB128 +ENUMX + BFD_RELOC_NDS32_25_ABS +ENUMX + BFD_RELOC_NDS32_DATA +ENUMX + BFD_RELOC_NDS32_TRAN +ENUMX + BFD_RELOC_NDS32_17IFC_PCREL +ENUMX + BFD_RELOC_NDS32_10IFCU_PCREL +ENUMDOC + relaxation relative relocation types + + +ENUM BFD_RELOC_V850_9_PCREL ENUMDOC This is a 9-bit reloc diff --git a/bfd/targets.c b/bfd/targets.c index b117bfe..26fb972 100644 --- a/bfd/targets.c +++ b/bfd/targets.c @@ -673,6 +673,10 @@ extern const bfd_target bfd_elf32_ntradbigmips_vec; extern const bfd_target bfd_elf32_ntradlittlemips_vec; extern const bfd_target bfd_elf32_ntradbigmips_freebsd_vec; extern const bfd_target bfd_elf32_ntradlittlemips_freebsd_vec; +extern const bfd_target bfd_elf32_nds32be_vec; +extern const bfd_target bfd_elf32_nds32le_vec; +extern const bfd_target bfd_elf32_nds32belin_vec; +extern const bfd_target bfd_elf32_nds32lelin_vec; extern const bfd_target bfd_elf32_openrisc_vec; extern const bfd_target bfd_elf32_or32_big_vec; extern const bfd_target bfd_elf32_pj_vec; @@ -1061,6 +1065,10 @@ static const bfd_target * const _bfd_target_vector[] = &bfd_elf32_ntradbigmips_freebsd_vec, &bfd_elf32_ntradlittlemips_freebsd_vec, #endif + &bfd_elf32_nds32be_vec, + &bfd_elf32_nds32le_vec, + &bfd_elf32_nds32belin_vec, + &bfd_elf32_nds32lelin_vec, &bfd_elf32_openrisc_vec, &bfd_elf32_or32_big_vec, &bfd_elf32_pj_vec, diff --git a/binutils/ChangeLog b/binutils/ChangeLog index 9a3d22a..49f1b74 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,18 @@ +2013-12-13 Kuan-Lin Chen <kuanlinchentw@gmail.com> + Wei-Cheng Wang <cole945@gmail.com> + + * readelf.c: Include elf/nds32.h + (guess_is_rela): Add case for EM_NDS32. + (dump_relocations): Add case for EM_NDS32. + (decode_NDS32_machine_flags): New. + (get_machine_flags): Add case for EM_NDS32. + (is_32bit_abs_reloc): Likewise. + (is_16bit_abs_reloc): Likewise. + (process_nds32_specific): New. + (process_arch_specific): Add case for EM_NDS32. + * NEWS: Announce Andes nds32 support. + * MAINTAINERS: Add nds32 maintainers. + 2013-12-10 Roland McGrath <mcgrathr@google.com> * Makefile.am (install-exec-local): Prefix libtool invocation with diff --git a/binutils/MAINTAINERS b/binutils/MAINTAINERS index f3ecf09..cb6aa03 100644 --- a/binutils/MAINTAINERS +++ b/binutils/MAINTAINERS @@ -109,6 +109,8 @@ responsibility among the other maintainers. MN10300 Alexandre Oliva <aoliva@redhat.com> Moxie Anthony Green <green@moxielogic.com> MSP430 Dmitry Diky <diwil@spec.ru> + NDS32 Kuan-Lin Chen <kuanlinchentw@gmail.com> + NDS32 Wei-Cheng Wang <cole945@gmail.com> NetBSD support Matt Thomas <matt@netbsd.org> Nios II Sandra Loosemore <sandra@codesourcery.com> Nios II Andrew Jenner <andrew@codesourcery.com> diff --git a/binutils/NEWS b/binutils/NEWS index 26555b0..f1f0061 100644 --- a/binutils/NEWS +++ b/binutils/NEWS @@ -1,5 +1,7 @@ -*- text -*- +* Add support for the Andes NDS32. + Changes in 2.24: * Objcopy now supports wildcard characters in command line options that take diff --git a/binutils/readelf.c b/binutils/readelf.c index b9b06a1..7d228d6 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -131,6 +131,7 @@ #include "elf/moxie.h" #include "elf/mt.h" #include "elf/msp430.h" +#include "elf/nds32.h" #include "elf/nios2.h" #include "elf/or32.h" #include "elf/pj.h" @@ -626,6 +627,7 @@ guess_is_rela (unsigned int e_machine) case EM_MSP430: case EM_MSP430_OLD: case EM_MT: + case EM_NDS32: case EM_NIOS32: case EM_PPC64: case EM_PPC: @@ -1144,6 +1146,10 @@ dump_relocations (FILE * file, rtype = elf_msp430_reloc_type (type); break; + case EM_NDS32: + rtype = elf_nds32_reloc_type (type); + break; + case EM_PPC: rtype = elf_ppc_reloc_type (type); break; @@ -2307,6 +2313,207 @@ decode_ARM_machine_flags (unsigned e_flags, char buf[]) strcat (buf,_(", <unknown>")); } +static void +decode_NDS32_machine_flags (unsigned e_flags, char buf[], size_t size) +{ + unsigned abi; + unsigned arch; + unsigned config; + unsigned version; + int has_fpu = 0; + int r = 0; + + static const char *ABI_STRINGS[] = + { + "ABI v0", /* use r5 as return register; only used in N1213HC */ + "ABI v1", /* use r0 as return register */ + "ABI v2", /* use r0 as return register and don't reserve 24 bytes for arguments */ + "ABI v2fp", /* for FPU */ + "AABI" + }; + static const char *VER_STRINGS[] = + { + "Andes ELF V1.3 or older", + "Andes ELF V1.3.1", + "Andes ELF V1.4" + }; + static const char *ARCH_STRINGS[] = + { + "", + "Andes Star v1.0", + "Andes Star v2.0", + "Andes Star v3.0", + "Andes Star v3.0m" + }; + + abi = EF_NDS_ABI & e_flags; + arch = EF_NDS_ARCH & e_flags; + config = EF_NDS_INST & e_flags; + version = EF_NDS32_ELF_VERSION & e_flags; + + memset (buf, 0, size); + + switch (abi) + { + case E_NDS_ABI_V0: + case E_NDS_ABI_V1: + case E_NDS_ABI_V2: + case E_NDS_ABI_V2FP: + case E_NDS_ABI_AABI: + /* In case there are holes in the array. */ + r += snprintf (buf + r, size - r, ", %s", ABI_STRINGS[abi >> EF_NDS_ABI_SHIFT]); + break; + + default: + r += snprintf (buf + r, size - r, ", <unrecognized ABI>"); + break; + } + + switch (version) + { + case E_NDS32_ELF_VER_1_2: + case E_NDS32_ELF_VER_1_3: + case E_NDS32_ELF_VER_1_4: + r += snprintf (buf + r, size - r, ", %s", VER_STRINGS[version >> EF_NDS32_ELF_VERSION_SHIFT]); + break; + + default: + r += snprintf (buf + r, size - r, ", <unrecognized ELF version number>"); + break; + } + + if (E_NDS_ABI_V0 == abi) + { + /* OLD ABI; only used in N1213HC, has performance extension 1. */ + r += snprintf (buf + r, size - r, ", Andes Star v1.0, N1213HC, MAC, PERF1"); + if (arch == E_NDS_ARCH_STAR_V1_0) + r += snprintf (buf + r, size -r, ", 16b"); /* has 16-bit instructions */ + return; + } + + switch (arch) + { + case E_NDS_ARCH_STAR_V1_0: + case E_NDS_ARCH_STAR_V2_0: + case E_NDS_ARCH_STAR_V3_0: + case E_NDS_ARCH_STAR_V3_M: + r += snprintf (buf + r, size - r, ", %s", ARCH_STRINGS[arch >> EF_NDS_ARCH_SHIFT]); + break; + + default: + r += snprintf (buf + r, size - r, ", <unrecognized architecture>"); + /* ARCH version determines how the e_flags are interpreted. + If it is unknown, we cannot proceed. */ + return; + } + + /* Newer ABI; Now handle architecture specific flags. */ + if (arch == E_NDS_ARCH_STAR_V1_0) + { + if (config & E_NDS32_HAS_MFUSR_PC_INST) + r += snprintf (buf + r, size -r, ", MFUSR_PC"); + + if (!(config & E_NDS32_HAS_NO_MAC_INST)) + r += snprintf (buf + r, size -r, ", MAC"); + + if (config & E_NDS32_HAS_DIV_INST) + r += snprintf (buf + r, size -r, ", DIV"); + + if (config & E_NDS32_HAS_16BIT_INST) + r += snprintf (buf + r, size -r, ", 16b"); + } + else + { + if (config & E_NDS32_HAS_MFUSR_PC_INST) + { + if (version <= E_NDS32_ELF_VER_1_3) + r += snprintf (buf + r, size -r, ", [B8]"); + else + r += snprintf (buf + r, size -r, ", EX9"); + } + + if (config & E_NDS32_HAS_MAC_DX_INST) + r += snprintf (buf + r, size -r, ", MAC_DX"); + + if (config & E_NDS32_HAS_DIV_DX_INST) + r += snprintf (buf + r, size -r, ", DIV_DX"); + + if (config & E_NDS32_HAS_16BIT_INST) + { + if (version <= E_NDS32_ELF_VER_1_3) + r += snprintf (buf + r, size -r, ", 16b"); + else + r += snprintf (buf + r, size -r, ", IFC"); + } + } + + if (config & E_NDS32_HAS_EXT_INST) + r += snprintf (buf + r, size -r, ", PERF1"); + + if (config & E_NDS32_HAS_EXT2_INST) + r += snprintf (buf + r, size -r, ", PERF2"); + + if (config & E_NDS32_HAS_FPU_INST) + { + has_fpu = 1; + r += snprintf (buf + r, size -r, ", FPU_SP"); + } + + if (config & E_NDS32_HAS_FPU_DP_INST) + { + has_fpu = 1; + r += snprintf (buf + r, size -r, ", FPU_DP"); + } + + if (config & E_NDS32_HAS_FPU_MAC_INST) + { + has_fpu = 1; + r += snprintf (buf + r, size -r, ", FPU_MAC"); + } + + if (has_fpu) + { + switch ((config & E_NDS32_FPU_REG_CONF) >> E_NDS32_FPU_REG_CONF_SHIFT) + { + case E_NDS32_FPU_REG_8SP_4DP: + r += snprintf (buf + r, size -r, ", FPU_REG:8/4"); + break; + case E_NDS32_FPU_REG_16SP_8DP: + r += snprintf (buf + r, size -r, ", FPU_REG:16/8"); + break; + case E_NDS32_FPU_REG_32SP_16DP: + r += snprintf (buf + r, size -r, ", FPU_REG:32/16"); + break; + case E_NDS32_FPU_REG_32SP_32DP: + r += snprintf (buf + r, size -r, ", FPU_REG:32/32"); + break; + } + } + + if (config & E_NDS32_HAS_AUDIO_INST) + r += snprintf (buf + r, size -r, ", AUDIO"); + + if (config & E_NDS32_HAS_STRING_INST) + r += snprintf (buf + r, size -r, ", STR"); + + if (config & E_NDS32_HAS_REDUCED_REGS) + r += snprintf (buf + r, size -r, ", 16REG"); + + if (config & E_NDS32_HAS_VIDEO_INST) + { + if (version <= E_NDS32_ELF_VER_1_3) + r += snprintf (buf + r, size -r, ", VIDEO"); + else + r += snprintf (buf + r, size -r, ", SATURATION"); + } + + if (config & E_NDS32_HAS_ENCRIPT_INST) + r += snprintf (buf + r, size -r, ", ENCRP"); + + if (config & E_NDS32_HAS_L2C_INST) + r += snprintf (buf + r, size -r, ", L2C"); +} + static char * get_machine_flags (unsigned e_flags, unsigned e_machine) { @@ -2649,6 +2856,10 @@ get_machine_flags (unsigned e_flags, unsigned e_machine) } break; + case EM_NDS32: + decode_NDS32_machine_flags (e_flags, buf, sizeof buf); + break; + case EM_SH: switch ((e_flags & EF_SH_MACH_MASK)) { @@ -10261,6 +10472,8 @@ is_32bit_abs_reloc (unsigned int reloc_type) return reloc_type == 1; /* R_MSP430_32 or R_MSP320_ABS32. */ case EM_MT: return reloc_type == 2; /* R_MT_32. */ + case EM_NDS32: + return reloc_type == 20; /* R_NDS32_RELA. */ case EM_ALTERA_NIOS2: return reloc_type == 12; /* R_NIOS2_BFD_RELOC_32. */ case EM_NIOS32: @@ -10514,6 +10727,8 @@ is_16bit_abs_reloc (unsigned int reloc_type) return reloc_type == 2; /* R_MSP430_ABS16. */ case EM_MSP430_OLD: return reloc_type == 5; /* R_MSP430_16_BYTE. */ + case EM_NDS32: + return reloc_type == 19; /* R_NDS32_RELA. */ case EM_ALTERA_NIOS2: return reloc_type == 13; /* R_NIOS2_BFD_RELOC_16. */ case EM_NIOS32: @@ -10577,6 +10792,12 @@ is_none_reloc (unsigned int reloc_type) return reloc_type == 0; case EM_AARCH64: return reloc_type == 0 || reloc_type == 256; + case EM_NDS32: + return (reloc_type == 0 /* R_XTENSA_NONE. */ + || reloc_type == 204 /* R_NDS32_DIFF8. */ + || reloc_type == 205 /* R_NDS32_DIFF16. */ + || reloc_type == 206 /* R_NDS32_DIFF32. */ + || reloc_type == 207 /* R_NDS32_ULEB128. */); case EM_XTENSA_OLD: case EM_XTENSA: return (reloc_type == 0 /* R_XTENSA_NONE. */ @@ -12982,6 +13203,40 @@ process_mips_specific (FILE * file) } static int +process_nds32_specific (FILE * file) +{ + Elf_Internal_Shdr *sect = NULL; + + sect = find_section (".nds32_e_flags"); + if (sect != NULL) + { + unsigned int *flag; + + printf ("\nNDS32 elf flags section:\n"); + flag = get_data (NULL, file, sect->sh_offset, 1, + sect->sh_size, _("NDS32 elf flags section")); + + switch ((*flag) & 0x3) + { + case 0: + printf ("(VEC_SIZE):\tNo entry.\n"); + break; + case 1: + printf ("(VEC_SIZE):\t4 bytes\n"); + break; + case 2: + printf ("(VEC_SIZE):\t16 bytes\n"); + break; + case 3: + printf ("(VEC_SIZE):\treserved\n"); + break; + } + } + + return TRUE; +} + +static int process_gnu_liblist (FILE * file) { Elf_Internal_Shdr * section; @@ -13807,6 +14062,9 @@ process_arch_specific (FILE * file) case EM_MIPS_RS3_LE: return process_mips_specific (file); break; + case EM_NDS32: + return process_nds32_specific (file); + break; case EM_PPC: return process_power_specific (file); break; diff --git a/binutils/testsuite/ChangeLog b/binutils/testsuite/ChangeLog index 26cda41..1023526 100644 --- a/binutils/testsuite/ChangeLog +++ b/binutils/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2013-12-13 Kuan-Lin Chen <kuanlinchentw@gmail.com> + Wei-Cheng Wang <cole945@gmail.com> + + * binutils-all/objdump.exp: Add NDS32 cpu. + * binutils-all/readelf.r: Skip extra reloc created by NDS32. + 2013-12-12 H.J. Lu <hongjiu.lu@intel.com> PR binutils/16318 diff --git a/binutils/testsuite/binutils-all/objdump.exp b/binutils/testsuite/binutils-all/objdump.exp index adb7a79..aa1ff2c 100644 --- a/binutils/testsuite/binutils-all/objdump.exp +++ b/binutils/testsuite/binutils-all/objdump.exp @@ -39,8 +39,8 @@ set cpus_expected [list] lappend cpus_expected aarch64 alpha arc arm cris lappend cpus_expected d10v d30v fr30 fr500 fr550 h8 hppa i386 i860 i960 ip2022 lappend cpus_expected m16c m32c m32r m68hc11 m68hc12 m68k m88k MCore MicroBlaze -lappend cpus_expected mips mn10200 mn10300 ms1 msp MSP430 ns32k pj powerpc pyramid -lappend cpus_expected romp rs6000 s390 sh sparc +lappend cpus_expected mips mn10200 mn10300 ms1 msp MSP430 nds32 n1h_v3 ns32k +lappend cpus_expected pj powerpc pyramid romp rs6000 s390 sh sparc lappend cpus_expected tahoe tic54x tic80 tilegx tms320c30 tms320c4x tms320c54x lappend cpus_expected v850 vax we32k x86-64 xscale xtensa z8k z8001 z8002 diff --git a/binutils/testsuite/binutils-all/readelf.r b/binutils/testsuite/binutils-all/readelf.r index 689049f..065fccf 100644 --- a/binutils/testsuite/binutils-all/readelf.r +++ b/binutils/testsuite/binutils-all/readelf.r @@ -1,4 +1,6 @@ -Relocation section '.rel.*text' at offset 0x.* contains 1 entries: +Relocation section '.rel.*text' at offset 0x.* contains . entries: Offset Info Type Sym.Value Sym. Name.* +# NDS32 targets puts R_NDS32_RELAX_ENT here +#... 00000004 [0-9A-Fa-f]+ *R_.*00000000 external_symbol.* diff --git a/binutils/testsuite/binutils-all/readelf.s b/binutils/testsuite/binutils-all/readelf.s index 8361a7e..22fb5d1 100644 --- a/binutils/testsuite/binutils-all/readelf.s +++ b/binutils/testsuite/binutils-all/readelf.s @@ -6,7 +6,7 @@ Section Headers: # On the normal MIPS systems, sections must be aligned to 16 byte # boundaries. On IA64, text sections are aligned to 16 byte boundaries. +\[ 1\] .text +PROGBITS +00000000 0000(34|40) 0000(08|10) 00 +AX +0 +0 +(.|..) - +\[ 2\] .rel.+text +REL. +0+ 0+.* 00000. 0. +. +1 +4 + +\[ 2\] .rel.+text +REL. +0+ 0+.* 0000.. 0. +. +1 +4 # MIPS targets put .rela.text here. #... +\[ .\] .data +PROGBITS +00000000 0000(3c|48|50) 0000(04|10) 00 +WA +0 +0 +(.|..) diff --git a/gas/ChangeLog b/gas/ChangeLog index 2c2068d..f745bf5 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,23 @@ +2013-12-13 Kuan-Lin Chen <kuanlinchentw@gmail.com> + Wei-Cheng Wang <cole945@gmail.com> + Hsiang-Kai Wang <hsiangkai@gmail.com> + Hui-Wen Ni <sabrinanitw@gmail.com> + + * Makefile.am (TARGET_CPU_CFILES): Add config/tc-nds32.c. + (TARGET_CPU_HFILES): Add config/tc-nds32.h. + * Makefile.in: Regenerate. + * configure.in (nds32): Add nds32 target extension config support. + * configure.tgt : Add case for nds32-*-elf* and nds32-*-linux*. + * configure: Regenerate. + * config/tc-nds32.c: New file for nds32. + * config/tc-nds32.h: New file for nds32. + * doc/Makefile.am (CPU_DOCS): Add c-nds32.texi. + * doc/Makefile.in: Regenerate. + * doc/as.texinfo: Add nds32 options. + * doc/all.texi: Set NDS32. + * doc/c-nds32.texi: New file dor nds32 document. + * NEWS: Announce Andes nds32 support. + 2013-12-10 Roland McGrath <mcgrathr@google.com> * Makefile.am (install-exec-bindir): Prefix libtool invocation diff --git a/gas/Makefile.am b/gas/Makefile.am index 40a833c..cf6d877 100644 --- a/gas/Makefile.am +++ b/gas/Makefile.am @@ -164,6 +164,7 @@ TARGET_CPU_CFILES = \ config/tc-moxie.c \ config/tc-msp430.c \ config/tc-mt.c \ + config/tc-nds32.c \ config/tc-nios2.c \ config/tc-ns32k.c \ config/tc-openrisc.c \ @@ -235,6 +236,7 @@ TARGET_CPU_HFILES = \ config/tc-mn10300.h \ config/tc-msp430.h \ config/tc-mt.h \ + config/tc-nds32.h \ config/tc-nios2.h \ config/tc-ns32k.h \ config/tc-openrisc.h \ diff --git a/gas/Makefile.in b/gas/Makefile.in index dd0318f..01475a1 100644 --- a/gas/Makefile.in +++ b/gas/Makefile.in @@ -433,6 +433,7 @@ TARGET_CPU_CFILES = \ config/tc-moxie.c \ config/tc-msp430.c \ config/tc-mt.c \ + config/tc-nds32.c \ config/tc-nios2.c \ config/tc-ns32k.c \ config/tc-openrisc.c \ @@ -504,6 +505,7 @@ TARGET_CPU_HFILES = \ config/tc-mn10300.h \ config/tc-msp430.h \ config/tc-mt.h \ + config/tc-nds32.h \ config/tc-nios2.h \ config/tc-ns32k.h \ config/tc-openrisc.h \ @@ -854,6 +856,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-moxie.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-msp430.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-mt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-nds32.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-nios2.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-ns32k.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-openrisc.Po@am__quote@ @@ -1468,6 +1471,20 @@ tc-mt.obj: config/tc-mt.c @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-mt.obj `if test -f 'config/tc-mt.c'; then $(CYGPATH_W) 'config/tc-mt.c'; else $(CYGPATH_W) '$(srcdir)/config/tc-mt.c'; fi` +tc-nds32.o: config/tc-nds32.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tc-nds32.o -MD -MP -MF $(DEPDIR)/tc-nds32.Tpo -c -o tc-nds32.o `test -f 'config/tc-nds32.c' || echo '$(srcdir)/'`config/tc-nds32.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/tc-nds32.Tpo $(DEPDIR)/tc-nds32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/tc-nds32.c' object='tc-nds32.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-nds32.o `test -f 'config/tc-nds32.c' || echo '$(srcdir)/'`config/tc-nds32.c + +tc-nds32.obj: config/tc-nds32.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tc-nds32.obj -MD -MP -MF $(DEPDIR)/tc-nds32.Tpo -c -o tc-nds32.obj `if test -f 'config/tc-nds32.c'; then $(CYGPATH_W) 'config/tc-nds32.c'; else $(CYGPATH_W) '$(srcdir)/config/tc-nds32.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/tc-nds32.Tpo $(DEPDIR)/tc-nds32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/tc-nds32.c' object='tc-nds32.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-nds32.obj `if test -f 'config/tc-nds32.c'; then $(CYGPATH_W) 'config/tc-nds32.c'; else $(CYGPATH_W) '$(srcdir)/config/tc-nds32.c'; fi` + tc-nios2.o: config/tc-nios2.c @am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tc-nios2.o -MD -MP -MF $(DEPDIR)/tc-nios2.Tpo -c -o tc-nios2.o `test -f 'config/tc-nios2.c' || echo '$(srcdir)/'`config/tc-nios2.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/tc-nios2.Tpo $(DEPDIR)/tc-nios2.Po @@ -1,5 +1,7 @@ -*- text -*- +* Add support for the Andes NDS32. + Changes in 2.24: * Add support for the Texas Instruments MSP430X processor. diff --git a/gas/config/tc-nds32.c b/gas/config/tc-nds32.c new file mode 100644 index 0000000..91f8855 --- /dev/null +++ b/gas/config/tc-nds32.c @@ -0,0 +1,5920 @@ +/* tc-nds32.c -- Assemble for the nds32 + Copyright (C) 2012-2013 Free Software Foundation, Inc. + Contributed by Andes Technology Corporation. + + This file is part of GAS, the GNU Assembler. + + 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 "safe-ctype.h" +#include "subsegs.h" +#include "symcat.h" +#include "dwarf2dbg.h" +#include "dw2gencfi.h" +#include "opcodes/nds32-asm.h" +#include "elf/nds32.h" +#include "bfd/elf32-nds32.h" +#include "hash.h" +#include "sb.h" +#include "macro.h" +#include "struc-symbol.h" +#include "opcode/nds32.h" + +#include <stdio.h> + +/* GAS definitions. */ + +/* Characters which start a comment. */ +const char comment_chars[] = "!"; +/* Characters which start a comment when they appear at the start of a line. */ +const char line_comment_chars[] = "#!"; +/* Characters which separate lines (null and newline are by default). */ +const char line_separator_chars[] = ";"; +/* Characters which may be used as the exponent character + in a floating point number. */ +const char EXP_CHARS[] = "eE"; +/* Characters which may be used to indicate a floating point constant. */ +const char FLT_CHARS[] = "dDfF"; + +static int enable_16bit = 1; +/* Save for md_assemble to distinguish if this instruction is + expanded from the pseudo instruction. */ +static bfd_boolean pseudo_opcode = FALSE; +static struct nds32_relocs_pattern *relocs_list = NULL; +struct nds32_relocs_pattern +{ + segT seg; + fragS *frag; + frchainS *frchain; + symbolS *sym; + int reloc; + unsigned int insn; + unsigned int size; + char *where; + struct nds32_relocs_pattern *next; +}; +/* +static int relax_jal_bound = 3; +static int multi_call_relax; +static int pltgot_call_relax; +*/ +static int vec_size = 0; +/* If the assembly code is generated by compiler, it is supposed to have + ".flag verbatim" at beginning of the content. We have + 'nds32_flag' to parse it and set this field to be non-zero. */ +static int verbatim = 0; +static struct hash_control *nds32_gprs_hash; +static struct hash_control *nds32_hint_hash; + +/* Generate relocation for relax or not, and the default is true. */ +static int enable_relax_relocs = 1; +/* The value will be used in RELAX_ENTRY. */ +static int enable_relax_ex9 = 0; +/* The value will be used in RELAX_ENTRY. */ +static int enable_relax_ifc = 0; +/* Save option -O for perfomance. */ +static int optimize = 0; +/* Save option -Os for code size. */ +static int optimize_for_space = 0; + +struct nds32_keyword nds32_fsrs[] = +{ + /* Standard names. */ + {"$fs0", 0, 0}, {"$fs1", 1, 0}, {"$fs2", 2, 0}, {"$fs3", 3, 0}, + {"$fs4", 4, 0}, {"$fs5", 5, 0}, {"$fs6", 6, 0}, {"$fs7", 7, 0}, + {"$fs8", 8, 0}, {"$fs9", 9, 0}, {"$fs10", 10, 0}, {"$fs11", 11, 0}, + {"$fs12", 12, 0}, {"$fs13", 13, 0}, {"$fs14", 14, 0}, {"$fs15", 15, 0}, + {"$fs16", 16, 0}, {"$fs17", 17, 0}, {"$fs18", 18, 0}, {"$fs19", 19, 0}, + {"$fs20", 20, 0}, {"$fs21", 21, 0}, {"$fs22", 22, 0}, {"$fs23", 23, 0}, + {"$fs24", 24, 0}, {"$fs25", 25, 0}, {"$fs26", 26, 0}, {"$fs27", 27, 0}, + {"$fs28", 28, 0}, {"$fs29", 29, 0}, {"$fs30", 30, 0}, {"$fs31", 31, 0}, + {NULL, 0, 0} +}; + +struct nds32_keyword nds32_fdrs[] = +{ + /* Standard names. */ + {"$fd0", 0, 0}, {"$fd1", 1, 0}, {"$fd2", 2, 0}, {"$fd3", 3, 0}, + {"$fd4", 4, 0}, {"$fd5", 5, 0}, {"$fd6", 6, 0}, {"$fd7", 7, 0}, + {"$fd8", 8, 0}, {"$fd9", 9, 0}, {"$fd10", 10, 0}, {"$fd11", 11, 0}, + {"$fd12", 12, 0}, {"$fd13", 13, 0}, {"$fd14", 14, 0}, {"$fd15", 15, 0}, + {"$fd16", 16, 0}, {"$fd17", 17, 0}, {"$fd18", 18, 0}, {"$fd19", 19, 0}, + {"$fd20", 20, 0}, {"$fd21", 21, 0}, {"$fd22", 22, 0}, {"$fd23", 23, 0}, + {"$fd24", 24, 0}, {"$fd25", 25, 0}, {"$fd26", 26, 0}, {"$fd27", 27, 0}, + {"$fd28", 28, 0}, {"$fd29", 29, 0}, {"$fd30", 30, 0}, {"$fd31", 31, 0}, + {NULL, 0, 0} +}; + +struct nds32_keyword nds32_gprs[] = +{ + /* Standard names. */ + {"$r0", 0, 0}, {"$r1", 1, 0}, {"$r2", 2, 0}, {"$r3", 3, 0}, + {"$r4", 4, 0}, {"$r5", 5, 0}, {"$r6", 6, 0}, {"$r7", 7, 0}, + {"$r8", 8, 0}, {"$r9", 9, 0}, {"$r10", 10, 0}, {"$r11", 11, 0}, + {"$r12", 12, 0}, {"$r13", 13, 0}, {"$r14", 14, 0}, {"$r15", 15, 0}, + {"$r16", 16, 0}, {"$r17", 17, 0}, {"$r18", 18, 0}, {"$r19", 19, 0}, + {"$r20", 20, 0}, {"$r21", 21, 0}, {"$r22", 22, 0}, {"$r23", 23, 0}, + {"$r24", 24, 0}, {"$r25", 25, 0}, {"$r26", 26, 0}, {"$r27", 27, 0}, + {"$r28", 28, 0}, {"$r29", 29, 0}, {"$r30", 30, 0}, {"$r31", 31, 0}, + /* Names for parameter passing. */ + {"$a0", 0, 0}, {"$a1", 1, 0}, {"$a2", 2, 0}, {"$a3", 3, 0}, + {"$a4", 4, 0}, {"$a5", 5, 0}, + /* Names reserved for 5-bit addressing only. */ + {"$s0", 6, 0}, {"$s1", 7, 0}, {"$s2", 8, 0}, {"$s3", 9, 0}, + {"$s4", 10, 0}, {"$s5", 11, 0}, {"$s6", 12, 0}, {"$s7", 13, 0}, + {"$s8", 14, 0}, {"$s9", 28, 0}, + {"$ta", 15, 0}, + {"$t0", 16, 0}, {"$t1", 17, 0}, {"$t2", 18, 0}, {"$t3", 19, 0}, + {"$t4", 20, 0}, {"$t5", 21, 0}, {"$t6", 22, 0}, {"$t7", 23, 0}, + {"$t8", 24, 0}, {"$t9", 25, 0}, + {"$p0", 26, 0}, {"$p1", 27, 0}, + {"$fp", 28, 0}, {"$gp", 29, 0}, {"$lp", 30, 0}, {"$sp", 31, 0}, + /* Names reserved for 4-bit addressing only. */ + {"$h0", 0, 0}, {"$h1", 1, 0}, {"$h2", 2, 0}, {"$h3", 3, 0}, + {"$h4", 4, 0}, {"$h5", 5, 0}, {"$h6", 6, 0}, {"$h7", 7, 0}, + {"$h8", 8, 0}, {"$h9", 9, 0}, {"$h10", 10, 0}, {"$h11", 11, 0}, + {"$h12", 16, 0}, {"$h13", 17, 0}, {"$h14", 18, 0}, {"$h15", 19, 0}, + /* Names reserved for 3-bit addressing only. */ + {"$o0", 0, 0}, {"$o1", 1, 0}, {"$o2", 2, 0}, {"$o3", 3, 0}, + {"$o4", 4, 0}, {"$o5", 5, 0}, {"$o6", 6, 0}, {"$o7", 7, 0}, + {NULL, 0, 0} +}; + + +static struct hash_control *nds32_relax_info_hash; +static relax_info_t relax_table[] = +{ + { + "jal", /* opcode */ + BR_RANGE_S16M, /* br_range */ + {{0, 0, 0}}, /* cond_field */ + { + { + INSN_JAL /* jal label */ + }, /* BR_RANGE_S256 */ + { + INSN_JAL /* jal label */ + }, /* BR_RANGE_S16K */ + { + INSN_JAL /* jal label */ + }, /* BR_RANGE_S64K */ + { + INSN_JAL /* jal label */ + }, /* BR_RANGE_S16M */ + { + INSN_SETHI_TA, /* sethi $ta, label */ + INSN_ORI_TA, /* ori $ta, $ta, label */ + INSN_JRAL_TA + }, /* BR_RANGE_U4G */ + }, /* relax_code_seq */ + { + {{0, 0, 0}}, /* BR_RANGE_S256 */ + {{0, 0, 0}}, /* BR_RANGE_S16K */ + {{0, 0, 0}}, /* BR_RANGE_S64K */ + {{0, 0, 0}}, /* BR_RANGE_S16M */ + {{0, 0, 0}} /* BR_RANGE_U4G */ + }, /* relax_code_condition */ + {4, 4, 4, 4, 12}, /* relax_code_size */ + {4, 4, 4, 4, 4}, /* relax_branch_isize */ + { + { + {0, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 4, 0, BFD_RELOC_NDS32_HI20}, + {0, 12, NDS32_RELAX, BFD_RELOC_NDS32_LONGCALL1}, + {4, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, + {8, 4, NDS32_ORIGIN, 0}, + {8, 2, NDS32_CONVERT, 0}, + {0, 0, 0, 0} + } /* BR_RANGE_U4G */ + } /* relax_fixup */ + }, + { + "bltzal", /* opcode */ + BR_RANGE_S64K, /* br_range */ + { + {0, 20, 0x1F}, + { 0, 0, 0 } + }, /* cond_field */ + { + { + INSN_BLTZAL /* bltzal $rt, label */ + }, /* BR_RANGE_S256 */ + { + INSN_BLTZAL /* bltzal $rt, label */ + }, /* BR_RANGE_S16K */ + { + INSN_BLTZAL /* bltzal $rt, label */ + }, /* BR_RANGE_S64K */ + { + INSN_BGEZ, /* bgez $rt, $1 */ + INSN_JAL /* jal label */ + }, /* BR_RANGE_S16M */ + { + INSN_BGEZ, /* bgez $rt, $1 */ + INSN_SETHI_TA, /* sethi $ta, label */ + INSN_ORI_TA, /* ori $ta, $ta, label */ + INSN_JRAL_TA /* jral $ta */ + } /* BR_RANGE_U4G */ + }, /* relax_code_seq */ + { + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 20, 0x1F}, + {0, 0, 0} + } /* BR_RANGE_U4G */ + }, /* relax_code_condition */ + {4, 4, 4, 8, 16}, /* relax_code_size */ + {4, 4, 4, 4, 4}, /* relax_branch_isize */ + { + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 4, NDS32_CREATE_LABLE, BFD_RELOC_NDS32_17_PCREL}, + {0, 8, NDS32_RELAX, BFD_RELOC_NDS32_LONGCALL2}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 4, NDS32_CREATE_LABLE, BFD_RELOC_NDS32_17_PCREL}, + {0, 16, NDS32_RELAX, BFD_RELOC_NDS32_LONGCALL3}, + {4, 4, 0, BFD_RELOC_NDS32_HI20}, + {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, + {12, 4, NDS32_ORIGIN, 0}, + {12, 2, NDS32_CONVERT, 0}, + {0, 0, 0, 0} + } /* BR_RANGE_U4G */ + } /* relax_fixup */ + }, + { + "bgezal", /* opcode */ + BR_RANGE_S64K, /* br_range */ + { + {0, 20, 0x1F}, + { 0, 0, 0 } + }, /* cond_field */ + { + { + INSN_BGEZAL /* bgezal $rt, label */ + }, /* BR_RANGE_S256 */ + { + INSN_BGEZAL /* bgezal $rt, label */ + }, /* BR_RANGE_S16K */ + { + INSN_BGEZAL /* bgezal $rt, label */ + }, /* BR_RANGE_S64K */ + { + INSN_BLTZ, /* bltz $rt, $1 */ + INSN_JAL /* jal label */ + }, /* BR_RANGE_S16M */ + { + INSN_BLTZ, /* bltz $rt, $1 */ + INSN_SETHI_TA, /* sethi $ta, label */ + INSN_ORI_TA, /* ori $ta, $ta, label */ + INSN_JRAL_TA /* jral $ta */ + } /* BR_RANGE_U4G */ + }, /* relax_code_seq */ + { + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 20, 0x1F}, + {0, 0, 0} + } /* BR_RANGE_U4G */ + }, /* relax_code_condition */ + {4, 4, 4, 8, 16}, /* relax_code_size */ + {4, 4, 4, 4, 4}, /* relax_branch_isize */ + { + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 4, NDS32_CREATE_LABLE, BFD_RELOC_NDS32_17_PCREL}, + {0, 8, NDS32_RELAX, BFD_RELOC_NDS32_LONGCALL2}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 4, NDS32_CREATE_LABLE, BFD_RELOC_NDS32_17_PCREL}, + {0, 16, NDS32_RELAX, BFD_RELOC_NDS32_LONGCALL3}, + {4, 4, 0, BFD_RELOC_NDS32_HI20}, + {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, + {12, 4, NDS32_ORIGIN, 0}, + {12, 2, NDS32_CONVERT, 0}, + {0, 0, 0, 0} + } /* BR_RANGE_U4G */ + } /* relax_fixup */ + }, + { + "j", /* opcode */ + BR_RANGE_S16M, /* br_range */ + {{0, 0, 0}}, /* cond_field */ + { + { + (INSN_J8 << 16) /* j8 label */ + }, /* BR_RANGE_S256 */ + { + INSN_J /* j label */ + }, /* BR_RANGE_S16K */ + { + INSN_J /* j label */ + }, /* BR_RANGE_S64K */ + { + INSN_J /* j label */ + }, /* BR_RANGE_S16M */ + { + INSN_SETHI_TA, /* sethi $ta, label */ + INSN_ORI_TA, /* ori $ta, $ta, label */ + INSN_JR_TA /* jr $ta */ + }, /* BR_RANGE_U4G */ + }, /* relax_code_seq */ + { + {{0, 0, 0}}, /* BR_RANGE_S256 */ + {{0, 0, 0}}, /* BR_RANGE_S16K */ + {{0, 0, 0}}, /* BR_RANGE_S64K */ + {{0, 0, 0}}, /* BR_RANGE_S16M */ + {{0, 0, 0}} /* BR_RANGE_U4G */ + }, /* relax_code_condition */ + {2, 4, 4, 4, 12}, /* relax_code_size */ + {2, 4, 4, 4, 4}, /* relax_branch_isize */ + { + { + {0, 2, 0, BFD_RELOC_NDS32_9_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 4, 0, BFD_RELOC_NDS32_HI20}, + {0, 12, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP1}, + {4, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, + {8, 4, NDS32_ORIGIN, 0}, + {8, 2, NDS32_CONVERT, 0}, + {0, 0, 0, 0} + } /* BR_RANGE_U4G */ + } /* relax_fixup */ + }, + { + "j8", /* opcode */ + BR_RANGE_S256, /* br_range */ + {{0, 0, 0}}, /* cond_field */ + { + { + (INSN_J8 << 16) /* j8 label */ + }, /* BR_RANGE_S256 */ + { + INSN_J /* j label */ + }, /* BR_RANGE_S16K */ + { + INSN_J /* j label */ + }, /* BR_RANGE_S64K */ + { + INSN_J /* j label */ + }, /* BR_RANGE_S16M */ + { + INSN_SETHI_TA, /* sethi $ta, label */ + INSN_ORI_TA, /* ori $ta, $ta, label */ + INSN_JR_TA /* jr $ta */ + }, /* BR_RANGE_U4G */ + }, /* relax_code_seq */ + { + {{0, 0, 0}}, /* BR_RANGE_S256 */ + {{0, 0, 0}}, /* BR_RANGE_S16K */ + {{0, 0, 0}}, /* BR_RANGE_S64K */ + {{0, 0, 0}}, /* BR_RANGE_S16M */ + {{0, 0, 0}} /* BR_RANGE_U4G */ + }, /* relax_code_condition */ + {2, 4, 4, 4, 12}, /* relax_code_size */ + {2, 4, 4, 4, 4}, /* relax_branch_isize */ + { + { + {0, 2, 0, BFD_RELOC_NDS32_9_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 4, 0, BFD_RELOC_NDS32_HI20}, + {0, 12, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP1}, + {4, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, + {8, 4, NDS32_ORIGIN, 0}, + {8, 2, NDS32_CONVERT, 0}, + {0, 0, 0, 0} + } /* BR_RANGE_U4G */ + } /* relax_fixup */ + }, + { + "beqz", /* opcode */ + BR_RANGE_S64K, /* br_range */ + { + {0, 20, 0x1F}, + { 0, 0, 0 } + }, /* cond_field */ + { + { + INSN_BEQZ /* beqz $rt, label */ + }, /* BR_RANGE_S256 */ + { + INSN_BEQZ /* beqz $rt, label */ + }, /* BR_RANGE_S16K */ + { + INSN_BEQZ /* beqz $rt, label */ + }, /* BR_RANGE_S64K */ + { + INSN_BNEZ, /* bnez $rt, $1 */ + INSN_J /* j label */ + }, /* BR_RANGE_S16M */ + { + INSN_BNEZ, /* bnez $rt, $1 */ + INSN_SETHI_TA, /* sethi $ta, label */ + INSN_ORI_TA, /* ori $ta, $ta, label */ + INSN_JR_TA /* jr $ta */ + } /* BR_RANGE_U4G */ + }, /* relax_code_seq */ + { + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 20, 0x1F}, + {0, 0, 0} + } /* BR_RANGE_U4G */ + }, /* relax_code_condition */ + {4, 4, 4, 8, 16}, /* relax_code_size */ + {4, 4, 4, 4, 4}, /* relax_branch_isize */ + { + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 2, NDS32_CONVERT, BFD_RELOC_NDS32_9_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 8, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP2}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 16, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP3}, + {4, 4, 0, BFD_RELOC_NDS32_HI20}, + {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, + {12, 4, NDS32_ORIGIN, 0}, + {12, 2, NDS32_CONVERT, 0}, + {0, 0, 0, 0} + } /* BR_RANGE_U4G */ + } /* relax_fixup */ + }, + { + "bgez", /* opcode */ + BR_RANGE_S64K, /* br_range */ + { + {0, 20, 0x1F}, + { 0, 0, 0 } + }, /* cond_field */ + { + { + INSN_BGEZ /* bgez $rt, label */ + }, /* BR_RANGE_S256 */ + { + INSN_BGEZ /* bgez $rt, label */ + }, /* BR_RANGE_S16K */ + { + INSN_BGEZ /* bgez $rt, label */ + }, /* BR_RANGE_S64K */ + { + INSN_BLTZ, /* bltz $rt, $1 */ + INSN_J /* j label */ + }, /* BR_RANGE_S16M */ + { + INSN_BLTZ, /* bltz $rt, $1 */ + INSN_SETHI_TA, /* sethi $ta, label */ + INSN_ORI_TA, /* ori $ta, $ta, label */ + INSN_JR_TA /* jr $ta */ + } /* BR_RANGE_U4G */ + }, /* relax_code_seq */ + { + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 20, 0x1F}, + {0, 0, 0} + } /* BR_RANGE_U4G */ + }, /* relax_code_condition */ + {4, 4, 4, 8, 16}, /* relax_code_size */ + {4, 4, 4, 4, 4}, /* relax_branch_isize */ + { + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 4, NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 8, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP2}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 4, NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 16, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP3}, + {4, 4, 0, BFD_RELOC_NDS32_HI20}, + {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, + {12, 4, NDS32_ORIGIN, 0}, + {12, 2, NDS32_CONVERT, 0}, + {0, 0, 0, 0} + } /* BR_RANGE_U4G */ + } /* relax_fixup */ + }, + { + "bnez", /* opcode */ + BR_RANGE_S64K, /* br_range */ + { + {0, 20, 0x1F}, + { 0, 0, 0 } + }, /* cond_field */ + { + { + INSN_BNEZ /* bnez $rt, label */ + }, /* BR_RANGE_S256 */ + { + INSN_BNEZ /* bnez $rt, label */ + }, /* BR_RANGE_S16K */ + { + INSN_BNEZ /* bnez $rt, label */ + }, /* BR_RANGE_S64K */ + { + INSN_BEQZ, /* beqz $rt, $1 */ + INSN_J /* j label */ + }, /* BR_RANGE_S16M */ + { + INSN_BEQZ, /* beqz $rt, $1 */ + INSN_SETHI_TA, /* sethi $ta, label */ + INSN_ORI_TA, /* ori $ta, $ta, label */ + INSN_JR_TA /* jr $ta */ + } /* BR_RANGE_U4G */ + }, /* relax_code_seq */ + { + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 20, 0x1F}, + {0, 0, 0} + } /* BR_RANGE_U4G */ + }, /* relax_code_condition */ + {4, 4, 4, 8, 16}, /* relax_code_size */ + {4, 4, 4, 4, 4}, /* relax_branch_isize */ + { + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 2, NDS32_CONVERT, BFD_RELOC_NDS32_9_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 8, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP2}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 16, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP3}, + {4, 4, 0, BFD_RELOC_NDS32_HI20}, + {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, + {12, 4, NDS32_ORIGIN, 0}, + {12, 2, NDS32_CONVERT, 0}, + {0, 0, 0, 0} + } /* BR_RANGE_U4G */ + } /* relax_fixup */ + }, + { + "bgtz", /* opcode */ + BR_RANGE_S64K, /* br_range */ + { + {0, 20, 0x1F}, + { 0, 0, 0 } + }, /* cond_field */ + { + { + INSN_BGTZ /* bgtz $rt, label */ + }, /* BR_RANGE_S256 */ + { + INSN_BGTZ /* bgtz $rt, label */ + }, /* BR_RANGE_S16K */ + { + INSN_BGTZ /* bgtz $rt, label */ + }, /* BR_RANGE_S64K */ + { + INSN_BLEZ, /* blez $rt, $1 */ + INSN_J /* j label */ + }, /* BR_RANGE_S16M */ + { + INSN_BLEZ, /* blez $rt, $1 */ + INSN_SETHI_TA, /* sethi $ta, label */ + INSN_ORI_TA, /* ori $ta, $ta, label */ + INSN_JR_TA /* jr $ta */ + } /* BR_RANGE_U4G */ + }, /* relax_code_seq */ + { + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 20, 0x1F}, + {0, 0, 0} + } /* BR_RANGE_U4G */ + }, /* relax_code_condition */ + {4, 4, 4, 8, 16}, /* relax_code_size */ + {4, 4, 4, 4, 4}, /* relax_branch_isize */ + { + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 4, NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 8, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP2}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 4, NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 16, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP3}, + {4, 4, 0, BFD_RELOC_NDS32_HI20}, + {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, + {12, 4, NDS32_ORIGIN, 0}, + {12, 2, NDS32_CONVERT, 0}, + {0, 0, 0, 0} + } /* BR_RANGE_U4G */ + } /* relax_fixup */ + }, + { + "blez", /* opcode */ + BR_RANGE_S64K, /* br_range */ + { + {0, 20, 0x1F}, + { 0, 0, 0 } + }, /* cond_field */ + { + { + INSN_BLEZ /* blez $rt, label */ + }, /* BR_RANGE_S256 */ + { + INSN_BLEZ /* blez $rt, label */ + }, /* BR_RANGE_S16K */ + { + INSN_BLEZ /* blez $rt, label */ + }, /* BR_RANGE_S64K */ + { + INSN_BGTZ, /* bgtz $rt, $1 */ + INSN_J /* j label */ + }, /* BR_RANGE_S16M */ + { + INSN_BGTZ, /* bgtz $rt, $1 */ + INSN_SETHI_TA, /* sethi $ta, label */ + INSN_ORI_TA, /* ori $ta, $ta, label */ + INSN_JR_TA /* jr $ta */ + } /* BR_RANGE_U4G */ + }, /* relax_code_seq */ + { + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 20, 0x1F}, + {0, 0, 0} + } /* BR_RANGE_U4G */ + }, /* relax_code_condition */ + {4, 4, 4, 8, 16}, /* relax_code_size */ + {4, 4, 4, 4, 4}, /* relax_branch_isize */ + { + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 4, NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 8, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP2}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 4, NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 16, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP3}, + {4, 4, 0, BFD_RELOC_NDS32_HI20}, + {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, + {12, 4, NDS32_ORIGIN, 0}, + {12, 2, NDS32_CONVERT, 0}, + {0, 0, 0, 0} + } /* BR_RANGE_U4G */ + } /* relax_fixup */ + }, + { + "bltz", /* opcode */ + BR_RANGE_S64K, /* br_range */ + { + {0, 20, 0x1F}, + { 0, 0, 0 } + }, /* cond_field */ + { + { + INSN_BLTZ /* bltz $rt, label */ + }, /* BR_RANGE_S256 */ + { + INSN_BLTZ /* bltz $rt, label */ + }, /* BR_RANGE_S16K */ + { + INSN_BLTZ /* bltz $rt, label */ + }, /* BR_RANGE_S64K */ + { + INSN_BGEZ, /* bgez $rt, $1 */ + INSN_J /* j label */ + }, /* BR_RANGE_S16M */ + { + INSN_BGEZ, /* bgez $rt, $1 */ + INSN_SETHI_TA, /* sethi $ta, label */ + INSN_ORI_TA, /* ori $ta, $ta, label */ + INSN_JR_TA /* jr $ta */ + } /* BR_RANGE_U4G */ + }, /* relax_code_seq */ + { + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 20, 0x1F}, + {0, 0, 0} + } /* BR_RANGE_U4G */ + }, /* relax_code_condition */ + {4, 4, 4, 8, 16}, /* relax_code_size */ + {4, 4, 4, 4, 4}, /* relax_branch_isize */ + { + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 4, NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 8, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP2}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 4, NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 16, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP3}, + {4, 4, 0, BFD_RELOC_NDS32_HI20}, + {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, + {12, 4, NDS32_ORIGIN, 0}, + {12, 2, NDS32_CONVERT, 0}, + {0, 0, 0, 0} + } /* BR_RANGE_U4G */ + } /* relax_fixup */ + }, + { + "beq", /* opcode */ + BR_RANGE_S16K, /* br_range */ + { + {0, 20, 0x1F}, + {0, 15, 0x1F}, + { 0, 0, 0 } + }, /* cond_field */ + { + { + INSN_BEQ /* beq $rt, $ra, label */ + }, /* BR_RANGE_S256 */ + { + INSN_BEQ /* beq $rt, $ra, label */ + }, /* BR_RANGE_S16K */ + { + INSN_BNE, /* bne $rt, $ra, $1 */ + INSN_J /* j label */ + }, /* BR_RANGE_S64K */ + { + INSN_BNE, /* bne $rt, $ra, $1 */ + INSN_J /* j label */ + }, /* BR_RANGE_S16M */ + { + INSN_BNE, /* bne $rt, $ra, $1 */ + INSN_SETHI_TA, /* sethi $ta, label */ + INSN_ORI_TA, /* ori $ta, $ta, label */ + INSN_JR_TA /* jr $ta */ + } /* BR_RANGE_U4G */ + }, /* relax_code_seq */ + { + { + {0, 20, 0x1F}, + {0, 15, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 20, 0x1F}, + {0, 15, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 20, 0x1F}, + {0, 15, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 20, 0x1F}, + {0, 15, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 20, 0x1F}, + {0, 15, 0x1F}, + {0, 0, 0} + } /* BR_RANGE_U4G */ + }, /* relax_code_condition */ + {4, 4, 8, 8, 16}, /* relax_code_size */ + {4, 4, 4, 4, 4}, /* relax_branch_isize */ + { + { + {0, 4, NDS32_ORIGIN, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT, BFD_RELOC_NDS32_9_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 4, 0, BFD_RELOC_NDS32_15_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 8, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP2}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 8, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP2}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 16, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP3}, + {4, 4, 0, BFD_RELOC_NDS32_HI20}, + {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, + {12, 4, NDS32_ORIGIN, 0}, + {12, 2, NDS32_CONVERT, 0}, + {0, 0, 0, 0} + } /* BR_RANGE_U4G */ + } /* relax_fixup */ + }, + { + "bne", /* opcode */ + BR_RANGE_S16K, /* br_range */ + { + {0, 20, 0x1F}, + {0, 15, 0x1F}, + { 0, 0, 0 } + }, /* cond_field */ + { + { + INSN_BNE /* bne $rt, $ra, label */ + }, /* BR_RANGE_S256 */ + { + INSN_BNE /* bne $rt, $ra, label */ + }, /* BR_RANGE_S16K */ + { + INSN_BEQ, /* beq $rt, $ra, $1 */ + INSN_J /* j label */ + }, /* BR_RANGE_S64K */ + { + INSN_BEQ, /* beq $rt, $ra, $1 */ + INSN_J /* j label */ + }, /* BR_RANGE_S16M */ + { + INSN_BEQ, /* beq $rt, $ra, $1 */ + INSN_SETHI_TA, /* sethi $ta, label */ + INSN_ORI_TA, /* ori $ta, $ta, label */ + INSN_JR_TA /* jr $ta */ + } /* BR_RANGE_U4G */ + }, /* relax_code_seq */ + { + { + {0, 20, 0x1F}, + {0, 15, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 20, 0x1F}, + {0, 15, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 20, 0x1F}, + {0, 15, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 20, 0x1F}, + {0, 15, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 20, 0x1F}, + {0, 15, 0x1F}, + {0, 0, 0} + } /* BR_RANGE_U4G */ + }, /* relax_code_condition */ + {4, 4, 8, 8, 16}, /* relax_code_size */ + {4, 4, 4, 4, 4}, /* relax_branch_isize */ + { + { + {0, 4, NDS32_ORIGIN, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT, BFD_RELOC_NDS32_9_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 4, 0, BFD_RELOC_NDS32_15_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 8, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP2}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 8, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP2}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 16, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP3}, + {4, 4, 0, BFD_RELOC_NDS32_HI20}, + {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, + {12, 4, NDS32_ORIGIN, 0}, + {12, 2, NDS32_CONVERT, 0}, + {0, 0, 0, 0} + } /* BR_RANGE_U4G */ + } /* relax_fixup */ + }, + { + "beqz38", /* opcode */ + BR_RANGE_S256, /* br_range */ + { + {0, 8, 0x7}, + { 0, 0, 0 } + }, /* cond_field */ + { + { + INSN_BEQZ /* beqz $rt, label */ + }, /* BR_RANGE_S256 */ + { + INSN_BEQZ /* beqz $rt, label */ + }, /* BR_RANGE_S16K */ + { + INSN_BEQZ /* beqz $rt, label */ + }, /* BR_RANGE_S64K */ + { + INSN_BNEZ, /* bnez $rt, $1 */ + INSN_J /* j label */ + }, /* BR_RANGE_S16M */ + { + INSN_BNEZ, /* bnez $rt, $1 */ + INSN_SETHI_TA, /* sethi $ta, label */ + INSN_ORI_TA, /* ori $ta, $ta, label */ + INSN_JR_TA /* jr $ta */ + } /* BR_RANGE_U4G */ + }, /* relax_code_seq */ + { + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 20, 0x1F}, + {0, 0, 0} + } /* BR_RANGE_U4G */ + }, /* relax_code_condition */ + {4, 4, 4, 8, 16}, /* relax_code_size */ + {4, 4, 4, 4, 4}, /* relax_branch_isize */ + { + { + {0, 4, NDS32_ORIGIN, BFD_RELOC_NDS32_17_PCREL}, + {0, 2, NDS32_CONVERT, BFD_RELOC_NDS32_9_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 8, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP2}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 16, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP3}, + {4, 4, 0, BFD_RELOC_NDS32_HI20}, + {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, + {12, 4, NDS32_ORIGIN, 0}, + {12, 2, NDS32_CONVERT, 0}, + {0, 0, 0, 0} + } /* BR_RANGE_U4G */ + } /* relax_fixup */ + }, + { + "bnez38", /* opcode */ + BR_RANGE_S256, /* br_range */ + { + {0, 8, 0x7}, + { 0, 0, 0 } + }, /* cond_field */ + { + { + INSN_BNEZ /* bnez $rt, label */ + }, /* BR_RANGE_S256 */ + { + INSN_BNEZ /* bnez $rt, label */ + }, /* BR_RANGE_S16K */ + { + INSN_BNEZ /* bnez $rt, label */ + }, /* BR_RANGE_S64K */ + { + INSN_BEQZ, /* beqz $rt, $1 */ + INSN_J /* j label */ + }, /* BR_RANGE_S16M */ + { + INSN_BEQZ, /* beqz $rt, $1 */ + INSN_SETHI_TA, /* sethi $ta, label */ + INSN_ORI_TA, /* ori $ta, $ta, label */ + INSN_JR_TA /* jr $ta */ + } /* BR_RANGE_U4G */ + }, /* relax_code_seq */ + { + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 20, 0x1F}, + {0, 0, 0} + } /* BR_RANGE_U4G */ + }, /* relax_code_condition */ + {4, 4, 4, 8, 16}, /* relax_code_size */ + {4, 4, 4, 4, 4}, /* relax_branch_isize */ + { + { + {0, 4, NDS32_ORIGIN, BFD_RELOC_NDS32_17_PCREL}, + {0, 2, NDS32_CONVERT, BFD_RELOC_NDS32_9_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 8, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP2}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 16, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP3}, + {4, 4, 0, BFD_RELOC_NDS32_HI20}, + {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, + {12, 4, NDS32_ORIGIN, 0}, + {12, 2, NDS32_CONVERT, 0}, + {0, 0, 0, 0} + } /* BR_RANGE_U4G */ + } /* relax_fixup */ + }, + { + "beqzs8", /* opcode */ + BR_RANGE_S256, /* br_range */ + {{0, 0, 0}}, /* cond_field */ + { + { + INSN_BEQZ_TA /* beqz $r15, label */ + }, /* BR_RANGE_S256 */ + { + INSN_BNEZ /* bnez $rt, label */ + }, /* BR_RANGE_S16K */ + { + INSN_BNEZ /* bnez $rt, label */ + }, /* BR_RANGE_S64K */ + { + INSN_BNEZ_TA, /* bnez $r15, $1 */ + INSN_J /* j label */ + }, /* BR_RANGE_S16M */ + { + INSN_BNEZ_TA, /* bnez $r15, $1 */ + INSN_SETHI_TA, /* sethi $ta, label */ + INSN_ORI_TA, /* ori $ta, $ta, label */ + INSN_JR_TA /* jr $ta */ + } /* BR_RANGE_U4G */ + }, /* relax_code_seq */ + { + {{0, 0, 0}}, /* BR_RANGE_S256 */ + {{0, 0, 0}}, /* BR_RANGE_S16K */ + {{0, 0, 0}}, /* BR_RANGE_S64K */ + {{0, 0, 0}}, /* BR_RANGE_S16M */ + {{0, 0, 0}} /* BR_RANGE_U4G */ + }, /* relax_code_condition */ + {4, 4, 4, 8, 16}, /* relax_code_size */ + {4, 4, 4, 4, 4}, /* relax_branch_isize */ + { + { + {0, 4, NDS32_ORIGIN, BFD_RELOC_NDS32_17_PCREL}, + {0, 2, NDS32_CONVERT, BFD_RELOC_NDS32_9_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 8, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP2}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 16, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP3}, + {4, 4, 0, BFD_RELOC_NDS32_HI20}, + {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, + {12, 4, NDS32_ORIGIN, 0}, + {12, 2, NDS32_CONVERT, 0}, + {0, 0, 0, 0} + } /* BR_RANGE_U4G */ + } /* relax_fixup */ + }, + { + "bnezs8", /* opcode */ + BR_RANGE_S256, /* br_range */ + {{0, 0, 0}}, /* cond_field */ + { + { + INSN_BNEZ_TA /* bnez $r15, label */ + }, /* BR_RANGE_S256 */ + { + INSN_BNEZ_TA /* bnez $r15, label */ + }, /* BR_RANGE_S16K */ + { + INSN_BNEZ_TA /* bnez $r15, label */ + }, /* BR_RANGE_S64K */ + { + INSN_BEQZ_TA, /* beqz $r15, $1 */ + INSN_J /* j label */ + }, /* BR_RANGE_S16M */ + { + INSN_BEQZ_TA, /* beqz $r15, $1 */ + INSN_SETHI_TA, /* sethi $ta, label */ + INSN_ORI_TA, /* ori $ta, $ta, label */ + INSN_JR_TA /* jr $ta */ + } /* BR_RANGE_U4G */ + }, /* relax_code_seq */ + { + {{0, 0, 0}}, /* BR_RANGE_S256 */ + {{0, 0, 0}}, /* BR_RANGE_S16K */ + {{0, 0, 0}}, /* BR_RANGE_S64K */ + {{0, 0, 0}}, /* BR_RANGE_S16M */ + {{0, 0, 0}} /* BR_RANGE_U4G */ + }, /* relax_code_condition */ + {4, 4, 4, 8, 16}, /* relax_code_size */ + {4, 4, 4, 4, 4}, /* relax_branch_isize */ + { + { + {0, 4, NDS32_ORIGIN, BFD_RELOC_NDS32_17_PCREL}, + {0, 2, NDS32_CONVERT, BFD_RELOC_NDS32_9_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 4, 0, BFD_RELOC_NDS32_17_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 8, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP2}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 16, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP3}, + {4, 4, 0, BFD_RELOC_NDS32_HI20}, + {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, + {12, 4, NDS32_ORIGIN, 0}, + {12, 2, NDS32_CONVERT, 0}, + {0, 0, 0, 0} + } /* BR_RANGE_U4G */ + } /* relax_fixup */ + }, + { + "bnes38", /* opcode */ + BR_RANGE_S256, /* br_range */ + { + {0, 8, 0x7}, + { 0, 0, 0 } + }, /* cond_field */ + { + { + INSN_BNE_R5 /* bne $rt, $r5, label */ + }, /* BR_RANGE_S256 */ + { + INSN_BNE_R5 /* bne $rt, $r5, label */ + }, /* BR_RANGE_S16K */ + { + INSN_BEQ_R5, /* beq $rt, $r5, $1 */ + INSN_J /* j label */ + }, /* BR_RANGE_S64K */ + { + INSN_BEQ_R5, /* beq $rt, $r5, $1 */ + INSN_J /* j label */ + }, /* BR_RANGE_S16M */ + { + INSN_BEQ_R5, /* beq $rt, $r5, $1 */ + INSN_SETHI_TA, /* sethi $ta, label */ + INSN_ORI_TA, /* ori $ta, $ta, label */ + INSN_JR_TA /* jr $ta */ + } /* BR_RANGE_U4G */ + }, /* relax_code_seq */ + { + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 20, 0x1F}, + {0, 0, 0} + } /* BR_RANGE_U4G */ + }, /* relax_code_condition */ + {4, 4, 8, 8, 16}, /* relax_code_size */ + {4, 4, 4, 4, 4}, /* relax_branch_isize */ + { + { + {0, 4, NDS32_ORIGIN, BFD_RELOC_NDS32_17_PCREL}, + {0, 2, NDS32_CONVERT, BFD_RELOC_NDS32_9_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 4, 0, BFD_RELOC_NDS32_15_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 8, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP2}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 8, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP2}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 16, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP3}, + {4, 4, 0, BFD_RELOC_NDS32_HI20}, + {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, + {12, 4, NDS32_ORIGIN, 0}, + {12, 2, NDS32_CONVERT, 0}, + {0, 0, 0, 0} + } /* BR_RANGE_U4G */ + } /* relax_fixup */ + }, + { + "beqs38", /* opcode */ + BR_RANGE_S256, /* br_range */ + { + {0, 8, 0x7}, + { 0, 0, 0 } + }, /* cond_field */ + { + { + INSN_BEQ_R5 /* beq $rt, $r5, label */ + }, /* BR_RANGE_S256 */ + { + INSN_BEQ_R5 /* beq $rt, $r5, label */ + }, /* BR_RANGE_S16K */ + { + INSN_BNE_R5, /* bne $rt, $r5, $1 */ + INSN_J /* j label */ + }, /* BR_RANGE_S64K */ + { + INSN_BNE_R5, /* bne $rt, $r5, $1 */ + INSN_J /* j label */ + }, /* BR_RANGE_S16M */ + { + INSN_BNE_R5, /* bne $rt, $r5, $1 */ + INSN_SETHI_TA, /* sethi $ta, label */ + INSN_ORI_TA, /* ori $ta, $ta, label */ + INSN_JR_TA /* jr $ta */ + } /* BR_RANGE_U4G */ + }, /* relax_code_seq */ + { + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 20, 0x1F}, + {0, 0, 0} + } /* BR_RANGE_U4G */ + }, /* relax_code_condition */ + {4, 4, 8, 8, 16}, /* relax_code_size */ + {4, 4, 4, 4, 4}, /* relax_branch_isize */ + { + { + {0, 4, NDS32_ORIGIN, BFD_RELOC_NDS32_17_PCREL}, + {0, 2, NDS32_CONVERT, BFD_RELOC_NDS32_9_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 4, 0, BFD_RELOC_NDS32_15_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 8, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP2}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 8, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP2}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 4, NDS32_ORIGIN| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_15_PCREL}, + {0, 2, NDS32_CONVERT| NDS32_CREATE_LABLE, BFD_RELOC_NDS32_9_PCREL}, + {0, 16, NDS32_RELAX, BFD_RELOC_NDS32_LONGJUMP3}, + {4, 4, 0, BFD_RELOC_NDS32_HI20}, + {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, + {12, 4, NDS32_ORIGIN, 0}, + {12, 2, NDS32_CONVERT, 0}, + {0, 0, 0, 0} + } /* BR_RANGE_U4G */ + } /* relax_fixup */ + }, + { + "beqc", /* opcode */ + BR_RANGE_S256, /* br_range */ + { + {0, 8, 0x7FF}, + {0, 20, 0x1F}, + {0, 0, 0} + }, /* cond_field */ + { + { + INSN_BEQC /* beqc $rt, imm11s, label */ + }, /* BR_RANGE_S256 */ + { + INSN_MOVI_TA, /* movi $ta, imm11s */ + INSN_BEQ_TA /* beq $rt, $ta, label */ + }, /* BR_RANGE_S16K */ + { + INSN_MOVI_TA, /* movi $ta, imm11s */ + INSN_BEQ_TA /* beq $rt, $ta, label */ + }, /* BR_RANGE_S64K */ + { + INSN_BNEC, /* bnec $rt, imm11s, $1 */ + INSN_J /* j label */ + }, /* BR_RANGE_S16M */ + { + INSN_BNEC, /* bnec $rt, imm11s, $1 */ + INSN_SETHI_TA, /* sethi $ta, label */ + INSN_ORI_TA, /* ori $ta, $ta, label */ + INSN_JR_TA /* jr $ta */ + } /* BR_RANGE_U4G */ + }, /* relax_code_seq */ + { + { + {0, 8, 0x7FF}, + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 0, 0xFFFFF}, + {4, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 0, 0xFFFFF}, + {4, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 8, 0x7FF}, + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 8, 0x7FF}, + {0, 20, 0x1F}, + {0, 0, 0} + } /* BR_RANGE_U4G */ + }, /* relax_code_condition */ + {4, 8, 8, 8, 16}, /* relax_code_size */ + {4, 4, 4, 4, 4}, /* relax_branch_isize */ + { + { + {0, 4, 0, BFD_RELOC_NDS32_WORD_9_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {4, 4, 0, BFD_RELOC_NDS32_15_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {4, 4, 0, BFD_RELOC_NDS32_15_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 4, NDS32_CREATE_LABLE, BFD_RELOC_NDS32_WORD_9_PCREL}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 4, NDS32_CREATE_LABLE, BFD_RELOC_NDS32_WORD_9_PCREL}, + {4, 4, 0, BFD_RELOC_NDS32_HI20}, + {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, + {12, 4, NDS32_ORIGIN, 0}, + {12, 2, NDS32_CONVERT, 0}, + {0, 0, 0, 0} + } /* BR_RANGE_U4G */ + } /* relax_fixup */ + }, + { + "bnec", /* opcode */ + BR_RANGE_S256, /* br_range */ + { + {0, 8, 0x7FF}, + {0, 20, 0x1F}, + {0, 0, 0} + }, /* cond_field */ + { + { + INSN_BNEC /* bnec $rt, imm11s, label */ + }, /* BR_RANGE_S256 */ + { + INSN_MOVI_TA, /* movi $ta, imm11s */ + INSN_BNE_TA /* bne $rt, $ta, label */ + }, /* BR_RANGE_S16K */ + { + INSN_MOVI_TA, /* movi $ta, imm11s */ + INSN_BNE_TA /* bne $rt, $ta, label */ + }, /* BR_RANGE_S64K */ + { + INSN_BEQC, /* beqc $rt, imm11s, $1 */ + INSN_J /* j label */ + }, /* BR_RANGE_S16M */ + { + INSN_BEQC, /* beqc $rt, imm11s, $1 */ + INSN_SETHI_TA, /* sethi $ta, label */ + INSN_ORI_TA, /* ori $ta, $ta, label */ + INSN_JR_TA /* jr $ta */ + } /* BR_RANGE_U4G */ + }, /* relax_code_seq */ + { + { + {0, 8, 0x7FF}, + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {0, 0, 0xFFFFF}, + {4, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {0, 0, 0xFFFFF}, + {4, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 8, 0x7FF}, + {0, 20, 0x1F}, + {0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 8, 0x7FF}, + {0, 20, 0x1F}, + {0, 0, 0} + } /* BR_RANGE_U4G */ + }, /* relax_code_condition */ + {4, 8, 8, 8, 16}, /* relax_code_size */ + {4, 4, 4, 4, 4}, /* relax_branch_isize */ + { + { + {0, 4, 0, BFD_RELOC_NDS32_WORD_9_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S256 */ + { + {4, 4, 0, BFD_RELOC_NDS32_15_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16K */ + { + {4, 4, 0, BFD_RELOC_NDS32_15_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S64K */ + { + {0, 4, NDS32_CREATE_LABLE, BFD_RELOC_NDS32_WORD_9_PCREL}, + {4, 4, 0, BFD_RELOC_NDS32_25_PCREL}, + {0, 0, 0, 0} + }, /* BR_RANGE_S16M */ + { + {0, 4, NDS32_CREATE_LABLE, BFD_RELOC_NDS32_WORD_9_PCREL}, + {4, 4, 0, BFD_RELOC_NDS32_HI20}, + {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI}, + {12, 4, NDS32_ORIGIN, 0}, + {12, 2, NDS32_CONVERT, 0}, + {0, 0, 0, 0} + } /* BR_RANGE_U4G */ + } /* relax_fixup */ + }, + { + NULL, /* opcode */ + 0, /* br_range */ + {{0, 0, 0}}, /* cond_field */ + {{0}}, /* relax_code_seq */ + {{{0, 0, 0}}}, /* relax_code_condition */ + {0}, /* relax_code_size */ + {0}, /* relax_branch_isize */ + {{{0, 0, 0, 0}}}, /* relax_fixup */ + }, +}; + + +/* GAS definitions for command-line options. */ +enum options +{ + OPTION_BIG = OPTION_MD_BASE, + OPTION_LITTLE, + OPTION_TURBO, + OPTION_PIC, + OPTION_RELAX_FP_AS_GP_OFF, + OPTION_RELAX_B2BB_ON, + OPTION_RELAX_ALL_OFF, + OPTION_OPTIMIZE, + OPTION_OPTIMIZE_SPACE +}; + +const char *md_shortopts = "m:G:O"; +struct option md_longopts[] = +{ + {"O1", no_argument, NULL, OPTION_OPTIMIZE}, + {"Os", no_argument, NULL, OPTION_OPTIMIZE_SPACE}, + {"big", no_argument, NULL, OPTION_BIG}, + {"little", no_argument, NULL, OPTION_LITTLE}, + {"EB", no_argument, NULL, OPTION_BIG}, + {"EL", no_argument, NULL, OPTION_LITTLE}, + {"meb", no_argument, NULL, OPTION_BIG}, + {"mel", no_argument, NULL, OPTION_LITTLE}, + {"mall-ext", no_argument, NULL, OPTION_TURBO}, + {"mpic", no_argument, NULL, OPTION_PIC}, + /* Relaxation related options. */ + {"mno-fp-as-gp-relax", no_argument, NULL, OPTION_RELAX_FP_AS_GP_OFF}, + {"mb2bb", no_argument, NULL, OPTION_RELAX_B2BB_ON}, + {"mno-all-relax", no_argument, NULL, OPTION_RELAX_ALL_OFF}, + {NULL, no_argument, NULL, 0} +}; + +size_t md_longopts_size = sizeof (md_longopts); + +struct nds32_parse_option_table +{ + const char *name; /* Option string. */ + char *help; /* Help description. */ + int (*func) (char *arg); /* How to parse it. */ +}; + + +/* The value `-1' represents this option has *NOT* been set. */ +#ifdef NDS32_DEFAULT_ARCH_NAME +static char* nds32_arch_name = NDS32_DEFAULT_ARCH_NAME; +#else +static char* nds32_arch_name = "v3"; +#endif +static int nds32_baseline = -1; +static int nds32_gpr16 = -1; +static int nds32_fpu_sp_ext = -1; +static int nds32_fpu_dp_ext = -1; +static int nds32_freg = -1; +static int nds32_abi = -1; + +/* Record ELF flags */ +static int nds32_elf_flags = 0; +static int nds32_fpu_com = 0; + +static int nds32_parse_arch (char *str); +static int nds32_parse_baseline (char *str); +static int nds32_parse_freg (char *str); +static int nds32_parse_abi (char *str); + +static struct nds32_parse_option_table parse_opts [] = +{ + {"arch=", N_("<arch name>\t Assemble for architecture <arch name>\n\ + <arch name> could be\n\ + v3, v3j, v3m, v3f, v3s, "\ + "v2, v2j, v2f, v2s"), nds32_parse_arch}, + {"baseline=", N_("<baseline>\t Assemble for baseline <baseline>\n\ + <baseline> could be v2, v3, v3m"), + nds32_parse_baseline}, + {"fpu-freg=", N_("<freg>\t Specify a FPU configuration\n\ + <freg>\n\ + 0: 8 SP / 4 DP registers\n\ + 1: 16 SP / 8 DP registers\n\ + 2: 32 SP / 16 DP registers\n\ + 3: 32 SP / 32 DP registers"), nds32_parse_freg}, + {"abi=", N_("<abi>\t Specify a abi version\n\ + <abi> could be v1, v2, v2fp, v2fpp"), nds32_parse_abi}, + {NULL, NULL, NULL} +}; + +static int nds32_mac = 1; +static int nds32_div = 1; +static int nds32_16bit_ext = 1; +static int nds32_dx_regs = 1; +static int nds32_perf_ext = 1; +static int nds32_perf_ext2 = 1; +static int nds32_string_ext = 1; +static int nds32_audio_ext = 1; +static int nds32_fpu_fma = 0; +static int nds32_pic = 0; +static int nds32_relax_fp_as_gp = 1; +static int nds32_relax_b2bb = 0; +static int nds32_relax_all = 1; +struct nds32_set_option_table +{ + const char *name; /* Option string. */ + char *help; /* Help description. */ + int *var; /* Variable to be set. */ + int value; /* Value to set. */ +}; + +/* The option in this group has both Enable/Disable settings. + Just list on here. */ + +static struct nds32_set_option_table toggle_opts [] = +{ + {"mac", N_("Multiply instructions support"), &nds32_mac, 1}, + {"div", N_("Divide instructions support"), &nds32_div, 1}, + {"16bit-ext", N_("16-bit extension"), &nds32_16bit_ext, 1}, + {"dx-regs", N_("d0/d1 registers"), &nds32_dx_regs, 1}, + {"perf-ext", N_("Performance extension"), &nds32_perf_ext, 1}, + {"perf2-ext", N_("Performance extension 2"), &nds32_perf_ext2, 1}, + {"string-ext", N_("String extension"), &nds32_string_ext, 1}, + {"reduced-regs", N_("Reduced Register configuration (GPR16) option"), &nds32_gpr16, 1}, + {"audio-isa-ext", N_("AUDIO ISA extension"), &nds32_audio_ext, 1}, + {"fpu-sp-ext", N_("FPU SP extension"), &nds32_fpu_sp_ext, 1}, + {"fpu-dp-ext", N_("FPU DP extension"), &nds32_fpu_dp_ext, 1}, + {"fpu-fma", N_("FPU fused-multiply-add instructions"), &nds32_fpu_fma, 1}, + {NULL, NULL, NULL, 0} +}; + + +/* GAS declarations. */ + +/* This is the callback for nds32-asm.c to parse operands. */ +int +nds32_asm_parse_operand (struct nds32_asm_desc *pdesc, + struct nds32_asm_insn *pinsn, + char **pstr, int64_t *value); + + +struct nds32_asm_desc asm_desc; + +/* md_after_parse_args () + + GAS will call md_after_parse_args whenever it is defined. + This function checks any conflicting options specified. */ + +void +nds32_after_parse_args (void) +{ + /* If -march option is not used in command-line, set the value of option + variable according to NDS32_DEFAULT_ARCH_NAME. */ + nds32_parse_arch (nds32_arch_name); +} + +/* This function is called when printing usage message (--help). */ + +void +md_show_usage (FILE *stream) +{ + struct nds32_parse_option_table *coarse_tune; + struct nds32_set_option_table *fine_tune; + + fprintf (stream, _("\n NDS32-specific assembler options:\n")); + fprintf (stream, _("\ + -O1, Optimize for performance\n\ + -Os Optimize for space\n")); + fprintf (stream, _("\ + -EL, -mel or -little Produce little endian output\n\ + -EB, -meb or -big Produce big endian output\n\ + -mpic Generate PIC\n\ + -mno-fp-as-gp-relax Suppress fp-as-gp relaxation for this file\n\ + -mb2bb-relax Back-to-back branch optimization\n\ + -mno-all-relax Suppress all relaxation for this file\n")); + + for (coarse_tune = parse_opts; coarse_tune->name != NULL; coarse_tune++) + { + if (coarse_tune->help != NULL) + fprintf (stream, _(" -m%s%s\n"), + coarse_tune->name, _(coarse_tune->help)); + } + + for (fine_tune = toggle_opts; fine_tune->name != NULL; fine_tune++) + { + if (fine_tune->help != NULL) + fprintf (stream, _(" -m[no-]%-17sEnable/Disable %s\n"), + fine_tune->name, _(fine_tune->help)); + } + + fprintf (stream, _("\ + -mall-ext Turn on all extensions and instructions support\n")); +} + +void +nds32_frag_init (fragS *fragp) +{ + fragp->tc_frag_data.flag = 0; + fragp->tc_frag_data.opcode = NULL; + fragp->tc_frag_data.fixup = NULL; +} + + + +/* This function reads an expression from a C string and returns a pointer past + the end of the expression. */ + +static char * +parse_expression (char *str, expressionS *exp) +{ + char *s; + char *tmp; + + tmp = input_line_pointer; /* Save line pointer. */ + input_line_pointer = str; + expression (exp); + s = input_line_pointer; + input_line_pointer = tmp; /* Restore line pointer. */ + + return s; /* Return pointer to where parsing stopped. */ +} + +void +nds32_start_line_hook (void) +{ +} + +/* + * Pseudo opcodes + */ + +typedef void (*nds32_pseudo_opcode_func) (int argc, char *argv[], int pv); +struct nds32_pseudo_opcode +{ + const char *opcode; + int argc; + nds32_pseudo_opcode_func proc; + int pseudo_val; + + /* Some instructions are not pseudo opcode, but they might still be + expanded or changed with other instruction combination for some + conditions. We also apply this structure to assist such work. + + For example, if the distance of branch target '.L0' is larger than + imm8s<<1 range, + + the instruction: + + beqzs8 .L0 + + will be transformed into: + + bnezs8 .LCB0 + j .L0 + .LCB0: + + However, sometimes we do not want assembler to do such changes + because compiler knows how to generate corresponding instruction sequence. + Use this field to indicate that this opcode is also a physical instruction. + If the flag 'verbatim' is nozero and this opcode + is a physical instruction, we should not expand it. */ + int physical_op; +}; +#define PV_DONT_CARE 0 + +static struct hash_control *nds32_pseudo_opcode_hash = NULL; + +static int +builtin_isreg (const char *s, const char *x ATTRIBUTE_UNUSED) +{ + return s[0] == '$'; +} + +static int +builtin_regnum (const char *s, const char *x ATTRIBUTE_UNUSED) +{ + struct nds32_keyword *k; + + k = hash_find (nds32_gprs_hash, s); + + if (k == NULL) + return -1; + + return k->value; +} + +static int +builtin_addend (const char *s, char *x ATTRIBUTE_UNUSED) +{ + const char *ptr = s; + + while (*ptr != '+' && *ptr != '-' && *ptr) + ++ptr; + + if (*ptr == 0) + return 0; + else + return strtol (ptr, NULL, 0); +} + +static void +md_assemblef (char *format, ...) +{ + /* FIXME: hope this is long enough. */ + char line[1024]; + va_list ap; + unsigned int r; + + va_start (ap, format); + r = vsnprintf (line, sizeof (line), format, ap); + md_assemble (line); + + gas_assert (r < sizeof (line)); +} + +/* Some prototypes here, since some op may use another op. */ +static void do_pseudo_li_internal (char *rt, int imm32s); +static void do_pseudo_move_reg_internal (char *dst, char *src); + +static void +do_pseudo_b (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + char *arg_label = argv[0]; + /* b label */ + if (nds32_pic + && (strstr (arg_label, "@GOT") || strstr (arg_label, "@PLT"))) + { + md_assemblef ("sethi $ta,hi20(%s)", arg_label); + md_assemblef ("ori $ta,$ta,lo12(%s)", arg_label); + md_assemble ("add $ta,$ta,$gp"); + md_assemble ("jr $ta"); + } + else + { + md_assemblef ("j %s", arg_label); + } +} + +static void +do_pseudo_bal (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + char *arg_label = argv[0]; + /* bal|call label */ + if (nds32_pic + && (strstr (arg_label, "@GOT") || strstr (arg_label, "@PLT"))) + { + md_assemblef ("sethi $ta,hi20(%s)", arg_label); + md_assemblef ("ori $ta,$ta,lo12(%s)", arg_label); + md_assemble ("add $ta,$ta,$gp"); + md_assemble ("jral $ta"); + } + else + { + md_assemblef ("jal %s", arg_label); + } +} + +static void +do_pseudo_bge (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + /* rt5, ra5, label */ + md_assemblef ("slt $ta,%s,%s", argv[0], argv[1]); + md_assemblef ("beqz $ta,%s", argv[2]); +} + +static void +do_pseudo_bges (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + /* rt5, ra5, label */ + md_assemblef ("slts $ta,%s,%s", argv[0], argv[1]); + md_assemblef ("beqz $ta,%s", argv[2]); +} + +static void +do_pseudo_bgt (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + /* bgt rt5, ra5, label */ + md_assemblef ("slt $ta,%s,%s", argv[1], argv[0]); + md_assemblef ("bnez $ta,%s", argv[2]); +} + +static void +do_pseudo_bgts (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + /* bgt rt5, ra5, label */ + md_assemblef ("slts $ta,%s,%s", argv[1], argv[0]); + md_assemblef ("bnez $ta,%s", argv[2]); +} + +static void +do_pseudo_ble (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + /* bgt rt5, ra5, label */ + md_assemblef ("slt $ta,%s,%s", argv[1], argv[0]); + md_assemblef ("beqz $ta,%s", argv[2]); +} + +static void +do_pseudo_bles (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + /* bgt rt5, ra5, label */ + md_assemblef ("slts $ta,%s,%s", argv[1], argv[0]); + md_assemblef ("beqz $ta,%s", argv[2]); +} + +static void +do_pseudo_blt (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + /* rt5, ra5, label */ + md_assemblef ("slt $ta,%s,%s", argv[0], argv[1]); + md_assemblef ("bnez $ta,%s", argv[2]); +} + +static void +do_pseudo_blts (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + /* rt5, ra5, label */ + md_assemblef ("slts $ta,%s,%s", argv[0], argv[1]); + md_assemblef ("bnez $ta,%s", argv[2]); +} + +static void +do_pseudo_br (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + md_assemblef ("jr %s", argv[0]); +} + +static void +do_pseudo_bral (int argc, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + if (argc == 1) + md_assemblef ("jral $lp,%s", argv[0]); + else + md_assemblef ("jral %s,%s", argv[0], argv[1]); +} + +static void +do_pseudo_la_internal (const char *arg_reg, const char *arg_label, const char *line) +{ + /* rt, label */ + if (!nds32_pic) + { + md_assemblef ("sethi %s,hi20(%s)", arg_reg, arg_label); + md_assemblef ("ori %s,%s,lo12(%s)", arg_reg, arg_reg, arg_label); + } + else if ((strstr (arg_label, "@PLT") || strstr (arg_label, "@GOTOFF"))) + { + md_assemblef ("sethi $ta,hi20(%s)", arg_label); + md_assemblef ("ori $ta,$ta,lo12(%s)", arg_label); + md_assemblef ("add %s,$ta,$gp", arg_reg); + } + else if (strstr (arg_label, "@GOT")) + { + long addend = builtin_addend (arg_label, NULL); + + md_assemblef ("sethi $ta,hi20(%s)", arg_label); + md_assemblef ("ori $ta,$ta,lo12(%s)", arg_label); + md_assemblef ("lw %s,[$gp+$ta]", arg_reg); + if (addend != 0) + { + if (addend < 0x4000 && addend >= -0x4000) + { + md_assemblef ("addi %s,%s,%d", arg_reg, arg_reg, addend); + } + else + { + do_pseudo_li_internal ("$ta", addend); + md_assemblef ("add %s,$ta,%s", arg_reg, arg_reg); + } + } + } + else + as_bad (_("need PIC qualifier with symbol. '%s'"), line); +} + +static void +do_pseudo_la (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + do_pseudo_la_internal (argv[0], argv[1], argv[argc]); +} + +static void +do_pseudo_li_internal (char *rt, int imm32s) +{ + if (enable_16bit && imm32s <= 0xf && imm32s >= -0x10) + md_assemblef ("movi55 %s,%d", rt, imm32s); + else if (imm32s <= 0x7ffff && imm32s >= -0x80000) + md_assemblef ("movi %s,%d", rt, imm32s); + else if ((imm32s & 0xfff) == 0) + md_assemblef ("sethi %s,hi20(%d)", rt, imm32s); + else + { + md_assemblef ("sethi %s,hi20(%d)", rt, imm32s); + md_assemblef ("ori %s,%s,lo12(%d)", rt, rt, imm32s); + } +} + +static void +do_pseudo_li (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + /* Validate argv[1] for constant expression. */ + expressionS exp; + + parse_expression (argv[1], &exp); + if (exp.X_op != O_constant) + { + as_bad (_("Operand is not a constant. `%s'"), argv[argc]); + return; + } + + do_pseudo_li_internal (argv[0], exp.X_add_number); +} + +static void +do_pseudo_ls_bhw (int argc ATTRIBUTE_UNUSED, char *argv[], int pv) +{ + char ls = 'r'; + char size = 'x'; + const char *sign = ""; + + /* Prepare arguments for various load/store. */ + sign = (pv & 0x10) ? "s" : ""; + ls = (pv & 0x80000000) ? 's' : 'l'; + switch (pv & 0x3) + { + case 0: size = 'b'; break; + case 1: size = 'h'; break; + case 2: size = 'w'; break; + } + + if (ls == 's' || size == 'w') + sign = ""; + + if (builtin_isreg (argv[1], NULL)) + { + /* lwi */ + md_assemblef ("%c%c%si %s,[%s]", ls, size, argv[0], argv[1]); + } + else if (!nds32_pic) + { + /* lwi */ + md_assemblef ("sethi $ta,hi20(%s)", argv[1]); + md_assemblef ("%c%c%si %s,[$ta+lo12(%s)]", ls, size, sign, argv[0], argv[1]); + } + else + { + /* PIC code. */ + if (strstr (argv[1], "@GOTOFF")) + { + /* lw */ + md_assemblef ("sethi $ta,hi20(%s)", argv[1]); + md_assemblef ("ori $ta,$ta,lo12(%s)", argv[1]); + md_assemblef ("%c%c%s %s,[$ta+$gp]", ls, size, sign, argv[0]); + } + else if (strstr (argv[1], "@GOT")) + { + long addend = builtin_addend (argv[1], NULL); + /* lw */ + md_assemblef ("sethi $ta,hi20(%s)", argv[1]); + md_assemblef ("ori $ta,$ta,lo12(%s)", argv[1]); + md_assemble ("lw $ta,[$gp+$ta]"); /* Load address word. */ + if (addend < 0x10000 && addend >= -0x10000) + { + md_assemblef ("%c%c%si %s,[$ta+(%d)]", ls, size, sign, argv[0], addend); + } + else + { + /* lw */ + do_pseudo_li_internal (argv[0], addend); + md_assemblef ("%c%c%s %s,[$ta+%s]", ls, size, sign, argv[0], argv[0]); + } + } + else + { + as_bad (_("needs @GOT or @GOTOFF. %s"), argv[argc]); + } + } +} + +static void +do_pseudo_ls_bhwp (int argc ATTRIBUTE_UNUSED, char *argv[], int pv) +{ + char *arg_rt = argv[0]; + char *arg_label = argv[1]; + char *arg_inc = argv[2]; + char ls = 'r'; + char size = 'x'; + const char *sign = ""; + + /* Prepare arguments for various load/store. */ + sign = (pv & 0x10) ? "s" : ""; + ls = (pv & 0x80000000) ? 's' : 'l'; + switch (pv & 0x3) + { + case 0: size = 'b'; break; + case 1: size = 'h'; break; + case 2: size = 'w'; break; + } + + if (ls == 's' || size == 'w') + sign = ""; + + do_pseudo_la_internal ("$ta", arg_label, argv[argc]); + md_assemblef ("%c%c%si.bi %s,[$ta],%s", ls, size, sign, arg_rt, arg_inc); +} + +static void +do_pseudo_ls_bhwpc (int argc ATTRIBUTE_UNUSED, char *argv[], int pv) +{ + char *arg_rt = argv[0]; + char *arg_inc = argv[2]; + char ls = 'r'; + char size = 'x'; + const char *sign = ""; + + /* Prepare arguments for various load/store. */ + sign = (pv & 0x10) ? "s" : ""; + ls = (pv & 0x80000000) ? 's' : 'l'; + switch (pv & 0x3) + { + case 0: size = 'b'; break; + case 1: size = 'h'; break; + case 2: size = 'w'; break; + } + + if (ls == 's' || size == 'w') + sign = ""; + + md_assemblef ("%c%c%si.bi %s,[$ta],%s", ls, size, sign, arg_rt, arg_inc); +} + +static void +do_pseudo_ls_bhwi (int argc ATTRIBUTE_UNUSED, char *argv[], int pv) +{ + char ls = 'r'; + char size = 'x'; + const char *sign = ""; + + /* Prepare arguments for various load/store. */ + sign = (pv & 0x10) ? "s" : ""; + ls = (pv & 0x80000000) ? 's' : 'l'; + switch (pv & 0x3) + { + case 0: size = 'b'; break; + case 1: size = 'h'; break; + case 2: size = 'w'; break; + } + + if (ls == 's' || size == 'w') + sign = ""; + + md_assemblef ("%c%c%si.bi %s,%s,%s", + ls, size, sign, argv[0], argv[1], argv[2]); +} + +static void +do_pseudo_move_reg_internal (char *dst, char *src) +{ + if (enable_16bit) + md_assemblef ("mov55 %s,%s", dst, src); + else + md_assemblef ("ori %s,%s,0", dst, src); +} + +static void +do_pseudo_move (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + if (builtin_isreg (argv[1], NULL)) + do_pseudo_move_reg_internal (argv[0], argv[1]); + else + /* move $rt, imm -> li $rt, imm */ + do_pseudo_li (argc, argv, PV_DONT_CARE); +} + +static void +do_pseudo_neg (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + md_assemble ("movi $ta,0"); + md_assemblef ("sub %s,$ta,%s", argv[0], argv[1]); +} + +static void +do_pseudo_not (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + md_assemblef ("nor %s,%s,%s", argv[0], argv[1], argv[1]); +} + +static void +do_pseudo_pushpopm (int argc, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + /* posh/pop $ra, $rb */ + /* SMW.{b | a}{i | d}{m?} Rb, [Ra], Re, Enable4 */ + int rb, re, ra, en4; + int i; + char *opc = "pushpopm"; + + if (argc == 3) + as_bad ("'pushm/popm $ra5, $rb5, $label' is deprecated. " + "Only 'pushm/popm $ra5' is supported now. %s", argv[argc]); + else if (argc == 1) + as_bad ("'pushm/popm $ra5, $rb5'. %s\n", argv[argc]); + + if (strstr (argv[argc], "pop") == argv[argc]) + opc = "lmw.bim"; + else if (strstr (argv[argc], "push") == argv[argc]) + opc = "smw.adm"; + else + as_fatal ("nds32-as internal error. %s", argv[argc]); + + rb = builtin_regnum (argv[0], NULL); + re = builtin_regnum (argv[1], NULL); + + if (re < rb) + { + as_warn ("$rb should not be smaller than $ra. %s", argv[argc]); + /* Swap to right order. */ + ra = re; + re = rb; + rb = ra; + } + + /* Build enable4 mask. */ + en4 = 0; + if (re >= 28 || rb >= 28) + { + for (i = (rb >= 28? rb: 28); i <= re; i++) + en4 |= 1 << (3 - (i - 28)); + } + + /* Adjust $re, $rb. */ + if (rb >= 28) + rb = re = 31; + else if (re >= 28) + re = 27; + + md_assemblef ("%s $r%d,[$sp],$r%d,%d", opc, rb, re, en4); +} + +static void +do_pseudo_pushpop (int argc, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + /* push/pop $ra5, $label=$sp */ + char *argvm[3]; + + if (argc == 2) + as_bad ("'push/pop $ra5, rb5' is deprecated. " + "Only 'push/pop $ra5' is supported now. %s", argv[argc]); + + argvm[0] = argv[0]; + argvm[1] = argv[0]; + argvm[2] = argv[argc]; + do_pseudo_pushpopm (2, argvm, PV_DONT_CARE); +} + +static void +do_pseudo_v3push (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + md_assemblef ("push25 %s,%s", argv[0], argv[1]); +} + +static void +do_pseudo_v3pop (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + md_assemblef ("pop25 %s,%s", argv[0], argv[1]); +} + +/* pv == 0, parsing "push.s" pseudo instruction operands. + pv != 0, parsing "pop.s" pseudo instruction operands. */ + +static void +do_pseudo_pushpop_stack (int argc, char *argv[], int pv) +{ + /* push.s Rb,Re,{$fp $gp $lp $sp} ==> smw.adm Rb,[$sp],Re,Eable4 */ + /* pop.s Rb,Re,{$fp $gp $lp $sp} ==> lmw.bim Rb,[$sp],Re,Eable4 */ + + int rb, re; + int en4; + int last_arg_index; + char *opc = (pv == 0) ? "smw.adm" : "lmw.bim"; + + rb = re = 0; + + if (argc == 1) + { + /* argc=1, operands pattern: { $fp $gp $lp $sp } */ + + /* Set register number Rb = Re = $sp = $r31. */ + rb = re = 31; + } + else if (argc == 2 || argc == 3) + { + /* argc=2, operands pattern: Rb, Re */ + /* argc=3, operands pattern: Rb, Re, { $fp $gp $lp $sp } */ + + /* Get register number in integer. */ + rb = builtin_regnum (argv[0], NULL); + re = builtin_regnum (argv[1], NULL); + + /* Rb should be equal/less than Re. */ + if (rb > re) + as_bad ("The first operand (%s) should be equal to or smaller than " + "second operand (%s).", argv[0], argv[1]); + + /* forbid using $fp|$gp|$lp|$sp in Rb or Re + r28 r29 r30 r31 */ + if (rb >= 28) + as_bad ("Cannot use $fp, $gp, $lp, or $sp at first operand !!"); + if (re >= 28) + as_bad ("Cannot use $fp, $gp, $lp, or $sp at second operand !!"); + } + else + { + as_bad ("Invalid operands pattern !!"); + } + + /* Build Enable4 mask. */ + /* Using last_arg_index for argc=1|2|3 is safe, because $fp, $gp, $lp, + and $sp only appear in argc=1 or argc=3 if argc=2, en4 remains 0, + which is also valid for code generation. */ + en4 = 0; + last_arg_index = argc - 1; + if (strstr (argv[last_arg_index], "$fp")) + en4 |= 8; + if (strstr (argv[last_arg_index], "$gp")) + en4 |= 4; + if (strstr (argv[last_arg_index], "$lp")) + en4 |= 2; + if (strstr (argv[last_arg_index], "$sp")) + en4 |= 1; + + md_assemblef ("%s $r%d,[$sp],$r%d,%d", opc, rb, re, en4); +} + +static void +do_pseudo_push_bhwd (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + char size = 'x'; + /* If users omit push location, use $sp as default value. */ + char location[8] = "$sp"; /* 8 is enough for register name. */ + + switch (pv & 0x3) + { + case 0: size = 'b'; break; + case 1: size = 'h'; break; + case 2: size = 'w'; break; + case 3: size = 'w'; break; + } + + if (argc == 2) + { + strncpy (location, argv[1], 8); + location[7] = '\0'; + } + + md_assemblef ("l.%c $ta,%s", size, argv[0]); + md_assemblef ("smw.adm $ta,[%s],$ta", location); + + if ((pv & 0x3) == 0x3) /* double-word */ + { + md_assemblef ("l.w $ta,%s+4", argv[0]); + md_assemblef ("smw.adm $ta,[%s],$ta", location); + } +} + +static void +do_pseudo_pop_bhwd (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + char size = 'x'; + /* If users omit pop location, use $sp as default value. */ + char location[8] = "$sp"; /* 8 is enough for register name. */ + + switch (pv & 0x3) + { + case 0: size = 'b'; break; + case 1: size = 'h'; break; + case 2: size = 'w'; break; + case 3: size = 'w'; break; + } + + if (argc == 3) + { + strncpy (location, argv[2], 8); + location[7] = '\0'; + } + + if ((pv & 0x3) == 0x3) /* double-word */ + { + md_assemblef ("lmw.bim %s,[%s],%s", argv[1], location, argv[1]); + md_assemblef ("s.w %s,%s+4", argv[1], argv[0]); + } + + md_assemblef ("lmw.bim %s,[%s],%s", argv[1], location, argv[1]); + md_assemblef ("s.%c %s,%s", size, argv[1], argv[0]); +} + +static void +do_pseudo_pusha (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + /* If users omit push location, use $sp as default value. */ + char location[8] = "$sp"; /* 8 is enough for register name. */ + + if (argc == 2) + { + strncpy (location, argv[1], 8); + location[7] = '\0'; + } + + md_assemblef ("la $ta,%s", argv[0]); + md_assemblef ("smw.adm $ta,[%s],$ta", location); +} + +static void +do_pseudo_pushi (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED) +{ + /* If users omit push location, use $sp as default value. */ + char location[8] = "$sp"; /* 8 is enough for register name. */ + + if (argc == 2) + { + strncpy (location, argv[1], 8); + location[7] = '\0'; + } + + md_assemblef ("li $ta,%s", argv[0]); + md_assemblef ("smw.adm $ta,[%s],$ta", location); +} + +struct nds32_pseudo_opcode nds32_pseudo_opcode_table[] = +{ + {"b", 1, do_pseudo_b, 0, 0}, + {"bal", 1, do_pseudo_bal, 0, 0}, + + {"bge", 3, do_pseudo_bge, 0, 0}, + {"bges", 3, do_pseudo_bges, 0, 0}, + + {"bgt", 3, do_pseudo_bgt, 0, 0}, + {"bgts", 3, do_pseudo_bgts, 0, 0}, + + {"ble", 3, do_pseudo_ble, 0, 0}, + {"bles", 3, do_pseudo_bles, 0, 0}, + + {"blt", 3, do_pseudo_blt, 0, 0}, + {"blts", 3, do_pseudo_blts, 0, 0}, + + {"br", 1, do_pseudo_br, 0, 0}, + {"bral", 1, do_pseudo_bral, 0, 0}, + + {"call", 1, do_pseudo_bal, 0, 0}, + + {"la", 2, do_pseudo_la, 0, 0}, + {"li", 2, do_pseudo_li, 0, 0}, + + {"l.b", 2, do_pseudo_ls_bhw, 0, 0}, + {"l.h", 2, do_pseudo_ls_bhw, 1, 0}, + {"l.w", 2, do_pseudo_ls_bhw, 2, 0}, + {"l.bs", 2, do_pseudo_ls_bhw, 0 | 0x10, 0}, + {"l.hs", 2, do_pseudo_ls_bhw, 1 | 0x10, 0}, + {"s.b", 2, do_pseudo_ls_bhw, 0 | 0x80000000, 0}, + {"s.h", 2, do_pseudo_ls_bhw, 1 | 0x80000000, 0}, + {"s.w", 2, do_pseudo_ls_bhw, 2 | 0x80000000, 0}, + + {"l.bp", 3, do_pseudo_ls_bhwp, 0, 0}, + {"l.bpc", 3, do_pseudo_ls_bhwpc, 0, 0}, + {"l.hp", 3, do_pseudo_ls_bhwp, 1, 0}, + {"l.hpc", 3, do_pseudo_ls_bhwpc, 1, 0}, + {"l.wp", 3, do_pseudo_ls_bhwp, 2, 0}, + {"l.wpc", 3, do_pseudo_ls_bhwpc, 2, 0}, + {"l.bsp", 3, do_pseudo_ls_bhwp, 0 | 0x10, 0}, + {"l.bspc", 3, do_pseudo_ls_bhwpc, 0 | 0x10, 0}, + {"l.hsp", 3, do_pseudo_ls_bhwp, 1 | 0x10, 0}, + {"l.hspc", 3, do_pseudo_ls_bhwpc, 1 | 0x10, 0}, + {"s.bp", 3, do_pseudo_ls_bhwp, 0 | 0x80000000, 0}, + {"s.bpc", 3, do_pseudo_ls_bhwpc, 0 | 0x80000000, 0}, + {"s.hp", 3, do_pseudo_ls_bhwp, 1 | 0x80000000, 0}, + {"s.hpc", 3, do_pseudo_ls_bhwpc, 1 | 0x80000000, 0}, + {"s.wp", 3, do_pseudo_ls_bhwp, 2 | 0x80000000, 0}, + {"s.wpc", 3, do_pseudo_ls_bhwpc, 2 | 0x80000000, 0}, + {"s.bsp", 3, do_pseudo_ls_bhwp, 0 | 0x80000000 | 0x10, 0}, + {"s.hsp", 3, do_pseudo_ls_bhwp, 1 | 0x80000000 | 0x10, 0}, + + {"lbi.p", 3, do_pseudo_ls_bhwi, 0, 0}, + {"lhi.p", 3, do_pseudo_ls_bhwi, 1, 0}, + {"lwi.p", 3, do_pseudo_ls_bhwi, 2, 0}, + {"sbi.p", 3, do_pseudo_ls_bhwi, 0 | 0x80000000, 0}, + {"shi.p", 3, do_pseudo_ls_bhwi, 1 | 0x80000000, 0}, + {"swi.p", 3, do_pseudo_ls_bhwi, 2 | 0x80000000, 0}, + {"lbsi.p", 3, do_pseudo_ls_bhwi, 0 | 0x10, 0}, + {"lhsi.p", 3, do_pseudo_ls_bhwi, 1 | 0x10, 0}, + {"lwsi.p", 3, do_pseudo_ls_bhwi, 0 | 0x10, 0}, + + {"move", 2, do_pseudo_move, 0, 0}, + {"neg", 2, do_pseudo_neg, 0, 0}, + {"not", 2, do_pseudo_not, 0, 0}, + + {"pop", 2, do_pseudo_pushpop, 0, 0}, + {"push", 2, do_pseudo_pushpop, 0, 0}, + {"popm", 2, do_pseudo_pushpopm, 0, 0}, + {"pushm", 3, do_pseudo_pushpopm, 0, 0}, + + {"v3push", 2, do_pseudo_v3push, 0, 0}, + {"v3pop", 2, do_pseudo_v3pop, 0, 0}, + + /* Support pseudo instructions of pushing/poping registers into/from stack + push.s Rb, Re, { $fp $gp $lp $sp } ==> smw.adm Rb,[$sp],Re,Enable4 + pop.s Rb, Re, { $fp $gp $lp $sp } ==> lmw.bim Rb,[$sp],Re,Enable4 */ + { "push.s", 3, do_pseudo_pushpop_stack, 0, 0 }, + { "pop.s", 3, do_pseudo_pushpop_stack, 1, 0 }, + { "push.b", 2, do_pseudo_push_bhwd, 0, 0 }, + { "push.h", 2, do_pseudo_push_bhwd, 1, 0 }, + { "push.w", 2, do_pseudo_push_bhwd, 2, 0 }, + { "push.d", 2, do_pseudo_push_bhwd, 3, 0 }, + { "pop.b", 3, do_pseudo_pop_bhwd, 0, 0 }, + { "pop.h", 3, do_pseudo_pop_bhwd, 1, 0 }, + { "pop.w", 3, do_pseudo_pop_bhwd, 2, 0 }, + { "pop.d", 3, do_pseudo_pop_bhwd, 3, 0 }, + { "pusha", 2, do_pseudo_pusha, 0, 0 }, + { "pushi", 2, do_pseudo_pushi, 0, 0 }, + + {NULL, 0, NULL, 0, 0} +}; + +static void +nds32_init_nds32_pseudo_opcodes (void) +{ + struct nds32_pseudo_opcode *opcode = nds32_pseudo_opcode_table; + + nds32_pseudo_opcode_hash = hash_new (); + for ( ; opcode->opcode; opcode++) + { + void *op; + + op = hash_find (nds32_pseudo_opcode_hash, opcode->opcode); + if (op != NULL) + { + as_warn (_("Duplicated pseudo-opcode %s."), opcode->opcode); + continue; + } + hash_insert (nds32_pseudo_opcode_hash, opcode->opcode, opcode); + } +} + +static struct nds32_pseudo_opcode * +nds32_lookup_pseudo_opcode (char *str) +{ + int i = 0; + /* Assume pseudo-opcode are less than 16-char in length. */ + char op[16] = {0}; + + for (i = 0; i < (int)ARRAY_SIZE (op); i++) + { + if (ISSPACE (op[i] = str[i])) + break; + } + + if (i >= (int)ARRAY_SIZE (op)) + return NULL; + + op[i] = '\0'; + + return hash_find (nds32_pseudo_opcode_hash, op); +} + +static void +nds32_pseudo_opcode_wrapper (char *line, struct nds32_pseudo_opcode *opcode) +{ + int argc = 0; + char *argv[8] = {NULL}; + char *s; + char *str = xstrdup (line); + + /* Parse arguments for opcode. */ + s = str + strlen (opcode->opcode); + + if (!s[0]) + goto end; + + /* Dummy comma to ease separate arguments as below. */ + s[0] = ','; + do + { + if (s[0] == ',') + { + if (argc >= opcode->argc + || (argc >= (int)ARRAY_SIZE (argv) - 1)) + as_bad (_("Too many argument. `%s'"), line); + + argv[argc] = s + 1; + argc ++; + s[0] = '\0'; + } + ++s; + } while (s[0] != '\0'); +end: + /* Put the origin line for debugging. */ + argv[argc] = line; + opcode->proc (argc, argv, opcode->pseudo_val); + free (str); +} + +/* This function will be invoked from function `nds32_after_parse_args'. + Thus, if the value of option has been set, keep the value the way it is. */ + +static int +nds32_parse_arch (char *str) +{ + static const struct nds32_arch + { + const char *name; + int baseline; + int reduced_reg; + int fpu_sp_ext; + int fpu_dp_ext; + int fpu_freg; + int abi; + } archs[] = + { + {"v3m", ISA_V3M, 1, 0, 0, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_AABI}, + {"v3j", ISA_V3, 1, 0, 0, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_AABI}, + {"v3s", ISA_V3, 0, 1, 0, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_V2FP_PLUS}, + {"v3f", ISA_V3, 0, 1, 1, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_V2FP_PLUS}, + {"v3", ISA_V3, 0, 0, 0, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_AABI}, + {"v2j", ISA_V2, 1, 0, 0, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_AABI}, + {"v2s", ISA_V2, 0, 1, 0, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_V2FP_PLUS}, + {"v2f", ISA_V2, 0, 1, 1, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_V2FP_PLUS}, + {"v2", ISA_V2, 0, 0, 0, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_AABI}, + }; + size_t i; + + for (i = 0; i < ARRAY_SIZE (archs); i++) + { + if (strcmp (str, archs[i].name) != 0) + continue; + + /* The value `-1' represents this option has *NOT* been set. */ + nds32_baseline = (-1 != nds32_baseline) ? nds32_baseline : archs[i].baseline; + nds32_gpr16 = (-1 != nds32_gpr16) ? nds32_gpr16 : archs[i].reduced_reg; + nds32_fpu_sp_ext = (-1 != nds32_fpu_sp_ext) ? nds32_fpu_sp_ext : archs[i].fpu_sp_ext; + nds32_fpu_dp_ext = (-1 != nds32_fpu_dp_ext) ? nds32_fpu_dp_ext : archs[i].fpu_dp_ext; + nds32_freg = (-1 != nds32_freg) ? nds32_freg : archs[i].fpu_freg; + nds32_abi = (-1 != nds32_abi) ? nds32_abi : archs[i].abi; + + return 1; + } + + /* Logic here rejects the input arch name. */ + as_bad (_("unknown arch name `%s'\n"), str); + + return 1; +} + +/* This function parses "baseline" specified. */ + +static int +nds32_parse_baseline (char *str) +{ + if (strcmp (str, "v3") == 0) + nds32_baseline = ISA_V3; + else if (strcmp (str, "v3m") == 0) + nds32_baseline = ISA_V3M; + else if (strcmp (str, "v2") == 0) + nds32_baseline = ISA_V2; + else + { + /* Logic here rejects the input baseline. */ + as_bad (_("unknown baseline `%s'\n"), str); + return 0; + } + + return 1; +} + +/* This function parses "fpu-freg" specified. */ + +static int +nds32_parse_freg (char *str) +{ + if (strcmp (str, "2") == 0) + nds32_freg = E_NDS32_FPU_REG_32SP_16DP; + else if (strcmp (str, "3") == 0) + nds32_freg = E_NDS32_FPU_REG_32SP_32DP; + else if (strcmp (str, "1") == 0) + nds32_freg = E_NDS32_FPU_REG_16SP_8DP; + else if (strcmp (str, "0") == 0) + nds32_freg = E_NDS32_FPU_REG_8SP_4DP; + else + { + /* Logic here rejects the input FPU configuration. */ + as_bad (_("unknown FPU configuration `%s'\n"), str); + return 0; + } + + return 1; +} + +/* This function parse "abi=" specified. */ + +static int +nds32_parse_abi (char *str) +{ + if (strcmp (str, "v2") == 0) + nds32_abi = E_NDS_ABI_AABI; + /* Obsolete. */ + else if (strcmp (str, "v2fp") == 0) + nds32_abi = E_NDS_ABI_V2FP; + else if (strcmp (str, "v1") == 0) + nds32_abi = E_NDS_ABI_V1; + else if (strcmp (str,"v2fpp") == 0) + nds32_abi = E_NDS_ABI_V2FP_PLUS; + else + { + /* Logic here rejects the input abi version. */ + as_bad (_("unknown ABI version`%s'\n"), str); + return 0; + } + + return 1; +} + +/* This function turn on all extensions and instructions support. */ + +static int +nds32_all_ext (void) +{ + nds32_mac = 1; + nds32_div = 1; + nds32_dx_regs = 1; + nds32_16bit_ext = 1; + nds32_perf_ext = 1; + nds32_perf_ext2 = 1; + nds32_string_ext = 1; + nds32_audio_ext = 1; + nds32_fpu_fma = 1; + nds32_fpu_sp_ext = 1; + nds32_fpu_dp_ext = 1; + + return 1; +} + +/* GAS will call md_parse_option whenever getopt returns an unrecognized code, + presumably indicating a special code value which appears in md_longopts. + This function should return non-zero if it handled the option and zero + otherwise. There is no need to print a message about an option not being + recognized. This will be handled by the generic code. */ + +int +nds32_parse_option (int c, char *arg) +{ + struct nds32_parse_option_table *coarse_tune; + struct nds32_set_option_table *fine_tune; + char *ptr_arg = NULL; + + switch (c) + { + case OPTION_OPTIMIZE: + optimize = 1; + optimize_for_space = 0; + break; + case OPTION_OPTIMIZE_SPACE: + optimize = 0; + optimize_for_space = 1; + break; + case OPTION_BIG: + target_big_endian = 1; + break; + case OPTION_LITTLE: + target_big_endian = 0; + break; + case OPTION_TURBO: + nds32_all_ext (); + break; + case OPTION_PIC: + nds32_pic = 1; + break; + case OPTION_RELAX_FP_AS_GP_OFF: + nds32_relax_fp_as_gp = 0; + break; + case OPTION_RELAX_B2BB_ON: + nds32_relax_b2bb = 1; + break; + case OPTION_RELAX_ALL_OFF: + nds32_relax_all = 0; + break; + default: + /* Determination of which option table to search for to save time. */ + ptr_arg = strchr (arg, '='); + if (ptr_arg) + { + /* Find the value after '='. */ + if (ptr_arg != NULL) + ptr_arg++; + for (coarse_tune = parse_opts; coarse_tune->name != NULL; coarse_tune++) + { + if (strncmp (arg, coarse_tune->name, (ptr_arg - arg)) == 0) + { + coarse_tune->func (ptr_arg); + return 1; + } + } + } + else + { + for (fine_tune = toggle_opts; fine_tune->name != NULL; fine_tune++) + { + int disable = 0; + + /* Filter out the Disable option first. */ + if (strncmp (arg, "no-", 3) == 0) + { + disable = 1; + arg += 3; + } + + if (strcmp (arg, fine_tune->name) == 0) + { + if (fine_tune->var != NULL) + *fine_tune->var = (disable) ? 0 : 1; + return 1; + } + } + } + /* Nothing match. */ + return 0; + } + + return 1; +} + +/* tc_check_label */ + +void +nds32_check_label (symbolS *label ATTRIBUTE_UNUSED) +{ + /* The code used to create BB is move to frob_label. + They should go there. */ +} + +static void +set_endian_little (int on) +{ + target_big_endian = !on; +} + +/* These functions toggles the generation of 16-bit. First encounter signals + the beginning of not generating 16-bit instructions and next encounter + signals the restoring back to default behavior. */ + +static void +trigger_16bit (int trigger) +{ + enable_16bit = trigger; +} + +static int backup_16bit_mode; +static void +restore_16bit (int no_use ATTRIBUTE_UNUSED) +{ + enable_16bit = backup_16bit_mode; +} + +static void +off_16bit (int no_use ATTRIBUTE_UNUSED) +{ + backup_16bit_mode = enable_16bit; + enable_16bit = 0; +} + +/* Built-in segments for small object. */ +typedef struct nds32_seg_entryT +{ + segT s; + const char *name; + flagword flags; +} nds32_seg_entry; + +nds32_seg_entry nds32_seg_table[] = +{ + {NULL, ".sdata_f", SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA + | SEC_HAS_CONTENTS | SEC_SMALL_DATA}, + {NULL, ".sdata_b", SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA + | SEC_HAS_CONTENTS | SEC_SMALL_DATA}, + {NULL, ".sdata_h", SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA + | SEC_HAS_CONTENTS | SEC_SMALL_DATA}, + {NULL, ".sdata_w", SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA + | SEC_HAS_CONTENTS | SEC_SMALL_DATA}, + {NULL, ".sdata_d", SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA + | SEC_HAS_CONTENTS | SEC_SMALL_DATA}, + {NULL, ".sbss_f", SEC_ALLOC | SEC_SMALL_DATA}, + {NULL, ".sbss_b", SEC_ALLOC | SEC_SMALL_DATA}, + {NULL, ".sbss_h", SEC_ALLOC | SEC_SMALL_DATA}, + {NULL, ".sbss_w", SEC_ALLOC | SEC_SMALL_DATA}, + {NULL, ".sbss_d", SEC_ALLOC | SEC_SMALL_DATA} +}; + +/* Indexes to nds32_seg_table[]. */ +enum NDS32_SECTIONS_ENUM +{ + SDATA_F_SECTION = 0, + SDATA_B_SECTION = 1, + SDATA_H_SECTION = 2, + SDATA_W_SECTION = 3, + SDATA_D_SECTION = 4, + SBSS_F_SECTION = 5, + SBSS_B_SECTION = 6, + SBSS_H_SECTION = 7, + SBSS_W_SECTION = 8, + SBSS_D_SECTION = 9 +}; + +/* The following code is borrowed from v850_seg. Revise this is needed. */ + +static void +do_nds32_seg (int i, subsegT sub) +{ + nds32_seg_entry *seg = nds32_seg_table + i; + + obj_elf_section_change_hook (); + + if (seg->s != NULL) + subseg_set (seg->s, sub); + else + { + seg->s = subseg_new (seg->name, sub); + if (OUTPUT_FLAVOR == bfd_target_elf_flavour) + { + bfd_set_section_flags (stdoutput, seg->s, seg->flags); + if ((seg->flags & SEC_LOAD) == 0) + seg_info (seg->s)->bss = 1; + } + } +} + +static void +nds32_seg (int i) +{ + subsegT sub = get_absolute_expression (); + + do_nds32_seg (i, sub); + demand_empty_rest_of_line (); +} + +/* Set if label adjustment is needed. I should not adjust .xbyte in dwarf. */ +static symbolS *nds32_last_label; /* Last label for aligment. */ + +/* This code is referred from D30V for adjust label to be with pedning + aligment. For example, + LBYTE: .byte 0x12 + LHALF: .half 0x12 + LWORD: .word 0x12 + Without this, the above label will not attatch to incoming data. */ + +static void +nds32_adjust_label (int n) +{ + /* FIXME: I think adjust lable and alignment is + the programmer's obligation. Saddly, VLSI team doesn't + properly use .align for their test cases. + So I re-implement cons_align and auto adjust labels, again. + + I think d30v's implmentation is simple and good enough. */ + + symbolS *label = nds32_last_label; + nds32_last_label = NULL; + + /* SEC_ALLOC is used to eliminate .debug_ sections. + SEC_CODE is used to include section for ILM. */ + if (((now_seg->flags & SEC_ALLOC) == 0 && (now_seg->flags & SEC_CODE) == 0) + || strcmp (now_seg->name, ".eh_frame") == 0 + || strcmp (now_seg->name, ".gcc_except_table") == 0) + return; + + /* Only frag by alignment when needed. + Otherwise, it will fail to optimize labels on 4-byte boundary. (bug8454) + See md_convert_frag () and RELAX_SET_RELAXABLE (frag) for details. */ + if (frag_now_fix () & ((1 << n) -1 )) + { + if (subseg_text_p (now_seg)) + frag_align_code (n, 0); + else + frag_align (n, 0, 0); + + /* Record the minimum alignment for this segment. */ + record_alignment (now_seg, n - OCTETS_PER_BYTE_POWER); + } + + if (label != NULL) + { + symbolS *sym; + int label_seen = FALSE; + struct frag *old_frag; + valueT old_value, new_value; + + gas_assert (S_GET_SEGMENT (label) == now_seg); + + old_frag = symbol_get_frag (label); + old_value = S_GET_VALUE (label); + new_value = (valueT) frag_now_fix (); + + /* Multiple labels may be on the same address. And the last symbol + may not be a label at all, e.g., register name, external function names, + so I have to track the last label in tc_frob_label instead of + just using symbol_lastP. */ + for (sym = symbol_lastP; sym != NULL; sym = symbol_previous (sym)) + { + if (symbol_get_frag (sym) == old_frag + && S_GET_VALUE (sym) == old_value) + { + /* Warning HERE! */ + label_seen = TRUE; + symbol_set_frag (sym, frag_now); + S_SET_VALUE (sym, new_value); + } + else if (label_seen && symbol_get_frag (sym) != old_frag) + break; + } + } +} + +void +nds32_cons_align (int size ATTRIBUTE_UNUSED) +{ + /* Do nothing here. + This is called before `md_flush_pending_output' is called by `cons'. + + There are two things should be done for auto-adjust-label. + 1. Align data/instructions and adjust label to be attached to them. + 2. Clear auto-adjust state, so incommng data/instructions will not + adjust the label. + + For example, + .byte 0x1 + .L0: + .word 0x2 + .word 0x3 + in this case, '.word 0x2' will adjust the label, .L0, but '.word 0x3' should not. + + I think `md_flush_pending_output' is a good place to clear the auto-adjust state, + but it is also called by `cons' before this function. + To simplify the code, instead of overriding .zero, .fill, .space, etc, + I think we should just adjust label in `nds32_aligned_X_cons' instead of here. */ +} + +static void +nds32_aligned_cons (int idx) +{ + nds32_adjust_label (idx); + /* Call default handler. */ + cons (1 << idx); + if (now_seg->flags & SEC_CODE + && now_seg->flags & SEC_ALLOC && now_seg->flags & SEC_RELOC) + { + /* Use BFD_RELOC_NDS32_DATA to avoid EX9 optimization replacing data. */ + expressionS exp; + + exp.X_add_number = 0; + exp.X_op = O_constant; + fix_new_exp (frag_now, + frag_now_fix () - (1 << idx), + 1 << idx, + &exp, + 0, + BFD_RELOC_NDS32_DATA); + } +} + +/* `.double' directive. */ + +static void +nds32_aligned_float_cons (int type) +{ + switch (type) + { + case 'f': + case 'F': + case 's': + case 'S': + nds32_adjust_label (2); + break; + case 'd': + case 'D': + case 'r': + case 'R': + nds32_adjust_label (4); + break; + default: + as_bad ("Unrecognized float type, %c\n", (char)type); + } + /* Call default handler. */ + float_cons (type); +} + +static void +nds32_enable_pic (int ignore ATTRIBUTE_UNUSED) +{ + /* Another way to do -mpic. + This is for GCC internal use and should always be first line + of code, otherwise, the effect is not determined. */ + nds32_pic = 1; +} + +static void +nds32_set_abi (int ver) +{ + nds32_abi = ver; +} + +/* Relax directive to set relocation R_NDS32_RELAX_ENTRY value. */ + +static void +nds32_relax_relocs (int relax) +{ + char saved_char; + char *name; + int i; + char *subtype_relax[] = + {"", "", "ex9", "ifc"}; + + name = input_line_pointer; + while (*input_line_pointer && !ISSPACE (*input_line_pointer)) + input_line_pointer++; + saved_char = *input_line_pointer; + *input_line_pointer = 0; + + for (i = 0; i < (int) ARRAY_SIZE (subtype_relax); i++) + { + if (strcmp (name, subtype_relax[i]) == 0) + { + switch (i) + { + case 0: + case 1: + enable_relax_relocs = relax & enable_relax_relocs; + enable_relax_ex9 = relax & enable_relax_ex9; + enable_relax_ifc = relax & enable_relax_ifc; + break; + case 2: + enable_relax_ex9 = relax; + break; + case 3: + enable_relax_ifc = relax; + break; + default: + break; + } + break; + } + } + *input_line_pointer = saved_char; + ignore_rest_of_line (); +} + +/* Record which arguments register($r0 ~ $r5) is not used in callee. + bit[i] for $ri */ + +static void +nds32_set_hint_func_args (int ignore ATTRIBUTE_UNUSED) +{ + ignore_rest_of_line (); +} + +/* Insert relocations to mark the begin and end of a fp-omitted function, + for further relaxation use. + bit[i] for $ri */ + +static void +nds32_omit_fp_begin (int mode) +{ + expressionS exp; + + if (nds32_relax_fp_as_gp == 0) + return; + exp.X_op = O_symbol; + exp.X_add_symbol = abs_section_sym; + if (mode == 1) + { + exp.X_add_number = R_NDS32_RELAX_REGION_OMIT_FP_FLAG; + fix_new_exp (frag_now, frag_now_fix (), 0, &exp, 0, + BFD_RELOC_NDS32_RELAX_REGION_BEGIN); + } + else + { + exp.X_add_number = R_NDS32_RELAX_REGION_OMIT_FP_FLAG; + fix_new_exp (frag_now, frag_now_fix (), 0, &exp, 0, + BFD_RELOC_NDS32_RELAX_REGION_END); + } +} + +/* Insert relocations to mark the begin and end of ex9 region, + for further relaxation use. + bit[i] for $ri */ + +static void +nds32_no_ex9_begin (int mode) +{ + expressionS exp; + + exp.X_op = O_symbol; + exp.X_add_symbol = abs_section_sym; + if (mode == 1) + { + exp.X_add_number = R_NDS32_RELAX_REGION_NO_EX9_FLAG; + fix_new_exp (frag_now, frag_now_fix (), 0, &exp, 0, + BFD_RELOC_NDS32_RELAX_REGION_BEGIN); + } + else + { + exp.X_add_number = R_NDS32_RELAX_REGION_NO_EX9_FLAG; + fix_new_exp (frag_now, frag_now_fix (), 0, &exp, 0, + BFD_RELOC_NDS32_RELAX_REGION_END); + } +} + +static void +nds32_loop_begin (int mode) +{ + /* Insert loop region relocation here. */ + expressionS exp; + + exp.X_op = O_symbol; + exp.X_add_symbol = abs_section_sym; + if (mode == 1) + { + exp.X_add_number = R_NDS32_RELAX_REGION_INNERMOST_LOOP_FLAG; + fix_new_exp (frag_now, frag_now_fix (), 0, &exp, 0, + BFD_RELOC_NDS32_RELAX_REGION_BEGIN); + } + else + { + exp.X_add_number = R_NDS32_RELAX_REGION_INNERMOST_LOOP_FLAG; + fix_new_exp (frag_now, frag_now_fix (), 0, &exp, 0, + BFD_RELOC_NDS32_RELAX_REGION_END); + } +} + +struct nds32_relocs_group +{ + struct nds32_relocs_pattern *pattern; + struct nds32_relocs_group *next; +}; + +static struct nds32_relocs_group *nds32_relax_hint_current = NULL; + +/* Insert a relax hint. */ + +static void +nds32_relax_hint (int mode ATTRIBUTE_UNUSED) +{ + char *name; + char saved_char; + struct nds32_relocs_pattern *relocs = NULL; + struct nds32_relocs_group *group, *new; + + name = input_line_pointer; + while (*input_line_pointer && !ISSPACE (*input_line_pointer)) + input_line_pointer++; + saved_char = *input_line_pointer; + *input_line_pointer = 0; + name = strdup (name); + + /* Find relax hint entry for next instruction, and all member will be + initialized at that time. */ + relocs = hash_find (nds32_hint_hash, name); + if (relocs == NULL) + { + relocs = malloc (sizeof (struct nds32_relocs_pattern)); + hash_insert (nds32_hint_hash, name, relocs); + } + else + { + while (relocs->next) + relocs=relocs->next; + relocs->next = malloc (sizeof (struct nds32_relocs_pattern)); + relocs = relocs->next; + } + + relocs->next = NULL; + *input_line_pointer = saved_char; + ignore_rest_of_line (); + + /* Get the final one of relax hint series. */ + + /* It has to build this list because there are maybe more than one + instructions relative to the same instruction. It to connect to + next instruction after md_assemble. */ + new = malloc (sizeof (struct nds32_relocs_group)); + new->pattern = relocs; + new->next = NULL; + group = nds32_relax_hint_current; + if (!group) + nds32_relax_hint_current = new; + else + { + while (group->next != NULL) + group = group->next; + group->next = new; + } +} + +/* Decide the size of vector entries, only accepts 4 or 16 now. */ + +static void +nds32_vec_size (int ignore ATTRIBUTE_UNUSED) +{ + expressionS exp; + + expression (&exp); + + if (exp.X_op == O_constant) + { + if (exp.X_add_number == 4 || exp.X_add_number == 16) + { + if (vec_size == 0) + vec_size = exp.X_add_number; + else if (vec_size != exp.X_add_number) + as_warn (_("Different arguments of .vec_size are found, " + "previous %d, current %d"), + (int) vec_size, (int) exp.X_add_number); + } + else + as_warn (_("Argument of .vec_size is expected 4 or 16, actual: %d."), + (int) exp.X_add_number); + } + else + as_warn (_("Argument of .vec_size is not a constant.")); +} + +/* The behavior of ".flag" directive varies depending on the target. + In nds32 target, we use it to recognize whether this assembly content is + generated by compiler. Other features can also be added in this function + in the future. */ + +static void +nds32_flag (int ignore ATTRIBUTE_UNUSED) +{ + char *name; + char saved_char; + int i; + char *possible_flags[] = { "verbatim" }; + + /* Skip whitespaces. */ + name = input_line_pointer; + while (*input_line_pointer && !ISSPACE (*input_line_pointer)) + input_line_pointer++; + saved_char = *input_line_pointer; + *input_line_pointer = 0; + + for (i = 0; i < (int) ARRAY_SIZE (possible_flags); i++) + { + if (strcmp (name, possible_flags[i]) == 0) + { + switch (i) + { + case 0: + /* flag: verbatim */ + verbatim = 1; + break; + default: + break; + } + /* Already found the flag, no need to continue next loop. */ + break; + } + } + + *input_line_pointer = saved_char; + ignore_rest_of_line (); +} + +static void +nds32_n12hc (int ignore ATTRIBUTE_UNUSED) +{ + /* N1213HC core is used. */ +} + + +/* The target specific pseudo-ops which we support. */ +const pseudo_typeS md_pseudo_table[] = +{ + /* Forced alignment if declared these ways. */ + {"ascii", stringer, 8 + 0}, + {"asciz", stringer, 8 + 1}, + {"double", nds32_aligned_float_cons, 'd'}, + {"dword", nds32_aligned_cons, 3}, + {"float", nds32_aligned_float_cons, 'f'}, + {"half", nds32_aligned_cons, 1}, + {"hword", nds32_aligned_cons, 1}, + {"int", nds32_aligned_cons, 2}, + {"long", nds32_aligned_cons, 2}, + {"octa", nds32_aligned_cons, 4}, + {"quad", nds32_aligned_cons, 3}, + {"qword", nds32_aligned_cons, 4}, + {"short", nds32_aligned_cons, 1}, + {"byte", nds32_aligned_cons, 0}, + {"single", nds32_aligned_float_cons, 'f'}, + {"string", stringer, 8 + 1}, + {"word", nds32_aligned_cons, 2}, + + {"little", set_endian_little, 1}, + {"big", set_endian_little, 0}, + {"16bit_on", trigger_16bit, 1}, + {"16bit_off", trigger_16bit, 0}, + {"restore_16bit", restore_16bit, 0}, + {"off_16bit", off_16bit, 0}, + + {"sdata_d", nds32_seg, SDATA_D_SECTION}, + {"sdata_w", nds32_seg, SDATA_W_SECTION}, + {"sdata_h", nds32_seg, SDATA_H_SECTION}, + {"sdata_b", nds32_seg, SDATA_B_SECTION}, + {"sdata_f", nds32_seg, SDATA_F_SECTION}, + + {"sbss_d", nds32_seg, SBSS_D_SECTION}, + {"sbss_w", nds32_seg, SBSS_W_SECTION}, + {"sbss_h", nds32_seg, SBSS_H_SECTION}, + {"sbss_b", nds32_seg, SBSS_B_SECTION}, + {"sbss_f", nds32_seg, SBSS_F_SECTION}, + + {"pic", nds32_enable_pic, 0}, + {"n12_hc", nds32_n12hc, 0}, + {"abi_1", nds32_set_abi, E_NDS_ABI_V1}, + {"abi_2", nds32_set_abi, E_NDS_ABI_AABI}, + /* Obsolete. */ + {"abi_2fp", nds32_set_abi, E_NDS_ABI_V2FP}, + {"abi_2fp_plus", nds32_set_abi, E_NDS_ABI_V2FP_PLUS}, + {"relax", nds32_relax_relocs, 1}, + {"no_relax", nds32_relax_relocs, 0}, + {"hint_func_args", nds32_set_hint_func_args, 0}, /* Abandon?? */ + {"omit_fp_begin", nds32_omit_fp_begin, 1}, + {"omit_fp_end", nds32_omit_fp_begin, 0}, + {"no_ex9_begin", nds32_no_ex9_begin, 1}, + {"no_ex9_end", nds32_no_ex9_begin, 0}, + {"vec_size", nds32_vec_size, 0}, + {"flag", nds32_flag, 0}, + {"innermost_loop_begin", nds32_loop_begin, 1}, + {"innermost_loop_end", nds32_loop_begin, 0}, + {"relax_hint", nds32_relax_hint, 0}, + {NULL, NULL, 0} +}; + +void +nds32_pre_do_align (int n, char *fill, int len, int max) +{ + /* Only make a frag if we HAVE to... */ + if (n != 0 && !need_pass_2) + { + if (fill == NULL) + { + if (subseg_text_p (now_seg)) + frag_align_code (n, max); + else + frag_align (n, 0, max); + } + else if (len <= 1) + frag_align (n, *fill, max); + else + frag_align_pattern (n, fill, len, max); + } +} + +void +nds32_do_align (int n) +{ + /* Optimize for space and label exists. */ + expressionS exp; + + /* FIXME:I think this will break debug info sections and except_table. */ + if (!enable_relax_relocs || !subseg_text_p (now_seg)) + return; + + /* Create and attach a BFD_RELOC_NDS32_LABEL fixup + the size of instruction may not be correct because + it could be relaxable. */ + exp.X_op = O_symbol; + exp.X_add_symbol = section_symbol (now_seg); + exp.X_add_number = n; + fix_new_exp (frag_now, + frag_now_fix (), 0, &exp, 0, BFD_RELOC_NDS32_LABEL); +} + +/* Supported Andes machines. */ +struct nds32_machs +{ + enum bfd_architecture bfd_mach; + int mach_flags; +}; + +/* This is the callback for nds32-asm.c to parse operands. */ + +int +nds32_asm_parse_operand (struct nds32_asm_desc *pdesc ATTRIBUTE_UNUSED, + struct nds32_asm_insn *pinsn, + char **pstr, int64_t *value) +{ + char *hold; + expressionS *pexp = pinsn->info; + + hold = input_line_pointer; + input_line_pointer = *pstr; + expression (pexp); + *pstr = input_line_pointer; + input_line_pointer = hold; + + switch (pexp->X_op) + { + case O_symbol: + *value = 0; + return NASM_R_SYMBOL; + case O_constant: + *value = pexp->X_add_number; + return NASM_R_CONST; + case O_illegal: + case O_absent: + case O_register: + default: + return NASM_R_ILLEGAL; + } +} + +/* GAS will call this function at the start of the assembly, after the command + line arguments have been parsed and all the machine independent + initializations have been completed. */ + +void +md_begin (void) +{ + struct nds32_keyword *k; + relax_info_t *relax_info; + + bfd_set_arch_mach (stdoutput, TARGET_ARCH, nds32_baseline); + + nds32_init_nds32_pseudo_opcodes (); + asm_desc.parse_operand = nds32_asm_parse_operand; + nds32_asm_init (&asm_desc, 0); + + /* Initial general pupose registers hash table. */ + nds32_gprs_hash = hash_new (); + for (k = nds32_gprs; k->name; k++) + hash_insert (nds32_gprs_hash, k->name, k); + + /* Initial branch hash table. */ + nds32_relax_info_hash = hash_new (); + for (relax_info = relax_table; relax_info->opcode; relax_info++) + hash_insert (nds32_relax_info_hash, relax_info->opcode, relax_info); + + /* Initial relax hint hash table. */ + nds32_hint_hash = hash_new (); +} + +/* HANDLE_ALIGN in write.c. */ + +void +nds32_handle_align (fragS *fragp) +{ + static const unsigned char nop16[] = { 0x92, 0x00 }; + static const unsigned char nop32[] = { 0x40, 0x00, 0x00, 0x09 }; + int bytes; + char *p; + + if (fragp->fr_type != rs_align_code) + return; + + bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix; + p = fragp->fr_literal + fragp->fr_fix; + + if (bytes & 1) + { + *p++ = 0; + bytes--; + } + + if (bytes & 2) + { + expressionS exp_t; + exp_t.X_op = O_symbol; + exp_t.X_add_symbol = abs_section_sym; + exp_t.X_add_number = R_NDS32_INSN16_CONVERT_FLAG; + fix_new_exp (fragp, fragp->fr_fix, 2, &exp_t, 0, + BFD_RELOC_NDS32_INSN16); + memcpy (p, nop16, 2); + p += 2; + bytes -= 2; + } + + while (bytes >= 4) + { + memcpy (p, nop32, 4); + p += 4; + bytes -= 4; + } + + bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix; + fragp->fr_fix += bytes; +} + +/* md_flush_pending_output */ + +void +nds32_flush_pending_output (void) +{ + nds32_last_label = NULL; +} + +void +nds32_frob_label (symbolS *label) +{ + dwarf2_emit_label (label); +} + +/* TC_START_LABEL */ + +int +nds32_start_label (int asmdone ATTRIBUTE_UNUSED, int secdone ATTRIBUTE_UNUSED) +{ + return 1; +} + +/* TARGET_FORMAT */ + +const char * +nds32_target_format (void) +{ +#ifdef TE_LINUX + if (target_big_endian) + return "elf32-nds32be-linux"; + else + return "elf32-nds32le-linux"; +#else + if (target_big_endian) + return "elf32-nds32be"; + else + return "elf32-nds32le"; +#endif +} + +static enum nds32_br_range +get_range_type (const struct nds32_field *field) +{ + gas_assert (field != NULL); + + if (field->bitpos != 0) + return BR_RANGE_U4G; + + if (field->bitsize == 24 && field->shift == 1) + return BR_RANGE_S16M; + else if (field->bitsize == 16 && field->shift == 1) + return BR_RANGE_S64K; + else if (field->bitsize == 14 && field->shift == 1) + return BR_RANGE_S16K; + else if (field->bitsize == 8 && field->shift == 1) + return BR_RANGE_S256; + else + return BR_RANGE_U4G; +} + +/* Save pseudo instruction relocation list. */ + +static struct nds32_relocs_pattern* +nds32_elf_save_pseudo_pattern (int reloc, struct nds32_asm_insn *insn, + char *out, symbolS *sym, + struct nds32_relocs_pattern *reloc_ptr) +{ + if (!reloc_ptr) + reloc_ptr = malloc (sizeof (struct nds32_relocs_pattern)); + reloc_ptr->seg = now_seg; + reloc_ptr->sym = sym; + reloc_ptr->frag = frag_now; + reloc_ptr->frchain = frchain_now; + reloc_ptr->reloc = reloc; + reloc_ptr->insn = insn->opcode->value; + reloc_ptr->size = insn->opcode->isize; + reloc_ptr->where = out; + reloc_ptr->next = NULL; + return reloc_ptr; +} + +/* Check X_md to transform relocation. */ + +static void +nds32_elf_record_fixup_exp (char *str, const struct nds32_field *fld, + expressionS *pexp, char* out, + struct nds32_asm_insn *insn) +{ + int reloc = -1; + symbolS *sym = NULL; + struct nds32_relocs_group *group; + struct nds32_relocs_pattern *reloc_ptr; + + /* Handle instruction relocation. */ + if (fld && fld->bitpos == 0 && (insn->attr & NASM_ATTR_HI20)) + { + /* Relocation for hi20 modifier. */ + sym = pexp->X_add_symbol; + switch (pexp->X_md) + { + case BFD_RELOC_NDS32_GOTOFF: + /* @GOTOFF */ + reloc = BFD_RELOC_NDS32_GOTOFF_HI20; + break; + case BFD_RELOC_NDS32_GOT20: + /* @GOT */ + reloc = BFD_RELOC_NDS32_GOT_HI20; + break; + case BFD_RELOC_NDS32_25_PLTREL: + /* @PLT */ + if (!nds32_pic) + as_bad (_("Invalid PIC expression.")); + else + reloc = BFD_RELOC_NDS32_PLT_GOTREL_HI20; + break; + default: + /* No suffix. */ + reloc = BFD_RELOC_NDS32_HI20; + break; + } + + fix_new_exp (frag_now, out - frag_now->fr_literal, + insn->opcode->isize, insn->info, 0 /* pcrel */, + reloc); + } + else if (fld && fld->bitpos == 0 && (insn->attr & NASM_ATTR_LO12)) + { + /* Relocation for lo12 modifier. */ + if (fld->bitsize == 15 && fld->shift == 0) + { + switch (pexp->X_md) + { + case BFD_RELOC_NDS32_GOTOFF: + /* @GOTOFF */ + reloc = BFD_RELOC_NDS32_GOTOFF_LO12; + break; + case BFD_RELOC_NDS32_GOT20: + /* @GOT */ + reloc = BFD_RELOC_NDS32_GOT_LO12; + break; + case BFD_RELOC_NDS32_25_PLTREL: + /* @PLT */ + if (!nds32_pic) + as_bad (_("Invalid PIC expression.")); + else + reloc = BFD_RELOC_NDS32_PLT_GOTREL_LO12; + break; + default: + /* No suffix. */ + reloc = BFD_RELOC_NDS32_LO12S0; /* [ls]bi || ori */ + break; + } + } + else if (fld->bitsize == 15 && fld->shift == 1) + reloc = BFD_RELOC_NDS32_LO12S1; /* [ls]hi */ + else if (fld->bitsize == 15 && fld->shift == 2) + reloc = BFD_RELOC_NDS32_LO12S2; /* [ls]wi */ + else if (fld->bitsize == 15 && fld->shift == 3) + reloc = BFD_RELOC_NDS32_LO12S3; /* [ls]di */ + else if (fld->bitsize == 12 && fld->shift == 2) + reloc = BFD_RELOC_NDS32_LO12S2; /* f[ls][sd]i */ + + fix_new_exp (frag_now, out - frag_now->fr_literal, + insn->opcode->isize, insn->info, 0 /* pcrel */, + reloc); + } + else if (fld && fld->bitpos == 0 && insn->opcode->isize == 4 + && (insn->attr & NASM_ATTR_PCREL)) + { + /* Relocation for 32-bit branch instructions. */ + if (fld->bitsize == 24 && fld->shift == 1) + reloc = BFD_RELOC_NDS32_25_PCREL; + else if (fld->bitsize == 16 && fld->shift == 1) + reloc = BFD_RELOC_NDS32_17_PCREL; + else if (fld->bitsize == 14 && fld->shift == 1) + reloc = BFD_RELOC_NDS32_15_PCREL; + else if (fld->bitsize == 8 && fld->shift == 1) + reloc = BFD_RELOC_NDS32_WORD_9_PCREL; + else + abort (); + + fix_new_exp (frag_now, out - frag_now->fr_literal, + insn->opcode->isize, insn->info, 1 /* pcrel */, + reloc); + } + else if (fld && fld->bitpos == 0 && insn->opcode->isize == 4 + && (insn->attr & NASM_ATTR_GPREL)) + { + /* Relocation for 32-bit gp-relative instructions. */ + if (fld->bitsize == 19 && fld->shift == 0) + reloc = BFD_RELOC_NDS32_SDA19S0; + else if (fld->bitsize == 18 && fld->shift == 1) + reloc = BFD_RELOC_NDS32_SDA18S1; + else if (fld->bitsize == 17 && fld->shift == 2) + reloc = BFD_RELOC_NDS32_SDA17S2; + else + abort (); + + fix_new_exp (frag_now, out - frag_now->fr_literal, + insn->opcode->isize, insn->info, 0 /* pcrel */, + reloc); + } + else if (fld && fld->bitpos == 0 && insn->opcode->isize == 2 + && (insn->attr & NASM_ATTR_PCREL)) + { + /* Relocation for 16-bit branch instructions. */ + if (fld->bitsize == 8 && fld->shift == 1) + reloc = BFD_RELOC_NDS32_9_PCREL; + else + abort (); + + fix_new_exp (frag_now, out - frag_now->fr_literal, + insn->opcode->isize, insn->info, 1 /* pcrel */, + reloc); + } + else if (fld) + { + as_bad (_("Don't know how to handle this field. %s"), + str); + } + + if (pseudo_opcode) + { + /* Save instruction relation for pseudo instruction expanding pattern. */ + reloc_ptr = nds32_elf_save_pseudo_pattern (reloc, insn, out, sym, NULL); + if (!relocs_list) + relocs_list = reloc_ptr; + else + { + struct nds32_relocs_pattern *temp = relocs_list; + while (temp->next) + temp = temp->next; + temp->next = reloc_ptr; + } + } + else if (nds32_relax_hint_current) + { + /* Save instruction relation by relax hint. */ + group = nds32_relax_hint_current; + while (group) + { + nds32_elf_save_pseudo_pattern (reloc, insn, out, sym, group->pattern); + group = group->next; + free (nds32_relax_hint_current); + nds32_relax_hint_current = group; + } + } +} + +#define N32_MEM_EXT(insn) (N32_OP6_MEM<< 25| insn) + +/* Relax pattern for link time relaxation. */ + +static struct nds32_relocation_map relocation_table[] = +{ + { + /* Load-Store: sethi lwi+ + Load address: sethi ori */ + BFD_RELOC_NDS32_HI20, /* main_type */ + { + { + {BFD_RELOC_NDS32_LOADSTORE, 0}, + {0, 0} + }, + { + {BFD_RELOC_NDS32_INSN16, 0}, + {0, 0} + }, + { + {0, 0} + } + }, + }, + { + /* Load-Store: sethi ori lwi+ + Load address: sethi ori add */ + BFD_RELOC_NDS32_GOTOFF_HI20, /* main_type */ + { + { + {BFD_RELOC_NDS32_LOADSTORE, 0}, + {0, 0} + }, + { + {BFD_RELOC_NDS32_INSN16, 0}, + {BFD_RELOC_NDS32_PTR, 0}, + {BFD_RELOC_NDS32_PTR_COUNT, 0}, + {0, 0} + }, + { + {BFD_RELOC_NDS32_GOTOFF_SUFF, 0}, + {BFD_RELOC_NDS32_PTR_RESOLVED, 0}, + {0, 0} + }, + { + {0, 0} + } + }, + }, + { + /* Load-Store: sethi ori lw lwi+ + Load address: sethi ori lw [addi|add] */ + BFD_RELOC_NDS32_GOT_HI20, /* main_type */ + { + { + {BFD_RELOC_NDS32_LOADSTORE, 0}, + {0, 0} + }, + { + {BFD_RELOC_NDS32_INSN16, 0}, + /* For pseudo la and l.w. + Lw is the next one instruction. */ + {BFD_RELOC_NDS32_PTR, N32_MEM_EXT (N32_MEM_LW)}, + {BFD_RELOC_NDS32_PTR_COUNT, 0}, + {0, 0} + }, + { + {BFD_RELOC_NDS32_GOT_SUFF, N32_MEM_EXT (N32_MEM_LW)}, + {BFD_RELOC_NDS32_PTR_RESOLVED, N32_MEM_EXT (N32_MEM_LW)}, + {0, 0} + }, + { + {0, 0}, + }, + }, + }, + { + BFD_RELOC_NDS32_PLT_GOTREL_HI20, /* main_type */ + { + { + {BFD_RELOC_NDS32_LOADSTORE, 0}, + {0, 0} + }, + { + {BFD_RELOC_NDS32_INSN16, 0}, + /* For pseudo bal. + jral is the target instruction. */ + {BFD_RELOC_NDS32_PTR, INSN_JRAL}, + {BFD_RELOC_NDS32_PTR, (INSN_JRAL | (REG_LP << 20))}, + {BFD_RELOC_NDS32_PTR_COUNT, 0}, + {0, 0} + }, + { + /* For pseudo bal. + jral is the target instruction. */ + {BFD_RELOC_NDS32_PTR, INSN_JRAL}, + {BFD_RELOC_NDS32_PTR, (INSN_JRAL | (REG_LP << 20))}, + {BFD_RELOC_NDS32_PTR_COUNT, 0}, + {0, 0} + }, + { + {BFD_RELOC_NDS32_PLT_GOT_SUFF, 0}, + {BFD_RELOC_NDS32_PTR_RESOLVED, 0}, + {0, 0} + }, + { + {0, 0}, + }, + }, + }, + { + 0, + { + { + {0, 0}, + }, + }, + } +}; + +/* Since sethi loadstore relocation has to using next instruction to determine + elimination itself or not, we have to return the next instruction range. */ + +static int +nds32_elf_sethi_range (struct nds32_relocs_pattern *relocs_ptr) +{ + unsigned int insn = relocs_ptr->insn; + int range; + switch (insn) + { + case INSN_LBI: + case INSN_SBI: + case INSN_LBSI: + case N32_MEM_EXT (N32_MEM_LB): + case N32_MEM_EXT (N32_MEM_LBS): + case N32_MEM_EXT (N32_MEM_SB): + range = 0x01; + break; + case INSN_LHI: + case INSN_SHI: + case INSN_LHSI: + case N32_MEM_EXT (N32_MEM_LH): + case N32_MEM_EXT (N32_MEM_LHS): + case N32_MEM_EXT (N32_MEM_SH): + range = 0x02; + break; + case INSN_LWI: + case INSN_SWI: + case N32_MEM_EXT (N32_MEM_LW): + case N32_MEM_EXT (N32_MEM_SW): + range = 0x04; + break; + case INSN_FLSI: + case INSN_FSSI: + range = 0x08; + break; + case INSN_FLDI: + case INSN_FSDI: + range = 0x10; + break; + case INSN_ORI: + range = 0x20; + break; + default: + range = 0x0; + break; + } + return range; +} + +/* The args means: instruction size, the 1st instruction is converted to 16 or + not, optimize option, 16 bit instruction is enable. */ +#define SET_ADDEND( size, convertible, optimize, insn16_on ) \ + (((size) & 0xff) | ((convertible) ? 1 << 31 : 0) \ + | ((optimize) ? 1<< 30 : 0) | (insn16_on ? 1 << 29 : 0)) + +/* Insert new fix. */ + +static void +nds32_elf_insert_relocation (struct nds32_relocs_pattern *pattern, unsigned int reloc, + unsigned int insn_mask, symbolS *sym) +{ + expressionS exp; + symbolS *sym_t; + struct nds32_relocs_pattern *pattern_t; + int range; + fragS *frag = pattern->frag; + char *out = pattern->where; + unsigned int size = pattern->size; + static int ptr_count = 0; + + exp.X_op = O_symbol; + exp.X_add_symbol = abs_section_sym; + exp.X_add_number = 0; + + switch (reloc) + { + case BFD_RELOC_NDS32_LOADSTORE: + /* To get the sethi match pattern. */ + range = nds32_elf_sethi_range (pattern->next); + exp.X_add_number = SET_ADDEND (4 /* size */, 0, optimize, enable_16bit); + exp.X_add_number |= ((range & 0x3f) << 8); + fix_new_exp (frag, out - frag->fr_literal, size, &exp, 0 /* pcrel */, reloc); + break; + + case BFD_RELOC_NDS32_PTR: + pattern_t = pattern->next; + while (pattern_t) + { + if (insn_mask == 0 || pattern_t->insn == insn_mask) + { + sym_t = symbol_temp_new (pattern_t->seg, + pattern_t->where - pattern_t->frag->fr_literal, + pattern_t->frag); + exp.X_add_symbol = sym_t; + fix_new_exp (frag, out - frag->fr_literal, 0, &exp, 0 /* pcrel */, reloc); + ptr_count++; + break; + } + pattern_t = pattern_t->next; + } + break; + + case BFD_RELOC_NDS32_PTR_COUNT: + /* In current design, it only be referanced once. */ + if (ptr_count != 0) + { + exp.X_add_number = ptr_count; + fix_new_exp (frag, out - frag->fr_literal, size, &exp, 0, reloc); + } + ptr_count = 0; + break; + + case BFD_RELOC_NDS32_GOTOFF_SUFF: + case BFD_RELOC_NDS32_GOT_SUFF: + case BFD_RELOC_NDS32_PLT_GOT_SUFF: + /* It has to record symbol. */ + if (insn_mask == 0 || pattern->insn == insn_mask) + { + exp.X_add_symbol = sym; + fix_new_exp (frag, out - frag->fr_literal, size, &exp, 0, reloc); + } + break; + + case BFD_RELOC_NDS32_PTR_RESOLVED: + default: + if (insn_mask == 0 || pattern->insn == insn_mask) + { + fix_new_exp (frag, out - frag->fr_literal, size, &exp, 0, reloc); + } + } +} + +static void +nds32_set_elf_flags_by_insn (struct nds32_asm_insn * insn) +{ + /* Set E_NDS32_HAS_EXT_INST. */ + if (insn->opcode->attr & NASM_ATTR_PERF_EXT) + { + if (nds32_perf_ext) + nds32_elf_flags |= E_NDS32_HAS_EXT_INST; + else + as_bad (_("instruction %s requires enabling performance extension"), + insn->opcode->opcode); + } + else if (insn->opcode->attr & NASM_ATTR_PERF2_EXT) + { + if (nds32_perf_ext2) + nds32_elf_flags |= E_NDS32_HAS_EXT2_INST; + else + as_bad (_("instruction %s requires enabling performance extension II"), + insn->opcode->opcode); + } + else if (insn->opcode->attr & NASM_ATTR_AUDIO_ISAEXT) + { + if (nds32_audio_ext) + nds32_elf_flags |= E_NDS32_HAS_AUDIO_INST; + else + as_bad (_("instruction %s requires enabling AUDIO extension"), + insn->opcode->opcode); + } + else if (insn->opcode->attr & NASM_ATTR_STR_EXT) + { + if (nds32_string_ext) + nds32_elf_flags |= E_NDS32_HAS_STRING_INST; + else + as_bad (_("instruction %s requires enabling STRING extension"), + insn->opcode->opcode); + } + else if ((insn->opcode->attr & NASM_ATTR_DIV) + && (insn->opcode->attr & NASM_ATTR_DXREG)) + { + if (nds32_div && nds32_dx_regs) + nds32_elf_flags |= E_NDS32_HAS_DIV_DX_INST; + else + as_bad (_("instruction %s requires enabling DIV & DX_REGS extension"), + insn->opcode->opcode); + } + else if (insn->opcode->attr & NASM_ATTR_FPU) + { + if (nds32_fpu_sp_ext || nds32_fpu_dp_ext) + { + if (!(nds32_elf_flags & (E_NDS32_HAS_FPU_INST | E_NDS32_HAS_FPU_DP_INST))) + nds32_fpu_com = 1; + } + else + as_bad (_("instruction %s requires enabling FPU extension"), + insn->opcode->opcode); + } + else if (insn->opcode->attr & NASM_ATTR_FPU_SP_EXT) + { + if (nds32_fpu_sp_ext) + nds32_elf_flags |= E_NDS32_HAS_FPU_INST; + else + as_bad (_("instruction %s requires enabling FPU_SP extension"), + insn->opcode->opcode); + } + else if ((insn->opcode->attr & NASM_ATTR_FPU_SP_EXT) + && (insn->opcode->attr & NASM_ATTR_MAC)) + { + if (nds32_fpu_sp_ext && nds32_mac) + { + nds32_elf_flags |= E_NDS32_HAS_FPU_MAC_INST; + nds32_elf_flags |= E_NDS32_HAS_FPU_INST; + } + else + as_bad (_("instruction %s requires enabling FPU_MAC extension"), + insn->opcode->opcode); + } + else if (insn->opcode->attr & NASM_ATTR_FPU_DP_EXT) + { + if (nds32_fpu_dp_ext) + nds32_elf_flags |= E_NDS32_HAS_FPU_DP_INST; + else + as_bad (_("instruction %s requires enabling FPU_DP extension"), + insn->opcode->opcode); + } + else if ((insn->opcode->attr & NASM_ATTR_FPU_DP_EXT) + && (insn->opcode->attr & NASM_ATTR_MAC)) + { + if (nds32_fpu_dp_ext && nds32_mac) + { + nds32_elf_flags |= E_NDS32_HAS_FPU_MAC_INST; + nds32_elf_flags |= E_NDS32_HAS_FPU_DP_INST; + } + else + as_bad (_("instruction %s requires enabling FPU_MAC extension"), + insn->opcode->opcode); + } + /* TODO: FPU_BOTH */ + else if ((insn->opcode->attr & NASM_ATTR_MAC) + && (insn->opcode->attr & NASM_ATTR_DXREG)) + { + if (nds32_mac && nds32_dx_regs) + nds32_elf_flags |= E_NDS32_HAS_MAC_DX_INST; + else + as_bad (_("instruction %s requires enabling DX_REGS extension"), + insn->opcode->opcode); + } + /* TODO: for DX_REG set but not for MAC, DIV, AUDIO */ + else if (insn->opcode->attr & NASM_ATTR_IFC_EXT) + { + nds32_elf_flags |= E_NDS32_HAS_IFC_INST; + } + /* TODO: E_NDS32_HAS_SATURATION_INST */ +} + +/* Append relax relocation for link time relaxing. */ + +static void +nds32_elf_append_relax_relocs (const char *key ATTRIBUTE_UNUSED, void *value) +{ + struct nds32_relocs_pattern *relocs_temp = + (struct nds32_relocs_pattern *) value; + unsigned int reloc, group_type, insn; + symbolS *sym; + unsigned int i = 0, x = 0, y; + segT seg_bak = now_seg; + frchainS *frchain_bak = frchain_now; + + if (!relocs_temp) + return; + + group_type = relocs_temp->reloc; + sym = relocs_temp->sym; + /* Inserting fix up must specify now_seg or frchain_now. */ + now_seg = relocs_temp->seg; + frchain_now = relocs_temp->frchain; + + /* Find pattern in relocation table. */ + while (i < (sizeof (relocation_table)/sizeof (relocation_table[0])) + &&relocation_table[i].main_type != group_type) + i++; + + /* Can not find relocation pattern. */ + if (relocation_table[i].main_type == 0) + return; + + while (relocs_temp) + { + y = 0; + + while (relocation_table[i].reloc_insn[x][y][0] != 0) + { + reloc = relocation_table[i].reloc_insn[x][y][0]; + insn = relocation_table[i].reloc_insn[x][y][1]; + nds32_elf_insert_relocation (relocs_temp, reloc, insn, sym); + y++; + } + + /* Next instruction. */ + relocs_temp = relocs_temp->next; + + /* There are load store instruction shared setting symbol part, so + re-using the final relocation. */ + if (relocation_table[i].reloc_insn[x+1][0][0] != 0) + x++; + } + + now_seg = seg_bak; + frchain_now = frchain_bak; +} + +/* Check instruction if it can be used for the baseline. */ + +static bfd_boolean +nds32_check_insn_available (struct nds32_asm_insn insn, char *str) +{ + int attr = insn.attr & ATTR_ALL; + static int baseline_isa = 0; + /* No isa setting or all isa can use. */ + if (attr == 0 || attr == ATTR_ALL) + return TRUE; + + if (baseline_isa == 0) + { + /* Map option baseline and instruction attribute. */ + switch (nds32_baseline) + { + case ISA_V2: + baseline_isa = ATTR (ISA_V2); + break; + case ISA_V3: + baseline_isa = ATTR (ISA_V3); + break; + case ISA_V3M: + baseline_isa = ATTR (ISA_V3M); + break; + } + } + + if ((baseline_isa & attr) == 0) + { + as_bad (_("Not support instrcution %s in the baseline."), str); + return FALSE; + } + return TRUE; +} + +/* Stub of machine dependent. */ + +void +md_assemble (char *str) +{ + struct nds32_asm_insn insn; + char *out; + struct nds32_pseudo_opcode *popcode; + const struct nds32_field *fld = NULL; + fixS *fixP ATTRIBUTE_UNUSED; + int insn_type; + uint16_t insn_16; + uint32_t insn_32; + struct nds32_relocs_pattern *relocs_temp; + expressionS *pexp; + + popcode = nds32_lookup_pseudo_opcode (str); + /* Note that we need to check 'verbatim' and + 'opcode->physical_op'. If the assembly content is generated by + compiler and this opcode is a physical instruction, there is no + need to perform pseudo instruction expansion/transformation. */ + if (popcode && !(verbatim && popcode->physical_op)) + { + pseudo_opcode = TRUE; + nds32_pseudo_opcode_wrapper (str, popcode); + pseudo_opcode = FALSE; + nds32_elf_append_relax_relocs (NULL, relocs_list); + + /* Free pseudo list. */ + relocs_temp = relocs_list; + while (relocs_temp) + { + relocs_list = relocs_list->next; + free (relocs_temp); + relocs_temp = relocs_list; + } + + return; + } + + insn.info = (expressionS *) alloca (sizeof (expressionS)); + nds32_assemble (&asm_desc, &insn, str); + + switch (asm_desc.result) + { + case NASM_ERR_UNKNOWN_OP: + as_bad (_("Unrecognized opcode, %s."), str); + return; + case NASM_ERR_SYNTAX: + as_bad (_("Incorrect syntax, %s."), str); + return; + case NASM_ERR_OPERAND: + as_bad (_("Unrecognized operand, %s."), str); + return; + case NASM_ERR_OUT_OF_RANGE: + as_bad (_("Operand out of range, %s."), str); + return; + case NASM_ERR_REG_REDUCED: + as_bad (_("Prohibited register used for reduced-register, %s."), str); + return; + case NASM_ERR_JUNK_EOL: + as_bad (_("Junk at end of line, %s."), str); + return; + } + + gas_assert (insn.opcode); + + nds32_set_elf_flags_by_insn (&insn); + + gas_assert (insn.opcode->isize == 4 || insn.opcode->isize == 2); + + if (!nds32_check_insn_available (insn, str)) + return; + + /* Create new frag if the instruction can be relaxed. */ + fld = insn.field; + if (!verbatim && fld && (insn.attr & NASM_ATTR_BRANCH)) + { + /* User assembly code branch relax for it. */ + fragS *fragp = frag_now; + + /* If fld is not NULL, it is a symbol. */ + /* Get branch range type. */ + enum nds32_br_range range_type; + range_type = get_range_type (fld); + + pexp = insn.info; + + out = frag_var (rs_machine_dependent, + NDS32_MAXCHAR, + 0, /* VAR is un-used. */ + range_type, /* SUBTYPE is used as range type. */ + pexp->X_add_symbol, + pexp->X_add_number, + 0); + /* If the original frag is full, the instruction must save in next + one. */ + while (fragp->fr_next != frag_now) + fragp = fragp->fr_next; + fragp->fr_fix += insn.opcode->isize; + fragp->tc_frag_data.opcode = insn.opcode; + fragp->tc_frag_data.insn = insn.insn; + dwarf2_emit_insn (insn.opcode->isize); + if (insn.opcode->isize == 4) + bfd_putb32 (insn.insn, out); + else if (insn.opcode->isize == 2) + bfd_putb16 (insn.insn, out); + return; + /* md_convert_frag will insert relocations. */ + } + else if (!verbatim && !fld && (optimize || optimize_for_space)) + { + /* User assembly code without relocating convert it to 16bits if needed. */ + insn_32 = insn.insn; + + /* Convert instruction to 16-bits. */ + if (insn.opcode->isize == 4 + && nds32_convert_32_to_16 (stdoutput, insn_32, + &insn_16, &insn_type)) + { + out = frag_more (2); + frag_var (rs_fill, 0, 0, 0, NULL, 0, NULL); + bfd_putb16 (insn_16, out); + dwarf2_emit_insn (2); + return; + } + } + + out = frag_more (insn.opcode->isize); + + if (insn.opcode->isize == 4) + bfd_putb32 (insn.insn, out); + else if (insn.opcode->isize == 2) + bfd_putb16 (insn.insn, out); + + dwarf2_emit_insn (insn.opcode->isize); + + if (fld) + { + /* Compiler generating code and user assembly pseudo load-store, insert + fixup here. */ + pexp = insn.info; + nds32_elf_record_fixup_exp (str, fld, pexp, out, &insn); + } +} + +/* md_macro_start */ + +void +nds32_macro_start (void) +{ +} + +/* md_macro_info */ + +void +nds32_macro_info (void *info ATTRIBUTE_UNUSED) +{ +} + +/* md_macro_end */ + +void +nds32_macro_end (void) +{ +} + +/* GAS will call this function with one argument, an expressionS pointer, for + any expression that can not be recognized. When the function is called, + input_line_pointer will point to the start of the expression. */ + +void +md_operand (expressionS *expressionP) +{ + if (*input_line_pointer == '#') + { + input_line_pointer++; + expression (expressionP); + } +} + +/* GAS will call this function for each section at the end of the assembly, to + permit the CPU back end to adjust the alignment of a section. The function + must take two arguments, a segT for the section and a valueT for the size of + the section, and return a valueT for the rounded size. */ + +valueT +md_section_align (segT segment, valueT size) +{ + int align = bfd_get_section_alignment (stdoutput, segment); + + return ((size + (1 << align) - 1) & (-1 << align)); +} + +/* GAS will call this function when a symbol table lookup fails, before it + creates a new symbol. Typically this would be used to supply symbols whose + name or value changes dynamically, possibly in a context sensitive way. + Predefined symbols with fixed values, such as register names or condition + codes, are typically entered directly into the symbol table when md_begin + is called. One argument is passed, a char * for the symbol. */ + +symbolS * +md_undefined_symbol (char *name ATTRIBUTE_UNUSED) +{ + return NULL; +} + +static long +nds32_calc_branch_offset (segT segment, fragS *fragP, + long stretch ATTRIBUTE_UNUSED, + relax_info_t *relax_info, + enum nds32_br_range branch_range_type) +{ + struct nds32_opcode *opcode = fragP->tc_frag_data.opcode; + symbolS *branch_symbol = fragP->fr_symbol; + offsetT branch_offset = fragP->fr_offset; + offsetT branch_target_address; + offsetT branch_insn_address; + long offset = 0; + + if ((S_GET_SEGMENT (branch_symbol) != segment) + || S_IS_WEAK (branch_symbol)) + { + /* The symbol is not in the SEGMENT. It could be far far away. */ + offset = 0x80000000; + } + else + { + /* Calculate symbol-to-instruction offset. */ + branch_target_address = S_GET_VALUE (branch_symbol) + branch_offset; + /* If the destination symbol is beyond current frag address, + STRETCH will take effect to symbol's position. */ + if (S_GET_VALUE (branch_symbol) > fragP->fr_address) + branch_target_address += stretch; + + branch_insn_address = fragP->fr_address + fragP->fr_fix; + branch_insn_address -= opcode->isize; + + /* Update BRANCH_INSN_ADDRESS to relaxed position. */ + branch_insn_address += (relax_info->relax_code_size[branch_range_type] + - relax_info->relax_branch_isize[branch_range_type]); + + offset = branch_target_address - branch_insn_address; + } + + return offset; +} + +static enum nds32_br_range +nds32_convert_to_range_type (long offset) +{ + enum nds32_br_range range_type; + + if (-(0x100) <= offset && offset < 0x100) /* 256 bytes */ + range_type = BR_RANGE_S256; + else if (-(0x4000) <= offset && offset < 0x4000) /* 16K bytes */ + range_type = BR_RANGE_S16K; + else if (-(0x10000) <= offset && offset < 0x10000) /* 64K bytes */ + range_type = BR_RANGE_S64K; + else if (-(0x1000000) <= offset && offset < 0x1000000) /* 16M bytes */ + range_type = BR_RANGE_S16M; + else /* 4G bytes */ + range_type = BR_RANGE_U4G; + + return range_type; +} + +/* Set insntruction register mask. */ + +static void +nds32_elf_get_set_cond (relax_info_t *relax_info, int offset, uint32_t *insn, + uint32_t ori_insn, int range) +{ + nds32_cond_field_t *cond_fields = relax_info->cond_field; + nds32_cond_field_t *code_seq_cond = relax_info->relax_code_condition[range]; + uint32_t mask; + int i = 0; + + /* The instruction has conditions. Collect condition values. */ + while (offset == code_seq_cond[i].offset) + { + mask = (ori_insn >> cond_fields[i].bitpos) & cond_fields[i].bitmask; + *insn |= (mask & code_seq_cond[i].bitmask) << code_seq_cond[i].bitpos; + i++; + } +} + + +static int +nds32_relax_branch_instructions (segT segment, fragS *fragP, + long stretch ATTRIBUTE_UNUSED, + int init) +{ + enum nds32_br_range branch_range_type; + struct nds32_opcode *opcode = fragP->tc_frag_data.opcode; + long offset = 0; + enum nds32_br_range real_range_type; + int adjust = 0; + relax_info_t *relax_info; + int diff = 0; + int i, j, k; + int code_seq_size; + uint32_t *code_seq; + uint32_t insn; + int insn_size; + uint16_t insn_16; + int insn_type; + int code_seq_offset; + + /* Replace with gas_assert (fragP->fr_symbol != NULL); */ + if (fragP->fr_symbol == NULL) + return adjust; + + /* If frag_var is not enough room, the previos frag is fr_full and with + opcode. The new one is rs_dependent but without opcode. */ + if (opcode == NULL) + return adjust; + + relax_info = hash_find (nds32_relax_info_hash, opcode->opcode); + + if (relax_info == NULL) + return adjust; + + if (init) + branch_range_type = relax_info->br_range; + else + branch_range_type = fragP->fr_subtype; + + offset = nds32_calc_branch_offset (segment, fragP, stretch, + relax_info, branch_range_type); + + real_range_type = nds32_convert_to_range_type (offset); + + /* If actual range is equal to instruction jump range, do nothing. */ + if (real_range_type == branch_range_type) + return adjust; + + /* Find out proper relaxation code sequence. */ + for (i = BR_RANGE_S256; i < BR_RANGE_NUM; i++) + { + if (real_range_type <= (unsigned int) i) + { + if (init) + diff = relax_info->relax_code_size[i] - opcode->isize; + else + diff = relax_info->relax_code_size[i] + - relax_info->relax_code_size[branch_range_type]; + + /* If the instruction could be converted to 16-bits, + minus the difference. */ + code_seq_offset = 0; + j = 0; + k = 0; + code_seq_size = relax_info->relax_code_size[i]; + code_seq = relax_info->relax_code_seq[i]; + while (code_seq_offset < code_seq_size) + { + insn = code_seq[j]; + if (insn & 0x80000000) /* 16-bits instruction. */ + { + insn_size = 2; + } + else /* 32-bits instruction. */ + { + insn_size = 4; + + while (relax_info->relax_fixup[i][k].size !=0 + && relax_info->relax_fixup[i][k].offset < code_seq_offset) + k++; + if (relax_info->relax_fixup[i][k].size !=0 + && relax_info->relax_fixup[i][k].ramp & NDS32_ORIGIN) + { + /* Set register num to insntruction. */ + nds32_elf_get_set_cond (relax_info, code_seq_offset, &insn, + fragP->tc_frag_data.insn, i); + + /* Try to convert to 16-bits instruction. */ + if (nds32_convert_32_to_16 (stdoutput, + insn, &insn_16, &insn_type)) + diff -= 2; + } + } + + code_seq_offset += insn_size; + j++; + } + + /* Update fr_subtype to new NDS32_BR_RANGE. */ + fragP->fr_subtype = i; + break; + } + } + + return diff + adjust; +} + +/* md_relax_frag */ + +int +nds32_relax_frag (segT segment, fragS *fragP, long stretch ATTRIBUTE_UNUSED) +{ + /* Currently, there are two kinds of relaxation in nds32 assembler. + 1. relax for branch + 2. relax for 32-bits to 16-bits */ + + int adjust; + + adjust = nds32_relax_branch_instructions (segment, fragP, stretch, 0); + + return adjust; +} + +/* This function returns an initial guess of the length by which a fragment + must grow to hold a branch to reach its destination. Also updates + fr_type/fr_subtype as necessary. + + It is called just before doing relaxation. Any symbol that is now undefined + will not become defined. The guess for fr_var is ACTUALLY the growth beyond + fr_fix. Whatever we do to grow fr_fix or fr_var contributes to our returned + value. Although it may not be explicit in the frag, pretend fr_var starts + with a 0 value. */ + +int +md_estimate_size_before_relax (fragS *fragP, segT segment) +{ + /* Currently, there are two kinds of relaxation in nds32 assembler. + 1. relax for branch + 2. relax for 32-bits to 16-bits */ + + int adjust; + + adjust = nds32_relax_branch_instructions (segment, fragP, 0, 1); + + return adjust; +} + +/* GAS will call this for each rs_machine_dependent fragment. The instruction + is completed using the data from the relaxation pass. It may also create any + necessary relocations. + + *FRAGP has been relaxed to its final size, and now needs to have the bytes + inside it modified to conform to the new size. It is called after relaxation + is finished. + + fragP->fr_type == rs_machine_dependent. + fragP->fr_subtype is the subtype of what the address relaxed to. */ + +void +md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec, fragS *fragP) +{ + /* Convert branch relaxation instructions. */ + symbolS *branch_symbol = fragP->fr_symbol; + offsetT branch_offset = fragP->fr_offset; + enum nds32_br_range branch_range_type = fragP->fr_subtype; + struct nds32_opcode *opcode = fragP->tc_frag_data.opcode; + uint32_t origin_insn = fragP->tc_frag_data.insn; + int backup_endian; + relax_info_t *relax_info; + char *fr_buffer; + int fr_where; + int addend ATTRIBUTE_UNUSED; + offsetT branch_target_address; + offsetT branch_insn_address; + expressionS exp; + fixS *fixP; + uint32_t *code_seq; + int code_size; + uint32_t insn; + int insn_size; + int offset; + int i, j, k; + uint16_t insn_16; + int insn_type; + int buf_offset; + nds32_relax_fixup_info_t fixup_info[MAX_RELAX_NUM]; + /* Save the 1st instruction is converted to 16 bit or not. */ + bfd_boolean insn_convert = FALSE; + int fixup_size; + + /* Replace with gas_assert (branch_symbol != NULL); */ + if (branch_symbol == NULL) + return; + + /* If frag_var is not enough room, the previos frag is fr_full and with + opcode. The new one is rs_dependent but without opcode. */ + if (opcode == NULL) + return; + + relax_info = hash_find (nds32_relax_info_hash, opcode->opcode); + + if (relax_info == NULL) + return; + + backup_endian = target_big_endian; + target_big_endian = 1; + + fr_where = fragP->fr_fix - opcode->isize; + fr_buffer = fragP->fr_literal + fr_where; + + if ((S_GET_SEGMENT (branch_symbol) != sec) + || S_IS_WEAK (branch_symbol)) + { + if (fragP->fr_offset & 3) + as_warn (_("Addend to unresolved symbol is not on word boundary.")); + addend = 0; + } + else + { + /* Calculate symbol-to-instruction offset. */ + branch_target_address = S_GET_VALUE (branch_symbol) + branch_offset; + branch_insn_address = fragP->fr_address + fr_where; + addend = (branch_target_address - branch_insn_address) >> 1; + } + + code_size = relax_info->relax_code_size[branch_range_type]; + code_seq = relax_info->relax_code_seq[branch_range_type]; + + memcpy (fixup_info, + relax_info->relax_fixup[branch_range_type], + sizeof (fixup_info)); + + /* Fill in frag. */ + i = 0; + k = 0; + offset = 0; /* code_seq offset */ + buf_offset = 0; /* fr_buffer offset */ + while (offset < code_size) + { + insn = code_seq[i]; + if (insn & 0x80000000) /* 16-bits instruction. */ + { + insn = (insn >> 16) & 0xFFFF; + insn_size = 2; + } + else /* 32-bits instruction. */ + { + insn_size = 4; + } + + nds32_elf_get_set_cond (relax_info, offset, &insn, + origin_insn, branch_range_type); + + /* Try to convert to 16-bits instruction. Currently, only the first + insntruction in pattern can be converted. EX: bnez sethi ori jr, + only bnez can be converted to 16 bit and ori can't. */ + + while (fixup_info[k].size != 0 + && relax_info->relax_fixup[branch_range_type][k].offset < offset) + k++; + if ((fixup_info[k].size != 0 + && fixup_info[k].ramp & NDS32_ORIGIN) + && nds32_convert_32_to_16 (stdoutput, insn, &insn_16, &insn_type)) + { + /* Reduce to 16-bits instructions, adjust fixup_info[j]->offset. */ + for (j = 0; fixup_info[j].size != 0; j++) + { + if (fixup_info[j].ramp & NDS32_RELAX) + fixup_info[j].size -= 2; + + if (fixup_info[j].offset > buf_offset) + fixup_info[j].offset -= 2; + } + + md_number_to_chars (fr_buffer + buf_offset, insn_16, 2); + buf_offset += 2; + if (offset == 0) + insn_convert = TRUE; + } + else + { + md_number_to_chars (fr_buffer + buf_offset, insn, insn_size); + buf_offset += insn_size; + } + + offset += insn_size; + i++; + } + + /* Set up fixup. */ + exp.X_op = O_symbol; + + for (i = 0; fixup_info[i].size != 0; i++) + { + fixup_size = fixup_info[i].size; + + if (((fixup_info[i].ramp & NDS32_ORIGIN) && insn_convert == TRUE) + ||((fixup_info[i].ramp & NDS32_CONVERT) && insn_convert == FALSE)) + continue; + + if ((fixup_info[i].ramp & NDS32_CREATE_LABLE) != 0) + { + /* This is a reverse branch. */ + exp.X_add_symbol = symbol_temp_new (sec, 0, fragP->fr_next); + exp.X_add_number = 0; + } + else if ((fixup_info[i].ramp & NDS32_RELAX) != 0) + { + /* This is a relax relocation. */ + exp.X_add_symbol = abs_section_sym; + exp.X_add_number = + SET_ADDEND (fixup_size /* size */ , + insn_convert , optimize, enable_16bit); + } + else + { + exp.X_add_symbol = branch_symbol; + exp.X_add_number = branch_offset; + } + + if (fixup_info[i].r_type != 0) + { + fixP = fix_new_exp (fragP, + fr_where + fixup_info[i].offset, + fixup_size, + &exp, + 0, + fixup_info[i].r_type); + fixP->fx_addnumber = fixP->fx_offset; + } + } + + fragP->fr_fix = fr_where + buf_offset; + + target_big_endian = backup_endian; +} + +/* tc_frob_file_before_fix */ + +void +nds32_frob_file_before_fix (void) +{ +} + +/* TC_FORCE_RELOCATION */ + +int +nds32_force_relocation (fixS *fix ATTRIBUTE_UNUSED) +{ + /* Always force relocation, because linker may adjust the code. */ + return 1; +} + + +/* TC_VALIDATE_FIX_SUB */ + +int +nds32_validate_fix_sub (fixS *fix, segT add_symbol_segment) +{ + segT sub_symbol_segment; + + /* This code is referred from Xtensa. Check their implementation for + details. */ + + /* Make sure both symbols are in the same segment, and that segment is + "normal" and relaxable. */ + sub_symbol_segment = S_GET_SEGMENT (fix->fx_subsy); + return (sub_symbol_segment == add_symbol_segment + && add_symbol_segment != undefined_section); +} + +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); +} + +/* Equal to MAX_PRECISION in atof-ieee.c. */ +#define MAX_LITTLENUMS 6 + +/* This function is called to convert an ASCII string into a floating point + value in format used by the CPU. */ + +char * +md_atof (int type, char *litP, int *sizeP) +{ + int i; + int prec; + LITTLENUM_TYPE words[MAX_LITTLENUMS]; + char *t; + + switch (type) + { + case 'f': + case 'F': + case 's': + case 'S': + prec = 2; + break; + case 'd': + case 'D': + case 'r': + case 'R': + prec = 4; + break; + default: + *sizeP = 0; + return _("Bad call to md_atof()"); + } + + t = atof_ieee (input_line_pointer, type, words); + if (t) + input_line_pointer = t; + *sizeP = prec * sizeof (LITTLENUM_TYPE); + + if (target_big_endian) + { + for (i = 0; i < prec; i++) + { + md_number_to_chars (litP, (valueT) words[i], + sizeof (LITTLENUM_TYPE)); + litP += sizeof (LITTLENUM_TYPE); + } + } + else + { + for (i = prec - 1; i >= 0; i--) + { + md_number_to_chars (litP, (valueT) words[i], + sizeof (LITTLENUM_TYPE)); + litP += sizeof (LITTLENUM_TYPE); + } + } + + return 0; +} + +/* md_elf_section_change_hook */ + +void +nds32_elf_section_change_hook (void) +{ +} + +/* md_cleanup */ + +void +nds32_cleanup (void) +{ +} + +/* This function is used to scan leb128 subtraction expressions, + and insert fixups for them. + + e.g., .leb128 .L1 - .L0 + + These expressions are heavily used in debug information or + exception tables. Because relaxation will change code size, + we must resolve them in link time. */ + +static void +nds32_insert_leb128_fixes (bfd *abfd ATTRIBUTE_UNUSED, + asection *sec, void *xxx ATTRIBUTE_UNUSED) +{ + segment_info_type *seginfo = seg_info (sec); + struct frag *fragP; + + subseg_set (sec, 0); + + for (fragP = seginfo->frchainP->frch_root; + fragP; fragP = fragP->fr_next) + { + expressionS *exp; + + /* Only unsigned leb128 can be handle. */ + if (fragP->fr_type != rs_leb128 || fragP->fr_subtype != 0 + || fragP->fr_symbol == NULL) + continue; + + exp = symbol_get_value_expression (fragP->fr_symbol); + + if (exp->X_op != O_subtract) + continue; + + fix_new_exp (fragP, fragP->fr_fix, 0, + exp, 0, BFD_RELOC_NDS32_DIFF_ULEB128); + } +} + +static void +nds32_insert_relax_entry (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, + void *xxx ATTRIBUTE_UNUSED) +{ + segment_info_type *seginfo; + fragS *fragP; + fixS *fixP; + expressionS exp; + fixS *fixp; + + seginfo = seg_info (sec); + if (!seginfo || !symbol_rootP || !subseg_text_p (sec)) + return; + /* If there is no relocation and relax is disabled, it is not necessary to + insert R_NDS32_RELAX_ENTRY for linker do EX9 or IFC optimization. */ + for (fixp = seginfo->fix_root; fixp; fixp = fixp->fx_next) + if (!fixp->fx_done) + break; + if (!fixp && !enable_relax_ex9 && !verbatim) + return; + + subseg_change (sec, 0); + + /* Set RELAX_ENTRY flags for linker. */ + fragP = seginfo->frchainP->frch_root; + exp.X_op = O_symbol; + exp.X_add_symbol = section_symbol (sec); + exp.X_add_number = 0; + if (!enable_relax_relocs) + exp.X_add_number |= R_NDS32_RELAX_ENTRY_DISABLE_RELAX_FLAG; + else + { + /* These flags are only enabled when global relax is enabled. + Maybe we can check DISABLE_RELAX_FLAG at linke-time, + so we set them anyway. */ + if (enable_relax_ex9) + exp.X_add_number |= R_NDS32_RELAX_ENTRY_EX9_FLAG; + if (enable_relax_ifc) + exp.X_add_number |= R_NDS32_RELAX_ENTRY_IFC_FLAG; + if (verbatim) + exp.X_add_number |= R_NDS32_RELAX_ENTRY_VERBATIM_FLAG; + } + if (optimize) + exp.X_add_number |= R_NDS32_RELAX_ENTRY_OPTIMIZE_FLAG; + if (optimize_for_space) + exp.X_add_number |= R_NDS32_RELAX_ENTRY_OPTIMIZE_FOR_SPACE_FLAG; + + fixP = fix_new_exp (fragP, 0, 0, &exp, 0, BFD_RELOC_NDS32_RELAX_ENTRY); + fixP->fx_no_overflow = 1; +} + +/* Analysis relax hint and insert suitable relocation pattern. */ + +static void +nds32_elf_analysis_relax_hint (void) +{ + hash_traverse (nds32_hint_hash, nds32_elf_append_relax_relocs); +} + +void +md_end (void) +{ + nds32_elf_analysis_relax_hint (); + bfd_map_over_sections (stdoutput, nds32_insert_leb128_fixes, NULL); +} + +/* Implement md_allow_local_subtract. */ + +bfd_boolean +nds32_allow_local_subtract (expressionS *expr_l ATTRIBUTE_UNUSED, + expressionS *expr_r ATTRIBUTE_UNUSED, + segT sec ATTRIBUTE_UNUSED) +{ + /* Don't allow any subtraction, because relax may change the code. */ + return FALSE; +} + +/* Sort relocation by address. + + We didn't use qsort () in stdlib, because quick-sort is not a stable + sorting algorithm. Relocations at the same address (r_offset) must keep + their relative order. For example, RELAX_ENTRY must be the very first + relocation entry. + + Currently, this function implements insertion-sort. */ + +static int +compar_relent (const void *lhs, const void *rhs) +{ + const arelent **l = (const arelent **) lhs; + const arelent **r = (const arelent **) rhs; + + if ((*l)->address > (*r)->address) + return 1; + else if ((*l)->address == (*r)->address) + return 0; + else + return -1; +} + +/* SET_SECTION_RELOCS () + + Although this macro is originally used to set a relocation for each section, + we use it to sort relocations in the same section by the address of the + relocation. */ + +void +nds32_set_section_relocs (asection *sec, arelent ** relocs ATTRIBUTE_UNUSED, + unsigned int n ATTRIBUTE_UNUSED) +{ + bfd *abfd ATTRIBUTE_UNUSED = sec->owner; + if (bfd_get_section_flags (abfd, sec) & (flagword) SEC_RELOC) + nds32_insertion_sort (sec->orelocation, sec->reloc_count, sizeof (arelent**), + compar_relent); +} + +long +nds32_pcrel_from_section (fixS *fixP, segT sec ATTRIBUTE_UNUSED) +{ + if (fixP->fx_addsy == NULL || !S_IS_DEFINED (fixP->fx_addsy) + || S_IS_EXTERNAL (fixP->fx_addsy) || S_IS_WEAK (fixP->fx_addsy)) + { + /* Let linker resolve undefined symbols. */ + return 0; + } + + return fixP->fx_frag->fr_address + fixP->fx_where; +} + +/* md_post_relax_hook () + Insert relax entry relocation into sections. */ + +void +nds32_post_relax_hook (void) +{ + bfd_map_over_sections (stdoutput, nds32_insert_relax_entry, NULL); +} + + + +/* tc_fix_adjustable () + + Return whether this symbol (fixup) can be replaced with + section symbols. */ + +bfd_boolean +nds32_fix_adjustable (fixS *fixP) +{ + switch (fixP->fx_r_type) + { + case BFD_RELOC_NDS32_WORD_9_PCREL: + case BFD_RELOC_NDS32_9_PCREL: + case BFD_RELOC_NDS32_15_PCREL: + case BFD_RELOC_NDS32_17_PCREL: + case BFD_RELOC_NDS32_25_PCREL: + case BFD_RELOC_NDS32_HI20: + case BFD_RELOC_NDS32_LO12S0: + case BFD_RELOC_8: + case BFD_RELOC_16: + case BFD_RELOC_32: + case BFD_RELOC_NDS32_PTR: + return 1; + default: + return 0; + } +} + +/* elf_tc_final_processing */ + +void +elf_nds32_final_processing (void) +{ + /* An FPU_COM instruction is found without previous non-FPU_COM instruction. */ + if (nds32_fpu_com + && !(nds32_elf_flags & (E_NDS32_HAS_FPU_INST | E_NDS32_HAS_FPU_DP_INST))) + { + /* Since only FPU_COM instructions are used and no other FPU instructions + are used. The nds32_elf_flags will be decided by the enabled options by + command line or default configuration. */ + if (nds32_fpu_dp_ext || nds32_fpu_sp_ext) + { + nds32_elf_flags |= nds32_fpu_dp_ext ? E_NDS32_HAS_FPU_DP_INST : 0; + nds32_elf_flags |= nds32_fpu_sp_ext ? E_NDS32_HAS_FPU_INST : 0; + } + else + { + /* Should never here. */ + as_bad (_("Used FPU instructions requires enabling FPU extension")); + } + } + + if (nds32_elf_flags & (E_NDS32_HAS_FPU_INST | E_NDS32_HAS_FPU_DP_INST)) + { + /* Single/double FPU has been used, set FPU register config. */ + /* We did not check the actual number of register used. We may + want to do it while assemble. */ + nds32_elf_flags &= ~E_NDS32_FPU_REG_CONF; + nds32_elf_flags |= (nds32_freg << E_NDS32_FPU_REG_CONF_SHIFT); + } + + if (nds32_pic) + nds32_elf_flags |= E_NDS32_HAS_PIC; + + if (nds32_gpr16) + nds32_elf_flags |= E_NDS32_HAS_REDUCED_REGS; + + nds32_elf_flags |= (E_NDS32_ELF_VER_1_4 | nds32_abi); + elf_elfheader (stdoutput)->e_flags |= nds32_elf_flags; +} + +/* Implement md_apply_fix. Apply the fix-up or tranform the fix-up for + later relocation generation. */ + +void +nds32_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) +{ + char *where = fixP->fx_frag->fr_literal + fixP->fx_where; + bfd_vma value = *valP; + + if (fixP->fx_r_type < BFD_RELOC_UNUSED + && fixP->fx_r_type > BFD_RELOC_NONE + && fixP->fx_r_type != BFD_RELOC_NDS32_DIFF_ULEB128) + { + /* FIXME: This implementation is partially borrowed from our old + nds32 binutils. Its purpose is to leave original bfd + relocation untouched, while other relocation created by CGEN + will be converted into general bfd relocations. + However, since we no longer use CGEN, we can simply use + a little piece of code to deal with general bfd relocation, + especially for the BFD_RELOC_NDS32_DATA, which is just used + as a marker for different purpose. + It is believed that we can construct a better mechanism to + deal with the whole relocation issue in nds32 target + without using CGEN. */ + fixP->fx_addnumber = value; + fixP->tc_fix_data = NULL; + if (fixP->fx_r_type == BFD_RELOC_NDS32_DATA) + fixP->fx_done = 1; + return; + } + + if (fixP->fx_addsy == (symbolS *) NULL) + fixP->fx_done = 1; + + if (fixP->fx_subsy != (symbolS *) NULL) + { + /* HOW DIFF RELOCATION WORKS. + + First of all, this relocation is used to calculate the distance + between two symbols in the SAME section. It is used for jump- + table, debug information, exception table, et al. Therefore, + it is a unsigned positive value. It is NOT used for general- + purpose arithmetic. + + Consider this example, the distance between .LEND and .LBEGIN + is stored at the address of foo. + + ---- >8 ---- >8 ---- >8 ---- >8 ---- + .data + foo: + .word .LBEGIN - .LEND + + .text + [before] + .LBEGIN + \ + [between] distance + / + .LEND + [after] + ---- 8< ---- 8< ---- 8< ---- 8< ---- + + We use a single relocation entry for this expression. + * The initial distance value is stored direcly in that location + specified by r_offset (i.e., foo in this example.) + * The begin of the region, i.e., .LBEGIN, is specified by + r_info/R_SYM and r_addend, e.g., .text + 0x32. + * The end of region, i.e., .LEND, is represented by + .LBEGIN + distance instead of .LEND, so we only need + a single relocation entry instead of two. + + When an instruction is relaxed, we adjust the relocation entry + depending on where the instruction locates. There are three + cases, before, after and between the region. + * between: Distance value is read from r_offset, adjusted and + written back into r_offset. + * before: Only r_addend is adjust. + * after: We don't care about it. + + Hereby, there are some limitation. + + `(.LEND - 1) - .LBEGIN' and `(.LEND - .LBEGIN) - 1' + are semantically different, and we cannot handle latter case + when relaxation. + + The latter expression means subtracting 1 from the distance + between .LEND and .LBEGIN. And the former expression means + the distance between (.LEND - 1) and .LBEGIN. + + The nuance affects whether to adjust distance value when relax + an instruction. In another words, whether the instruction + locates in the region. Because we use a single relocation entry, + there is no field left for .LEND and the subtrahend. + + Since GCC-4.5, GCC may produce debug information in such expression + .long .L1-1-.L0 + in order to describe register clobbering during an function-call. + .L0: + call foo + .L1: + + Check http://gcc.gnu.org/ml/gcc-patches/2009-06/msg01317.html + for details. */ + + value -= S_GET_VALUE (fixP->fx_subsy); + *valP = value; + fixP->fx_subsy = NULL; + fixP->fx_offset -= value; + + switch (fixP->fx_r_type) + { + case BFD_RELOC_8: + fixP->fx_r_type = BFD_RELOC_NDS32_DIFF8; + md_number_to_chars (where, value, 1); + break; + case BFD_RELOC_16: + fixP->fx_r_type = BFD_RELOC_NDS32_DIFF16; + md_number_to_chars (where, value, 2); + break; + case BFD_RELOC_32: + fixP->fx_r_type = BFD_RELOC_NDS32_DIFF32; + md_number_to_chars (where, value, 4); + break; + case BFD_RELOC_NDS32_DIFF_ULEB128: + /* cvt_frag_to_fill () has called output_leb128 () for us. */ + break; + default: + as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex")); + return; + } + } + else if (fixP->fx_done) + { + /* We're finished with this fixup. Install it because + bfd_install_relocation won't be called to do it. */ + switch (fixP->fx_r_type) + { + case BFD_RELOC_8: + md_number_to_chars (where, value, 1); + break; + case BFD_RELOC_16: + md_number_to_chars (where, value, 2); + break; + case BFD_RELOC_32: + md_number_to_chars (where, value, 4); + break; + case BFD_RELOC_64: + md_number_to_chars (where, value, 8); + default: + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Internal error: Unknown fixup type %d (`%s')"), + fixP->fx_r_type, bfd_get_reloc_code_name (fixP->fx_r_type)); + break; + } + } +} + +/* Implement tc_gen_reloc. Generate ELF relocation for a fix-up. */ + +arelent * +tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP) +{ + arelent *reloc; + bfd_reloc_code_real_type code; + + reloc = (arelent *) xmalloc (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; + + code = fixP->fx_r_type; + + reloc->howto = bfd_reloc_type_lookup (stdoutput, code); + if (reloc->howto == (reloc_howto_type *) NULL) + { + as_bad_where (fixP->fx_file, fixP->fx_line, + _("internal error: can't export reloc type %d (`%s')"), + fixP->fx_r_type, bfd_get_reloc_code_name (code)); + return NULL; + } + + /* Add relocation handling here. */ + + switch (fixP->fx_r_type) + { + default: + /* In general, addend of a relocation is the offset to the + associated symbol. */ + reloc->addend = fixP->fx_offset; + break; + + case BFD_RELOC_NDS32_DATA: + /* Prevent linker from optimizing data in text sections. + For example, jump table. */ + reloc->addend = fixP->fx_size; + break; + } + + return reloc; +} + +/* Implement md_parse_name. */ + +int +nds32_parse_name (char const *name, expressionS *exprP, + enum expr_mode mode ATTRIBUTE_UNUSED, + char *nextcharP ATTRIBUTE_UNUSED) +{ + char *suffix_table[] = { "GOTOFF", "GOT", "PLT" }; + short unsigned int reloc_table [] = + { + BFD_RELOC_NDS32_GOTOFF, BFD_RELOC_NDS32_GOT20, + BFD_RELOC_NDS32_25_PLTREL + }; + segT segment; + + exprP->X_op_symbol = NULL; + exprP->X_md = BFD_RELOC_UNUSED; + + exprP->X_add_symbol = symbol_find_or_make (name); + + segment = S_GET_SEGMENT (exprP->X_add_symbol); + if (segment != undefined_section) + return 0; + + if (*nextcharP == '@') + { + size_t i; + char *next; + for (i = 0; i < ARRAY_SIZE (suffix_table); i++) + { + next = input_line_pointer + 1 + strlen (suffix_table[i]); + if (strncasecmp (input_line_pointer + 1, suffix_table[i], + strlen (suffix_table[i])) == 0 + && !is_part_of_name (*next)) + { + exprP->X_md = reloc_table[i]; + *input_line_pointer = *nextcharP; + input_line_pointer = next; + *nextcharP = *input_line_pointer; + *input_line_pointer = '\0'; + break; + } + } + } + + exprP->X_op = O_symbol; + exprP->X_add_number = 0; + + return 1; +} + +/* Implement tc_regname_to_dw2regnum. */ + +int +tc_nds32_regname_to_dw2regnum (char *regname) +{ + symbolS *sym = symbol_find (regname); + + if (S_GET_SEGMENT (sym) == reg_section + && sym->sy_value.X_add_number < 32) + return sym->sy_value.X_add_number; + return -1; +} + +void +tc_nds32_frame_initial_instructions (void) +{ + /* CIE */ + /* Default cfa is register-28/sp. */ + cfi_add_CFA_def_cfa (31, 0); +} diff --git a/gas/config/tc-nds32.h b/gas/config/tc-nds32.h new file mode 100644 index 0000000..255fbae --- /dev/null +++ b/gas/config/tc-nds32.h @@ -0,0 +1,264 @@ +/* tc-nds32.h -- Header file for tc-nds32.c. + Copyright (C) 2012-2013 Free Software Foundation, Inc. + Contributed by Andes Technology Corporation. + + 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_NDS32 +#define TC_NDS32 + +#include "bfd_stdint.h" + +#define LISTING_HEADER \ + (target_big_endian ? "NDS32 GAS" : "NDS32 GAS Little Endian") + +/* The target BFD architecture. */ +#define TARGET_ARCH bfd_arch_nds32 + +/* mapping to mach_table[5] */ +#define ISA_V1 bfd_mach_n1h +#define ISA_V2 bfd_mach_n1h_v2 +#define ISA_V3 bfd_mach_n1h_v3 +#define ISA_V3M bfd_mach_n1h_v3m + +/* Default to big endian. Please note that for Andes architecture, + instructions are always in big-endian format. */ +#ifndef TARGET_BYTES_BIG_ENDIAN +#define TARGET_BYTES_BIG_ENDIAN 1 +#endif + +/* This is used to construct expressions out of @GOTOFF, @PLT and @GOT + symbols. The relocation type is stored in X_md. */ +#define O_PIC_reloc O_md1 + +/* as.c. */ +/* Extend GAS command line option handling capability. */ +extern int nds32_parse_option (int, char *); +extern void nds32_after_parse_args (void); +/* The endianness of the target format may change based on command + line arguments. */ +extern const char * nds32_target_format (void); + +#define md_parse_option(optc, optarg) nds32_parse_option (optc, optarg) +#define md_after_parse_args() nds32_after_parse_args () +#define TARGET_FORMAT nds32_target_format() + +/* expr.c */ +extern int nds32_parse_name (char const *, expressionS *, enum expr_mode, char *); +extern bfd_boolean nds32_allow_local_subtract (expressionS *, expressionS *, segT); +#define md_parse_name(name, exprP, mode, nextcharP) \ + nds32_parse_name (name, exprP, mode, nextcharP) +#define md_allow_local_subtract(lhs,rhs,sect) nds32_allow_local_subtract (lhs, rhs, sect) + +/* dwarf2dbg.c. */ +#define DWARF2_USE_FIXED_ADVANCE_PC 1 + +/* write.c. */ +extern long nds32_pcrel_from_section (struct fix *, segT); +extern bfd_boolean nds32_fix_adjustable (struct fix *); +extern void nds32_frob_file (void); +extern void nds32_post_relax_hook (void); +extern void nds32_frob_file_before_fix (void); +extern void elf_nds32_final_processing (void); +extern int nds32_validate_fix_sub (struct fix *, segT); +extern int nds32_force_relocation (struct fix *); +extern void nds32_set_section_relocs (asection *, arelent ** , unsigned int); + +/* Fill in rs_align_code fragments. TODO: Review this. */ +extern void nds32_handle_align (fragS *); +extern int nds32_relax_frag (segT, fragS *, long); +extern int tc_nds32_regname_to_dw2regnum (char *); +extern void tc_nds32_frame_initial_instructions (void); + +#define MD_PCREL_FROM_SECTION(fix, sect) nds32_pcrel_from_section (fix, sect) +#define TC_FINALIZE_SYMS_BEFORE_SIZE_SEG 0 +#define tc_fix_adjustable(FIX) nds32_fix_adjustable (FIX) +#define md_apply_fix(fixP, addn, seg) nds32_apply_fix (fixP, addn, seg) +#define md_post_relax_hook nds32_post_relax_hook () +#define tc_frob_file_before_fix() nds32_frob_file_before_fix () +#define elf_tc_final_processing() elf_nds32_final_processing () +/* For DIFF relocations. The default behavior is inconsistent with the + asm internal document. */ +#define TC_FORCE_RELOCATION_SUB_SAME(FIX, SEC) \ + (! SEG_NORMAL (SEC) || TC_FORCE_RELOCATION (FIX)) +#define TC_FORCE_RELOCATION(fix) nds32_force_relocation (fix) +#define TC_VALIDATE_FIX_SUB(FIX,SEG) nds32_validate_fix_sub (FIX,SEG) +#define SET_SECTION_RELOCS(sec, relocs, n) nds32_set_section_relocs (sec, relocs, n) +/* Values passed to md_apply_fix don't include the symbol value. */ +#define MD_APPLY_SYM_VALUE(FIX) 0 +#define HANDLE_ALIGN(f) nds32_handle_align (f) +#undef DIFF_EXPR_OK /* They should be fixed in linker. */ +#define md_relax_frag(segment, fragP, stretch) nds32_relax_frag (segment, fragP, stretch) +#define WORKING_DOT_WORD /* We don't need to handle .word strangely. */ +/* Using to chain fixup with previous fixup. */ +#define TC_FIX_TYPE struct fix * +#define TC_INIT_FIX_DATA(fixP) \ + do \ + { \ + fixP->tc_fix_data = NULL; \ + } \ + while (0) + +/* read.c. */ +/* Extend GAS macro handling capability. */ +extern void nds32_macro_start (void); +extern void nds32_macro_end (void); +extern void nds32_macro_info (void *); +extern void nds32_start_line_hook (void); +extern void nds32_elf_section_change_hook (void); +extern void md_begin (void); +extern void md_end (void); +extern int nds32_start_label (int, int); +extern void nds32_cleanup (void); +extern void nds32_flush_pending_output (void); +extern void nds32_cons_align (int); +extern void nds32_check_label (symbolS *); +extern void nds32_frob_label (symbolS *); +extern void nds32_pre_do_align (int, char *, int, int); +extern void nds32_do_align (int); + +#define md_macro_start() nds32_macro_start () +#define md_macro_end() nds32_macro_end () +#define md_macro_info(args) nds32_macro_info (args) +#define TC_START_LABEL(C, S, STR) (C == ':' && nds32_start_label (0, 0)) +#define tc_check_label(label) nds32_check_label (label) +#define tc_frob_label(label) nds32_frob_label (label) +#define md_end md_end +#define md_start_line_hook() nds32_start_line_hook () +#define md_cons_align(n) nds32_cons_align (n) +/* COLE: TODO: Review md_do_align. */ +#define md_do_align(N, FILL, LEN, MAX, LABEL) \ + nds32_pre_do_align (N, FILL, LEN, MAX); \ + if ((N) > 1 && (subseg_text_p (now_seg) \ + || strncmp (now_seg->name, ".gcc_except_table", sizeof(".gcc_except_table") - 1) == 0)) \ + nds32_do_align (N); \ + goto LABEL; +#define md_elf_section_change_hook() nds32_elf_section_change_hook () +#define md_flush_pending_output() nds32_flush_pending_output () +#define md_cleanup() nds32_cleanup () +#define LOCAL_LABELS_FB 1 /* Permit temporary numeric labels. */ + +/* frags.c. */ +struct nds32_frag_type +{ + relax_substateT flag; + struct nds32_opcode *opcode; + uint32_t insn; + /* To Save previos label fixup if existence. */ + struct fix *fixup; +}; + +extern void nds32_frag_init (fragS *); + +#define TC_FRAG_TYPE struct nds32_frag_type +#define TC_FRAG_INIT(fragP) nds32_frag_init (fragP) + +/* CFI directive. */ +extern void nds32_elf_frame_initial_instructions (void); +extern int tc_nds32_regname_to_dw2regnum (char *); + +#define TARGET_USE_CFIPOP 1 +#define DWARF2_DEFAULT_RETURN_COLUMN 30 +#define DWARF2_CIE_DATA_ALIGNMENT -4 +#define DWARF2_LINE_MIN_INSN_LENGTH 2 + +#define tc_regname_to_dw2regnum tc_nds32_regname_to_dw2regnum +#define tc_cfi_frame_initial_instructions tc_nds32_frame_initial_instructions + +/* COLE: TODO: Review These. They seem to be obsoleted. */ +#if 1 +#define TC_RELOC_RTSYM_LOC_FIXUP(FIX) \ + ((FIX)->fx_addsy == NULL \ + || (! S_IS_EXTERNAL ((FIX)->fx_addsy) \ + && ! S_IS_WEAK ((FIX)->fx_addsy) \ + && S_IS_DEFINED ((FIX)->fx_addsy) \ + && ! S_IS_COMMON ((FIX)->fx_addsy))) +#define TC_HANDLES_FX_DONE +/* This arranges for gas/write.c to not apply a relocation if + obj_fix_adjustable() says it is not adjustable. */ +#define TC_FIX_ADJUSTABLE(fixP) obj_fix_adjustable (fixP) +#endif + +/* Because linker may relax the code, assemble-time expression + optimization is not allowed. */ +#define md_allow_eh_opt 0 + +/* For nds32 relax. */ +enum nds32_br_range +{ + BR_RANGE_S256 = 0, + BR_RANGE_S16K, + BR_RANGE_S64K, + BR_RANGE_S16M, + BR_RANGE_U4G, + BR_RANGE_NUM +}; + +enum nds32_ramp +{ + NDS32_CREATE_LABLE = 1, + NDS32_RELAX = 2, + NDS32_ORIGIN = 4, + NDS32_CONVERT = 8 +}; + +typedef struct nds32_relax_fixup_info +{ + int offset; + int size; + /* Reverse branch has to jump to the end of instruction pattern. */ + int ramp; + enum bfd_reloc_code_real r_type; +} nds32_relax_fixup_info_t; + +typedef struct nds32_cond_field +{ + int offset; + int bitpos; /* Register position. */ + int bitmask; /* Number of register bits. */ +} nds32_cond_field_t; + +/* The max relaxation pattern is 20-bytes including the nop. */ +#define NDS32_MAXCHAR 20 +/* In current, the max entend number of instruction for one pseudo instruction + is 4, but its number of relocation may be 5. */ +#define MAX_RELAX_NUM 8 + +typedef struct nds32_relax_info +{ + /* Opcode for the instruction. */ + const char *opcode; + enum nds32_br_range br_range; + nds32_cond_field_t cond_field[MAX_RELAX_NUM]; /* TODO: Reuse nds32_field? */ + /* Code sequences for different branch range. */ + uint32_t relax_code_seq[BR_RANGE_NUM][MAX_RELAX_NUM]; + nds32_cond_field_t relax_code_condition[BR_RANGE_NUM][MAX_RELAX_NUM]; + int relax_code_size[BR_RANGE_NUM]; + int relax_branch_isize[BR_RANGE_NUM]; + nds32_relax_fixup_info_t relax_fixup[BR_RANGE_NUM][MAX_RELAX_NUM]; +} relax_info_t; + +/* Relocation table. */ +struct nds32_relocation_map +{ + unsigned int main_type; + /* Number of instructions, {relocations type, instruction type}. */ + unsigned int reloc_insn[6][6][3]; +}; + +#endif /* TC_NDS32 */ diff --git a/gas/configure b/gas/configure index 590528c..8a89e4f 100755 --- a/gas/configure +++ b/gas/configure @@ -12192,6 +12192,100 @@ _ACEOF using_cgen=yes ;; + nds32) + # Decide BASELINE, REDUCED_REGS, FPU_DP_EXT, FPU_SP_EXT features + # based on arch_name. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for default configuration of --with-arch" >&5 +$as_echo_n "checking for default configuration of --with-arch... " >&6; } + if test "x${with_arch}" != x; then + case ${with_arch} in + v2j | v2s | v2f | v2 | v3m | v3j | v3s | v3f | v3 ) + +cat >>confdefs.h <<_ACEOF +#define NDS32_DEFAULT_ARCH_NAME "$with_arch" +_ACEOF + + ;; + *) + as_fn_error "This kind of arch name does *NOT* exist!" "$LINENO" 5 + ;; + esac + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_arch" >&5 +$as_echo "$with_arch" >&6; } + + # Decide features one by one. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for default configuration of --enable-dx-regs" >&5 +$as_echo_n "checking for default configuration of --enable-dx-regs... " >&6; } + if test "x${enable_dx_regs}" == xyes; then + +$as_echo "#define NDS32_DEFAULT_DX_REGS 1" >>confdefs.h + + else + +$as_echo "#define NDS32_DEFAULT_DX_REGS 0" >>confdefs.h + + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_dx_regs" >&5 +$as_echo "$enable_dx_regs" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for default configuration of --enable-perf-ext" >&5 +$as_echo_n "checking for default configuration of --enable-perf-ext... " >&6; } + if test "x${enable_perf_ext}" == xno; then + +$as_echo "#define NDS32_DEFAULT_PERF_EXT 0" >>confdefs.h + + else + +$as_echo "#define NDS32_DEFAULT_PERF_EXT 1" >>confdefs.h + + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_perf_ext" >&5 +$as_echo "$enable_perf_ext" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for default configuration of --enable-perf-ext2" >&5 +$as_echo_n "checking for default configuration of --enable-perf-ext2... " >&6; } + if test "x${enable_perf_ext2}" == xno; then + +$as_echo "#define NDS32_DEFAULT_PERF_EXT2 0" >>confdefs.h + + else + +$as_echo "#define NDS32_DEFAULT_PERF_EXT2 1" >>confdefs.h + + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_perf_ext2" >&5 +$as_echo "$enable_perf_ext2" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for default configuration of --enable-string-ext" >&5 +$as_echo_n "checking for default configuration of --enable-string-ext... " >&6; } + if test "x${enable_string_ext}" == xno; then + +$as_echo "#define NDS32_DEFAULT_STRING_EXT 0" >>confdefs.h + + else + +$as_echo "#define NDS32_DEFAULT_STRING_EXT 1" >>confdefs.h + + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_string_ext" >&5 +$as_echo "$enable_string_ext" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for default configuration of --enable-audio-ext" >&5 +$as_echo_n "checking for default configuration of --enable-audio-ext... " >&6; } + if test "x${enable_audio_ext}" == xno; then + +$as_echo "#define NDS32_DEFAULT_AUDIO_EXT 0" >>confdefs.h + + else + +$as_echo "#define NDS32_DEFAULT_AUDIO_EXT 1" >>confdefs.h + + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_audio_ext" >&5 +$as_echo "$enable_audio_ext" >&6; } + ;; + i386 | s390 | sparc) if test $this_target = $target ; then diff --git a/gas/configure.in b/gas/configure.in index 121fcfc..9e1ba59 100644 --- a/gas/configure.in +++ b/gas/configure.in @@ -363,6 +363,75 @@ changequote([,])dnl using_cgen=yes ;; + nds32) + # Decide BASELINE, REDUCED_REGS, FPU_DP_EXT, FPU_SP_EXT features + # based on arch_name. + AC_MSG_CHECKING(for default configuration of --with-arch) + if test "x${with_arch}" != x; then + case ${with_arch} in + v2j | v2s | v2f | v2 | v3m | v3j | v3s | v3f | v3 ) + AC_DEFINE_UNQUOTED(NDS32_DEFAULT_ARCH_NAME, "$with_arch", + [Define value for nds32_arch_name]) + ;; + *) + AC_MSG_ERROR(This kind of arch name does *NOT* exist!) + ;; + esac + fi + AC_MSG_RESULT($with_arch) + + # Decide features one by one. + AC_MSG_CHECKING(for default configuration of --enable-dx-regs) + if test "x${enable_dx_regs}" == xyes; then + AC_DEFINE(NDS32_DEFAULT_DX_REGS, 1, + [Define value for nds32_dx_regs]) + else + AC_DEFINE(NDS32_DEFAULT_DX_REGS, 0, + [Define default value for nds32_dx_regs]) + fi + AC_MSG_RESULT($enable_dx_regs) + + AC_MSG_CHECKING(for default configuration of --enable-perf-ext) + if test "x${enable_perf_ext}" == xno; then + AC_DEFINE(NDS32_DEFAULT_PERF_EXT, 0, + [Define value for nds32_perf_ext]) + else + AC_DEFINE(NDS32_DEFAULT_PERF_EXT, 1, + [Define default value for nds32_perf_ext]) + fi + AC_MSG_RESULT($enable_perf_ext) + + AC_MSG_CHECKING(for default configuration of --enable-perf-ext2) + if test "x${enable_perf_ext2}" == xno; then + AC_DEFINE(NDS32_DEFAULT_PERF_EXT2, 0, + [Define value for nds32_perf_ext2]) + else + AC_DEFINE(NDS32_DEFAULT_PERF_EXT2, 1, + [Define default value for nds32_perf_ext2]) + fi + AC_MSG_RESULT($enable_perf_ext2) + + AC_MSG_CHECKING(for default configuration of --enable-string-ext) + if test "x${enable_string_ext}" == xno; then + AC_DEFINE(NDS32_DEFAULT_STRING_EXT, 0, + [Define value for nds32_string_ext]) + else + AC_DEFINE(NDS32_DEFAULT_STRING_EXT, 1, + [Define default value for nds32_string_ext]) + fi + AC_MSG_RESULT($enable_string_ext) + + AC_MSG_CHECKING(for default configuration of --enable-audio-ext) + if test "x${enable_audio_ext}" == xno; then + AC_DEFINE(NDS32_DEFAULT_AUDIO_EXT, 0, + [Define value for nds32_audio_ext]) + else + AC_DEFINE(NDS32_DEFAULT_AUDIO_EXT, 1, + [Define default value for nds32_audio_ext]) + fi + AC_MSG_RESULT($enable_audio_ext) + ;; + i386 | s390 | sparc) if test $this_target = $target ; then AC_DEFINE_UNQUOTED(DEFAULT_ARCH, "${arch}", [Default architecture.]) diff --git a/gas/configure.tgt b/gas/configure.tgt index 77c1d9b..fdc0612 100644 --- a/gas/configure.tgt +++ b/gas/configure.tgt @@ -79,6 +79,8 @@ case ${cpu} in mips*el) cpu_type=mips endian=little ;; mips*) cpu_type=mips endian=big ;; mt) cpu_type=mt endian=big ;; + nds32be) cpu_type=nds32 endian=big ;; + nds32le) cpu_type=nds32 endian=little ;; or32*) cpu_type=or32 endian=big ;; pjl*) cpu_type=pj endian=little ;; pj*) cpu_type=pj endian=big ;; @@ -344,6 +346,9 @@ case ${generic_target} in msp430-*-*) fmt=elf ;; + nds32-*-elf*) fmt=elf ;; + nds32-*-linux*) fmt=elf em=linux ;; + nios2-*-rtems*) fmt=elf ;; nios2*-linux*) fmt=elf em=linux ;; diff --git a/gas/doc/Makefile.am b/gas/doc/Makefile.am index 3d1e933..e5b3c5f 100644 --- a/gas/doc/Makefile.am +++ b/gas/doc/Makefile.am @@ -75,6 +75,7 @@ CPU_DOCS = \ c-mt.texi \ c-msp430.texi \ c-nios2.texi \ + c-nds32.texi \ c-ns32k.texi \ c-pdp11.texi \ c-pj.texi \ diff --git a/gas/doc/Makefile.in b/gas/doc/Makefile.in index 4c3c4fb..5a65e17 100644 --- a/gas/doc/Makefile.in +++ b/gas/doc/Makefile.in @@ -317,6 +317,7 @@ CPU_DOCS = \ c-mt.texi \ c-msp430.texi \ c-nios2.texi \ + c-nds32.texi \ c-ns32k.texi \ c-pdp11.texi \ c-pj.texi \ diff --git a/gas/doc/all.texi b/gas/doc/all.texi index 99dbf8f..5e1ed04 100644 --- a/gas/doc/all.texi +++ b/gas/doc/all.texi @@ -1,6 +1,4 @@ -@c Copyright 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001, 2002, -@c 2003, 2005, 2006, 2007, 2008, 2009, 2010, 2011 -@c Free Software Foundation, Inc. +@c Copyright 1992-2013 Free Software Foundation, Inc. @c This file is part of the documentation for the GAS manual @c Configuration settings for all-inclusive version of manual @@ -59,6 +57,7 @@ @set MS1 @set MSP430 @set NIOSII +@set NDS32 @set NS32K @set PDP11 @set PJ diff --git a/gas/doc/as.texinfo b/gas/doc/as.texinfo index 5fe06e6..739b5b9 100644 --- a/gas/doc/as.texinfo +++ b/gas/doc/as.texinfo @@ -437,6 +437,18 @@ gcc(1), ld(1), and the Info entries for @file{binutils} and @file{ld}. [@b{-relax-all}] [@b{-relax-section}] [@b{-no-relax}] [@b{-EB}] [@b{-EL}] @end ifset +@ifset NDS32 + +@emph{Target NDS32 options:} + [@b{-EL}] [@b{-EB}] [@b{-O}] [@b{-Os}] [@b{-mcpu=@var{cpu}}] + [@b{-misa=@var{isa}}] [@b{-mabi=@var{abi}}] [@b{-mall-ext}] + [@b{-m[no-]16-bit}] [@b{-m[no-]perf-ext}] [@b{-m[no-]perf2-ext}] + [@b{-m[no-]string-ext}] [@b{-m[no-]dsp-ext}] [@b{-m[no-]mac}] [@b{-m[no-]div}] + [@b{-m[no-]audio-isa-ext}] [@b{-m[no-]fpu-sp-ext}] [@b{-m[no-]fpu-dp-ext}] + [@b{-m[no-]fpu-fma}] [@b{-mfpu-freg=@var{FREG}}] [@b{-mreduced-regs}] + [@b{-mfull-regs}] [@b{-m[no-]dx-regs}] [@b{-mpic}] [@b{-mno-relax}] + [@b{-mb2bb}] +@end ifset @ifset PDP11 @emph{Target PDP11 options:} @@ -1471,6 +1483,25 @@ Meta processor. See the info pages for documentation of the MMIX-specific options. @end ifset +@ifset NDS32 + +@ifclear man +@xref{NDS32 Options}, for the options available when @value{AS} is configured +for a NDS32 processor. +@end ifclear +@c ended inside the included file +@end ifset + +@ifset man +@c man begin OPTIONS +The following options are available when @value{AS} is configured for a +NDS32 processor. +@c man end +@c man begin INCLUDE +@include c-nds32.texi +@c ended inside the included file +@end ifset + @c man end @ifset PPC @@ -7127,6 +7158,9 @@ subject, see the hardware manufacturer's manual. @ifset MSP430 * MSP430-Dependent:: MSP430 Dependent Features @end ifset +@ifset NDS32 +* NDS32-Dependent:: Andes NDS32 Dependent Features +@end ifset @ifset NIOSII * NiosII-Dependent:: Altera Nios II Dependent Features @end ifset @@ -7341,6 +7375,10 @@ family. @include c-msp430.texi @end ifset +@ifset NDS32 +@include c-nds32.texi +@end ifset + @ifset NIOSII @include c-nios2.texi @end ifset diff --git a/gas/doc/c-nds32.texi b/gas/doc/c-nds32.texi new file mode 100644 index 0000000..9636fcc --- /dev/null +++ b/gas/doc/c-nds32.texi @@ -0,0 +1,299 @@ +@c Copyright 2013 Free Software Foundation, Inc. +@c This is part of the GAS manual. +@c For copying conditions, see the file as.texinfo. +@c man end + +@ifset GENERIC +@page +@node NDS32-Dependent +@chapter NDS32 Dependent Features +@end ifset +@ifclear GENERIC +@node Machine Dependencies +@chapter NDS32 Dependent Features +@end ifclear + +@cindex NDS32 processor +The NDS32 processors family includes high-performance and low-power 32-bit +processors for high-end to low-end. @sc{gnu} @code{@value{AS}} for NDS32 +architectures supports NDS32 ISA version 3. For detail about NDS32 +instruction set, please see the AndeStar ISA User Manual which is availible +at http://www.andestech.com/en/index/index.htm + +@menu +* NDS32 Options:: Assembler options +* NDS32 Syntax:: High-level assembly macros +@end menu + +@node NDS32 Options +@section NDS32 Options + +@cindex NDS32 options +@cindex options for NDS32 +The NDS32 configurations of @sc{gnu} @code{@value{AS}} support these +special options: + +@c man begin OPTIONS +@table @code + +@item -O1 +Optimize for performance. + +@item -Os +Optimize for space. + +@item -EL +Produce little endian data output. + +@item -EB +Produce little endian data output. + +@item -mpic +Generate PIC. + +@item -mno-fp-as-gp-relax +Suppress fp-as-gp relaxation for this file. + +@item -mb2bb-relax +Back-to-back branch optimization. + +@item -mno-all-relax +Suppress all relaxation for this file. + +@item -march=<arch name> +Assemble for architecture <arch name> which could be v3, v3j, v3m, v3f, +v3s, v2, v2j, v2f, v2s. + +@item -mbaseline=<baseline> +Assemble for baseline <baseline> which could be v2, v3, v3m. + +@item -mfpu-freg=@var{FREG} +Specify a FPU configuration. +@table @code +@item 0 8 SP / 4 DP registers +@item 1 16 SP / 8 DP registers +@item 2 32 SP / 16 DP registers +@item 3 32 SP / 32 DP registers +@end table + +@item -mabi=@var{abi} +Specify a abi version <abi> could be v1, v2, v2fp, v2fpp. + +@item -m[no-]mac +Enable/Disable Multiply instructions support. + +@item -m[no-]div +Enable/Disable Divide instructions support. + +@item -m[no-]16bit-ext +Enable/Disable 16-bit extension + +@item -m[no-]dx-regs +Enable/Disable d0/d1 registers + +@item -m[no-]perf-ext +Enable/Disable Performance extension + +@item -m[no-]perf2-ext +Enable/Disable Performance extension 2 + +@item -m[no-]string-ext +Enable/Disable String extension + +@item -m[no-]reduced-regs +Enable/Disable Reduced Register configuration (GPR16) option + +@item -m[no-]audio-isa-ext +Enable/Disable AUDIO ISA extension + +@item -m[no-]fpu-sp-ext +Enable/Disable FPU SP extension + +@item -m[no-]fpu-dp-ext +Enable/Disable FPU DP extension + +@item -m[no-]fpu-fma +Enable/Disable FPU fused-multiply-add instructions + +@item -mall-ext +Turn on all extensions and instructions support +@end table +@c man end + +@node NDS32 Syntax +@section Syntax + +@menu +* NDS32-Chars:: Special Characters +* NDS32-Regs:: Register Names +* NDS32-Ops:: Pseudo Instructions +@end menu + +@node NDS32-Chars +@subsection Special Characters + +Use @samp{#} at column 1 and @samp{!} anywhere in the line except inside +quotes. + +Multiple instructions in a line are allowed though not recommended and +should be separated by @samp{;}. + +Assembler is not case-sensitive in general except user defined label. +For example, @samp{jral F1} is different from @samp{jral f1} while it is +the same as @samp{JRAL F1}. + +@node NDS32-Regs +@subsection Register Names +@table @code +@item General purpose registers (GPR) +There are 32 32-bit general purpose registers $r0 to $r31. + +@item Accumulators d0 and d1 +64-bit accumulators: $d0.hi, $d0.lo, $d1.hi, and $d1.lo. + +@item Assembler reserved register $ta +Register $ta ($r15) is reserved for assembler using. + +@item Operating system reserved registers $p0 and $p1 +Registers $p0 ($r26) and $p1 ($r27) are used by operating system as scratch +registers. + +@item Frame pointer $fp +Register $r28 is regarded as the frame pointer. + +@item Global pointer +Register $r29 is regarded as the global pointer. + +@item Link pointer +Register $r30 is regarded as the link pointer. + +@item Stack pointer +Register $r31 is regarded as the stack pointer. +@end table + +@node NDS32-Ops +@subsection Pseudo Instructions +@table @code +@item li rt5,imm32 +load 32-bit integer into register rt5. @samp{sethi rt5,hi20(imm32)} and then +@samp{ori rt5,reg,lo12(imm32)}. + +@item la rt5,var +Load 32-bit address of var into register rt5. @samp{sethi rt5,hi20(var)} and +then @samp{ori reg,rt5,lo12(var)} + +@item l.[bhw] rt5,var +Load value of var into register rt5. @samp{sethi $ta,hi20(var)} and then +@samp{l[bhw]i rt5,[$ta+lo12(var)]} + +@item l.[bh]s rt5,var +Load value of var into register rt5. @samp{sethi $ta,hi20(var)} and then +@samp{l[bh]si rt5,[$ta+lo12(var)]} + +@item l.[bhw]p rt5,var,inc +Load value of var into register rt5 and increment $ta by amount inc. +@samp{la $ta,var} and then @samp{l[bhw]i.bi rt5,[$ta],inc} + +@item l.[bhw]pc rt5,inc +Continue loading value of var into register rt5 and increment $ta by amount inc. +@samp{l[bhw]i.bi rt5,[$ta],inc.} + +@item l.[bh]sp rt5,var,inc +Load value of var into register rt5 and increment $ta by amount inc. +@samp{la $ta,var} and then @samp{l[bh]si.bi rt5,[$ta],inc} + +@item l.[bh]spc rt5,inc +Continue loading value of var into register rt5 and increment $ta by amount inc. +@samp{l[bh]si.bi rt5,[$ta],inc.} + +@item s.[bhw] rt5,var +Store register rt5 to var. +@samp{sethi $ta,hi20(var)} and then @samp{s[bhw]i rt5,[$ta+lo12(var)]} + +@item s.[bhw]p rt5,var,inc +Store register rt5 to var and increment $ta by amount inc. +@samp{la $ta,var} and then @samp{s[bhw]i.bi rt5,[$ta],inc} + +@item s.[bhw]pc rt5,inc +Continue storing register rt5 to var and increment $ta by amount inc. +@samp{s[bhw]i.bi rt5,[$ta],inc.} + +@item not rt5,ra5 +Alias of @samp{nor rt5,ra5,ra5}. + +@item neg rt5,ra5 +Alias of @samp{subri rt5,ra5,0}. + +@item br rb5 +Depending on how it is assembled, it is translated into @samp{r5 rb5} +or @samp{jr rb5}. + +@item b label +Branch to label depending on how it is assembled, it is translated into +@samp{j8 label}, @samp{j label}, or "@samp{la $ta,label} @samp{br $ta}". + +@item bral rb5 +Alias of jral br5 depending on how it is assembled, it is translated +into @samp{jral5 rb5} or @samp{jral rb5}. + +@item bal fname +Alias of jal fname depending on how it is assembled, it is translated into +@samp{jal fname} or "@samp{la $ta,fname} @samp{bral $ta}". + +@item call fname +Call function fname same as @samp{jal fname}. + +@item move rt5,ra5 +For 16-bit, this is @samp{mov55 rt5,ra5}. +For no 16-bit, this is @samp{ori rt5,ra5,0}. + +@item move rt5,var +This is the same as @samp{l.w rt5,var}. + +@item move rt5,imm32 +This is the same as @samp{li rt5,imm32}. + +@item pushm ra5,rb5 +Push contents of registers from ra5 to rb5 into stack. + +@item push ra5 +Push content of register ra5 into stack. (same @samp{pushm ra5,ra5}). + +@item push.d var +Push value of double-word variable var into stack. + +@item push.w var +Push value of word variable var into stack. + +@item push.h var +Push value of half-word variable var into stack. + +@item push.b var +Push value of byte variable var into stack. + +@item pusha var +Push 32-bit address of variable var into stack. + +@item pushi imm32 +Push 32-bit immediate value into stack. + +@item popm ra5,rb5 +Pop top of stack values into registers ra5 to rb5. + +@item pop rt5 +Pop top of stack value into register. (same as @samp{popm rt5,rt5}.) + +@item pop.d var,ra5 +Pop value of double-word variable var from stack using register ra5 +as 2nd scratch register. (1st is $ta) + +@item pop.w var,ra5 +Pop value of word variable var from stack using register ra5. + +@item pop.h var,ra5 +Pop value of half-word variable var from stack using register ra5. + +@item pop.b var,ra5 +Pop value of byte variable var from stack using register ra5. + +@end table diff --git a/gas/testsuite/ChangeLog b/gas/testsuite/ChangeLog index a0dba9f..d9ae4c0 100644 --- a/gas/testsuite/ChangeLog +++ b/gas/testsuite/ChangeLog @@ -1,3 +1,35 @@ +2013-12-13 Kuan-Lin Chen <kuanlinchentw@gmail.com> + + * gas/all/gas.exp: Add expected failures for NDS32. + * gas/elf/elf.exp: Likewise. + * gas/lns/lns.exp: Use alternate test. + * gas/macros/irp.d: Skip for NDS32. + * gas/macros/macros.exp: Skip some tests for the NDS32. + * gas/macros/rept.d: Skip for NDS32. + * gas/macros/test3.d: Skip for NDS32. + * gas/nds32: New directory. + * gas/nds32/alu-1.s: New test. + * gas/nds32/alu-1.d: Likewise. + * gas/nds32/alu-2.s: Likewise. + * gas/nds32/alu-2.d: Likewise. + * gas/nds32/br-1.d: Likewise. + * gas/nds32/br-1.s: Likewise. + * gas/nds32/br-2.d: Likewise. + * gas/nds32/br-2.s: Likewise. + * gas/nds32/ji-jr.d: Likewise. + * gas/nds32/ji-jr.s: Likewise. + * gas/nds32/ls.d: Likewise. + * gas/nds32/ls.s: Likewise. + * gas/nds32/lsi.d: Likewise. + * gas/nds32/lsi.s: Likewise. + * gas/nds32/to-16bit-v1.d: Likewise. + * gas/nds32/to-16bit-v1.s: Likewise. + * gas/nds32/to-16bit-v2.d: Likewise. + * gas/nds32/to-16bit-v2.s: Likewise. + * gas/nds32/to-16bit-v3.d: Likewise. + * gas/nds32/to-16bit-v3.s: Likewise. + * gas/nds32/nds32.exp: New test driver. + 2013-12-07 Mike Frysinger <vapier@gentoo.org> * gas/bfin/bit2.s: Remove +x file mode. diff --git a/gas/testsuite/gas/all/gas.exp b/gas/testsuite/gas/all/gas.exp index 35bcd95..f04a6d3 100644 --- a/gas/testsuite/gas/all/gas.exp +++ b/gas/testsuite/gas/all/gas.exp @@ -101,7 +101,7 @@ case $target_triplet in { # Some targets don't manage to resolve BFD_RELOC_8 for constants. setup_xfail "alpha*-*-*" "*c30*-*-*" "*c4x*-*-*" \ "d\[13\]0v*-*-*" "i860-*-*" "mips*-*-*" \ - "pdp11-*-*" "xtensa*-*-*" + "nds32*-*-*" "pdp11-*-*" "xtensa*-*-*" run_dump_test forward } } @@ -118,6 +118,7 @@ if { ![is_aout_format] } { { hppa*-*-hpux* } { } { mep-*-* } { } { mmix-*-* } { } + { nds32*-*-* } { } { tic30*-*-* } { } { tic4x*-*-* } { } { tic54x*-*-* } { } @@ -140,6 +141,7 @@ case $target_triplet in { { mn10200-*-* } { } { mn10300-*-* } { } { msp430*-*-* } { } + { nds32*-*-* } { } { pdp11-*-* } { } { tic30*-*-* } { } { tic4x*-*-* } { } @@ -366,12 +368,13 @@ if { ([istarget "i*86-*-*pe*"] && ![istarget "i*86-*-openbsd*"]) \ gas_test "fastcall.s" "" "" "fastcall labels" } -if { ![istarget "bfin-*-*"] } then { +if { ![istarget "bfin-*-*"] && ![istarget "nds32*-*-*"] } then { run_dump_test assign } run_dump_test sleb128 run_dump_test sleb128-2 run_dump_test sleb128-3 +setup_xfail "nds32*-*-*" run_dump_test sleb128-4 run_dump_test sleb128-5 # .byte is not 8 bits on either tic4x or tic54x @@ -414,6 +417,7 @@ case $target_triplet in { { *c54x*-*-* } { } { z80-*-* } { } default { + setup_xfail "nds32*-*-*" run_dump_test weakref1 run_dump_test weakref1g run_dump_test weakref1l diff --git a/gas/testsuite/gas/elf/elf.exp b/gas/testsuite/gas/elf/elf.exp index 4196fd7..d40e9f3 100644 --- a/gas/testsuite/gas/elf/elf.exp +++ b/gas/testsuite/gas/elf/elf.exp @@ -94,6 +94,7 @@ if { [is_elf_format] } then { if {![istarget "mn10300-*-*"] && ![istarget "xtensa*-*-*"] && ![istarget "msp430*-*-*"] + && ![istarget "nds32*-*-*"] && ![istarget "am3*-*-*"]} then { run_dump_test "ehopt0" } @@ -111,6 +112,7 @@ if { [is_elf_format] } then { run_dump_test "file" } } + setup_xfail "nds32*-*-*" run_dump_test "group0a" run_dump_test "group0b" run_dump_test "group0c" diff --git a/gas/testsuite/gas/lns/lns.exp b/gas/testsuite/gas/lns/lns.exp index f1d7f98..8f81998 100644 --- a/gas/testsuite/gas/lns/lns.exp +++ b/gas/testsuite/gas/lns/lns.exp @@ -39,6 +39,7 @@ if { || [istarget cr16-*-*] || [istarget crx-*-*] || [istarget msp430-*-*] + || [istarget nds32*-*-*] || [istarget mn10*-*-*] } { run_dump_test "lns-common-1-alt" run_dump_test "lns-big-delta" diff --git a/gas/testsuite/gas/macros/irp.d b/gas/testsuite/gas/macros/irp.d index 2c07f8e..7773792 100644 --- a/gas/testsuite/gas/macros/irp.d +++ b/gas/testsuite/gas/macros/irp.d @@ -1,7 +1,7 @@ #objdump: -r #name: macro irp #darwin (mach-o) reverses relocs. -#not-target: *-*-darwin* +#not-target: *-*-darwin* nds32*-*-* .*: +file format .* diff --git a/gas/testsuite/gas/macros/macros.exp b/gas/testsuite/gas/macros/macros.exp index 4a2716d..1253415 100644 --- a/gas/testsuite/gas/macros/macros.exp +++ b/gas/testsuite/gas/macros/macros.exp @@ -21,13 +21,13 @@ if { ![istarget hppa*-*-*] || [istarget *-*-linux*] } { run_dump_test test1 } -if { ![istarget *c54x*-*-*] && ![istarget *c4x*-*-*] } { +if { ![istarget *c54x*-*-*] && ![istarget *c4x*-*-*] && ![istarget "nds32*-*-*"] } { run_dump_test test2 } run_dump_test test3 -if { ![istarget *c54x*-*-*] && ![istarget *c4x*-*-*] } { +if { ![istarget *c54x*-*-*] && ![istarget *c4x*-*-*] && ![istarget "nds32*-*-*"] } { run_dump_test irp run_dump_test rept run_dump_test repeat diff --git a/gas/testsuite/gas/macros/rept.d b/gas/testsuite/gas/macros/rept.d index df2ed96..92d6410 100644 --- a/gas/testsuite/gas/macros/rept.d +++ b/gas/testsuite/gas/macros/rept.d @@ -1,7 +1,7 @@ #objdump: -r #name: macro rept #darwin (mach-o) reverses relocs. -#not-target: *-*-darwin* +#not-target: *-*-darwin* nds32*-*-* .*: +file format .* diff --git a/gas/testsuite/gas/macros/test3.d b/gas/testsuite/gas/macros/test3.d index 7680ad4..afd47fa 100644 --- a/gas/testsuite/gas/macros/test3.d +++ b/gas/testsuite/gas/macros/test3.d @@ -1,5 +1,6 @@ #objdump: -r #name: macro test 3 +#not-target: nds32*-*-* .*: +file format .* diff --git a/gas/testsuite/gas/nds32/alu-1.d b/gas/testsuite/gas/nds32/alu-1.d new file mode 100644 index 0000000..f1385cd --- /dev/null +++ b/gas/testsuite/gas/nds32/alu-1.d @@ -0,0 +1,47 @@ +#objdump: -d --prefix-addresses +#name: nds32 alu_1 instructions +#as: + +# Test alu_1 instructions + +.*: file format .* + +Disassembly of section .text: +0+0000 <[^>]*> add \$r0, \$r1, \$r2 +0+0004 <[^>]*> and \$r0, \$r1, \$r2 +0+0008 <[^>]*> cmovn \$r0, \$r1, \$r2 +0+000c <[^>]*> cmovz \$r0, \$r1, \$r2 +0+0010 <[^>]*> nop +0+0014 <[^>]*> nor \$r0, \$r1, \$r2 +0+0018 <[^>]*> or \$r0, \$r1, \$r2 +0+001c <[^>]*> rotr \$r0, \$r1, \$r2 +0+0020 <[^>]*> rotri \$r0, \$r1, #1 +0+0024 <[^>]*> seb \$r0, \$r1 +0+0028 <[^>]*> seh \$r0, \$r1 +0+002c <[^>]*> sll \$r0, \$r1, \$r2 +0+0030 <[^>]*> slli \$r0, \$r1, #1 +0+0034 <[^>]*> slt \$r0, \$r1, \$r2 +0+0038 <[^>]*> slts \$r0, \$r1, \$r2 +0+003c <[^>]*> sra \$r0, \$r1, \$r2 +0+0040 <[^>]*> srai \$r0, \$r1, #1 +0+0044 <[^>]*> srl \$r0, \$r1, \$r2 +0+0048 <[^>]*> srli \$r0, \$r1, #1 +0+004c <[^>]*> sub \$r0, \$r1, \$r2 +0+0050 <[^>]*> sva \$r0, \$r1, \$r2 +0+0054 <[^>]*> svs \$r0, \$r1, \$r2 +0+0058 <[^>]*> wsbh \$r0, \$r1 +0+005c <[^>]*> xor \$r0, \$r1, \$r2 +0+0060 <[^>]*> zeh \$r0, \$r1 +0+0064 <[^>]*> divr \$r0, \$r1, \$r2, \$r3 +0+0068 <[^>]*> divsr \$r0, \$r1, \$r2, \$r3 +0+006c <[^>]*> add_slli \$r0, \$r1, \$r2, #1 +0+0070 <[^>]*> add_srli \$r0, \$r1, \$r2, #1 +0+0074 <[^>]*> and_slli \$r0, \$r1, \$r2, #1 +0+0078 <[^>]*> and_srli \$r0, \$r1, \$r2, #1 +0+007c <[^>]*> bitc \$r0, \$r1, \$r2 +0+0080 <[^>]*> or_slli \$r0, \$r1, \$r2, #1 +0+0084 <[^>]*> or_srli \$r0, \$r1, \$r2, #1 +0+0088 <[^>]*> sub_slli \$r0, \$r1, \$r2, #1 +0+008c <[^>]*> sub_srli \$r0, \$r1, \$r2, #1 +0+0090 <[^>]*> xor_slli \$r0, \$r1, \$r2, #1 +0+0094 <[^>]*> xor_srli \$r0, \$r1, \$r2, #1 diff --git a/gas/testsuite/gas/nds32/alu-1.s b/gas/testsuite/gas/nds32/alu-1.s new file mode 100644 index 0000000..5f12783 --- /dev/null +++ b/gas/testsuite/gas/nds32/alu-1.s @@ -0,0 +1,39 @@ +foo: + add $r0, $r1, $r2 + and $r0, $r1, $r2 + cmovn $r0, $r1, $r2 + cmovz $r0, $r1, $r2 + nop + nor $r0, $r1, $r2 + or $r0, $r1, $r2 + rotr $r0, $r1, $r2 + rotri $r0, $r1, 1 + seb $r0, $r1 + seh $r0, $r1 + sll $r0, $r1, $r2 + slli $r0, $r1, 1 + slt $r0, $r1, $r2 + slts $r0, $r1, $r2 + sra $r0, $r1, $r2 + srai $r0, $r1, 1 + srl $r0, $r1, $r2 + srli $r0, $r1, 1 + sub $r0, $r1, $r2 + sva $r0, $r1, $r2 + svs $r0, $r1, $r2 + wsbh $r0, $r1 + xor $r0, $r1, $r2 + zeh $r0, $r1 + divr $r0, $r1, $r2, $r3 + divsr $r0, $r1, $r2, $r3 + add_slli $r0, $r1, $r2, 1 + add_srli $r0, $r1, $r2, 1 + and_slli $r0, $r1, $r2, 1 + and_srli $r0, $r1, $r2, 1 + bitc $r0, $r1, $r2 + or_slli $r0, $r1, $r2, 1 + or_srli $r0, $r1, $r2, 1 + sub_slli $r0, $r1, $r2, 1 + sub_srli $r0, $r1, $r2, 1 + xor_slli $r0, $r1, $r2, 1 + xor_srli $r0, $r1, $r2, 1 diff --git a/gas/testsuite/gas/nds32/alu-2.d b/gas/testsuite/gas/nds32/alu-2.d new file mode 100644 index 0000000..1b32f0b --- /dev/null +++ b/gas/testsuite/gas/nds32/alu-2.d @@ -0,0 +1,42 @@ +#objdump: -d --prefix-addresses +#name: nds32 alu_2 instructions +#as: + +# Test alu_2 instructions + +.*: file format .* + +Disassembly of section .text: +0+0000 <[^>]*> madd64 \$d0, \$r0, \$r1 +0+0004 <[^>]*> madds64 \$d0, \$r0, \$r1 +0+0008 <[^>]*> mfusr \$r0, \$pc +0+000c <[^>]*> msub64 \$d0, \$r0, \$r1 +0+0010 <[^>]*> msubs64 \$d0, \$r0, \$r1 +0+0014 <[^>]*> mtusr \$r0, \$pc +0+0018 <[^>]*> mul \$r0, \$r1, \$r2 +0+001c <[^>]*> mult32 \$d0, \$r1, \$r2 +0+0020 <[^>]*> mult64 \$d0, \$r1, \$r2 +0+0024 <[^>]*> mults64 \$d0, \$r1, \$r2 +0+0028 <[^>]*> abs \$r0, \$r1 +0+002c <[^>]*> ave \$r0, \$r1, \$r2 +0+0030 <[^>]*> bclr \$r0, \$r1, #1 +0+0034 <[^>]*> bset \$r0, \$r1, #1 +0+0038 <[^>]*> btgl \$r0, \$r1, #1 +0+003c <[^>]*> btst \$r0, \$r1, #1 +0+0040 <[^>]*> clip \$r0, \$r1, #1 +0+0044 <[^>]*> clips \$r0, \$r1, #1 +0+0048 <[^>]*> clo \$r0, \$r1 +0+004c <[^>]*> clz \$r0, \$r1 +0+0050 <[^>]*> max \$r0, \$r1, \$r2 +0+0054 <[^>]*> min \$r0, \$r1, \$r2 +0+0058 <[^>]*> bse \$r0, \$r1, \$r2 +0+005c <[^>]*> bsp \$r0, \$r1, \$r2 +0+0060 <[^>]*> ffb \$r0, \$r1, \$r2 +0+0064 <[^>]*> ffbi \$r0, \$r1, #0x8 +0+0068 <[^>]*> ffmism \$r0, \$r1, \$r2 +0+006c <[^>]*> flmism \$r0, \$r1, \$r2 +0+0070 <[^>]*> maddr32 \$r0, \$r0, \$r1 +0+0074 <[^>]*> msubr32 \$r0, \$r1, \$r2 +0+0078 <[^>]*> mulr64 \$r0, \$r1, \$r2 +0+007c <[^>]*> mulsr64 \$r0, \$r1, \$r2 + diff --git a/gas/testsuite/gas/nds32/alu-2.s b/gas/testsuite/gas/nds32/alu-2.s new file mode 100644 index 0000000..d1743db --- /dev/null +++ b/gas/testsuite/gas/nds32/alu-2.s @@ -0,0 +1,33 @@ +foo: + madd64 $d0, $r0, $r1 + madds64 $d0, $r0, $r1 + mfusr $r0, $pc + msub64 $d0, $r0, $r1 + msubs64 $d0, $r0, $r1 + mtusr $r0, $pc + mul $r0, $r1, $r2 + mult32 $d0, $r1, $r2 + mult64 $d0, $r1, $r2 + mults64 $d0, $r1, $r2 + abs $r0, $r1 + ave $r0, $r1, $r2 + bclr $r0, $r1, 1 + bset $r0, $r1, 1 + btgl $r0, $r1, 1 + btst $r0, $r1, 1 + clip $r0, $r1, 1 + clips $r0, $r1, 1 + clo $r0, $r1 + clz $r0, $r1 + max $r0, $r1, $r2 + min $r0, $r1, $r2 + bse $r0, $r1, $r2 + bsp $r0, $r1, $r2 + ffb $r0, $r1, $r2 + ffbi $r0, $r1, 1 + ffmism $r0, $r1, $r2 + flmism $r0, $r1, $r2 + maddr32 $r0, $r0, $r1 + msubr32 $r0, $r1, $r2 + mulr64 $r0, $r1, $r2 + mulsr64 $r0, $r1, $r2 diff --git a/gas/testsuite/gas/nds32/br-1.d b/gas/testsuite/gas/nds32/br-1.d new file mode 100644 index 0000000..e27f96c --- /dev/null +++ b/gas/testsuite/gas/nds32/br-1.d @@ -0,0 +1,15 @@ +#objdump: -dr --prefix-addresses +#name: nds32 branch 1 instructions +#as: + +# Test br-1 instructions + +.*: file format .* + +Disassembly of section .text: +0+0000 <[^>]*> beq \$r0, \$r1, 00000000 <foo> + 0: R_NDS32_15_PCREL_RELA .text + 0: R_NDS32_RELAX_ENTRY .text +0+0004 <[^>]*> bne \$r0, \$r1, 00000004 <foo\+0x4> + 4: R_NDS32_15_PCREL_RELA .text + diff --git a/gas/testsuite/gas/nds32/br-1.s b/gas/testsuite/gas/nds32/br-1.s new file mode 100644 index 0000000..58d993a --- /dev/null +++ b/gas/testsuite/gas/nds32/br-1.s @@ -0,0 +1,3 @@ +foo: + beq $r0, $r1, foo + bne $r0, $r1, foo diff --git a/gas/testsuite/gas/nds32/br-2.d b/gas/testsuite/gas/nds32/br-2.d new file mode 100644 index 0000000..0edc375 --- /dev/null +++ b/gas/testsuite/gas/nds32/br-2.d @@ -0,0 +1,25 @@ +#objdump: -dr --prefix-addresses +#name: nds32 branch 2 instructions +#as: + +# Test br-2 instructions + +.*: file format .* + +Disassembly of section .text: +0+0000 <[^>]*> beqz \$r0, 00000000 <foo> + 0: R_NDS32_17_PCREL_RELA .text + 0: R_NDS32_RELAX_ENTRY .text +0+0004 <[^>]*> bgez \$r0, 00000004 <foo\+0x4> + 4: R_NDS32_17_PCREL_RELA .text +0+0008 <[^>]*> bgezal \$r0, 00000008 <foo\+0x8> + 8: R_NDS32_17_PCREL_RELA .text +0+000c <[^>]*> bgtz \$r0, 0000000c <foo\+0xc> + c: R_NDS32_17_PCREL_RELA .text +0+0010 <[^>]*> blez \$r0, 00000010 <foo\+0x10> + 10: R_NDS32_17_PCREL_RELA .text +0+0014 <[^>]*> bltz \$r0, 00000014 <foo\+0x14> + 14: R_NDS32_17_PCREL_RELA .text +0+0018 <[^>]*> bltzal \$r0, 00000018 <foo\+0x18> + 18: R_NDS32_17_PCREL_RELA .text + diff --git a/gas/testsuite/gas/nds32/br-2.s b/gas/testsuite/gas/nds32/br-2.s new file mode 100644 index 0000000..554a8de --- /dev/null +++ b/gas/testsuite/gas/nds32/br-2.s @@ -0,0 +1,8 @@ +foo: + beqz $r0, foo + bgez $r0, foo + bgezal $r0, foo + bgtz $r0, foo + blez $r0, foo + bltz $r0, foo + bltzal $r0, foo diff --git a/gas/testsuite/gas/nds32/ji-jr.d b/gas/testsuite/gas/nds32/ji-jr.d new file mode 100644 index 0000000..31969e1 --- /dev/null +++ b/gas/testsuite/gas/nds32/ji-jr.d @@ -0,0 +1,18 @@ +#objdump: -dr --prefix-addresses +#name: nds32 load-store instructions +#as: + +# Test ls instructions + +.*: file format .* + +Disassembly of section .text: +0+0000 <[^>]*> j8 00000000 <foo> + 0: R_NDS32_9_PCREL_RELA .text + 0: R_NDS32_RELAX_ENTRY .text +0+0002 <[^>]*> jal 00000002 <foo\+0x2> + 2: R_NDS32_25_PCREL_RELA .text +0+0006 <[^>]*> jr \$r0 +0+000a <[^>]*> jral \$lp, \$r0 +0+000e <[^>]*> ret \$lp + diff --git a/gas/testsuite/gas/nds32/ji-jr.s b/gas/testsuite/gas/nds32/ji-jr.s new file mode 100644 index 0000000..2457d0b --- /dev/null +++ b/gas/testsuite/gas/nds32/ji-jr.s @@ -0,0 +1,6 @@ +foo: + j foo + jal foo + jr $r0 + jral $r0 + ret diff --git a/gas/testsuite/gas/nds32/ls.d b/gas/testsuite/gas/nds32/ls.d new file mode 100644 index 0000000..688ed11 --- /dev/null +++ b/gas/testsuite/gas/nds32/ls.d @@ -0,0 +1,25 @@ +#objdump: -d --prefix-addresses +#name: nds32 load-store instructions +#as: + +# Test ls instructions + +.*: file format .* + +Disassembly of section .text: +0+0000 <[^>]*> lw \$r0, \[\$r1 \+ \(\$r2 << 1\)\] +0+0004 <[^>]*> lh \$r0, \[\$r1 \+ \(\$r2 << 1\)\] +0+0008 <[^>]*> lhs \$r0, \[\$r1 \+ \(\$r2 << 1\)\] +0+000c <[^>]*> lb \$r0, \[\$r1 \+ \(\$r2 << 1\)\] +0+0010 <[^>]*> lbs \$r0, \[\$r1 \+ \(\$r2 << 1\)\] +0+0014 <[^>]*> sw \$r0, \[\$r1 \+ \(\$r2 << 1\)\] +0+0018 <[^>]*> sh \$r0, \[\$r1 \+ \(\$r2 << 1\)\] +0+001c <[^>]*> sb \$r0, \[\$r1 \+ \(\$r2 << 1\)\] +0+0020 <[^>]*> lw.bi \$r0, \[\$r1\], \(\$r2 << 1\) +0+0024 <[^>]*> lh.bi \$r0, \[\$r1\], \(\$r2 << 1\) +0+0028 <[^>]*> lhs.bi \$r0, \[\$r1\], \(\$r2 << 1\) +0+002c <[^>]*> lb.bi \$r0, \[\$r1\], \(\$r2 << 1\) +0+0030 <[^>]*> lbs.bi \$r0, \[\$r1\], \(\$r2 << 1\) +0+0034 <[^>]*> sw.bi \$r0, \[\$r1\], \(\$r2 << 1\) +0+0038 <[^>]*> sh.bi \$r0, \[\$r1\], \(\$r2 << 1\) +0+003c <[^>]*> sb.bi \$r0, \[\$r1\], \(\$r2 << 1\) diff --git a/gas/testsuite/gas/nds32/ls.s b/gas/testsuite/gas/nds32/ls.s new file mode 100644 index 0000000..88d3d33 --- /dev/null +++ b/gas/testsuite/gas/nds32/ls.s @@ -0,0 +1,17 @@ +foo: + lw $r0, [$r1 + ($r2 << 1)] + lh $r0, [$r1 + ($r2 << 1)] + lhs $r0, [$r1 + ($r2 << 1)] + lb $r0, [$r1 + ($r2 << 1)] + lbs $r0, [$r1 + ($r2 << 1)] + sw $r0, [$r1 + ($r2 << 1)] + sh $r0, [$r1 + ($r2 << 1)] + sb $r0, [$r1 + ($r2 << 1)] + lw.bi $r0, [$r1], $r2 << 1 + lh.bi $r0, [$r1], $r2 << 1 + lhs.bi $r0, [$r1], $r2 << 1 + lb.bi $r0, [$r1], $r2 << 1 + lbs.bi $r0, [$r1], $r2 << 1 + sw.bi $r0, [$r1], $r2 << 1 + sh.bi $r0, [$r1], $r2 << 1 + sb.bi $r0, [$r1], $r2 << 1 diff --git a/gas/testsuite/gas/nds32/lsi.d b/gas/testsuite/gas/nds32/lsi.d new file mode 100644 index 0000000..9fe5371 --- /dev/null +++ b/gas/testsuite/gas/nds32/lsi.d @@ -0,0 +1,26 @@ +#objdump: -d --prefix-addresses +#name: nds32 load-store immediate instructions +#as: + +# Test lsi instructions + +.*: file format .* + +Disassembly of section .text: +0+0000 <[^>]*> lwi \$r0, \[\$r1 \+ #4\] +0+0004 <[^>]*> lhi \$r0, \[\$r1 \+ #2\] +0+0008 <[^>]*> lhsi \$r0, \[\$r1 \+ #-2\] +0+000c <[^>]*> lbi \$r0, \[\$r1 \+ #1\] +0+0010 <[^>]*> lbsi \$r0, \[\$r1 \+ #-1\] +0+0014 <[^>]*> swi \$r0, \[\$r1 \+ #4\] +0+0018 <[^>]*> shi \$r0, \[\$r1 \+ #2\] +0+001c <[^>]*> sbi \$r0, \[\$r1 \+ #1\] +0+0020 <[^>]*> lwi.bi \$r0, \[\$r1\], #4 +0+0024 <[^>]*> lhi.bi \$r0, \[\$r1\], #2 +0+0028 <[^>]*> lhsi.bi \$r0, \[\$r1\], #-2 +0+002c <[^>]*> lbi.bi \$r0, \[\$r1\], #1 +0+0030 <[^>]*> lbsi.bi \$r0, \[\$r1\], #-1 +0+0034 <[^>]*> swi.bi \$r0, \[\$r1\], #4 +0+0038 <[^>]*> shi.bi \$r0, \[\$r1\], #2 +0+003c <[^>]*> sbi.bi \$r0, \[\$r1\], #1 + diff --git a/gas/testsuite/gas/nds32/lsi.s b/gas/testsuite/gas/nds32/lsi.s new file mode 100644 index 0000000..a2dd62a --- /dev/null +++ b/gas/testsuite/gas/nds32/lsi.s @@ -0,0 +1,17 @@ +foo: + lwi $r0, [$r1 + (1 << 2)] + lhi $r0, [$r1 + (1 << 1)] + lhsi $r0, [$r1 + (-1 << 1)] + lbi $r0, [$r1 + 1] + lbsi $r0, [$r1 + (-1)] + swi $r0, [$r1 + (1 << 2)] + shi $r0, [$r1 + (1 << 1)] + sbi $r0, [$r1 + 1] + lwi.bi $r0, [$r1], (1 << 2) + lhi.bi $r0, [$r1], (1 << 1) + lhsi.bi $r0, [$r1], (-1 << 1) + lbi.bi $r0, [$r1], 1 + lbsi.bi $r0, [$r1], -1 + swi.bi $r0, [$r1], (1 << 2) + shi.bi $r0, [$r1], (1 << 1) + sbi.bi $r0, [$r1], 1 diff --git a/gas/testsuite/gas/nds32/nds32.exp b/gas/testsuite/gas/nds32/nds32.exp new file mode 100644 index 0000000..f547697 --- /dev/null +++ b/gas/testsuite/gas/nds32/nds32.exp @@ -0,0 +1,30 @@ +# Copyright (C) 2012-2013 Free Software Foundation, Inc. +# Contributed by Andes Technology Corporation. + +# 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. + +if { [istarget nds32*] } { + run_dump_test "alu-1" + run_dump_test "alu-2" + run_dump_test "lsi" + run_dump_test "ls" + run_dump_test "br-1" + run_dump_test "br-2" + run_dump_test "ji-jr" + run_dump_test "to-16bit-v1" + run_dump_test "to-16bit-v2" + run_dump_test "to-16bit-v3" +} diff --git a/gas/testsuite/gas/nds32/to-16bit-v1.d b/gas/testsuite/gas/nds32/to-16bit-v1.d new file mode 100644 index 0000000..a45de36 --- /dev/null +++ b/gas/testsuite/gas/nds32/to-16bit-v1.d @@ -0,0 +1,79 @@ +#objdump: -d --prefix-addresses +#name: nds32 convert 32 to 16 (v1 instructions) +#as: -Os -mno-reduced-regs + +# Test the convert 32bits to 16bits + +.*: file format .* + + +Disassembly of section .text: +0+0000 .* +0+0002 .* +0+0004 .* +0+0006 .* +0+0008 .* +0+000a .* +0+000c .* +0+000e .* +0+0010 .* +0+0012 .* +0+0014 .* +0+0016 .* +0+0018 .* +0+001a .* +0+001c .* +0+001e .* +0+0020 .* +0+0022 .* +0+0024 .* +0+0026 .* +0+0028 .* +0+002a .* +0+002c .* +0+002e .* +0+0030 .* +0+0032 .* +0+0034 .* +0+0036 .* +0+0038 .* +0+003a .* +0+003c .* +0+003e .* +0+0040 .* +0+0042 .* +0+0044 .* +0+0046 .* +0+0048 .* +0+004a .* +0+004c .* +0+004e .* +0+0050 .* +0+0052 .* +0+0054 .* +0+0056 .* +0+0058 .* +0+005a .* +0+005c .* +0+005e .* +0+0060 .* +0+0062 .* +0+0064 .* +0+0066 .* +0+0068 .* +0+006a .* +0+006c .* +0+006e .* +0+0070 .* +0+0072 .* +0+0074 .* +0+0076 .* +0+0078 .* +0+007a .* +0+007c .* +0+007e .* +0+0080 .* +0+0082 .* +0+0084 .* +0+0086 .* +0+0088 .* diff --git a/gas/testsuite/gas/nds32/to-16bit-v1.s b/gas/testsuite/gas/nds32/to-16bit-v1.s new file mode 100644 index 0000000..195463c --- /dev/null +++ b/gas/testsuite/gas/nds32/to-16bit-v1.s @@ -0,0 +1,70 @@ +foo: + move $r0, $r0 + move $sp, $sp + movi $r0, -16 + movi $sp, 15 + add $r0, $r0, $r0 + add $r19, $sp, $r19 + sub $r0, $r0, $r0 + sub $r19, $r19, $sp + addi $r0, $r0, 0 + addi $r19, $r19, 31 + srai $r0, $r0, 0 + srai $r19, $r19, 31 + srli $r0, $r0, 0 + srli $r19, $r19, 31 + slli $r0, $r0, 0 + slli $r7, $r7, 7 + zeb $r0, $r0 + zeb $r7, $r7 + zeh $r0, $r0 + zeh $r7, $r7 + seb $r0, $r0 + seb $r7, $r7 + seh $r0, $r0 + seh $r7, $r7 + andi $r0, $r0, 1 + andi $r7, $r7, 0x7ff + add $r0, $r0, $r0 + add $r7, $r7, $r7 + sub $r0, $r0, $r0 + sub $r7, $r7, $r7 + addi $r0, $r0, 0 + addi $r7, $r7, 7 + lwi $r0, [$r0 + 0] + lwi $r7, [$r7 + 28] + lwi.bi $r0, [$r0], 0 + lwi.bi $r7, [$r7], 28 + lhi $r0, [$r0 + 0] + lhi $r7, [$r7 + 14] + lbi $r0, [$r0 + 0] + lbi $r7, [$r7 + 7] + swi $r0, [$r0 + 0] + swi $r7, [$r7 + 28] + swi.bi $r0, [$r0], 0 + swi.bi $r7, [$r7], 28 + shi $r0, [$r0 + 0] + shi $r7, [$r7 + 14] + sbi $r0, [$r0 + 0] + sbi $r7, [$r7 + 7] + lwi $r0, [$r0 + 0] + lwi $r19, [$sp + 0] + swi $r0, [$r0 + 0] + swi $r19, [$sp + 0] + lwi $r0, [$fp + 0] + lwi $r7, [$fp + 508] + swi $r0, [$fp + 0] + swi $r7, [$fp + 508] + jr $r0 + jr $sp + ret $r0 + ret $sp + jral $r0 + jral $sp + slts $r15, $r0, $r0 + slts $r15, $r19, $sp + slt $r15, $r0, $r0 + slt $r15, $r19, $sp + sltsi $r15, $r0, 0 + sltsi $r15, $r19, 31 + slti $r15, $r0, 0 diff --git a/gas/testsuite/gas/nds32/to-16bit-v2.d b/gas/testsuite/gas/nds32/to-16bit-v2.d new file mode 100644 index 0000000..5ca929c --- /dev/null +++ b/gas/testsuite/gas/nds32/to-16bit-v2.d @@ -0,0 +1,15 @@ +#objdump: -d --prefix-addresses +#name: nds32 convert 32 to 16 (v2 instructions) +#as: -Os -mno-reduced-regs + +# Test the convert 32bits to 16bits + +.*: file format .* + + +Disassembly of section .text: +0+0000 .* +0+0002 .* +0+0004 .* +0+0006 .* +0+0008 .* diff --git a/gas/testsuite/gas/nds32/to-16bit-v2.s b/gas/testsuite/gas/nds32/to-16bit-v2.s new file mode 100644 index 0000000..1ac3328 --- /dev/null +++ b/gas/testsuite/gas/nds32/to-16bit-v2.s @@ -0,0 +1,6 @@ +foo: +addi $sp, $sp, -512 +addi $sp, $sp, 511 +lwi $r0, [$sp + 0] +lwi $r7, [$sp + 508] +swi $r0, [$sp + 0] diff --git a/gas/testsuite/gas/nds32/to-16bit-v3.d b/gas/testsuite/gas/nds32/to-16bit-v3.d new file mode 100644 index 0000000..2efa8b7 --- /dev/null +++ b/gas/testsuite/gas/nds32/to-16bit-v3.d @@ -0,0 +1,25 @@ +#objdump: -d --prefix-addresses +#name: nds32 convert 32 to 16 (v3 instructions) +#as: -Os -mno-reduced-regs + +# Test the convert 32bits to 16bits + +.*: file format .* + + +Disassembly of section .text: +0+0000 .* +0+0002 .* +0+0004 .* +0+0006 .* +0+0008 .* +0+000a .* +0+000c .* +0+000e .* +0+0010 .* +0+0012 .* +0+0014 .* +0+0016 .* +0+0018 .* +0+001a .* +0+001c .* diff --git a/gas/testsuite/gas/nds32/to-16bit-v3.s b/gas/testsuite/gas/nds32/to-16bit-v3.s new file mode 100644 index 0000000..595a782 --- /dev/null +++ b/gas/testsuite/gas/nds32/to-16bit-v3.s @@ -0,0 +1,16 @@ +foo: +andi $r0, $r0, 1 +andi $r7, $r7, 255 +movi $r0, 16 +movi $r19, 47 +subri $r0, $r0, 0 +subri $r7, $r7, 0 +nor $r0, $r0, $r0 +nor $r7, $r7, $r7 +mul $r0, $r0, $r0 +mul $r7, $r7, $r7 +xor $r0, $r0, $r0 +xor $r7, $r7, $r7 +and $r0, $r0, $r0 +and $r7, $r7, $r7 +or $r0, $r0, $r0 diff --git a/include/ChangeLog b/include/ChangeLog index 6b8321b..07a2d4c 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,8 @@ +2013-12-13 Kuan-Lin Chen <kuanlinchentw@gmail.com> + Wei-Cheng Wang <cole945@gmail.com> + + * dis-asm.h (print_insn_nds32): Add nds32 target. + 2013-12-04 Richard Sandiford <rdsandiford@googlemail.com> * longlong.h: New file. diff --git a/include/dis-asm.h b/include/dis-asm.h index 78e9fc0..4afdc2a 100644 --- a/include/dis-asm.h +++ b/include/dis-asm.h @@ -1,7 +1,6 @@ /* Interface between the opcode library and its callers. - Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2010, - 2011, 2012 Free Software Foundation, Inc. + Copyright 1999-2013 Free Software Foundation, Inc. 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 @@ -277,6 +276,7 @@ extern int print_insn_mn10300 (bfd_vma, disassemble_info *); extern int print_insn_moxie (bfd_vma, disassemble_info *); extern int print_insn_msp430 (bfd_vma, disassemble_info *); extern int print_insn_mt (bfd_vma, disassemble_info *); +extern int print_insn_nds32 (bfd_vma, disassemble_info *); extern int print_insn_ns32k (bfd_vma, disassemble_info *); extern int print_insn_openrisc (bfd_vma, disassemble_info *); extern int print_insn_pdp11 (bfd_vma, disassemble_info *); diff --git a/include/elf/ChangeLog b/include/elf/ChangeLog index 1f7178e..8910d42 100644 --- a/include/elf/ChangeLog +++ b/include/elf/ChangeLog @@ -1,3 +1,8 @@ +2013-12-13 Kuan-Lin Chen <kuanlinchentw@gmail.com> + Wei-Cheng Wang <cole945@gmail.com> + + * nds32.h: New file for Andes NDS32. + 2013-12-07 Mike Frysinger <vapier@gentoo.org> * epiphany.h: Remove +x file mode. diff --git a/include/elf/nds32.h b/include/elf/nds32.h new file mode 100644 index 0000000..5f410cf --- /dev/null +++ b/include/elf/nds32.h @@ -0,0 +1,282 @@ +/* NDS32 ELF support for BFD. + Copyright (C) 2012-2013 Free Software Foundation, Inc. + Contributed by Andes Technology Corporation. + + 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. */ + +#ifndef _ELF_NDS32_H +#define _ELF_NDS32_H + +#include "elf/reloc-macros.h" + +/* Relocations. */ +START_RELOC_NUMBERS (elf_nds32_reloc_type) + RELOC_NUMBER (R_NDS32_NONE, 0) + /* REL relocations. */ + RELOC_NUMBER (R_NDS32_16, 1) + RELOC_NUMBER (R_NDS32_32, 2) + RELOC_NUMBER (R_NDS32_20, 3) + RELOC_NUMBER (R_NDS32_9_PCREL, 4) + RELOC_NUMBER (R_NDS32_15_PCREL, 5) + RELOC_NUMBER (R_NDS32_17_PCREL, 6) + RELOC_NUMBER (R_NDS32_25_PCREL, 7) + RELOC_NUMBER (R_NDS32_HI20, 8) + RELOC_NUMBER (R_NDS32_LO12S3, 9) + RELOC_NUMBER (R_NDS32_LO12S2, 10) + RELOC_NUMBER (R_NDS32_LO12S1, 11) + RELOC_NUMBER (R_NDS32_LO12S0, 12) + RELOC_NUMBER (R_NDS32_SDA15S3, 13) + RELOC_NUMBER (R_NDS32_SDA15S2, 14) + RELOC_NUMBER (R_NDS32_SDA15S1, 15) + RELOC_NUMBER (R_NDS32_SDA15S0, 16) + RELOC_NUMBER (R_NDS32_GNU_VTINHERIT, 17) + RELOC_NUMBER (R_NDS32_GNU_VTENTRY, 18) + + /* RELA relocations. */ + RELOC_NUMBER (R_NDS32_16_RELA, 19) + RELOC_NUMBER (R_NDS32_32_RELA, 20) + RELOC_NUMBER (R_NDS32_20_RELA, 21) + RELOC_NUMBER (R_NDS32_9_PCREL_RELA, 22) + RELOC_NUMBER (R_NDS32_15_PCREL_RELA, 23) + RELOC_NUMBER (R_NDS32_17_PCREL_RELA, 24) + RELOC_NUMBER (R_NDS32_25_PCREL_RELA, 25) + RELOC_NUMBER (R_NDS32_HI20_RELA, 26) + RELOC_NUMBER (R_NDS32_LO12S3_RELA, 27) + RELOC_NUMBER (R_NDS32_LO12S2_RELA, 28) + RELOC_NUMBER (R_NDS32_LO12S1_RELA, 29) + RELOC_NUMBER (R_NDS32_LO12S0_RELA, 30) + RELOC_NUMBER (R_NDS32_SDA15S3_RELA, 31) + RELOC_NUMBER (R_NDS32_SDA15S2_RELA, 32) + RELOC_NUMBER (R_NDS32_SDA15S1_RELA, 33) + RELOC_NUMBER (R_NDS32_SDA15S0_RELA, 34) + RELOC_NUMBER (R_NDS32_RELA_GNU_VTINHERIT, 35) + RELOC_NUMBER (R_NDS32_RELA_GNU_VTENTRY, 36) + + RELOC_NUMBER (R_NDS32_GOT20, 37) + RELOC_NUMBER (R_NDS32_25_PLTREL, 38) + RELOC_NUMBER (R_NDS32_COPY, 39) + RELOC_NUMBER (R_NDS32_GLOB_DAT, 40) + RELOC_NUMBER (R_NDS32_JMP_SLOT, 41) + RELOC_NUMBER (R_NDS32_RELATIVE, 42) + RELOC_NUMBER (R_NDS32_GOTOFF, 43) + RELOC_NUMBER (R_NDS32_GOTPC20, 44) + RELOC_NUMBER (R_NDS32_GOT_HI20, 45) + + RELOC_NUMBER (R_NDS32_GOT_LO12, 46) + RELOC_NUMBER (R_NDS32_GOTPC_HI20, 47) + RELOC_NUMBER (R_NDS32_GOTPC_LO12, 48) + RELOC_NUMBER (R_NDS32_GOTOFF_HI20, 49) + RELOC_NUMBER (R_NDS32_GOTOFF_LO12, 50) + RELOC_NUMBER (R_NDS32_INSN16, 51) + RELOC_NUMBER (R_NDS32_LABEL, 52) + RELOC_NUMBER (R_NDS32_LONGCALL1, 53) + RELOC_NUMBER (R_NDS32_LONGCALL2, 54) + RELOC_NUMBER (R_NDS32_LONGCALL3, 55) + RELOC_NUMBER (R_NDS32_LONGJUMP1, 56) + RELOC_NUMBER (R_NDS32_LONGJUMP2, 57) + RELOC_NUMBER (R_NDS32_LONGJUMP3, 58) + RELOC_NUMBER (R_NDS32_LOADSTORE, 59) + RELOC_NUMBER (R_NDS32_9_FIXED_RELA, 60) + RELOC_NUMBER (R_NDS32_15_FIXED_RELA, 61) + RELOC_NUMBER (R_NDS32_17_FIXED_RELA, 62) + RELOC_NUMBER (R_NDS32_25_FIXED_RELA, 63) + RELOC_NUMBER (R_NDS32_PLTREL_HI20, 64) + RELOC_NUMBER (R_NDS32_PLTREL_LO12, 65) + RELOC_NUMBER (R_NDS32_PLT_GOTREL_HI20, 66) + RELOC_NUMBER (R_NDS32_PLT_GOTREL_LO12, 67) + RELOC_NUMBER (R_NDS32_SDA12S2_DP_RELA, 68) + RELOC_NUMBER (R_NDS32_SDA12S2_SP_RELA, 69) + RELOC_NUMBER (R_NDS32_LO12S2_DP_RELA, 70) + RELOC_NUMBER (R_NDS32_LO12S2_SP_RELA, 71) + RELOC_NUMBER (R_NDS32_LO12S0_ORI_RELA, 72) + RELOC_NUMBER (R_NDS32_SDA16S3_RELA, 73) + RELOC_NUMBER (R_NDS32_SDA17S2_RELA, 74) + RELOC_NUMBER (R_NDS32_SDA18S1_RELA, 75) + RELOC_NUMBER (R_NDS32_SDA19S0_RELA, 76) + RELOC_NUMBER (R_NDS32_DWARF2_OP1_RELA, 77) + RELOC_NUMBER (R_NDS32_DWARF2_OP2_RELA, 78) + RELOC_NUMBER (R_NDS32_DWARF2_LEB_RELA, 79) + RELOC_NUMBER (R_NDS32_UPDATE_TA_RELA, 80) /* This is obsoleted. */ + RELOC_NUMBER (R_NDS32_9_PLTREL, 81) + RELOC_NUMBER (R_NDS32_PLT_GOTREL_LO20, 82) + RELOC_NUMBER (R_NDS32_PLT_GOTREL_LO15, 83) + RELOC_NUMBER (R_NDS32_PLT_GOTREL_LO19, 84) + RELOC_NUMBER (R_NDS32_GOT_LO15, 85) + RELOC_NUMBER (R_NDS32_GOT_LO19, 86) + RELOC_NUMBER (R_NDS32_GOTOFF_LO15, 87) + RELOC_NUMBER (R_NDS32_GOTOFF_LO19, 88) + RELOC_NUMBER (R_NDS32_GOT15S2_RELA, 89) + RELOC_NUMBER (R_NDS32_GOT17S2_RELA, 90) + RELOC_NUMBER (R_NDS32_5_RELA, 91) + RELOC_NUMBER (R_NDS32_10_UPCREL_RELA, 92) + RELOC_NUMBER (R_NDS32_SDA_FP7U2_RELA, 93) + RELOC_NUMBER (R_NDS32_WORD_9_PCREL_RELA, 94) + RELOC_NUMBER (R_NDS32_25_ABS_RELA, 95) + RELOC_NUMBER (R_NDS32_17IFC_PCREL_RELA, 96) + RELOC_NUMBER (R_NDS32_10IFCU_PCREL_RELA, 97) + + RELOC_NUMBER (R_NDS32_RELAX_ENTRY, 192) + RELOC_NUMBER (R_NDS32_GOT_SUFF, 193) + RELOC_NUMBER (R_NDS32_GOTOFF_SUFF, 194) + RELOC_NUMBER (R_NDS32_PLT_GOT_SUFF, 195) + RELOC_NUMBER (R_NDS32_MULCALL_SUFF, 196) + RELOC_NUMBER (R_NDS32_PTR, 197) + RELOC_NUMBER (R_NDS32_PTR_COUNT, 198) + RELOC_NUMBER (R_NDS32_PTR_RESOLVED, 199) + RELOC_NUMBER (R_NDS32_PLTBLOCK, 200) + RELOC_NUMBER (R_NDS32_RELAX_REGION_BEGIN, 201) + RELOC_NUMBER (R_NDS32_RELAX_REGION_END, 202) + RELOC_NUMBER (R_NDS32_MINUEND, 203) + RELOC_NUMBER (R_NDS32_SUBTRAHEND, 204) + RELOC_NUMBER (R_NDS32_DIFF8, 205) + RELOC_NUMBER (R_NDS32_DIFF16, 206) + RELOC_NUMBER (R_NDS32_DIFF32, 207) + RELOC_NUMBER (R_NDS32_DIFF_ULEB128, 208) + RELOC_NUMBER (R_NDS32_DATA, 209) + RELOC_NUMBER (R_NDS32_TRAN, 210) + RELOC_NUMBER (R_NDS32_FPBASE, 211) + +END_RELOC_NUMBERS (R_NDS32_max) + +/* 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. */ + +/* Processor specific flags for the ELF header e_flags field. + + 31 28 27 8 7 4 3 0 + --------------------------------------------- + | ARCH | CONFUGURAION FIELD | ABI | ELF_VER | + --------------------------------------------- */ + +/* Architechure definition. */ + +/* 4-bit (b31-b28) nds32 architecture field. + We can have up to 15 architectures; 0000 is for unknown. */ +#define EF_NDS_ARCH 0xF0000000 +#define EF_NDS_ARCH_SHIFT 28 +/* There could be more architectures. For now, only n1 and n1h. */ +#define E_NDS_ARCH_STAR_RESERVED 0x00000000 +#define E_NDS_ARCH_STAR_V1_0 0x10000000 +#define E_NDS_ARCH_STAR_V2_0 0x20000000 +#define E_NDS_ARCH_STAR_V3_0 0x30000000 +#define E_NDS_ARCH_STAR_V3_M 0x40000000 +#define E_NDS_ARCH_STAR_V0_9 0x90000000 /* Obsoleted. */ +/* n1 code. */ +#define E_N1_ARCH E_NDS_ARCH_STAR_V0_9 +/* n1h code. */ +#define E_N1H_ARCH E_NDS_ARCH_STAR_V1_0 + + +/* Configuration field definitioans. */ +#define EF_NDS_INST 0x0FFFFF00 + +/* E_NDS_ARCH_STAR_V1_0 configuration fields. + + E_NDS_ARCH_STAR_V2_0 configuration fields. + These are discarded in v2. + * E_NDS32_HAS_MFUSR_PC_INST 0x00000100 + * E_NDS32_HAS_DIV_INST 0x00002000 + * E_NDS32_HAS_NO_MAC_INST 0x00100000 + These are added in v2. + * E_NDS32_HAS_DIV_DX_INST 0x00002000 + * E_NDS32_HAS_MAC_DX_INST 0x00100000 */ + +/* MFUSR rt, PC and correct ISYNC, MSYNC instructions. + Old N1213HC has no such instructions. */ +#define E_NDS32_HAS_MFUSR_PC_INST 0x00000100 /* Reclaimed. */ +#define E_NDS32_HAS_EX9_INST 0x00000100 /* v3, ELF 1.4. */ +/* C/C++ performance extension instructions. */ +#define E_NDS32_HAS_EXT_INST 0x00000200 +/* Performance extension set II instructions. */ +#define E_NDS32_HAS_EXT2_INST 0x00000400 +/* Single precision Floating point processor instructions. */ +#define E_NDS32_HAS_FPU_INST 0x00000800 +/* Audio instructions with 32-bit audio dx.lo register. */ +#define E_NDS32_HAS_AUDIO_INST 0x00001000 +/* DIV instructions. */ +#define E_NDS32_HAS_DIV_INST 0x00002000 /* Reclaimed. */ +/* DIV instructions using d0/d1. */ +#define E_NDS32_HAS_DIV_DX_INST 0x00002000 /* v2. */ +/* 16-bit instructions. */ +#define E_NDS32_HAS_16BIT_INST 0x00004000 /* Reclaimed. */ +#define E_NDS32_HAS_IFC_INST 0x00004000 /* v3, ELF 1.4. */ +/* String operation instructions. */ +#define E_NDS32_HAS_STRING_INST 0x00008000 +/* Reduced register file. */ +#define E_NDS32_HAS_REDUCED_REGS 0x00010000 +/* Video instructions. */ +#define E_NDS32_HAS_VIDEO_INST 0x00020000 /* Reclaimed. */ +#define E_NDS32_HAS_SATURATION_INST 0x00020000 /* v3, ELF 1.4. */ +/* Encription instructions. */ +#define E_NDS32_HAS_ENCRIPT_INST 0x00040000 +/* Doulbe Precision Floating point processor instructions. */ +#define E_NDS32_HAS_FPU_DP_INST 0x00080000 +/* No MAC instruction used. */ +#define E_NDS32_HAS_NO_MAC_INST 0x00100000 /* Reclaimed when V2/V3. */ +/* MAC instruction using d0/d1. */ +#define E_NDS32_HAS_MAC_DX_INST 0x00100000 /* v2. */ +/* L2 cache instruction. */ +#define E_NDS32_HAS_L2C_INST 0x00200000 +/* FPU registers configuration when FPU SP/DP presents; 0x00c00000. */ +#define E_NDS32_FPU_REG_CONF_SHIFT 22 +#define E_NDS32_FPU_REG_CONF (0x3 << E_NDS32_FPU_REG_CONF_SHIFT) +#define E_NDS32_FPU_REG_8SP_4DP 0x0 +#define E_NDS32_FPU_REG_16SP_8DP 0x1 +#define E_NDS32_FPU_REG_32SP_16DP 0x2 +#define E_NDS32_FPU_REG_32SP_32DP 0x3 +/* FPU MAC instruction used. */ +#define E_NDS32_HAS_FPU_MAC_INST 0x01000000 +/* <<<Empty Check>>>. */ +#define E_NDS32_NULL 0x02000000 +/* PIC enabled. */ +#define E_NDS32_HAS_PIC 0x04000000 +/* Use custom section. */ +#define E_NDS32_HAS_CUSTOM_SEC 0x08000000 + +/* 4-bit for ABI signature, allow up to 16 ABIs + 0: for OLD ABI V0, phase out + 1: for V1 , starting with V0 toolchain + 2: for V2 + 3: for V2FP (fs0, fs1 as function parameter) + 4: for AABI */ +/* Only old N1213HC use V0. + New ABI is used due to return register is changed to r0 from r5. */ +#define EF_NDS_ABI 0x000000F0 +#define EF_NDS_ABI_SHIFT 4 +#define E_NDS_ABI_V0 0x00000000 +#define E_NDS_ABI_V1 0x00000010 +#define E_NDS_ABI_V2 0x00000020 +#define E_NDS_ABI_V2FP 0x00000030 +#define E_NDS_ABI_AABI 0x00000040 +#define E_NDS_ABI_V2FP_PLUS 0x00000050 + +/* This flag signifies the version of Andes ELF. + Some more information may exist somewhere which is TBD. */ +#define EF_NDS32_ELF_VERSION 0x0000000F +#define EF_NDS32_ELF_VERSION_SHIFT 0 + +/* Andes ELF Version 1.3 and before. */ +#define E_NDS32_ELF_VER_1_2 0x0 +/* Andes ELF Version 1.31. */ +#define E_NDS32_ELF_VER_1_3 0x1 +/* Andes ELF Version 1.4. Change the way we fix .debug_* and .gcc_except_table. + Change three bit for EX9, IFC and SAT. */ +#define E_NDS32_ELF_VER_1_4 0x2 + +#endif diff --git a/include/opcode/ChangeLog b/include/opcode/ChangeLog index 838f194..26621b5 100644 --- a/include/opcode/ChangeLog +++ b/include/opcode/ChangeLog @@ -1,3 +1,8 @@ +2013-12-13 Kuan-Lin Chen <kuanlinchentw@gmail.com> + Wei-Cheng Wang <cole945@gmail.com> + + * nds32.h: New file for Andes NDS32. + 2013-12-07 Mike Frysinger <vapier@gentoo.org> * bfin.h: Remove +x file mode. diff --git a/include/opcode/nds32.h b/include/opcode/nds32.h new file mode 100644 index 0000000..4479569 --- /dev/null +++ b/include/opcode/nds32.h @@ -0,0 +1,829 @@ +/* nds32.h -- Header file for nds32 opcode table + Copyright (C) 2012-2013 Free Software Foundation, Inc. + Contributed by Andes Technology Corporation. + + 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, 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. */ + +#ifndef OPCODE_NDS32_H +#define OPCODE_NDS32_H + +/* Registers. */ +#define REG_R5 5 +#define REG_R8 8 +#define REG_R10 10 +#define REG_R12 12 +#define REG_R15 15 +#define REG_R16 16 +#define REG_R20 20 +#define REG_TA 15 +#define REG_FP 28 +#define REG_GP 29 +#define REG_LP 30 +#define REG_SP 31 + +/* Macros for extracting fields or making an instruction. */ +static const int nds32_r45map[] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 16, 17, 18, 19 +}; + +static const int nds32_r54map[] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, -1, -1, -1, -1, + 12, 13, 14, 15, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +#define __BIT(n) (1 << (n)) +#define __MASK(n) (__BIT (n) - 1) +#define __MF(v, off, bs) (((v) & __MASK (bs)) << (off)) +#define __GF(v, off, bs) (((v) >> off) & __MASK (bs)) +#define __SEXT(v, bs) ((((v) & ((1 << (bs)) - 1)) ^ (1 << ((bs) - 1))) - (1 << ((bs) - 1))) + +/* Make nds32 instructions. */ + +#define N32_TYPE4(op6, rt5, ra5, rb5, rd5, sub5) \ + (__MF (N32_OP6_##op6, 25, 6) | __MF (rt5, 20, 5) \ + | __MF (ra5, 15, 5) | __MF (rb5, 10, 5) \ + | __MF (rd5, 5, 5) | __MF (sub5, 0, 5)) +#define N32_TYPE3(op6, rt5, ra5, rb5, sub10) \ + (N32_TYPE4 (op6, rt5, ra5, rb5, 0, 0) \ + | __MF (sub10, 0, 10)) +#define N32_TYPE2(op6, rt5, ra5, imm15) \ + (N32_TYPE3 (op6, rt5, ra5, 0, 0) | __MF (imm15, 0, 15)) +#define N32_TYPE1(op6, rt5, imm20) \ + (N32_TYPE2 (op6, rt5, 0, 0) | __MF (imm20, 0, 20)) +#define N32_TYPE0(op6, imm25) \ + (N32_TYPE1 (op6, 0, 0) | __MF (imm25, 0, 25)) +#define N32_ALU1(sub, rt, ra, rb) \ + N32_TYPE4 (ALU1, rt, ra, rb, 0, N32_ALU1_##sub) +#define N32_ALU1_SH(sub, rt, ra, rb, rd) \ + N32_TYPE4 (ALU1, rt, ra, rb, rd, N32_ALU1_##sub) +#define N32_ALU2(sub, rt, ra, rb) \ + N32_TYPE3 (ALU2, rt, ra, rb, N32_ALU2_##sub) +#define N32_BR1(sub, rt, ra, imm14s) \ + N32_TYPE2 (BR1, rt, ra, (N32_BR1_##sub << 14) | (imm14s & __MASK (14))) +#define N32_BR2(sub, rt, imm16s) \ + N32_TYPE1 (BR2, rt, (N32_BR2_##sub << 16) | (imm16s & __MASK (16))) +#define N32_BR3(sub, rt, imm11s, imm8s) \ + N32_TYPE1 (BR3, rt, (N32_BR3_##sub << 19) \ + | ((imm11s & __MASK (11)) << 8) \ + | (imm8s & __MASK (8))) +#define N32_JI(sub, imm24s) \ + N32_TYPE0 (JI, (N32_JI_##sub << 24) | (imm24s & __MASK (24))) +#define N32_JREG(sub, rt, rb, dtit, hint) \ + N32_TYPE4(JREG, rt, 0, rb, (dtit << 3) | (hint & 7), N32_JREG_##sub) +#define N32_MEM(sub, rt, ra, rb, sv) \ + N32_TYPE3 (MEM, rt, ra, rb, (sv << 8) | N32_MEM_##sub) + +#define N16_TYPE55(op5, rt5, ra5) \ + (0x8000 | __MF (N16_T55_##op5, 10, 5) | __MF (rt5, 5, 5) \ + | __MF (ra5, 0, 5)) +#define N16_TYPE45(op6, rt4, ra5) \ + (0x8000 | __MF (N16_T45_##op6, 9, 6) | __MF (rt4, 5, 4) \ + | __MF (ra5, 0, 5)) +#define N16_TYPE333(op6, rt3, ra3, rb3) \ + (0x8000 | __MF (N16_T333_##op6, 9, 6) | __MF (rt3, 6, 3) \ + | __MF (ra3, 3, 3) | __MF (rb3, 0, 3)) +#define N16_TYPE36(op6, rt3, imm6) \ + (0x8000 | __MF (N16_T36_##op6, 9, 6) | __MF (rt3, 6, 3) \ + | __MF (imm6, 0, 6)) +#define N16_TYPE38(op4, rt3, imm8) \ + (0x8000 | __MF (N16_T38_##op4, 11, 4) | __MF (rt3, 8, 3) \ + | __MF (imm8, 0, 8)) +#define N16_TYPE37(op4, rt3, ls, imm7) \ + (0x8000 | __MF (N16_T37_##op4, 11, 4) | __MF (rt3, 8, 3) \ + | __MF (imm7, 0, 7) | __MF (ls, 7, 1)) +#define N16_TYPE5(op10, imm5) \ + (0x8000 | __MF (N16_T5_##op10, 5, 10) | __MF (imm5, 0, 5)) +#define N16_TYPE8(op7, imm8) \ + (0x8000 | __MF (N16_T8_##op7, 8, 7) | __MF (imm8, 0, 8)) +#define N16_TYPE9(op6, imm9) \ + (0x8000 | __MF (N16_T9_##op6, 9, 6) | __MF (imm9, 0, 9)) +#define N16_TYPE10(op5, imm10) \ + (0x8000 | __MF (N16_T10_##op5, 10, 5) | __MF (imm10, 0, 10)) +#define N16_TYPE25(op8, re, imm5) \ + (0x8000 | __MF (N16_T25_##op8, 7, 8) | __MF (re, 5, 2) \ + | __MF (imm5, 0, 5)) + +#define N16_MISC33(sub, rt, ra) \ + N16_TYPE333 (MISC33, rt, ra, N16_MISC33_##sub) +#define N16_BFMI333(sub, rt, ra) \ + N16_TYPE333 (BFMI333, rt, ra, N16_BFMI333_##sub) + +/* Get instruction fields. + + Macros used for handling 32-bit and 16-bit instructions are + prefixed with N32_ and N16_ respectively. */ + +#define N32_OP6(insn) (((insn) >> 25) & 0x3f) +#define N32_RT5(insn) (((insn) >> 20) & 0x1f) +#define N32_RT53(insn) (N32_RT5 (insn) & 0x7) +#define N32_RT54(insn) nds32_r54map[N32_RT5 (insn)] +#define N32_RA5(insn) (((insn) >> 15) & 0x1f) +#define N32_RA53(insn) (N32_RA5 (insn) & 0x7) +#define N32_RA54(insn) nds32_r54map[N32_RA5 (insn)] +#define N32_RB5(insn) (((insn) >> 10) & 0x1f) +#define N32_UB5(insn) (((insn) >> 10) & 0x1f) +#define N32_RB53(insn) (N32_RB5 (insn) & 0x7) +#define N32_RB54(insn) nds32_r54map[N32_RB5 (insn)] +#define N32_RD5(insn) (((insn) >> 5) & 0x1f) +#define N32_SH5(insn) (((insn) >> 5) & 0x1f) +#define N32_SUB5(insn) (((insn) >> 0) & 0x1f) +#define N32_SWID(insn) (((insn) >> 5) & 0x3ff) +#define N32_IMMU(insn, bs) ((insn) & __MASK (bs)) +#define N32_IMMS(insn, bs) ((signed) __SEXT (((insn) & __MASK (bs)), bs)) +#define N32_IMM5U(insn) N32_IMMU (insn, 5) +#define N32_IMM12S(insn) N32_IMMS (insn, 12) +#define N32_IMM14S(insn) N32_IMMS (insn, 14) +#define N32_IMM15U(insn) N32_IMMU (insn, 15) +#define N32_IMM15S(insn) N32_IMMS (insn, 15) +#define N32_IMM16S(insn) N32_IMMS (insn, 16) +#define N32_IMM17S(insn) N32_IMMS (insn, 17) +#define N32_IMM20S(insn) N32_IMMS (insn, 20) +#define N32_IMM20U(insn) N32_IMMU (insn, 20) +#define N32_IMM24S(insn) N32_IMMS (insn, 24) + +#define N16_RT5(insn) (((insn) >> 5) & 0x1f) +#define N16_RT4(insn) nds32_r45map[(((insn) >> 5) & 0xf)] +#define N16_RT3(insn) (((insn) >> 6) & 0x7) +#define N16_RT38(insn) (((insn) >> 8) & 0x7) +#define N16_RT8(insn) (((insn) >> 8) & 0x7) +#define N16_RA5(insn) ((insn) & 0x1f) +#define N16_RA3(insn) (((insn) >> 3) & 0x7) +#define N16_RB3(insn) ((insn) & 0x7) +#define N16_IMM3U(insn) N32_IMMU (insn, 3) +#define N16_IMM5U(insn) N32_IMMU (insn, 5) +#define N16_IMM5S(insn) N32_IMMS (insn, 5) +#define N16_IMM6U(insn) N32_IMMU (insn, 6) +#define N16_IMM7U(insn) N32_IMMU (insn, 7) +#define N16_IMM8S(insn) N32_IMMS (insn, 8) +#define N16_IMM9U(insn) N32_IMMU (insn, 9) +#define N16_IMM10S(insn) N32_IMMS (insn, 10) + +#define IS_WITHIN_U(v, n) (((v) >> n) == 0) +#define IS_WITHIN_S(v, n) IS_WITHIN_U ((v) + (1 << ((n) - 1)), n) + +/* Get fields for specific instruction. */ +#define N32_JREG_T(insn) (((insn) >> 8) & 0x3) +#define N32_JREG_HINT(insn) (((insn) >> 5) & 0x7) +#define N32_BR2_SUB(insn) (((insn) >> 16) & 0xf) +#define N32_COP_SUB(insn) ((insn) & 0xf) +#define N32_COP_CP(insn) (((insn) >> 4) & 0x3) + +/* Check fields. */ +#define N32_IS_RT3(insn) (N32_RT5 (insn) < 8) +#define N32_IS_RA3(insn) (N32_RA5 (insn) < 8) +#define N32_IS_RB3(insn) (N32_RB5 (insn) < 8) +#define N32_IS_RT4(insn) (nds32_r54map[N32_RT5 (insn)] != -1) +#define N32_IS_RA4(insn) (nds32_r54map[N32_RA5 (insn)] != -1) +#define N32_IS_RB4(insn) (nds32_r54map[N32_RB5 (insn)] != -1) + + +/* These are opcodes for Nxx_TYPE macros. + They are prefixed by corresponding TYPE to avoid misusing. */ + +enum n32_opcodes +{ + /* Main opcodes (OP6). */ + + N32_OP6_LBI = 0x0, + N32_OP6_LHI, + N32_OP6_LWI, + N32_OP6_LDI, + N32_OP6_LBI_BI, + N32_OP6_LHI_BI, + N32_OP6_LWI_BI, + N32_OP6_LDI_BI, + + N32_OP6_SBI = 0x8, + N32_OP6_SHI, + N32_OP6_SWI, + N32_OP6_SDI, + N32_OP6_SBI_BI, + N32_OP6_SHI_BI, + N32_OP6_SWI_BI, + N32_OP6_SDI_BI, + + N32_OP6_LBSI = 0x10, + N32_OP6_LHSI, + N32_OP6_LWSI, + N32_OP6_DPREFI, + N32_OP6_LBSI_BI, + N32_OP6_LHSI_BI, + N32_OP6_LWSI_BI, + N32_OP6_LBGP, + + N32_OP6_LWC = 0x18, + N32_OP6_SWC, + N32_OP6_LDC, + N32_OP6_SDC, + N32_OP6_MEM, + N32_OP6_LSMW, + N32_OP6_HWGP, + N32_OP6_SBGP, + + N32_OP6_ALU1 = 0x20, + N32_OP6_ALU2, + N32_OP6_MOVI, + N32_OP6_SETHI, + N32_OP6_JI, + N32_OP6_JREG, + N32_OP6_BR1, + N32_OP6_BR2, + + N32_OP6_ADDI = 0x28, + N32_OP6_SUBRI, + N32_OP6_ANDI, + N32_OP6_XORI, + N32_OP6_ORI, + N32_OP6_BR3, + N32_OP6_SLTI, + N32_OP6_SLTSI, + + N32_OP6_AEXT = 0x30, + N32_OP6_CEXT, + N32_OP6_MISC, + N32_OP6_BITCI, + N32_OP6_0x34, + N32_OP6_COP, + N32_OP6_0x36, + N32_OP6_0x37, + + N32_OP6_SIMD = 0x38, + + /* Sub-opcodes of specific opcode. */ + + /* bit-24 */ + N32_BR1_BEQ = 0, + N32_BR1_BNE = 1, + + /* bit[16:19] */ + N32_BR2_IFCALL = 0, + N32_BR2_BEQZ = 2, + N32_BR2_BNEZ = 3, + N32_BR2_BGEZ = 4, + N32_BR2_BLTZ = 5, + N32_BR2_BGTZ = 6, + N32_BR2_BLEZ = 7, + N32_BR2_BGEZAL = 0xc, + N32_BR2_BLTZAL = 0xd, + + /* bit-19 */ + N32_BR3_BEQC = 0, + N32_BR3_BNEC = 1, + + /* bit-24 */ + N32_JI_J = 0, + N32_JI_JAL = 1, + + /* bit[0:4] */ + N32_JREG_JR = 0, + N32_JREG_JRAL = 1, + N32_JREG_JRNEZ = 2, + N32_JREG_JRALNEZ = 3, + + /* bit[0:4] */ + N32_ALU1_ADD_SLLI = 0x0, + N32_ALU1_SUB_SLLI, + N32_ALU1_AND_SLLI, + N32_ALU1_XOR_SLLI, + N32_ALU1_OR_SLLI, + N32_ALU1_ADD = 0x0, + N32_ALU1_SUB, + N32_ALU1_AND, + N32_ALU1_XOR, + N32_ALU1_OR, + N32_ALU1_NOR, + N32_ALU1_SLT, + N32_ALU1_SLTS, + N32_ALU1_SLLI = 0x8, + N32_ALU1_SRLI, + N32_ALU1_SRAI, + N32_ALU1_ROTRI, + N32_ALU1_SLL, + N32_ALU1_SRL, + N32_ALU1_SRA, + N32_ALU1_ROTR, + N32_ALU1_SEB = 0x10, + N32_ALU1_SEH, + N32_ALU1_BITC, + N32_ALU1_ZEH, + N32_ALU1_WSBH, + N32_ALU1_OR_SRLI, + N32_ALU1_DIVSR, + N32_ALU1_DIVR, + N32_ALU1_SVA = 0x18, + N32_ALU1_SVS, + N32_ALU1_CMOVZ, + N32_ALU1_CMOVN, + N32_ALU1_ADD_SRLI, + N32_ALU1_SUB_SRLI, + N32_ALU1_AND_SRLI, + N32_ALU1_XOR_SRLI, + + /* bit[0:5], where bit[6:9] == 0 */ + N32_ALU2_MAX = 0, + N32_ALU2_MIN, + N32_ALU2_AVE, + N32_ALU2_ABS, + N32_ALU2_CLIPS, + N32_ALU2_CLIP, + N32_ALU2_CLO, + N32_ALU2_CLZ, + N32_ALU2_BSET = 0x8, + N32_ALU2_BCLR, + N32_ALU2_BTGL, + N32_ALU2_BTST, + N32_ALU2_BSE, + N32_ALU2_BSP, + N32_ALU2_FFB, + N32_ALU2_FFMISM, + N32_ALU2_ADD_SC = 0x10, + N32_ALU2_SUB_SC, + N32_ALU2_ADD_WC, + N32_ALU2_SUB_WC, + N32_ALU2_0x14, + N32_ALU2_0x15, + N32_ALU2_0x16, + N32_ALU2_FFZMISM, + N32_ALU2_QADD = 0x18, + N32_ALU2_QSUB, + N32_ALU2_MFUSR = 0x20, + N32_ALU2_MTUSR, + N32_ALU2_0x22, + N32_ALU2_0x23, + N32_ALU2_MUL, + N32_ALU2_0x25, + N32_ALU2_0x26, + N32_ALU2_MULTS64 = 0x28, + N32_ALU2_MULT64, + N32_ALU2_MADDS64, + N32_ALU2_MADD64, + N32_ALU2_MSUBS64, + N32_ALU2_MSUB64, + N32_ALU2_DIVS, + N32_ALU2_DIV, + N32_ALU2_0x30 = 0x30, + N32_ALU2_MULT32, + N32_ALU2_0x32, + N32_ALU2_MADD32, + N32_ALU2_0x34, + N32_ALU2_MSUB32, + + /* bit[0:5], where bit[6:9] != 0 */ + N32_ALU2_FFBI = 0xe, + N32_ALU2_FLMISM = 0xf, + N32_ALU2_MULSR64 = 0x28, + N32_ALU2_MULR64 = 0x29, + N32_ALU2_MADDR32 = 0x33, + N32_ALU2_MSUBR32 = 0x35, + + /* bit[0:5] */ + N32_MEM_LB = 0, + N32_MEM_LH, + N32_MEM_LW, + N32_MEM_LD, + N32_MEM_LB_BI, + N32_MEM_LH_BI, + N32_MEM_LW_BI, + N32_MEM_LD_BI, + N32_MEM_SB, + N32_MEM_SH, + N32_MEM_SW, + N32_MEM_SD, + N32_MEM_SB_BI, + N32_MEM_SH_BI, + N32_MEM_SW_BI, + N32_MEM_SD_BI, + N32_MEM_LBS, + N32_MEM_LHS, + N32_MEM_LWS, /* Not used. */ + N32_MEM_DPREF, + N32_MEM_LBS_BI, + N32_MEM_LHS_BI, + N32_MEM_LWS_BI, /* Not used. */ + N32_MEM_0x17, /* Not used. */ + N32_MEM_LLW, + N32_MEM_SCW, + N32_MEM_LBUP = 0x20, + N32_MEM_LWUP = 0x22, + N32_MEM_SBUP = 0x28, + N32_MEM_SWUP = 0x2a, + + /* bit[0:1] */ + N32_LSMW_LSMW = 0, + N32_LSMW_LSMWA, + N32_LSMW_LSMWZB, + + /* bit[2:4] */ + N32_LSMW_BI = 0, + N32_LSMW_BIM, + N32_LSMW_BD, + N32_LSMW_BDM, + N32_LSMW_AI, + N32_LSMW_AIM, + N32_LSMW_AD, + N32_LSMW_ADM, + + /* bit[0:4] */ + N32_MISC_STANDBY = 0, + N32_MISC_CCTL, + N32_MISC_MFSR, + N32_MISC_MTSR, + N32_MISC_IRET, + N32_MISC_TRAP, + N32_MISC_TEQZ, + N32_MISC_TNEZ, + N32_MISC_DSB = 0x8, + N32_MISC_ISB, + N32_MISC_BREAK, + N32_MISC_SYSCALL, + N32_MISC_MSYNC, + N32_MISC_ISYNC, + N32_MISC_TLBOP, + N32_MISC_0xf, + + /* bit[0;4] */ + N32_SIMD_PBSAD = 0, + N32_SIMD_PBSADA = 1, + + /* bit[0:3] */ + N32_COP_CPE1 = 0, + N32_COP_MFCP, + N32_COP_CPLW, + N32_COP_CPLD, + N32_COP_CPE2, + N32_COP_CPE3 = 8, + N32_COP_MTCP, + N32_COP_CPSW, + N32_COP_CPSD, + N32_COP_CPE4, + + /* cop/0 b[3:0] */ + N32_FPU_FS1 = 0, + N32_FPU_MFCP, + N32_FPU_FLS, + N32_FPU_FLD, + N32_FPU_FS2, + N32_FPU_FD1 = 8, + N32_FPU_MTCP, + N32_FPU_FSS, + N32_FPU_FSD, + N32_FPU_FD2, + + /* FS1 b[9:6] */ + N32_FPU_FS1_FADDS = 0, + N32_FPU_FS1_FSUBS, + N32_FPU_FS1_FCPYNSS, + N32_FPU_FS1_FCPYSS, + N32_FPU_FS1_FMADDS, + N32_FPU_FS1_FMSUBS, + N32_FPU_FS1_FCMOVNS, + N32_FPU_FS1_FCMOVZS, + N32_FPU_FS1_FNMADDS, + N32_FPU_FS1_FNMSUBS, + N32_FPU_FS1_10, + N32_FPU_FS1_11, + N32_FPU_FS1_FMULS = 12, + N32_FPU_FS1_FDIVS, + N32_FPU_FS1_14, + N32_FPU_FS1_F2OP = 15, + + /* FS1/F2OP b[14:10] */ + N32_FPU_FS1_F2OP_FS2D = 0x00, + N32_FPU_FS1_F2OP_FSQRTS = 0x01, + N32_FPU_FS1_F2OP_FABSS = 0x05, + N32_FPU_FS1_F2OP_FUI2S = 0x08, + N32_FPU_FS1_F2OP_FSI2S = 0x0c, + N32_FPU_FS1_F2OP_FS2UI = 0x10, + N32_FPU_FS1_F2OP_FS2UI_Z = 0x14, + N32_FPU_FS1_F2OP_FS2SI = 0x18, + N32_FPU_FS1_F2OP_FS2SI_Z = 0x1c, + + /* FS2 b[9:6] */ + N32_FPU_FS2_FCMPEQS = 0x0, + N32_FPU_FS2_FCMPLTS = 0x2, + N32_FPU_FS2_FCMPLES = 0x4, + N32_FPU_FS2_FCMPUNS = 0x6, + N32_FPU_FS2_FCMPEQS_E = 0x1, + N32_FPU_FS2_FCMPLTS_E = 0x3, + N32_FPU_FS2_FCMPLES_E = 0x5, + N32_FPU_FS2_FCMPUNS_E = 0x7, + + /* FD1 b[9:6] */ + N32_FPU_FD1_FADDD = 0, + N32_FPU_FD1_FSUBD, + N32_FPU_FD1_FCPYNSD, + N32_FPU_FD1_FCPYSD, + N32_FPU_FD1_FMADDD, + N32_FPU_FD1_FMSUBD, + N32_FPU_FD1_FCMOVND, + N32_FPU_FD1_FCMOVZD, + N32_FPU_FD1_FNMADDD, + N32_FPU_FD1_FNMSUBD, + N32_FPU_FD1_10, + N32_FPU_FD1_11, + N32_FPU_FD1_FMULD = 12, + N32_FPU_FD1_FDIVD, + N32_FPU_FD1_14, + N32_FPU_FD1_F2OP = 15, + + /* FD1/F2OP b[14:10] */ + N32_FPU_FD1_F2OP_FD2S = 0x00, + N32_FPU_FD1_F2OP_FSQRTD = 0x01, + N32_FPU_FD1_F2OP_FABSD = 0x05, + N32_FPU_FD1_F2OP_FUI2D = 0x08, + N32_FPU_FD1_F2OP_FSI2D = 0x0c, + N32_FPU_FD1_F2OP_FD2UI = 0x10, + N32_FPU_FD1_F2OP_FD2UI_Z = 0x14, + N32_FPU_FD1_F2OP_FD2SI = 0x18, + N32_FPU_FD1_F2OP_FD2SI_Z = 0x1c, + + /* FD2 b[9:6] */ + N32_FPU_FD2_FCMPEQD = 0x0, + N32_FPU_FD2_FCMPLTD = 0x2, + N32_FPU_FD2_FCMPLED = 0x4, + N32_FPU_FD2_FCMPUND = 0x6, + N32_FPU_FD2_FCMPEQD_E = 0x1, + N32_FPU_FD2_FCMPLTD_E = 0x3, + N32_FPU_FD2_FCMPLED_E = 0x5, + N32_FPU_FD2_FCMPUND_E = 0x7, + + /* MFCP b[9:6] */ + N32_FPU_MFCP_FMFSR = 0x0, + N32_FPU_MFCP_FMFDR = 0x1, + N32_FPU_MFCP_XR = 0xc, + + /* MFCP/XR b[14:10] */ + N32_FPU_MFCP_XR_FMFCFG = 0x0, + N32_FPU_MFCP_XR_FMFCSR = 0x1, + + /* MTCP b[9:6] */ + N32_FPU_MTCP_FMTSR = 0x0, + N32_FPU_MTCP_FMTDR = 0x1, + N32_FPU_MTCP_XR = 0xc, + + /* MTCP/XR b[14:10] */ + N32_FPU_MTCP_XR_FMTCSR = 0x1 +}; + +enum n16_opcodes +{ + N16_T55_MOV55 = 0x0, + N16_T55_MOVI55 = 0x1, + + N16_T45_0 = 0, + N16_T45_ADD45 = 0x4, + N16_T45_SUB45 = 0x5, + N16_T45_ADDI45 = 0x6, + N16_T45_SUBI45 = 0x7, + N16_T45_SRAI45 = 0x8, + N16_T45_SRLI45 = 0x9, + N16_T45_LWI45_FE = 0x19, + N16_T45_LWI450 = 0x1a, + N16_T45_SWI450 = 0x1b, + N16_T45_SLTS45 = 0x30, + N16_T45_SLT45 = 0x31, + N16_T45_SLTSI45 = 0x32, + N16_T45_SLTI45 = 0x33, + N16_T45_MOVPI45 = 0x3d, + + N15_T44_MOVD44 = 0x7d, + + N16_T333_0 = 0, + N16_T333_SLLI333 = 0xa, + N16_T333_BFMI333 = 0xb, + N16_T333_ADD333 = 0xc, + N16_T333_SUB333 = 0xd, + N16_T333_ADDI333 = 0xe, + N16_T333_SUBI333 = 0xf, + N16_T333_LWI333 = 0x10, + N16_T333_LWI333_BI = 0x11, + N16_T333_LHI333 = 0x12, + N16_T333_LBI333 = 0x13, + N16_T333_SWI333 = 0x14, + N16_T333_SWI333_BI = 0x15, + N16_T333_SHI333 = 0x16, + N16_T333_SBI333 = 0x17, + N16_T333_MISC33 = 0x3f, + + N16_T36_ADDRI36_SP = 0x18, + + N16_T37_XWI37 = 0x7, + N16_T37_XWI37SP = 0xe, + + N16_T38_BEQZ38 = 0x8, + N16_T38_BNEZ38 = 0x9, + N16_T38_BEQS38 = 0xa, + N16_T38_BNES38 = 0xb, + + N16_T5_JR5 = 0x2e8, + N16_T5_JRAL5 = 0x2e9, + N16_T5_EX9IT = 0x2ea, + /* 0x2eb reserved. */ + N16_T5_RET5 = 0x2ec, + N16_T5_ADD5PC = 0x2ed, + /* 0x2e[ef] reserved. */ + N16_T5_BREAK16 = 0x350, + + N16_T8_J8 = 0x55, + N16_T8_BEQZS8 = 0x68, + N16_T8_BNEZS8 = 0x69, + + /* N16_T9_BREAK16 = 0x35 + Since v3, SWID of BREAK16 above 32 are used for encoding EX9.IT. */ + N16_T9_EX9IT = 0x35, + N16_T9_IFCALL9 = 0x3c, + + N16_T10_ADDI10S = 0x1b, + + N16_T25_PUSH25 = 0xf8, + N16_T25_POP25 = 0xf9, + + /* Sub-opcodes. */ + N16_MISC33_0 = 0, + N16_MISC33_1 = 1, + N16_MISC33_NEG33 = 2, + N16_MISC33_NOT33 = 3, + N16_MISC33_MUL33 = 4, + N16_MISC33_XOR33 = 5, + N16_MISC33_AND33 = 6, + N16_MISC33_OR33 = 7, + + N16_BFMI333_ZEB33 = 0, + N16_BFMI333_ZEH33 = 1, + N16_BFMI333_SEB33 = 2, + N16_BFMI333_SEH33 = 3, + N16_BFMI333_XLSB33 = 4, + N16_BFMI333_X11B33 = 5, + N16_BFMI333_BMSKI33 = 6, + N16_BFMI333_FEXTI33 = 7 +}; + +/* These macros a deprecated. DO NOT use them anymore. + And please help rewrite code used them. */ + +/* 32-bit instructions without operands. */ +#define INSN_SETHI 0x46000000 +#define INSN_ORI 0x58000000 +#define INSN_JR 0x4a000000 +#define INSN_RET 0x4a000020 +#define INSN_JAL 0x49000000 +#define INSN_J 0x48000000 +#define INSN_JRAL 0x4a000001 +#define INSN_BGEZAL 0x4e0c0000 +#define INSN_BLTZAL 0x4e0d0000 +#define INSN_BEQ 0x4c000000 +#define INSN_BNE 0x4c004000 +#define INSN_BEQZ 0x4e020000 +#define INSN_BNEZ 0x4e030000 +#define INSN_BGEZ 0x4e040000 +#define INSN_BLTZ 0x4e050000 +#define INSN_BGTZ 0x4e060000 +#define INSN_BLEZ 0x4e070000 +#define INSN_MOVI 0x44000000 +#define INSN_ADDI 0x50000000 +#define INSN_ANDI 0x54000000 +#define INSN_LDI 0x06000000 +#define INSN_SDI 0x16000000 +#define INSN_LWI 0x04000000 +#define INSN_LWSI 0x24000000 +#define INSN_LWIP 0x0c000000 +#define INSN_LHI 0x02000000 +#define INSN_LHSI 0x22000000 +#define INSN_LBI 0x00000000 +#define INSN_LBSI 0x20000000 +#define INSN_SWI 0x14000000 +#define INSN_SWIP 0x1c000000 +#define INSN_SHI 0x12000000 +#define INSN_SBI 0x10000000 +#define INSN_SLTI 0x5c000000 +#define INSN_SLTSI 0x5e000000 +#define INSN_ADD 0x40000000 +#define INSN_SUB 0x40000001 +#define INSN_SLT 0x40000006 +#define INSN_SLTS 0x40000007 +#define INSN_SLLI 0x40000008 +#define INSN_SRLI 0x40000009 +#define INSN_SRAI 0x4000000a +#define INSN_SEB 0x40000010 +#define INSN_SEH 0x40000011 +#define INSN_ZEB INSN_ANDI + 0xFF +#define INSN_ZEH 0x40000013 +#define INSN_BREAK 0x6400000a +#define INSN_NOP 0x40000009 +#define INSN_FLSI 0x30000000 +#define INSN_FSSI 0x32000000 +#define INSN_FLDI 0x34000000 +#define INSN_FSDI 0x36000000 +#define INSN_BEQC 0x5a000000 +#define INSN_BNEC 0x5a080000 +#define INSN_DSB 0x64000008 +#define INSN_IFCALL 0x4e000000 +#define INSN_IFRET 0x4a000060 +#define INSN_BR1 0x4c000000 +#define INSN_BR2 0x4e000000 + +/* 16-bit instructions without operand. */ +#define INSN_MOV55 0x8000 +#define INSN_MOVI55 0x8400 +#define INSN_ADD45 0x8800 +#define INSN_SUB45 0x8a00 +#define INSN_ADDI45 0x8c00 +#define INSN_SUBI45 0x8e00 +#define INSN_SRAI45 0x9000 +#define INSN_SRLI45 0x9200 +#define INSN_SLLI333 0x9400 +#define INSN_BFMI333 0x9600 +#define INSN_ADD333 0x9800 +#define INSN_SUB333 0x9a00 +#define INSN_ADDI333 0x9c00 +#define INSN_SUBI333 0x9e00 +#define INSN_LWI333 0xa000 +#define INSN_LWI333P 0xa200 +#define INSN_LHI333 0xa400 +#define INSN_LBI333 0xa600 +#define INSN_SWI333 0xa800 +#define INSN_SWI333P 0xaa00 +#define INSN_SHI333 0xac00 +#define INSN_SBI333 0xae00 +#define INSN_RSV01 0xb000 +#define INSN_RSV02 0xb200 +#define INSN_LWI450 0xb400 +#define INSN_SWI450 0xb600 +#define INSN_LWI37 0xb800 +#define INSN_SWI37 0xb880 +#define INSN_BEQZ38 0xc000 +#define INSN_BNEZ38 0xc800 +#define INSN_BEQS38 0xd000 +#define INSN_J8 0xd500 +#define INSN_BNES38 0xd800 +#define INSN_JR5 0xdd00 +#define INSN_RET5 0xdd80 +#define INSN_JRAL5 0xdd20 +#define INSN_EX9_IT_2 0xdd40 +#define INSN_SLTS45 0xe000 +#define INSN_SLT45 0xe200 +#define INSN_SLTSI45 0xe400 +#define INSN_SLTI45 0xe600 +#define INSN_BEQZS8 0xe800 +#define INSN_BNEZS8 0xe900 +#define INSN_BREAK16 0xea00 +#define INSN_EX9_IT_1 0xea00 +#define INSN_NOP16 0x9200 +/* 16-bit version 2. */ +#define INSN_ADDI10_SP 0xec00 +#define INSN_LWI37SP 0xf000 +#define INSN_SWI37SP 0xf080 +/* 16-bit version 3. */ +#define INSN_IFRET16 0x83ff +#define INSN_ADDRI36_SP 0xb000 +#define INSN_LWI45_FE 0xb200 +#define INSN_IFCALL9 0xf800 +#define INSN_MISC33 0xfe00 + +/* Instruction with specific operands. */ +#define INSN_ADDI_GP_TO_FP 0x51cd8000 /* BASELINE_V1. */ +#define INSN_ADDIGP_TO_FP 0x3fc80000 /* BASELINE_V2. */ +#define INSN_MOVI_TO_FP 0x45c00000 +#define INSN_MFUSR_PC 0x420F8020 +#define INSN_MFUSR_PC_MASK 0xFE0FFFFF + +/* Instructions use $ta register as operand. */ +#define INSN_SETHI_TA (INSN_SETHI | (REG_TA << 20)) +#define INSN_ORI_TA (INSN_ORI | (REG_TA << 20) | (REG_TA << 15)) +#define INSN_ADD_TA (INSN_ADD | (REG_TA << 20)) +#define INSN_ADD45_TA (INSN_ADD45 | (REG_TA << 5)) +#define INSN_JR5_TA (INSN_JR5 | (REG_TA << 0)) +#define INSN_RET5_TA (INSN_RET5 | (REG_TA << 0)) +#define INSN_JR_TA (INSN_JR | (REG_TA << 10)) +#define INSN_RET_TA (INSN_RET | (REG_TA << 10)) +#define INSN_JRAL_TA (INSN_JRAL | (REG_LP << 20) | (REG_TA << 10)) +#define INSN_JRAL5_TA (INSN_JRAL5 | (REG_TA << 0)) +#define INSN_BEQZ_TA (INSN_BEQZ | (REG_TA << 20)) +#define INSN_BNEZ_TA (INSN_BNEZ | (REG_TA << 20)) +#define INSN_MOVI_TA (INSN_MOVI | (REG_TA << 20)) +#define INSN_BEQ_TA (INSN_BEQ | (REG_TA << 15)) +#define INSN_BNE_TA (INSN_BNE | (REG_TA << 15)) + +/* Instructions use $r5 register as operand. */ +#define INSN_BNE_R5 (INSN_BNE | (REG_R5 << 15)) +#define INSN_BEQ_R5 (INSN_BEQ | (REG_R5 << 15)) + +#endif diff --git a/ld/ChangeLog b/ld/ChangeLog index 7dc40f7..aa624f5 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,23 @@ +2013-12-13 Kuan-Lin Chen <kuanlinchentw@gmail.com> + Wei-Cheng Wang <cole945@gmail.com> + Hui-Wen Ni <sabrinanitw@gmail.com> + + * Makefile.am (ALL_EMULATION_SOURCES): Add nds32 target. + * Makefile.in: Regenerate. + * configure.tgt: Add case for nds32*le-*-elf*, nds32*be-*-elf*, + nds32*le-*-linux-gnu*, and nds32*be-*-linux-gnu*. + * emulparams/nds32belf.sh: New file for nds32. + * emulparams/nds32belf_linux.sh: Likewise. + * emulparams/nds32belf16m.sh: Likewise. + * emulparams/nds32elf.sh: Likewise. + * emulparams/nds32elf_linux.sh: Likewise. + * emulparams/nds32elf16m.sh: Likewise. + * emultempl/nds32elf.em: Likewise. + * scripttempl/nds32elf.sc}: Likewise. + * gen-doc.texi: Set NDS32. + * ld.texinfo: Set NDS32. + * NEWS: Announce Andes nds32 support. + 2013-12-11 H.J. Lu <hongjiu.lu@intel.com> * ld.texinfo: Remove shared object from -Ttext-segment. diff --git a/ld/Makefile.am b/ld/Makefile.am index d7d4270..1abb340 100644 --- a/ld/Makefile.am +++ b/ld/Makefile.am @@ -397,6 +397,12 @@ ALL_EMULATION_SOURCES = \ emsp430xW425.c \ emsp430xW427.c \ emsp430X.c \ + ends32elf.c \ + ends32elf16m.c \ + ends32elf_linux.c \ + ends32belf.c \ + ends32belf16m.c \ + ends32belf_linux.c \ enews.c \ ens32knbsd.c \ eor32.c \ @@ -1753,6 +1759,30 @@ emsp430X.c: $(srcdir)/emulparams/msp430all.sh \ $(ELF_GEN_DEPS) $(srcdir)/scripttempl/elf32msp430.sc \ ${GEN_DEPENDS} ${GENSCRIPTS} msp430X "$(tdir_msp430X)" msp430all +ends32elf.c: $(srcdir)/emulparams/nds32elf.sh \ + $(ELF_DEPS) $(srcdir)/emultempl/nds32elf.em \ + $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS} + ${GENSCRIPTS} nds32elf "$(tdir_nds32)" +ends32elf16m.c: $(srcdir)/emulparams/nds32elf16m.sh \ + $(ELF_DEPS) $(srcdir)/emultempl/nds32elf.em \ + $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS} + ${GENSCRIPTS} nds32elf16m "$(tdir_nds32)" +ends32belf.c: $(srcdir)/emulparams/nds32belf.sh \ + $(ELF_DEPS) $(srcdir)/emultempl/nds32elf.em \ + $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS} + ${GENSCRIPTS} nds32belf "$(tdir_nds32belf)" +ends32belf16m.c: $(srcdir)/emulparams/nds32belf16m.sh \ + $(ELF_DEPS) $(srcdir)/emultempl/nds32elf.em \ + $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS} + ${GENSCRIPTS} nds32belf16m "$(tdir_nds32belf)" +ends32elf_linux.c: $(srcdir)/emulparams/nds32elf_linux.sh \ + $(ELF_DEPS) $(srcdir)/emultempl/nds32elf.em \ + $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS} + ${GENSCRIPTS} nds32elf_linux "$(tdir_nds32elf_linux)" +ends32belf_linux.c: $(srcdir)/emulparams/nds32belf_linux.sh \ + $(ELF_DEPS) $(srcdir)/emultempl/nds32elf.em \ + $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS} + ${GENSCRIPTS} nds32belf_linux "$(tdir_nds32belf_linux)" enews.c: $(srcdir)/emulparams/news.sh \ $(srcdir)/emultempl/generic.em $(srcdir)/scripttempl/aout.sc ${GEN_DEPENDS} ${GENSCRIPTS} news "$(tdir_news)" diff --git a/ld/Makefile.in b/ld/Makefile.in index e5ceda5..47a41cb 100644 --- a/ld/Makefile.in +++ b/ld/Makefile.in @@ -705,6 +705,12 @@ ALL_EMULATION_SOURCES = \ emsp430xW425.c \ emsp430xW427.c \ emsp430X.c \ + ends32elf.c \ + ends32elf16m.c \ + ends32elf_linux.c \ + ends32belf.c \ + ends32belf16m.c \ + ends32belf_linux.c \ enews.c \ ens32knbsd.c \ eor32.c \ @@ -1399,6 +1405,12 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/emsp430xW423.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/emsp430xW425.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/emsp430xW427.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ends32belf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ends32belf16m.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ends32belf_linux.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ends32elf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ends32elf16m.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ends32elf_linux.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/enews.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ens32knbsd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eor32.Po@am__quote@ @@ -3237,6 +3249,30 @@ emsp430X.c: $(srcdir)/emulparams/msp430all.sh \ $(ELF_GEN_DEPS) $(srcdir)/scripttempl/elf32msp430.sc \ ${GEN_DEPENDS} ${GENSCRIPTS} msp430X "$(tdir_msp430X)" msp430all +ends32elf.c: $(srcdir)/emulparams/nds32elf.sh \ + $(ELF_DEPS) $(srcdir)/emultempl/nds32elf.em \ + $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS} + ${GENSCRIPTS} nds32elf "$(tdir_nds32)" +ends32elf16m.c: $(srcdir)/emulparams/nds32elf16m.sh \ + $(ELF_DEPS) $(srcdir)/emultempl/nds32elf.em \ + $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS} + ${GENSCRIPTS} nds32elf16m "$(tdir_nds32)" +ends32belf.c: $(srcdir)/emulparams/nds32belf.sh \ + $(ELF_DEPS) $(srcdir)/emultempl/nds32elf.em \ + $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS} + ${GENSCRIPTS} nds32belf "$(tdir_nds32belf)" +ends32belf16m.c: $(srcdir)/emulparams/nds32belf16m.sh \ + $(ELF_DEPS) $(srcdir)/emultempl/nds32elf.em \ + $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS} + ${GENSCRIPTS} nds32belf16m "$(tdir_nds32belf)" +ends32elf_linux.c: $(srcdir)/emulparams/nds32elf_linux.sh \ + $(ELF_DEPS) $(srcdir)/emultempl/nds32elf.em \ + $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS} + ${GENSCRIPTS} nds32elf_linux "$(tdir_nds32elf_linux)" +ends32belf_linux.c: $(srcdir)/emulparams/nds32belf_linux.sh \ + $(ELF_DEPS) $(srcdir)/emultempl/nds32elf.em \ + $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS} + ${GENSCRIPTS} nds32belf_linux "$(tdir_nds32belf_linux)" enews.c: $(srcdir)/emulparams/news.sh \ $(srcdir)/emultempl/generic.em $(srcdir)/scripttempl/aout.sc ${GEN_DEPENDS} ${GENSCRIPTS} news "$(tdir_news)" @@ -1,5 +1,7 @@ -*- text -*- +* Add support for the Andes NDS32. + Changes in 2.24: * Add LOG2CEIL() builtin function to the linker script language diff --git a/ld/configure.tgt b/ld/configure.tgt index c50730b..72d7090 100644 --- a/ld/configure.tgt +++ b/ld/configure.tgt @@ -6,12 +6,12 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING3. If not see # <http://www.gnu.org/licenses/>. @@ -514,6 +514,14 @@ mt-*elf) targ_emul=elf32mt msp430-*-*) targ_emul=msp430x110 targ_extra_emuls="msp430x112 msp430x1101 msp430x1111 msp430x1121 msp430x1122 msp430x1132 msp430x122 msp430x123 msp430x1222 msp430x1232 msp430x133 msp430x135 msp430x1331 msp430x1351 msp430x147 msp430x148 msp430x149 msp430x155 msp430x156 msp430x157 msp430x167 msp430x168 msp430x169 msp430x1610 msp430x1611 msp430x1612 msp430x2101 msp430x2111 msp430x2121 msp430x2131 msp430x311 msp430x312 msp430x313 msp430x314 msp430x315 msp430x323 msp430x325 msp430x336 msp430x337 msp430x412 msp430x413 msp430x415 msp430x417 msp430xE423 msp430xE425 msp430xE427 msp430xW423 msp430xW425 msp430xW427 msp430xG437 msp430xG438 msp430xG439 msp430x435 msp430x436 msp430x437 msp430x447 msp430x448 msp430x449 msp430X" ;; +nds32*le-*-elf*) targ_emul=nds32elf + targ_extra_emuls="nds32elf16m nds32belf nds32belf16m" + ;; +nds32*be-*-elf*) targ_emul=nds32belf + targ_extra_emuls="nds32elf nds32elf16m nds32belf16m" + ;; +nds32*le-*-linux-gnu*) targ_emul=nds32elf_linux ;; +nds32*be-*-linux-gnu*) targ_emul=nds32belf_linux ;; nios2*-*-linux*) targ_emul=nios2linux ;; nios2*-*-*) targ_emul=nios2elf ;; ns32k-pc532-mach* | ns32k-pc532-ux*) targ_emul=pc532macha ;; @@ -752,7 +760,7 @@ xstormy16-*-*) targ_emul=elf32xstormy16 ;; xtensa*-*-*) targ_emul=elf32xtensa ;; -xgate-*-*) targ_emul=xgateelf +xgate-*-*) targ_emul=xgateelf ;; z80-*-coff) targ_emul=z80 ;; diff --git a/ld/emulparams/nds32belf.sh b/ld/emulparams/nds32belf.sh new file mode 100644 index 0000000..eee48fa --- /dev/null +++ b/ld/emulparams/nds32belf.sh @@ -0,0 +1,2 @@ +. ${srcdir}/emulparams/nds32elf.sh +OUTPUT_FORMAT="$BIG_OUTPUT_FORMAT" diff --git a/ld/emulparams/nds32belf16m.sh b/ld/emulparams/nds32belf16m.sh new file mode 100644 index 0000000..9839f7b --- /dev/null +++ b/ld/emulparams/nds32belf16m.sh @@ -0,0 +1,2 @@ +. ${srcdir}/emulparams/nds32elf16m.sh +OUTPUT_FORMAT="$BIG_OUTPUT_FORMAT" diff --git a/ld/emulparams/nds32belf_linux.sh b/ld/emulparams/nds32belf_linux.sh new file mode 100644 index 0000000..bc99e38 --- /dev/null +++ b/ld/emulparams/nds32belf_linux.sh @@ -0,0 +1,2 @@ +. ${srcdir}/emulparams/nds32elf_linux.sh +OUTPUT_FORMAT="$BIG_OUTPUT_FORMAT" diff --git a/ld/emulparams/nds32elf.sh b/ld/emulparams/nds32elf.sh new file mode 100644 index 0000000..c12048d --- /dev/null +++ b/ld/emulparams/nds32elf.sh @@ -0,0 +1,19 @@ +TEXT_START_ADDR=0x500000 +# This sets the stack to the top of simulator memory (48MB). +OTHER_END_SYMBOLS='PROVIDE (_stack = 0x3000000);' + +SCRIPT_NAME=nds32elf +TEMPLATE_NAME=elf32 +EXTRA_EM_FILE=nds32elf +BIG_OUTPUT_FORMAT="elf32-nds32be" +LITTLE_OUTPUT_FORMAT="elf32-nds32le" +OUTPUT_FORMAT="$LITTLE_OUTPUT_FORMAT" +ARCH=nds32 +MACHINE= +MAXPAGESIZE=0x20 +EMBEDDED=yes +COMMONPAGESIZE=0x20 + +# Instruct genscripts.sh not to compile scripts in by COMPILE_IN +# in order to use external linker scripts files. +EMULATION_LIBPATH= diff --git a/ld/emulparams/nds32elf16m.sh b/ld/emulparams/nds32elf16m.sh new file mode 100644 index 0000000..7d3b063 --- /dev/null +++ b/ld/emulparams/nds32elf16m.sh @@ -0,0 +1,19 @@ +TEXT_START_ADDR=0x300000 +# This sets the stack to the top of simulator memory (48MB). +OTHER_END_SYMBOLS='PROVIDE (_stack = 0x780000);' + +SCRIPT_NAME=nds32elf +TEMPLATE_NAME=elf32 +EXTRA_EM_FILE=nds32elf +BIG_OUTPUT_FORMAT="elf32-nds32be" +LITTLE_OUTPUT_FORMAT="elf32-nds32le" +OUTPUT_FORMAT="$LITTLE_OUTPUT_FORMAT" +ARCH=nds32 +MACHINE= +MAXPAGESIZE=0x20 +EMBEDDED=yes +COMMONPAGESIZE=0x20 + +# Instruct genscripts.sh not to compile scripts in by COMPILE_IN +# in order to use external linker scripts files. +EMULATION_LIBPATH= diff --git a/ld/emulparams/nds32elf_linux.sh b/ld/emulparams/nds32elf_linux.sh new file mode 100644 index 0000000..6d89f79 --- /dev/null +++ b/ld/emulparams/nds32elf_linux.sh @@ -0,0 +1,36 @@ +DEFAULT_TEXT_START_ADDR=0 +DEFAULT_STACK_START_ADDR=0 +MACHINE= +SCRIPT_NAME=nds32elf +TEMPLATE_NAME=elf32 +EXTRA_EM_FILE=nds32elf +BIG_OUTPUT_FORMAT="elf32-nds32be-linux" +LITTLE_OUTPUT_FORMAT="elf32-nds32le-linux" +OUTPUT_FORMAT="$LITTLE_OUTPUT_FORMAT" +LIB_PATH="=/usr/local/lib:=/lib:=/usr/lib/" + +if [ "${DEFAULT_TEXT_START_ADDR}" = "0" ]; then + TEXT_START_ADDR=0x8000 +else + TEXT_START_ADDR=${DEFAULT_TEXT_START_ADDR} +fi + +ARCH=nds32 +MACHINE= +MAXPAGESIZE="CONSTANT (MAXPAGESIZE)" +COMMONPAGESIZE="CONSTANT (COMMONPAGESIZE)" + +# Hmmm, there's got to be a better way. This sets the stack to the +# top of simulator memory (32MB). +if [ "${DEFAULT_STACK_START_ADDR}" = "0" ]; then + OTHER_RELOCATING_SECTIONS='PROVIDE (_stack = 0x2000000);' +else + OTHER_RELOCATING_SECTIONS="PROVIDE (_stack = ${DEFAULT_STACK_START_ADDR});" +fi + +GENERATE_SHLIB_SCRIPT=yes +GENERATE_PIE_SCRIPT=yes + +# Instruct genscripts.sh not to compile scripts in by COMPILE_IN +# in order to use external linker scripts files. +EMULATION_LIBPATH= diff --git a/ld/emultempl/nds32elf.em b/ld/emultempl/nds32elf.em new file mode 100644 index 0000000..d94506a --- /dev/null +++ b/ld/emultempl/nds32elf.em @@ -0,0 +1,475 @@ +# This shell script emits a C file. -*- C -*- +# Copyright (C) 2012-2013 Free Software Foundation, Inc. +# Contributed by Andes Technology Corporation. +# +# 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 "libbfd.h" +#include "elf-bfd.h" +#include "elf/nds32.h" +#include "elf32-nds32.h" + +static int relax_fp_as_gp = 1; /* --mrelax-omit-fp */ +static int eliminate_gc_relocs = 0; /* --meliminate-gc-relocs */ +static FILE *sym_ld_script = NULL; /* --mgen-symbol-ld-script=<file> */ +/* Disable if linking a dynamically linked executable. */ +static int load_store_relax = 1; +static int target_optimize = 0; /* Switch optimization. */ +static int relax_status = 0; /* Finished optimization. */ +static int relax_round = 0; /* Going optimization. */ +static FILE *ex9_export_file = NULL; /* --mexport-ex9=<file> */ +static FILE *ex9_import_file = NULL; /* --mimport-ex9=<file> */ +static int update_ex9_table = 0; /* --mupdate-ex9. */ +static int ex9_limit = 511; +static bfd_boolean ex9_loop_aware = FALSE; /* Ignore ex9 if inside a loop. */ +static bfd_boolean ifc_loop_aware = FALSE; /* Ignore ifc if inside a loop. */ + +/* Save the target options into output bfd to avoid using to many global + variables. Do this after the output has been created, but before + inputs are read. */ +static void +nds32_elf_create_output_section_statements (void) +{ + if (strstr (bfd_get_target (link_info.output_bfd), "nds32") == NULL) + { + /* Check the output target is nds32. */ + einfo ("%F%X%P: error: Cannot change output format whilst linking NDS32 binaries.\n"); + return; + } + + bfd_elf32_nds32_set_target_option (&link_info, relax_fp_as_gp, + eliminate_gc_relocs, + sym_ld_script, + load_store_relax, + target_optimize, relax_status, relax_round, + ex9_export_file, ex9_import_file, + update_ex9_table, ex9_limit, + ex9_loop_aware, ifc_loop_aware); +} + +static void +nds32_elf_after_parse (void) +{ + if (link_info.relocatable) + DISABLE_RELAXATION; + + if (!RELAXATION_ENABLED) + { + target_optimize = target_optimize & (!NDS32_RELAX_JUMP_IFC_ON); + target_optimize = target_optimize & (!NDS32_RELAX_EX9_ON); + relax_fp_as_gp = 0; + } + + if (ex9_import_file != NULL) + { + ex9_export_file = NULL; + target_optimize = target_optimize & (!NDS32_RELAX_EX9_ON); + } + else + update_ex9_table = 0; + + if (link_info.shared) + { + target_optimize = target_optimize & (!NDS32_RELAX_JUMP_IFC_ON); + target_optimize = target_optimize & (!NDS32_RELAX_EX9_ON); + } + + after_parse_default (); +} + +static void +nds32_elf_after_open (void) +{ + unsigned int arch_ver = (unsigned int)-1; + unsigned int abi_ver = (unsigned int)-1; + bfd *abfd; + + /* For now, make sure all object files are of the same architecture. + We may try to merge object files with different architecture together. */ + for (abfd = link_info.input_bfds; abfd != NULL; abfd = abfd->link_next) + { + if (arch_ver == (unsigned int)-1 && E_N1_ARCH != (elf_elfheader (abfd)->e_flags & EF_NDS_ARCH)) + arch_ver = elf_elfheader (abfd)->e_flags & EF_NDS_ARCH ; + + if (abi_ver == (unsigned int)-1) + { + /* Initialize ABI version, if not ABI0. + (OS uses empty file to create empty ELF with ABI0). */ + if ((elf_elfheader (abfd)->e_flags & EF_NDS_ABI) != 0) + abi_ver = elf_elfheader (abfd)->e_flags & EF_NDS_ABI ; + } + else if ((elf_elfheader (abfd)->e_flags & EF_NDS_ABI) != 0 + && abi_ver != (elf_elfheader (abfd)->e_flags & EF_NDS_ABI)) + { + /* Incompatible objects. */ + einfo (_("%F%B: ABI version of object files mismatched\n"), abfd); + } + + /* Append .ex9.itable section in the last input object file. */ + if (!link_info.relocatable && abfd->link_next == NULL) + { + asection *itable; + struct bfd_link_hash_entry *h; + itable = bfd_make_section_with_flags (abfd, ".ex9.itable", + SEC_CODE | SEC_ALLOC | SEC_LOAD + | SEC_HAS_CONTENTS | SEC_READONLY + | SEC_IN_MEMORY | SEC_KEEP); + if (itable) + { + itable->gc_mark = 1; + itable->alignment_power = 2; + if ((target_optimize & NDS32_RELAX_EX9_ON)) + { + itable->size = 0x1000; + itable->contents = bfd_zalloc (abfd, itable->size); + } + else + { + itable->size = 0x4; + itable->contents = bfd_zalloc (abfd, itable->size); + bfd_putb32 (INSN_BREAK_EA,itable->contents); + } + + /* Add a symbol in the head of ex9.itable to objdump clearly. */ + h = bfd_link_hash_lookup (link_info.hash, "_EX9_BASE_", + FALSE, FALSE, FALSE); + _bfd_generic_link_add_one_symbol + (&link_info, link_info.output_bfd, "_EX9_BASE_", + BSF_GLOBAL | BSF_WEAK, itable, 0, (const char *) NULL, FALSE, + get_elf_backend_data (link_info.output_bfd)->collect, &h); + } + } + } + + /* Check object files if the target is dynamic linked executable + or shared object. */ + if (elf_hash_table (&link_info)->dynamic_sections_created + || link_info.shared || link_info.pie) + { + for (abfd = link_info.input_bfds; abfd != NULL; abfd = abfd->link_next) + { + if (!(elf_elfheader (abfd)->e_flags & E_NDS32_HAS_PIC)) + { + /* Non-PIC object file is used. */ + if (link_info.shared || link_info.pie) + { + /* For PIE or shared object, all input must be PIC. */ + einfo (_("%B: must use -fpic to compile this file for shared object or PIE\n"), abfd); + } + else + { + /* Dynamic linked executable with SDA and non-PIC. + Turn off load/store relaxtion. */ + load_store_relax = 0 ; + relax_fp_as_gp = 0; + } + } + } + /* Turn off relax when building shared object or PIE + until we can support their relaxation. */ + } + + /* Call the standard elf routine. */ + gld${EMULATION_NAME}_after_open (); +} + +static void nds32_elf_relax_stub (bfd_boolean relax) +{ + /* Re-caculate memory map address. */ + lang_do_assignments (lang_assigning_phase_enum); + lang_reset_memory_regions (); + one_lang_size_sections_pass (&relax, FALSE); +} + +static void +nds32_elf_after_allocation (void) +{ + struct elf_nds32_link_hash_table *table; + table = nds32_elf_hash_table (&link_info); + + /* Call default after allocation callback. + 1. This is where relaxation is done. + 2. It calls gld${EMULATION_NAME}_map_segments to build ELF segment table. + 3. Any relaxation requires relax being done must be called after it. */ + gld${EMULATION_NAME}_after_allocation (); + + if (!table) + return; + + /* Use IFC */ + if ((target_optimize & NDS32_RELAX_JUMP_IFC_ON) + && !(table->relax_status & NDS32_RELAX_JUMP_IFC_DONE)) + { + table->relax_round = NDS32_RELAX_JUMP_IFC_ROUND; + /* Traverse all sections to build j and jal list. */ + nds32_elf_relax_stub (TRUE); + + /* Replace with ifc. */ + if (!nds32_elf_ifc_finish (&link_info)) + einfo (_("%F: Please report this bug. IFC error.\n")); + table->relax_round = NDS32_RELAX_NONE_ROUND; + + /* Adjust address after ifcall. */ + nds32_elf_relax_stub (FALSE); + + if (!nds32_elf_ifc_reloc ()) + einfo (_("%F: Please report this bug. IFC error.\n")); + } + + /* EX9 Instruction Table Relaxation. */ + if (!link_info.relocatable && !nds32_elf_ex9_itb_base (&link_info)) + einfo (_("%F: Please report this bug. Ex9 relocation error.\n")); + + + /* Generate ex9 table. */ + if ((target_optimize & NDS32_RELAX_EX9_ON) + && !(table->relax_status & NDS32_RELAX_EX9_DONE)) + { + /* Ex9 entry point. */ + table->relax_round = NDS32_RELAX_EX9_BUILD_ROUND; + + /* Initialize ex9 hash table. */ + if (!nds32_elf_ex9_init ()) + return; + + /* Build ex9 instruction table. */ + nds32_elf_relax_stub (TRUE); + nds32_elf_ex9_finish (&link_info); + /* Replace with ex9.it. */ + nds32_elf_relax_stub (TRUE); + table->relax_round = NDS32_RELAX_NONE_ROUND; + + /* Do ifc again. */ + if (target_optimize & NDS32_RELAX_JUMP_IFC_ON) + if (!nds32_elf_ifc_finish (&link_info)) + einfo (_("%F: Please report this bug. IFC error.\n")); + + /* Re-caculate memory map address. */ + lang_do_assignments (lang_assigning_phase_enum); + /* Relocation for .ex9.itable. */ + nds32_elf_ex9_reloc_jmp (&link_info); + } + else if (ex9_import_file != NULL + && !(table->relax_status = NDS32_RELAX_EX9_DONE)) + { + /* Import ex9 table. */ + + if (update_ex9_table == 1) + { + /* Build ex9 table. */ + table->relax_round = NDS32_RELAX_EX9_BUILD_ROUND; + /* Initialize ex9 hash table. */ + if (!nds32_elf_ex9_init ()) + return; + /* Build ex9 table. */ + nds32_elf_relax_stub (TRUE); + + /* Relocation for .ex9.itable. */ + lang_do_assignments (lang_assigning_phase_enum); + nds32_elf_ex9_reloc_jmp (&link_info); + } + nds32_elf_ex9_import_table (&link_info); + + /* Replace with ex9.it. */ + table->relax_round = NDS32_RELAX_EX9_REPLACE_ROUND; + table->relax_status |= NDS32_RELAX_EX9_DONE; + nds32_elf_relax_stub (TRUE); + } +} + +EOF +# Define some shell vars to insert bits of code into the standard elf +# parse_args and list_options functions. +# +PARSE_AND_LIST_PROLOGUE=' +#define OPTION_BASELINE 301 +#define OPTION_ELIM_GC_RELOCS (OPTION_BASELINE + 1) +#define OPTION_FP_AS_GP (OPTION_BASELINE + 2) +#define OPTION_NO_FP_AS_GP (OPTION_BASELINE + 3) +#define OPTION_REDUCE_FP_UPDATE (OPTION_BASELINE + 4) +#define OPTION_NO_REDUCE_FP_UPDATE (OPTION_BASELINE + 5) +#define OPTION_EXPORT_SYMBOLS (OPTION_BASELINE + 6) + +/* These are only available to ex9. */ +#if defined NDS32_EX9_EXT +#define OPTION_EX9_BASELINE 320 +#define OPTION_EX9_TABLE (OPTION_EX9_BASELINE + 1) +#define OPTION_NO_EX9_TABLE (OPTION_EX9_BASELINE + 2) +#define OPTION_EXPORT_EX9 (OPTION_EX9_BASELINE + 3) +#define OPTION_IMPORT_EX9 (OPTION_EX9_BASELINE + 4) +#define OPTION_UPDATE_EX9 (OPTION_EX9_BASELINE + 5) +#define OPTION_EX9_LIMIT (OPTION_EX9_BASELINE + 6) +#define OPTION_EX9_LOOP (OPTION_EX9_BASELINE + 7) +#endif + +/* These are only available to link-time ifc. */ +#if defined NDS32_IFC_EXT +#define OPTION_IFC_BASELINE 340 +#define OPTION_JUMP_IFC (OPTION_IFC_BASELINE + 1) +#define OPTION_NO_JUMP_IFC (OPTION_IFC_BASELINE + 2) +#define OPTION_IFC_LOOP (OPTION_IFC_BASELINE + 3) +#endif +' +PARSE_AND_LIST_LONGOPTS=' + { "mfp-as-gp", no_argument, NULL, OPTION_FP_AS_GP}, + { "mno-fp-as-gp", no_argument, NULL, OPTION_NO_FP_AS_GP}, + { "mgen-symbol-ld-script", required_argument, NULL, OPTION_EXPORT_SYMBOLS}, + /* These are deprecated options. Remove them in the future. */ + { "mrelax-reduce-fp-update", no_argument, NULL, OPTION_REDUCE_FP_UPDATE}, + { "mrelax-no-reduce-fp-update", no_argument, NULL, OPTION_NO_REDUCE_FP_UPDATE}, + { "mbaseline", required_argument, NULL, OPTION_BASELINE}, + { "meliminate-gc-relocs", no_argument, NULL, OPTION_ELIM_GC_RELOCS}, + { "mrelax-omit-fp", no_argument, NULL, OPTION_FP_AS_GP}, + { "mrelax-no-omit-fp", no_argument, NULL, OPTION_NO_FP_AS_GP}, + { "mgen-symbol-ld-script", required_argument, NULL, OPTION_EXPORT_SYMBOLS}, + /* These are specific optioins for ex9-ext support. */ +#if defined NDS32_EX9_EXT + { "mex9", no_argument, NULL, OPTION_EX9_TABLE}, + { "mno-ex9", no_argument, NULL, OPTION_NO_EX9_TABLE}, + { "mexport-ex9", required_argument, NULL, OPTION_EXPORT_EX9}, + { "mimport-ex9", required_argument, NULL, OPTION_IMPORT_EX9}, + { "mupdate-ex9", no_argument, NULL, OPTION_UPDATE_EX9}, + { "mex9-limit", required_argument, NULL, OPTION_EX9_LIMIT}, + { "mex9-loop-aware", no_argument, NULL, OPTION_EX9_LOOP}, +#endif + /* These are specific optioins for ifc-ext support. */ +#if defined NDS32_IFC_EXT + { "mifc", no_argument, NULL, OPTION_JUMP_IFC}, + { "mno-ifc", no_argument, NULL, OPTION_NO_JUMP_IFC}, + { "mifc-loop-aware", no_argument, NULL, OPTION_IFC_LOOP}, +#endif +' +PARSE_AND_LIST_OPTIONS=' + fprintf (file, _("\ + --m[no-]fp-as-gp Disable/enable fp-as-gp relaxation\n\ + --mexport-symbols=FILE Exporting symbols in linker script\n\ +")); + +#if defined NDS32_EX9_EXT + fprintf (file, _("\ + --m[no-]ex9 Disable/enable link-time EX9 relaxation\n\ + --mexport-ex9=FILE Export EX9 table after linking\n\ + --mimport-ex9=FILE Import Ex9 table for EX9 relaxation\n\ + --mupdate-ex9 Update existing EX9 table\n\ + --mex9-limit=NUM Maximum number of entries in ex9 table\n\ + --mex9-loop-aware Avoid generate EX9 instruction inside loop\n\ +")); +#endif + +#if defined NDS32_IFC_EXT + fprintf (file, _("\ + --m[no-]ifc Disable/enable link-time IFC optimization\n\ + --mifc-loop-aware Avoid generate IFC instruction inside loop\n\ +")); +#endif +' +PARSE_AND_LIST_ARGS_CASES=' + case OPTION_BASELINE: + einfo ("%P: --mbaseline is not used anymore.\n"); + break; + case OPTION_ELIM_GC_RELOCS: + eliminate_gc_relocs = 1; + break; + case OPTION_FP_AS_GP: + case OPTION_NO_FP_AS_GP: + relax_fp_as_gp = (optc == OPTION_FP_AS_GP); + break; + case OPTION_REDUCE_FP_UPDATE: + case OPTION_NO_REDUCE_FP_UPDATE: + einfo ("%P: --relax-[no-]reduce-fp-updat is not used anymore.\n"); + break; + case OPTION_EXPORT_SYMBOLS: + if (!optarg) + einfo (_("Missing file for --mgen-symbol-ld-script.\n"), optarg); + + if(strcmp (optarg, "-") == 0) + sym_ld_script = stdout; + else + { + sym_ld_script = fopen (optarg, FOPEN_WT); + if(sym_ld_script == NULL) + einfo (_("%P%F: cannot open map file %s: %E.\n"), optarg); + } + break; +#if defined NDS32_EX9_EXT + case OPTION_EX9_TABLE: + target_optimize = target_optimize | NDS32_RELAX_EX9_ON; + break; + case OPTION_NO_EX9_TABLE: + target_optimize = target_optimize & (!NDS32_RELAX_EX9_ON); + break; + case OPTION_EXPORT_EX9: + if (!optarg) + einfo (_("Missing file for --mexport-ex9=<file>.\n")); + + if(strcmp (optarg, "-") == 0) + ex9_export_file = stdout; + else + { + ex9_export_file = fopen (optarg, FOPEN_WT); + if(ex9_export_file == NULL) + einfo (_("ERROR %P%F: cannot open ex9 export file %s.\n"), optarg); + } + break; + case OPTION_IMPORT_EX9: + if (!optarg) + einfo (_("Missing file for --mimport-ex9=<file>.\n")); + + ex9_import_file = fopen (optarg, "r+"); + if(ex9_import_file == NULL) + einfo (_("ERROR %P%F: cannot open ex9 import file %s.\n"), optarg); + break; + case OPTION_UPDATE_EX9: + update_ex9_table = 1; + break; + case OPTION_EX9_LIMIT: + if (optarg) + { + ex9_limit = atoi (optarg); + if (ex9_limit > 511 || ex9_limit < 1) + { + einfo (_("ERROR: the range of ex9_limit must between 1 and 511\n")); + exit (1); + } + } + break; + case OPTION_EX9_LOOP: + target_optimize = target_optimize | NDS32_RELAX_EX9_ON; + ex9_loop_aware = 1; + break; +#endif +#if defined NDS32_IFC_EXT + case OPTION_JUMP_IFC: + target_optimize = target_optimize | NDS32_RELAX_JUMP_IFC_ON; + break; + case OPTION_NO_JUMP_IFC: + target_optimize = target_optimize & (!NDS32_RELAX_JUMP_IFC_ON); + break; + case OPTION_IFC_LOOP: + target_optimize = target_optimize | NDS32_RELAX_JUMP_IFC_ON; + ifc_loop_aware = 1; + break; +#endif +' +LDEMUL_AFTER_OPEN=nds32_elf_after_open +LDEMUL_AFTER_PARSE=nds32_elf_after_parse +LDEMUL_AFTER_ALLOCATION=nds32_elf_after_allocation +LDEMUL_CREATE_OUTPUT_SECTION_STATEMENTS=nds32_elf_create_output_section_statements diff --git a/ld/gen-doc.texi b/ld/gen-doc.texi index a2c88db..acc6c57 100644 --- a/ld/gen-doc.texi +++ b/ld/gen-doc.texi @@ -17,6 +17,7 @@ @set MIPS @set MMIX @set MSP430 +@set NDS32 @set POWERPC @set POWERPC64 @set Renesas diff --git a/ld/ld.texinfo b/ld/ld.texinfo index 78b15a5..398dd594 100644 --- a/ld/ld.texinfo +++ b/ld/ld.texinfo @@ -29,6 +29,7 @@ @set MIPS @set MMIX @set MSP430 +@set NDS32 @set POWERPC @set POWERPC64 @set Renesas @@ -6094,6 +6095,9 @@ functionality are not listed. @ifset MSP430 * MSP430:: @command{ld} and MSP430 @end ifset +@ifset NDS32 +* NDS32:: @command{ld} and NDS32 +@end ifset @ifset POWERPC * PowerPC ELF32:: @command{ld} and PowerPC 32-bit ELF Support @end ifset @@ -6684,6 +6688,54 @@ The last two sections are used by gcc. @end ifclear @end ifset +@ifset NDS32 +@ifclear GENERIC +@raisesections +@end ifclear + +@node NDS32 +@section @code{ld} and NDS32 +@kindex relaxing on NDS32 +For NDS32, there are some options to select relaxation behavior. The linker +relaxes objects according to these options. + +@table @code +@item @samp{--m[no-]fp-as-gp} +Disable/enable fp-as-gp relaxation. + +@item @samp{--mexport-symbols=FILE} +Exporting symbols and their address into FILE as linker script. + +@item @samp{--m[no-]ex9} +Disable/enable link-time EX9 relaxation. + +@item @samp{--mexport-ex9=FILE} +Export the EX9 table after linking. + +@item @samp{--mimport-ex9=FILE} +Import the Ex9 table for EX9 relaxation. + +@item @samp{--mupdate-ex9} +Update the existing EX9 table. + +@item @samp{--mex9-limit=NUM} +Maximum number of entries in the ex9 table. + +@item @samp{--mex9-loop-aware} +Avoid generating the EX9 instruction inside the loop. + +@item @samp{--m[no-]ifc} +Disable/enable the link-time IFC optimization. + +@item @samp{--mifc-loop-aware} +Avoid generating the IFC instruction inside the loop. +@end table + +@ifclear GENERIC +@lowersections +@end ifclear +@end ifset + @ifset POWERPC @ifclear GENERIC @raisesections diff --git a/ld/scripttempl/nds32elf.sc b/ld/scripttempl/nds32elf.sc new file mode 100644 index 0000000..80f60a1 --- /dev/null +++ b/ld/scripttempl/nds32elf.sc @@ -0,0 +1,615 @@ +# This file is variant of elf.sc. For nds32, because the data will be +# classified into different sections according to their size, this script +# describe these sections map. The order is ".sdata_d, .sdata_w, .sdata_h, +# .sdata_b, , sdata_f, .sbss_f, .sbss_b, .sbss_h, .sbss_w, .sbss_d". In +# this order we do not have to consider the alignment issue between these +# sections. + +if test -n "$NOP"; then + FILL="=$NOP" +else + FILL= +fi + +test -z "$RODATA_NAME" && RODATA_NAME=rodata +test -z "$SDATA_NAME" && SDATA_NAME=sdata +test -z "$SBSS_NAME" && SBSS_NAME=sbss +test -z "$BSS_NAME" && BSS_NAME=bss +test -z "$ENTRY" && ENTRY=${USER_LABEL_PREFIX}_start +test -z "${BIG_OUTPUT_FORMAT}" && BIG_OUTPUT_FORMAT=${OUTPUT_FORMAT} +test -z "${LITTLE_OUTPUT_FORMAT}" && LITTLE_OUTPUT_FORMAT=${OUTPUT_FORMAT} +if [ -z "$MACHINE" ]; then OUTPUT_ARCH=${ARCH}; else OUTPUT_ARCH=${ARCH}:${MACHINE}; fi +test -z "${ELFSIZE}" && ELFSIZE=32 +test -z "${ALIGNMENT}" && ALIGNMENT="${ELFSIZE} / 8" +test "$LD_FLAG" = "N" && DATA_ADDR=. +test -z "${ETEXT_NAME}" && ETEXT_NAME=${USER_LABEL_PREFIX}etext +test -n "$CREATE_SHLIB$CREATE_PIE" && test -n "$SHLIB_DATA_ADDR" && COMMONPAGESIZE="" +test -z "$CREATE_SHLIB$CREATE_PIE" && test -n "$DATA_ADDR" && COMMONPAGESIZE="" +test -n "$RELRO_NOW" && unset SEPARATE_GOTPLT +test -z "$ATTRS_SECTIONS" && ATTRS_SECTIONS=".gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }" +DATA_SEGMENT_ALIGN="ALIGN(${SEGMENT_SIZE}) + (. & (${MAXPAGESIZE} - 1))" +DATA_SEGMENT_RELRO_END="" +DATA_SEGMENT_END="" +if test -n "${COMMONPAGESIZE}"; then + DATA_SEGMENT_ALIGN="ALIGN (${SEGMENT_SIZE}) - ((${MAXPAGESIZE} - .) & (${MAXPAGESIZE} - 1)); . = DATA_SEGMENT_ALIGN (${MAXPAGESIZE}, ${COMMONPAGESIZE})" + DATA_SEGMENT_END=". = DATA_SEGMENT_END (.);" + DATA_SEGMENT_RELRO_END=". = DATA_SEGMENT_RELRO_END (${SEPARATE_GOTPLT-0}, .);" +fi +if test -z "${INITIAL_READONLY_SECTIONS}${CREATE_SHLIB}"; then + INITIAL_READONLY_SECTIONS=".interp ${RELOCATING-0} : { *(.interp) }" +fi +if test -z "$PLT"; then + IPLT=".iplt ${RELOCATING-0} : { *(.iplt) }" + PLT=".plt ${RELOCATING-0} : { *(.plt)${IREL_IN_PLT+ *(.iplt)} } + ${IREL_IN_PLT-$IPLT}" +fi +test -n "${DATA_PLT-${BSS_PLT-text}}" && TEXT_PLT= +if test -z "$GOT"; then + if test -z "$SEPARATE_GOTPLT"; then + GOT=".got ${RELOCATING-0} : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) }" + else + GOT=".got ${RELOCATING-0} : { *(.got) *(.igot) }" + GOTPLT=".got.plt ${RELOCATING-0} : { *(.got.plt) *(.igot.plt) }" + fi +fi +REL_IFUNC=".rel.ifunc ${RELOCATING-0} : { *(.rel.ifunc) }" +RELA_IFUNC=".rela.ifunc ${RELOCATING-0} : { *(.rela.ifunc) }" +REL_IPLT=".rel.iplt ${RELOCATING-0} : + { + ${RELOCATING+${CREATE_SHLIB-PROVIDE_HIDDEN (${USER_LABEL_PREFIX}__rel_iplt_start = .);}} + *(.rel.iplt) + ${RELOCATING+${CREATE_SHLIB-PROVIDE_HIDDEN (${USER_LABEL_PREFIX}__rel_iplt_end = .);}} + }" +RELA_IPLT=".rela.iplt ${RELOCATING-0} : + { + ${RELOCATING+${CREATE_SHLIB-PROVIDE_HIDDEN (${USER_LABEL_PREFIX}__rela_iplt_start = .);}} + *(.rela.iplt) + ${RELOCATING+${CREATE_SHLIB-PROVIDE_HIDDEN (${USER_LABEL_PREFIX}__rela_iplt_end = .);}} + }" +DYNAMIC=".dynamic ${RELOCATING-0} : { *(.dynamic) }" +RODATA=".${RODATA_NAME} ${RELOCATING-0} : { *(.${RODATA_NAME}${RELOCATING+ .${RODATA_NAME}.* .gnu.linkonce.r.*}) }" +DATARELRO=".data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }" +DISCARDED="/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }" +if test -z "${NO_SMALL_DATA}"; then + SBSS=".sbss_b ${RELOCATING-0} : + { + *(.sbss_b${RELOCATING+ .sbss_b.*}) + *(.scommon_b${RELOCATING+ .scommon_b.*}) + ${RELOCATING+. = ALIGN(2);} + } + .sbss_h ${RELOCATING-0} : + { + *(.sbss_h${RELOCATING+ .sbss_h.*}) + *(.scommon_h${RELOCATING+ .scommon_h.*}) + ${RELOCATING+. = ALIGN(4);} + } + .sbss_w ${RELOCATING-0} : + { + *(.sbss_w${RELOCATING+ .sbss_w.*}) + *(.scommon_w${RELOCATING+ .scommon_w.*}) + *(.dynsbss) + *(.scommon) + ${RELOCATING+. = ALIGN(8);} + } + .sbss_d ${RELOCATING-0} : + { + *(.sbss_d${RELOCATING+ .sbss_d.*}) + *(.scommon_d${RELOCATING+ .scommon_d.*}) + ${RELOCATING+PROVIDE (__sbss_end = .);} + ${RELOCATING+PROVIDE (___sbss_end = .);} + }" + SBSS2=".${SBSS_NAME}2 ${RELOCATING-0} : { *(.${SBSS_NAME}2${RELOCATING+ .${SBSS_NAME}2.* .gnu.linkonce.sb2.*}) }" + SDATA="/* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .${SDATA_NAME} ${RELOCATING-0} : + { + ${RELOCATING+${SDATA_START_SYMBOLS}} + ${CREATE_SHLIB+*(.${SDATA_NAME}2 .${SDATA_NAME}2.* .gnu.linkonce.s2.*)} + *(.${SDATA_NAME}${RELOCATING+ .${SDATA_NAME}.* .gnu.linkonce.s.*}) + } + .sdata_d ${RELOCATING-0} : + { + *(.sdata_d${RELOCATING+ .sdata_d.*}) + } + .sdata_w ${RELOCATING-0} : + { + *(.sdata_w${RELOCATING+ .sdata_w.*}) + } + .sdata_h ${RELOCATING-0} : + { + *(.sdata_h${RELOCATING+ .sdata_h.*}) + } + .sdata_b ${RELOCATING-0} : + { + *(.sdata_b${RELOCATING+ .sdata_b.*}) + } + .sdata_f ${RELOCATING-0} : + { + *(.sdata_f${RELOCATING+ .sdata_f.*}) + }" + SDATA2=".${SDATA_NAME}2 ${RELOCATING-0} : + { + ${RELOCATING+${SDATA2_START_SYMBOLS}} + *(.${SDATA_NAME}2${RELOCATING+ .${SDATA_NAME}2.* .gnu.linkonce.s2.*}) + }" + REL_SDATA=".rel.${SDATA_NAME} ${RELOCATING-0} : { *(.rel.${SDATA_NAME}${RELOCATING+ .rel.${SDATA_NAME}.* .rel.gnu.linkonce.s.*}) } + .rela.${SDATA_NAME} ${RELOCATING-0} : { *(.rela.${SDATA_NAME}${RELOCATING+ .rela.${SDATA_NAME}.* .rela.gnu.linkonce.s.*}) }" + REL_SBSS=".rel.${SBSS_NAME} ${RELOCATING-0} : { *(.rel.${SBSS_NAME}${RELOCATING+ .rel.${SBSS_NAME}.* .rel.gnu.linkonce.sb.*}) } + .rela.${SBSS_NAME} ${RELOCATING-0} : { *(.rela.${SBSS_NAME}${RELOCATING+ .rela.${SBSS_NAME}.* .rela.gnu.linkonce.sb.*}) }" + REL_SDATA2=".rel.${SDATA_NAME}2 ${RELOCATING-0} : { *(.rel.${SDATA_NAME}2${RELOCATING+ .rel.${SDATA_NAME}2.* .rel.gnu.linkonce.s2.*}) } + .rela.${SDATA_NAME}2 ${RELOCATING-0} : { *(.rela.${SDATA_NAME}2${RELOCATING+ .rela.${SDATA_NAME}2.* .rela.gnu.linkonce.s2.*}) }" + REL_SBSS2=".rel.${SBSS_NAME}2 ${RELOCATING-0} : { *(.rel.${SBSS_NAME}2${RELOCATING+ .rel.${SBSS_NAME}2.* .rel.gnu.linkonce.sb2.*}) } + .rela.${SBSS_NAME}2 ${RELOCATING-0} : { *(.rela.${SBSS_NAME}2${RELOCATING+ .rela.${SBSS_NAME}2.* .rela.gnu.linkonce.sb2.*}) }" +else + NO_SMALL_DATA=" " +fi +if test -z "${DATA_GOT}"; then + if test -n "${NO_SMALL_DATA}"; then + DATA_GOT=" " + fi +fi +if test -z "${SDATA_GOT}"; then + if test -z "${NO_SMALL_DATA}"; then + SDATA_GOT=" " + fi +fi +test -n "$SEPARATE_GOTPLT" && SEPARATE_GOTPLT=" " +test "${LARGE_SECTIONS}" = "yes" && REL_LARGE=" + .rel.ldata ${RELOCATING-0} : { *(.rel.ldata${RELOCATING+ .rel.ldata.* .rel.gnu.linkonce.l.*}) } + .rela.ldata ${RELOCATING-0} : { *(.rela.ldata${RELOCATING+ .rela.ldata.* .rela.gnu.linkonce.l.*}) } + .rel.lbss ${RELOCATING-0} : { *(.rel.lbss${RELOCATING+ .rel.lbss.* .rel.gnu.linkonce.lb.*}) } + .rela.lbss ${RELOCATING-0} : { *(.rela.lbss${RELOCATING+ .rela.lbss.* .rela.gnu.linkonce.lb.*}) } + .rel.lrodata ${RELOCATING-0} : { *(.rel.lrodata${RELOCATING+ .rel.lrodata.* .rel.gnu.linkonce.lr.*}) } + .rela.lrodata ${RELOCATING-0} : { *(.rela.lrodata${RELOCATING+ .rela.lrodata.* .rela.gnu.linkonce.lr.*}) }" +test "${LARGE_SECTIONS}" = "yes" && LARGE_BSS=" + .lbss ${RELOCATING-0} : + { + *(.dynlbss) + *(.lbss${RELOCATING+ .lbss.* .gnu.linkonce.lb.*}) + *(LARGE_COMMON) + }" +test "${LARGE_SECTIONS}" = "yes" && LARGE_SECTIONS=" + .lrodata ${RELOCATING-0} ${RELOCATING+ALIGN(${MAXPAGESIZE}) + (. & (${MAXPAGESIZE} - 1))} : + { + *(.lrodata${RELOCATING+ .lrodata.* .gnu.linkonce.lr.*}) + } + .ldata ${RELOCATING-0} ${RELOCATING+ALIGN(${MAXPAGESIZE}) + (. & (${MAXPAGESIZE} - 1))} : + { + *(.ldata${RELOCATING+ .ldata.* .gnu.linkonce.l.*}) + ${RELOCATING+. = ALIGN(. != 0 ? ${ALIGNMENT} : 1);} + }" +if test "${ENABLE_INITFINI_ARRAY}" = "yes"; then + SORT_INIT_ARRAY="KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))" + SORT_FINI_ARRAY="KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))" + CTORS_IN_INIT_ARRAY="EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o $OTHER_EXCLUDE_FILES) .ctors" + DTORS_IN_FINI_ARRAY="EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o $OTHER_EXCLUDE_FILES) .dtors" +else + SORT_INIT_ARRAY="KEEP (*(SORT(.init_array.*)))" + SORT_FINI_ARRAY="KEEP (*(SORT(.fini_array.*)))" + CTORS_IN_INIT_ARRAY= + DTORS_IN_FINI_ARRAY= +fi +INIT_ARRAY=".init_array ${RELOCATING-0} : + { + ${RELOCATING+${CREATE_SHLIB-PROVIDE_HIDDEN (${USER_LABEL_PREFIX}__init_array_start = .);}} + ${SORT_INIT_ARRAY} + KEEP (*(.init_array ${CTORS_IN_INIT_ARRAY})) + ${RELOCATING+${CREATE_SHLIB-PROVIDE_HIDDEN (${USER_LABEL_PREFIX}__init_array_end = .);}} + }" +FINI_ARRAY=".fini_array ${RELOCATING-0} : + { + ${RELOCATING+${CREATE_SHLIB-PROVIDE_HIDDEN (${USER_LABEL_PREFIX}__fini_array_start = .);}} + ${SORT_FINI_ARRAY} + KEEP (*(.fini_array ${DTORS_IN_FINI_ARRAY})) + ${RELOCATING+${CREATE_SHLIB-PROVIDE_HIDDEN (${USER_LABEL_PREFIX}__fini_array_end = .);}} + }" +CTOR=".ctors ${CONSTRUCTING-0} : + { + ${CONSTRUCTING+${CTOR_START}} + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o $OTHER_EXCLUDE_FILES) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + ${CONSTRUCTING+${CTOR_END}} + }" +DTOR=".dtors ${CONSTRUCTING-0} : + { + ${CONSTRUCTING+${DTOR_START}} + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o $OTHER_EXCLUDE_FILES) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + ${CONSTRUCTING+${DTOR_END}} + }" +STACK=" .stack ${RELOCATING-0}${RELOCATING+${STACK_ADDR}} : + { + ${RELOCATING+${USER_LABEL_PREFIX}_stack = .;} + *(.stack) + }" + +TEXT_START_ADDR="SEGMENT_START(\"text-segment\", ${TEXT_START_ADDR})" +SHLIB_TEXT_START_ADDR="SEGMENT_START(\"text-segment\", ${SHLIB_TEXT_START_ADDR:-0})" + +if [ -z "$SEPARATE_CODE" ]; then + SIZEOF_HEADERS_CODE=" + SIZEOF_HEADERS" +else + SIZEOF_HEADERS_CODE= +fi + +# if this is for an embedded system, don't add SIZEOF_HEADERS. +if [ -z "$EMBEDDED" ]; then + test -z "${TEXT_BASE_ADDRESS}" && TEXT_BASE_ADDRESS="${TEXT_START_ADDR}${SIZEOF_HEADERS_CODE}" + NDS32_INIT="" +else + test -z "${TEXT_BASE_ADDRESS}" && TEXT_BASE_ADDRESS="${TEXT_START_ADDR}" + NDS32_INIT=".nds32_init : { KEEP(*(.nds32_init)) }" +fi + +cat <<EOF +OUTPUT_FORMAT("${OUTPUT_FORMAT}", "${BIG_OUTPUT_FORMAT}", + "${LITTLE_OUTPUT_FORMAT}") +OUTPUT_ARCH(${OUTPUT_ARCH}) +${RELOCATING+ENTRY(${ENTRY})} + +${RELOCATING+${LIB_SEARCH_DIRS}} +${RELOCATING+${EXECUTABLE_SYMBOLS}} +${RELOCATING+${INPUT_FILES}} +${RELOCATING- /* For some reason, the Solaris linker makes bad executables + if gld -r is used and the intermediate file has sections starting + at non-zero addresses. Could be a Solaris ld bug, could be a GNU ld + bug. But for now assigning the zero vmas works. */} + +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + ${CREATE_SHLIB-${CREATE_PIE-${RELOCATING+PROVIDE (__executable_start = ${TEXT_START_ADDR}); . = ${TEXT_BASE_ADDRESS};}}} + /* Sections saved crt0 and crt1. */ + ${NDS32_INIT} + ${CREATE_SHLIB+${RELOCATING+. = ${SHLIB_TEXT_START_ADDR}${SIZEOF_HEADERS_CODE};}} + ${CREATE_PIE+${RELOCATING+PROVIDE (__executable_start = ${SHLIB_TEXT_START_ADDR}); . = ${SHLIB_TEXT_START_ADDR}${SIZEOF_HEADERS_CODE};}} +EOF + +emit_early_ro() +{ + cat <<EOF + ${INITIAL_READONLY_SECTIONS} + .note.gnu.build-id : { *(.note.gnu.build-id) } +EOF +} + +test -n "${SEPARATE_CODE}" || emit_early_ro + +test -n "${RELOCATING+0}" || unset NON_ALLOC_DYN +test -z "${NON_ALLOC_DYN}" || TEXT_DYNAMIC= +cat > ldscripts/dyntmp.$$ <<EOF + ${TEXT_DYNAMIC+${DYNAMIC}} + .hash ${RELOCATING-0} : { *(.hash) } + .gnu.hash ${RELOCATING-0} : { *(.gnu.hash) } + .dynsym ${RELOCATING-0} : { *(.dynsym) } + .dynstr ${RELOCATING-0} : { *(.dynstr) } + .gnu.version ${RELOCATING-0} : { *(.gnu.version) } + .gnu.version_d ${RELOCATING-0}: { *(.gnu.version_d) } + .gnu.version_r ${RELOCATING-0}: { *(.gnu.version_r) } +EOF + +if [ "x$COMBRELOC" = x ]; then + COMBRELOCCAT="cat >> ldscripts/dyntmp.$$" +else + COMBRELOCCAT="cat > $COMBRELOC" +fi +eval $COMBRELOCCAT <<EOF + ${INITIAL_RELOC_SECTIONS} + .rel.init ${RELOCATING-0} : { *(.rel.init) } + .rela.init ${RELOCATING-0} : { *(.rela.init) } + .rel.text ${RELOCATING-0} : { *(.rel.text${RELOCATING+ .rel.text.* .rel.gnu.linkonce.t.*}) } + .rela.text ${RELOCATING-0} : { *(.rela.text${RELOCATING+ .rela.text.* .rela.gnu.linkonce.t.*}) } + .rel.fini ${RELOCATING-0} : { *(.rel.fini) } + .rela.fini ${RELOCATING-0} : { *(.rela.fini) } + .rel.${RODATA_NAME} ${RELOCATING-0} : { *(.rel.${RODATA_NAME}${RELOCATING+ .rel.${RODATA_NAME}.* .rel.gnu.linkonce.r.*}) } + .rela.${RODATA_NAME} ${RELOCATING-0} : { *(.rela.${RODATA_NAME}${RELOCATING+ .rela.${RODATA_NAME}.* .rela.gnu.linkonce.r.*}) } + ${OTHER_READONLY_RELOC_SECTIONS} + .rel.data.rel.ro ${RELOCATING-0} : { *(.rel.data.rel.ro${RELOCATING+ .rel.data.rel.ro.* .rel.gnu.linkonce.d.rel.ro.*}) } + .rela.data.rel.ro ${RELOCATING-0} : { *(.rela.data.rel.ro${RELOCATING+ .rela.data.rel.ro.* .rela.gnu.linkonce.d.rel.ro.*}) } + .rel.data ${RELOCATING-0} : { *(.rel.data${RELOCATING+ .rel.data.* .rel.gnu.linkonce.d.*}) } + .rela.data ${RELOCATING-0} : { *(.rela.data${RELOCATING+ .rela.data.* .rela.gnu.linkonce.d.*}) } + ${OTHER_READWRITE_RELOC_SECTIONS} + .rel.tdata ${RELOCATING-0} : { *(.rel.tdata${RELOCATING+ .rel.tdata.* .rel.gnu.linkonce.td.*}) } + .rela.tdata ${RELOCATING-0} : { *(.rela.tdata${RELOCATING+ .rela.tdata.* .rela.gnu.linkonce.td.*}) } + .rel.tbss ${RELOCATING-0} : { *(.rel.tbss${RELOCATING+ .rel.tbss.* .rel.gnu.linkonce.tb.*}) } + .rela.tbss ${RELOCATING-0} : { *(.rela.tbss${RELOCATING+ .rela.tbss.* .rela.gnu.linkonce.tb.*}) } + .rel.ctors ${RELOCATING-0} : { *(.rel.ctors) } + .rela.ctors ${RELOCATING-0} : { *(.rela.ctors) } + .rel.dtors ${RELOCATING-0} : { *(.rel.dtors) } + .rela.dtors ${RELOCATING-0} : { *(.rela.dtors) } + .rel.got ${RELOCATING-0} : { *(.rel.got) } + .rela.got ${RELOCATING-0} : { *(.rela.got) } + ${OTHER_GOT_RELOC_SECTIONS} + ${REL_SDATA} + ${REL_SBSS} + ${REL_SDATA2} + ${REL_SBSS2} + .rel.${BSS_NAME} ${RELOCATING-0} : { *(.rel.${BSS_NAME}${RELOCATING+ .rel.${BSS_NAME}.* .rel.gnu.linkonce.b.*}) } + .rela.${BSS_NAME} ${RELOCATING-0} : { *(.rela.${BSS_NAME}${RELOCATING+ .rela.${BSS_NAME}.* .rela.gnu.linkonce.b.*}) } + ${REL_LARGE} + ${IREL_IN_PLT+$REL_IFUNC} + ${IREL_IN_PLT+$RELA_IFUNC} + ${IREL_IN_PLT-$REL_IPLT} + ${IREL_IN_PLT-$RELA_IPLT} +EOF + +if [ -n "$COMBRELOC" ]; then +cat >> ldscripts/dyntmp.$$ <<EOF + .rel.dyn ${RELOCATING-0} : + { +EOF +sed -e '/^[ ]*[{}][ ]*$/d;/:[ ]*$/d;/\.rela\./d;s/^.*: { *\(.*\)}$/ \1/' $COMBRELOC >> ldscripts/dyntmp.$$ +cat >> ldscripts/dyntmp.$$ <<EOF + } + .rela.dyn ${RELOCATING-0} : + { +EOF +sed -e '/^[ ]*[{}][ ]*$/d;/:[ ]*$/d;/\.rel\./d;s/^.*: { *\(.*\)}/ \1/' $COMBRELOC >> ldscripts/dyntmp.$$ +cat >> ldscripts/dyntmp.$$ <<EOF + } +EOF +fi + +cat >> ldscripts/dyntmp.$$ <<EOF + .rel.plt ${RELOCATING-0} : + { + *(.rel.plt) + ${IREL_IN_PLT+${RELOCATING+${CREATE_SHLIB-PROVIDE_HIDDEN (${USER_LABEL_PREFIX}__rel_iplt_start = .);}}} + ${IREL_IN_PLT+${RELOCATING+*(.rel.iplt)}} + ${IREL_IN_PLT+${RELOCATING+${CREATE_SHLIB-PROVIDE_HIDDEN (${USER_LABEL_PREFIX}__rel_iplt_end = .);}}} + } + .rela.plt ${RELOCATING-0} : + { + *(.rela.plt) + ${IREL_IN_PLT+${RELOCATING+${CREATE_SHLIB-PROVIDE_HIDDEN (${USER_LABEL_PREFIX}__rela_iplt_start = .);}}} + ${IREL_IN_PLT+${RELOCATING+*(.rela.iplt)}} + ${IREL_IN_PLT+${RELOCATING+${CREATE_SHLIB-PROVIDE_HIDDEN (${USER_LABEL_PREFIX}__rela_iplt_end = .);}}} + } + ${OTHER_PLT_RELOC_SECTIONS} +EOF + +emit_dyn() +{ + if test -z "${NO_REL_RELOCS}${NO_RELA_RELOCS}"; then + cat ldscripts/dyntmp.$$ + else + if test -z "${NO_REL_RELOCS}"; then + sed -e '/^[ ]*\.rela\.[^}]*$/,/}/d' -e '/^[ ]*\.rela\./d' ldscripts/dyntmp.$$ + fi + if test -z "${NO_RELA_RELOCS}"; then + sed -e '/^[ ]*\.rel\.[^}]*$/,/}/d' -e '/^[ ]*\.rel\./d' ldscripts/dyntmp.$$ + fi + fi + rm -f ldscripts/dyntmp.$$ +} + +test -n "${NON_ALLOC_DYN}${SEPARATE_CODE}" || emit_dyn + +cat <<EOF + .init ${RELOCATING-0} : + { + ${RELOCATING+${INIT_START}} + KEEP (*(SORT_NONE(.init))) + ${RELOCATING+${INIT_END}} + } ${FILL} + + ${TEXT_PLT+${PLT_NEXT_DATA-${PLT}}} + ${TINY_READONLY_SECTION} + .text ${RELOCATING-0} : + { + ${RELOCATING+${TEXT_START_SYMBOLS}} + ${RELOCATING+*(.text.unlikely .text.*_unlikely .text.unlikely.*)} + ${RELOCATING+*(.text.exit .text.exit.*)} + ${RELOCATING+*(.text.startup .text.startup.*)} + ${RELOCATING+*(.text.hot .text.hot.*)} + *(.text .stub${RELOCATING+ .text.* .gnu.linkonce.t.*}) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + ${RELOCATING+${OTHER_TEXT_SECTIONS}} + } ${FILL} + .fini ${RELOCATING-0} : + { + ${RELOCATING+${FINI_START}} + KEEP (*(SORT_NONE(.fini))) + ${RELOCATING+${FINI_END}} + } ${FILL} + ${RELOCATING+PROVIDE (__${ETEXT_NAME} = .);} + ${RELOCATING+PROVIDE (_${ETEXT_NAME} = .);} + ${RELOCATING+PROVIDE (${ETEXT_NAME} = .);} +EOF + +if test -n "${SEPARATE_CODE}"; then + if test -n "${RODATA_ADDR}"; then + RODATA_ADDR="\ +SEGMENT_START(\"rodata-segment\", ${RODATA_ADDR}) + SIZEOF_HEADERS" + else + RODATA_ADDR="ALIGN(${SEGMENT_SIZE}) + (. & (${MAXPAGESIZE} - 1))" + RODATA_ADDR="SEGMENT_START(\"rodata-segment\", ${RODATA_ADDR})" + fi + if test -n "${SHLIB_RODATA_ADDR}"; then + SHLIB_RODATA_ADDR="\ +SEGMENT_START(\"rodata-segment\", ${SHLIB_RODATA_ADDR}) + SIZEOF_HEADERS" + else + SHLIB_RODATA_ADDR="SEGMENT_START(\"rodata-segment\", ${SHLIB_RODATA_ADDR})" + SHLIB_RODATA_ADDR="ALIGN(${SEGMENT_SIZE}) + (. & (${MAXPAGESIZE} - 1))" + fi + cat <<EOF + /* Adjust the address for the rodata segment. We want to adjust up to + the same address within the page on the next page up. */ + ${CREATE_SHLIB-${CREATE_PIE-${RELOCATING+. = ${RODATA_ADDR};}}} + ${CREATE_SHLIB+${RELOCATING+. = ${SHLIB_RODATA_ADDR};}} + ${CREATE_PIE+${RELOCATING+. = ${SHLIB_RODATA_ADDR};}} +EOF + emit_early_ro + emit_dyn +fi + +cat <<EOF + ${WRITABLE_RODATA-${RODATA}} + .${RODATA_NAME}1 ${RELOCATING-0} : { *(.${RODATA_NAME}1) } + ${CREATE_SHLIB-${SDATA2}} + ${CREATE_SHLIB-${SBSS2}} + ${OTHER_READONLY_SECTIONS} + .eh_frame_hdr : { *(.eh_frame_hdr) } + .eh_frame ${RELOCATING-0} : ONLY_IF_RO { KEEP (*(.eh_frame)) } + .gcc_except_table ${RELOCATING-0} : ONLY_IF_RO { *(.gcc_except_table + .gcc_except_table.*) } + /* These sections are generated by the Sun/Oracle C++ compiler. */ + .exception_ranges ${RELOCATING-0} : ONLY_IF_RO { *(.exception_ranges + .exception_ranges*) } + ${TEXT_PLT+${PLT_NEXT_DATA+${PLT}}} + + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + ${CREATE_SHLIB-${CREATE_PIE-${RELOCATING+. = ${DATA_ADDR-${DATA_SEGMENT_ALIGN}};}}} + ${CREATE_SHLIB+${RELOCATING+. = ${SHLIB_DATA_ADDR-${DATA_SEGMENT_ALIGN}};}} + ${CREATE_PIE+${RELOCATING+. = ${SHLIB_DATA_ADDR-${DATA_SEGMENT_ALIGN}};}} + + /* Exception handling */ + .eh_frame ${RELOCATING-0} : ONLY_IF_RW { KEEP (*(.eh_frame)) } + .gcc_except_table ${RELOCATING-0} : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } + .exception_ranges ${RELOCATING-0} : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } + + /* Thread Local Storage sections */ + .tdata ${RELOCATING-0} : { *(.tdata${RELOCATING+ .tdata.* .gnu.linkonce.td.*}) } + .tbss ${RELOCATING-0} : { *(.tbss${RELOCATING+ .tbss.* .gnu.linkonce.tb.*})${RELOCATING+ *(.tcommon)} } + + .preinit_array ${RELOCATING-0} : + { + ${RELOCATING+${CREATE_SHLIB-PROVIDE_HIDDEN (${USER_LABEL_PREFIX}__preinit_array_start = .);}} + KEEP (*(.preinit_array)) + ${RELOCATING+${CREATE_SHLIB-PROVIDE_HIDDEN (${USER_LABEL_PREFIX}__preinit_array_end = .);}} + } + ${RELOCATING+${INIT_ARRAY}} + ${RELOCATING+${FINI_ARRAY}} + ${SMALL_DATA_CTOR-${RELOCATING+${CTOR}}} + ${SMALL_DATA_DTOR-${RELOCATING+${DTOR}}} + .jcr ${RELOCATING-0} : { KEEP (*(.jcr)) } + + ${RELOCATING+${DATARELRO}} + ${OTHER_RELRO_SECTIONS} + ${TEXT_DYNAMIC-${DYNAMIC}} + ${DATA_GOT+${RELRO_NOW+${GOT}}} + ${DATA_GOT+${RELRO_NOW+${GOTPLT}}} + ${DATA_GOT+${RELRO_NOW-${SEPARATE_GOTPLT+${GOT}}}} + ${RELOCATING+${DATA_SEGMENT_RELRO_END}} + ${INITIAL_READWRITE_SECTIONS} + ${DATA_GOT+${RELRO_NOW-${SEPARATE_GOTPLT-${GOT}}}} + ${DATA_GOT+${RELRO_NOW-${GOTPLT}}} + + ${DATA_PLT+${PLT_BEFORE_GOT-${PLT}}} + + /* For _SDA_BASE_ aligment. */ + ${RELOCATING+. = ALIGN(4);} + + .data ${RELOCATING-0} : + { + ${RELOCATING+${DATA_START_SYMBOLS}} + *(.data${RELOCATING+ .data.* .gnu.linkonce.d.*}) + ${CONSTRUCTING+SORT(CONSTRUCTORS)} + } + .data1 ${RELOCATING-0} : { *(.data1) } + ${WRITABLE_RODATA+${RODATA}} + ${OTHER_READWRITE_SECTIONS} + ${SMALL_DATA_CTOR+${RELOCATING+${CTOR}}} + ${SMALL_DATA_DTOR+${RELOCATING+${DTOR}}} + ${RELOCATING+. = ALIGN(4);} + ${DATA_PLT+${PLT_BEFORE_GOT+${PLT}}} + ${SDATA_GOT+${RELOCATING+${OTHER_GOT_SYMBOLS+. = .; ${OTHER_GOT_SYMBOLS}}}} + ${SDATA_GOT+${GOT}} + ${SDATA_GOT+${OTHER_GOT_SECTIONS}} + ${SDATA} + ${OTHER_SDATA_SECTIONS} + ${RELOCATING+. = ALIGN(4);} + ${RELOCATING+${DATA_END_SYMBOLS-${USER_LABEL_PREFIX}_edata = .; PROVIDE (${USER_LABEL_PREFIX}edata = .);}} + ${RELOCATING+. = .;} + ${RELOCATING+${USER_LABEL_PREFIX}__bss_start = .;} + ${RELOCATING+${OTHER_BSS_SYMBOLS}} + ${SBSS} + ${BSS_PLT+${PLT}} + .${BSS_NAME} ${RELOCATING-0} : + { + *(.dyn${BSS_NAME}) + *(.${BSS_NAME}${RELOCATING+ .${BSS_NAME}.* .gnu.linkonce.b.*}) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. + FIXME: Why do we need it? When there is no .bss section, we don't + pad the .data section. */ + ${RELOCATING+. = ALIGN(. != 0 ? ${ALIGNMENT} : 1);} + } + ${OTHER_BSS_SECTIONS} + ${LARGE_BSS_AFTER_BSS+${LARGE_BSS}} + ${RELOCATING+_end = .;} + ${RELOCATING+${OTHER_BSS_END_SYMBOLS}} + ${RELOCATING+. = ALIGN(${ALIGNMENT});} +EOF + +LARGE_DATA_ADDR=". = SEGMENT_START(\"ldata-segment\", ${LARGE_DATA_ADDR-.});" +SHLIB_LARGE_DATA_ADDR=". = SEGMENT_START(\"ldata-segment\", ${SHLIB_LARGE_DATA_ADDR-.});" + + cat <<EOF + ${RELOCATING+${CREATE_SHLIB-${CREATE_PIE-${LARGE_DATA_ADDR}}}} + ${RELOCATING+${CREATE_SHLIB+${SHLIB_LARGE_DATA_ADDR}}} + ${RELOCATING+${CREATE_PIE+${SHLIB_LARGE_DATA_ADDR}}} + ${LARGE_SECTIONS} + ${LARGE_BSS_AFTER_BSS-${LARGE_BSS}} + ${RELOCATING+. = ALIGN(${ALIGNMENT});} + ${RELOCATING+${OTHER_END_SYMBOLS}} + ${RELOCATING+${END_SYMBOLS-${USER_LABEL_PREFIX}_end = .; PROVIDE (${USER_LABEL_PREFIX}end = .);}} + ${RELOCATING+${DATA_SEGMENT_END}} +EOF + +test -z "${NON_ALLOC_DYN}" || emit_dyn + +cat <<EOF + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + + .comment 0 : { *(.comment) } + +EOF + +. $srcdir/scripttempl/DWARF.sc + +cat <<EOF + + ${TINY_DATA_SECTION} + ${TINY_BSS_SECTION} + + ${STACK_ADDR+${STACK}} + ${ATTRS_SECTIONS} + ${OTHER_SECTIONS} + ${RELOCATING+${OTHER_SYMBOLS}} + ${RELOCATING+${DISCARDED}} +} +EOF diff --git a/ld/testsuite/ChangeLog b/ld/testsuite/ChangeLog index 692b3b7..ba3f917 100644 --- a/ld/testsuite/ChangeLog +++ b/ld/testsuite/ChangeLog @@ -1,3 +1,29 @@ +2013-12-13 Kuan-Lin Chen <kuanlinchentw@gmail.com> + + * lib/ld-lib.exp: Add NDS32 to list of targets that do not support + shared library generation. + * ld-nds32: New directory. + * ld-nds32/branch.d: New test. + * ld-nds32/branch.ld: New test. + * ld-nds32/branch.s: New test. + * ld-nds32/diff.d: New test. + * ld-nds32/diff.ld: New test. + * ld-nds32/diff.s: New test. + * ld-nds32/gp.d: New test. + * ld-nds32/gp.ld: New test. + * ld-nds32/gp.s: New test. + * ld-nds32/imm.d: New test. + * ld-nds32/imm.ld: New test. + * ld-nds32/imm.s: New test. + * ld-nds32/imm_symbol.s: New test. + * ld-nds32/relax_jmp.d: New test. + * ld-nds32/relax_jmp.ld: New test. + * ld-nds32/relax_jmp.s: New test. + * ld-nds32/relax_load_store.d: New test. + * ld-nds32/relax_load_store.ld: New test. + * ld-nds32/relax_load_store.s: New test. + * ld-nds32/nds32.exp: New file. + 2013-12-12 H.J. Lu <hongjiu.lu@intel.com> * ld-elf/ehdr_start-userdef.d: Add "#...". diff --git a/ld/testsuite/ld-nds32/branch.d b/ld/testsuite/ld-nds32/branch.d new file mode 100644 index 0000000..85798e9 --- /dev/null +++ b/ld/testsuite/ld-nds32/branch.d @@ -0,0 +1,24 @@ +#as: -Os +#ld: -static --relax -T $srcdir/$subdir/branch.ld +#objdump: -d --prefix-addresses -j .text + +.*: file format .*nds32.* + + +Disassembly of section .text: +0+0000 <[^>]*> beq \$r0, \$r1, 0000002c <main> +0+0004 <[^>]*> bne \$r0, \$r1, 0000002c <main> +0+0008 <[^>]*> bnez38 \$r0, 0000002c <main> +0+000a <[^>]*> beqz38 \$r0, 0000002c <main> +0+000c <[^>]*> bgez \$r0, 0000002c <main> +.* +0+0012 <[^>]*> bgezal \$r0, 0000002c <main> +0+0016 <[^>]*> bgtz \$r0, 0000002c <main> +.* +0+001c <[^>]*> blez \$r0, 0000002c <main> +.* +0+0022 <[^>]*> bltz \$r0, 0000002c <main> +0+0026 <[^>]*> srli45 \$r0, 0 +0+0028 <[^>]*> bltzal \$r0, 0000002c <main> +0+002c <main>.* + diff --git a/ld/testsuite/ld-nds32/branch.ld b/ld/testsuite/ld-nds32/branch.ld new file mode 100644 index 0000000..28eb4e0 --- /dev/null +++ b/ld/testsuite/ld-nds32/branch.ld @@ -0,0 +1,6 @@ +SECTIONS +{ + .text 0x0 : { + * (.text .text.*); + } +} diff --git a/ld/testsuite/ld-nds32/branch.s b/ld/testsuite/ld-nds32/branch.s new file mode 100644 index 0000000..284645e --- /dev/null +++ b/ld/testsuite/ld-nds32/branch.s @@ -0,0 +1,18 @@ +.text +.global _start +_start: + beq $r0, $r1, main + bne $r0, $r1, main + beqz $r0, main + bnez $r0, main + bgez $r0, main + bgezal $r0, main + bgtz $r0, main + blez $r0, main + bltz $r0, main + bltzal $r0, main +.section .text.2, "ax" +.globl main +main: + nop + diff --git a/ld/testsuite/ld-nds32/diff.d b/ld/testsuite/ld-nds32/diff.d new file mode 100644 index 0000000..ef79586 --- /dev/null +++ b/ld/testsuite/ld-nds32/diff.d @@ -0,0 +1,16 @@ +#as: -Os +#ld: -static --relax -T $srcdir/$subdir/diff.ld +#objdump: -D --prefix-addresses -j .data --show-raw-insn + +.*: file format .*nds32.* + + +Disassembly of section .data: +00008000 <WORD> (7e 00 00 00|00 00 00 7e).* +00008004 <HALF> (7e 00 7e fe|00 7e 7e fe).* +00008006 <BYTE> 7e fe 00 fe.* +00008007 <ULEB128> fe 00.* + ... +00008009 <ULEB128_2> fe 00.* +.* +.* diff --git a/ld/testsuite/ld-nds32/diff.ld b/ld/testsuite/ld-nds32/diff.ld new file mode 100644 index 0000000..5e4dd29 --- /dev/null +++ b/ld/testsuite/ld-nds32/diff.ld @@ -0,0 +1,10 @@ +SECTIONS +{ + .text 0x4000 : { + * (.text .text.*); + } + + .data 0x8000 : { + * (.data .data.*); + } +} diff --git a/ld/testsuite/ld-nds32/diff.s b/ld/testsuite/ld-nds32/diff.s new file mode 100644 index 0000000..a2844b7 --- /dev/null +++ b/ld/testsuite/ld-nds32/diff.s @@ -0,0 +1,32 @@ + .global _start + .global WORD + .global HALF + .global BYTE + .global ULEB128 +.text +_start: + nop +.L0: + l.w $r0, WORD + .zero 122 +.L1: + nop + +.section code, "ax" +FOO: + ret + +.data +WORD: + .word .L1-.L0 +HALF: + .half .L1-.L0 +BYTE: + .byte .L1-.L0 +ULEB128: + .uleb128 .L1-.L0 +ULEB128_2: + .uleb128 .L1-.L0 + .align 2 +PAD: + .long 0 diff --git a/ld/testsuite/ld-nds32/gp.d b/ld/testsuite/ld-nds32/gp.d new file mode 100644 index 0000000..dac6eaa --- /dev/null +++ b/ld/testsuite/ld-nds32/gp.d @@ -0,0 +1,18 @@ +#as: -Os +#ld: -static -T \$srcdir/\$subdir/gp.ld +#objdump: -d --prefix-addresses -j .text + +.*: file format .*nds32.* + + +Disassembly of section .text: +0+0000 <[^>]*> addi.gp \$r0, 8192 +0+0004 <[^>]*> lbi.gp \$r0, \[\+ 8192\] +0+0008 <[^>]*> lbsi.gp \$r0, \[\+ 8192\] +0+000c <[^>]*> lhi.gp \$r0, \[\+ 8192\] +0+0010 <[^>]*> lhsi.gp \$r0, \[\+ 8192\] +0+0014 <[^>]*> lwi.gp \$r0, \[\+ 8192\] +0+0018 <[^>]*> sbi.gp \$r0, \[\+ 8192\] +0+001c <[^>]*> shi.gp \$r0, \[\+ 8192\] +0+0020 <[^>]*> swi.gp \$r0, \[\+ 8192\] + diff --git a/ld/testsuite/ld-nds32/gp.ld b/ld/testsuite/ld-nds32/gp.ld new file mode 100644 index 0000000..b537b95 --- /dev/null +++ b/ld/testsuite/ld-nds32/gp.ld @@ -0,0 +1,11 @@ +SECTIONS +{ + .text 0x0 : { + * (.text .text.*); + } + + .data 0x3000 : { + * (.data .data.*); + } + _SDA_BASE_ = 0x1000; +} diff --git a/ld/testsuite/ld-nds32/gp.s b/ld/testsuite/ld-nds32/gp.s new file mode 100644 index 0000000..d0fde2a --- /dev/null +++ b/ld/testsuite/ld-nds32/gp.s @@ -0,0 +1,18 @@ +.data +.global mydata +mydata: + .word 0x11 + +.text +.global _start +_start: + addi.gp $r0, mydata + lbi.gp $r0, [+mydata] + lbsi.gp $r0, [+mydata] + lhi.gp $r0, [+mydata] + lhsi.gp $r0, [+mydata] + lwi.gp $r0, [+mydata] + sbi.gp $r0, [+mydata] + shi.gp $r0, [+mydata] + swi.gp $r0, [+mydata] + diff --git a/ld/testsuite/ld-nds32/imm.d b/ld/testsuite/ld-nds32/imm.d new file mode 100644 index 0000000..fdaacf3 --- /dev/null +++ b/ld/testsuite/ld-nds32/imm.d @@ -0,0 +1,15 @@ +#source: imm.s +#source: imm_symbol.s +#as: -Os +#ld: -static -T $srcdir/$subdir/imm.ld --relax +#objdump: -d --prefix-addresses -j .text + +.*: file format .*nds32.* + + +Disassembly of section .text: +0+1000 <[^>]*> sethi \$r0, 0x11223 +0+1004 <[^>]*> ori \$r0, \$r0, 836 +0+1008 <[^>]*> movi \$r0, 70179 +0+100c <[^>]*> movi55 \$r0, 15 + diff --git a/ld/testsuite/ld-nds32/imm.ld b/ld/testsuite/ld-nds32/imm.ld new file mode 100644 index 0000000..b7d12cc --- /dev/null +++ b/ld/testsuite/ld-nds32/imm.ld @@ -0,0 +1,6 @@ +SECTIONS +{ + .text 0x1000 : { + * (.text .text.*); + } +} diff --git a/ld/testsuite/ld-nds32/imm.s b/ld/testsuite/ld-nds32/imm.s new file mode 100644 index 0000000..1d10831 --- /dev/null +++ b/ld/testsuite/ld-nds32/imm.s @@ -0,0 +1,7 @@ +.text +.global _start +_start: + la $r0, imm32 + la $r0, imm20 + la $r0, imm5 + diff --git a/ld/testsuite/ld-nds32/imm_symbol.s b/ld/testsuite/ld-nds32/imm_symbol.s new file mode 100644 index 0000000..1c75db2 --- /dev/null +++ b/ld/testsuite/ld-nds32/imm_symbol.s @@ -0,0 +1,7 @@ +.globl imm32 +.globl imm20 +.globl imm5 + +.set imm32, 0x11223344 +.set imm20, 0x11223 +.set imm5, 0xf diff --git a/ld/testsuite/ld-nds32/nds32.exp b/ld/testsuite/ld-nds32/nds32.exp new file mode 100644 index 0000000..6f95c17 --- /dev/null +++ b/ld/testsuite/ld-nds32/nds32.exp @@ -0,0 +1,26 @@ +# Copyright (C) 2012-2013 Free Software Foundation, Inc. +# Contributed by Andes Technology Corporation. + +# 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. + +if {[istarget "nds32*-*"]} { + run_dump_test "diff" + run_dump_test "gp" + run_dump_test "relax_jmp" + run_dump_test "imm" + run_dump_test "branch" + run_dump_test "relax_load_store" +} diff --git a/ld/testsuite/ld-nds32/relax_jmp.d b/ld/testsuite/ld-nds32/relax_jmp.d new file mode 100644 index 0000000..b1d2051 --- /dev/null +++ b/ld/testsuite/ld-nds32/relax_jmp.d @@ -0,0 +1,12 @@ +#as: -Os +#ld: -static --relax -T $srcdir/$subdir/relax_jmp.ld +#objdump: -d --prefix-addresses -j .text + +.*: file format .*nds32.* + + +Disassembly of section .text: +0+0000 <[^>]*> j8 00000006 <main> +0+0002 <[^>]*> jal 00000006 <main> +0+0006 <[^>]*> srli45 \$r0, 0 + diff --git a/ld/testsuite/ld-nds32/relax_jmp.ld b/ld/testsuite/ld-nds32/relax_jmp.ld new file mode 100644 index 0000000..28eb4e0 --- /dev/null +++ b/ld/testsuite/ld-nds32/relax_jmp.ld @@ -0,0 +1,6 @@ +SECTIONS +{ + .text 0x0 : { + * (.text .text.*); + } +} diff --git a/ld/testsuite/ld-nds32/relax_jmp.s b/ld/testsuite/ld-nds32/relax_jmp.s new file mode 100644 index 0000000..e281763 --- /dev/null +++ b/ld/testsuite/ld-nds32/relax_jmp.s @@ -0,0 +1,10 @@ +.text +.global _start +_start: + j main + jal main +.section .text.2, "ax" +.globl main +main: + nop + diff --git a/ld/testsuite/ld-nds32/relax_load_store.d b/ld/testsuite/ld-nds32/relax_load_store.d new file mode 100644 index 0000000..07c4fe7 --- /dev/null +++ b/ld/testsuite/ld-nds32/relax_load_store.d @@ -0,0 +1,12 @@ +#as: -Os +#ld: -static --relax -T \$srcdir/\$subdir/relax_load_store.ld +#objdump: -d --prefix-addresses -j .text + +.*: file format .*nds32.* + + +Disassembly of section .text: +0+0000 <[^>]*> lwi.gp \$r0, \[\+ 0\] +0+0004 <[^>]*> lhi.gp \$r0, \[\+ 4\] +0+0008 <[^>]*> lbi.gp \$r0, \[\+ 6\] + diff --git a/ld/testsuite/ld-nds32/relax_load_store.ld b/ld/testsuite/ld-nds32/relax_load_store.ld new file mode 100644 index 0000000..1bc15a9 --- /dev/null +++ b/ld/testsuite/ld-nds32/relax_load_store.ld @@ -0,0 +1,10 @@ +SECTIONS +{ + .text 0x0 : { + * (.text .text.*); + } + + .data 0x3000 : { + * (.data .data.*); + } +} diff --git a/ld/testsuite/ld-nds32/relax_load_store.s b/ld/testsuite/ld-nds32/relax_load_store.s new file mode 100644 index 0000000..790c97c --- /dev/null +++ b/ld/testsuite/ld-nds32/relax_load_store.s @@ -0,0 +1,17 @@ +.data +.global myword +myword: + .word 0x11 +.globl myshort +myshort: + .short 0x11 +.globl mybyte +mybyte: + .byte 0x11 + +.text +.global _start +_start: + l.w $r0, myword + l.h $r0, myshort + l.b $r0, mybyte diff --git a/ld/testsuite/lib/ld-lib.exp b/ld/testsuite/lib/ld-lib.exp index 175449a..c03f5ee 100644 --- a/ld/testsuite/lib/ld-lib.exp +++ b/ld/testsuite/lib/ld-lib.exp @@ -1596,6 +1596,7 @@ proc check_shared_lib_support { } { && ![istarget moxie-*-*] && ![istarget msp430-*-*] && ![istarget mt-*-*] + && ![istarget nds32*-*-*] && ![istarget openrisc-*-*] && ![istarget or32-*-*] && ![istarget pj-*-*] diff --git a/opcodes/ChangeLog b/opcodes/ChangeLog index 3f3e154..f3ac6fd 100644 --- a/opcodes/ChangeLog +++ b/opcodes/ChangeLog @@ -1,3 +1,17 @@ +2013-12-13 Kuan-Lin Chen <kuanlinchentw@gmail.com> + Wei-Cheng Wang <cole945@gmail.com> + + * Makefile.am (TARGET_LIBOPCODES_CFILES): Add nds32-asm.c + and nds32-dis.c. + * Makefile.in: Regenerate. + * configure.in: Add case for bfd_nds32_arch. + * configure: Regenerate. + * disassemble.c (ARCH_nds32): Define. + * nds32-asm.c: New file for nds32. + * nds32-asm.h: New file for nds32. + * nds32-dis.c: New file for nds32. + * nds32-opc.h: New file for nds32. + 2013-12-05 Nick Clifton <nickc@redhat.com> * s390-mkopc.c (dumpTable): Provide a format string to printf so diff --git a/opcodes/Makefile.am b/opcodes/Makefile.am index 0a65b75..1d43a25 100644 --- a/opcodes/Makefile.am +++ b/opcodes/Makefile.am @@ -206,6 +206,8 @@ TARGET_LIBOPCODES_CFILES = \ mt-dis.c \ mt-ibld.c \ mt-opc.c \ + nds32-asm.c \ + nds32-dis.c \ nios2-dis.c \ nios2-opc.c \ ns32k-dis.c \ diff --git a/opcodes/Makefile.in b/opcodes/Makefile.in index 42b107c..b161d62 100644 --- a/opcodes/Makefile.in +++ b/opcodes/Makefile.in @@ -478,6 +478,8 @@ TARGET_LIBOPCODES_CFILES = \ mt-dis.c \ mt-ibld.c \ mt-opc.c \ + nds32-asm.c \ + nds32-dis.c \ nios2-dis.c \ nios2-opc.c \ ns32k-dis.c \ @@ -879,6 +881,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mt-dis.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mt-ibld.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mt-opc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nds32-asm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nds32-dis.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nios2-dis.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nios2-opc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ns32k-dis.Plo@am__quote@ diff --git a/opcodes/configure b/opcodes/configure index 47bc29b..eb7532e 100755 --- a/opcodes/configure +++ b/opcodes/configure @@ -12546,6 +12546,7 @@ if test x${all_targets} = xfalse ; then bfd_mn10300_arch) ta="$ta m10300-dis.lo m10300-opc.lo" ;; bfd_mt_arch) ta="$ta mt-asm.lo mt-desc.lo mt-dis.lo mt-ibld.lo mt-opc.lo" using_cgen=yes ;; bfd_msp430_arch) ta="$ta msp430-dis.lo msp430-decode.lo" ;; + bfd_nds32_arch) ta="$ta nds32-asm.lo nds32-dis.lo" ;; bfd_nios2_arch) ta="$ta nios2-dis.lo nios2-opc.lo" ;; bfd_ns32k_arch) ta="$ta ns32k-dis.lo" ;; bfd_openrisc_arch) ta="$ta openrisc-asm.lo openrisc-desc.lo openrisc-dis.lo openrisc-ibld.lo openrisc-opc.lo" using_cgen=yes ;; diff --git a/opcodes/configure.in b/opcodes/configure.in index 8309373..0d5b6d2 100644 --- a/opcodes/configure.in +++ b/opcodes/configure.in @@ -293,6 +293,7 @@ if test x${all_targets} = xfalse ; then bfd_mn10300_arch) ta="$ta m10300-dis.lo m10300-opc.lo" ;; bfd_mt_arch) ta="$ta mt-asm.lo mt-desc.lo mt-dis.lo mt-ibld.lo mt-opc.lo" using_cgen=yes ;; bfd_msp430_arch) ta="$ta msp430-dis.lo msp430-decode.lo" ;; + bfd_nds32_arch) ta="$ta nds32-asm.lo nds32-dis.lo" ;; bfd_nios2_arch) ta="$ta nios2-dis.lo nios2-opc.lo" ;; bfd_ns32k_arch) ta="$ta ns32k-dis.lo" ;; bfd_openrisc_arch) ta="$ta openrisc-asm.lo openrisc-desc.lo openrisc-dis.lo openrisc-ibld.lo openrisc-opc.lo" using_cgen=yes ;; diff --git a/opcodes/disassemble.c b/opcodes/disassemble.c index 55a44ec..7ed1274 100644 --- a/opcodes/disassemble.c +++ b/opcodes/disassemble.c @@ -67,6 +67,7 @@ #define ARCH_moxie #define ARCH_mt #define ARCH_msp430 +#define ARCH_nds32 #define ARCH_nios2 #define ARCH_ns32k #define ARCH_openrisc @@ -296,6 +297,11 @@ disassembler (abfd) disassemble = print_insn_msp430; break; #endif +#ifdef ARCH_nds32 + case bfd_arch_nds32: + disassemble = print_insn_nds32; + break; +#endif #ifdef ARCH_ns32k case bfd_arch_ns32k: disassemble = print_insn_ns32k; diff --git a/opcodes/nds32-asm.c b/opcodes/nds32-asm.c new file mode 100644 index 0000000..958301a --- /dev/null +++ b/opcodes/nds32-asm.c @@ -0,0 +1,1634 @@ +/* NDS32-specific support for 32-bit ELF. + Copyright (C) 2012-2013 Free Software Foundation, Inc. + Contributed by Andes Technology Corporation. + + 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 <config.h> + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <assert.h> + +#include "safe-ctype.h" +#include "libiberty.h" +#include "hashtab.h" +#include "bfd.h" + +#include "opcode/nds32.h" +#include "nds32-asm.h" + +/* There at at most MAX_LEX_NUM lexical elements in a syntax. */ +#define MAX_LEX_NUM 32 +/* A operand in syntax string should be at most this long. */ +#define MAX_LEX_LEN 32 +/* The max length of a keyword can be. */ +#define MAX_KEYWORD_LEN 32 +/* This LEX is a plain char or operand. */ +#define IS_LEX_CHAR(c) (((c) >> 7) == 0) +#define LEX_SET_FIELD(c) ((c) | SYN_FIELD) +#define LEX_GET_FIELD(c) operand_fields[((c) & 0xff)] +/* Get the char in this lexical element. */ +#define LEX_CHAR(c) ((c) & 0xff) + +#define USRIDX(group, usr) ((group) | ((usr) << 5)) +#define SRIDX(major, minor, ext) \ + (((major) << 7) | ((minor) << 3) | (ext)) + +static int parse_re2 (struct nds32_asm_desc *, struct nds32_asm_insn *, + char **, int64_t *); +static int parse_fe5 (struct nds32_asm_desc *, struct nds32_asm_insn *, + char **, int64_t *); +static int parse_pi5 (struct nds32_asm_desc *, struct nds32_asm_insn *, + char **, int64_t *); + + +enum +{ + /* This is a field (operand) of just a separator char. */ + SYN_FIELD = 0x100, + + /* This operand is used for input or output. (define or use) */ + SYN_INPUT = 0x1000, + SYN_OUTPUT = 0x2000, + SYN_LOPT = 0x4000, + SYN_ROPT = 0x8000, + + /* Hardware resources. */ + HW_GPR = 0, + HW_USR, + HW_DXR, + HW_SR, + HW_FSR, + HW_FDR, + HW_CP, /* Co-processor ID. */ + HW_CPR, /* Co-processor registers. */ + HW_ABDIM, /* [ab][di]m? flag for LSMWA?. */ + HW_ABM, /* [ab]m? flag for LSMWZB. */ + HW_DTITON, + HW_DTITOFF, + HW_DPREF_ST, + HW_CCTL_ST0, + HW_CCTL_ST1, + HW_CCTL_ST2, + HW_CCTL_ST3, + HW_CCTL_ST4, + HW_CCTL_ST5, + HW_CCTL_LV, + HW_TLBOP_ST, + HW_STANDBY_ST, + HW_MSYNC_ST, + _HW_LAST, + /* TODO: Maybe we should add a new type to distinguish address and + const int. Only the former allows symbols and relocations. */ + HW_INT, + HW_UINT +}; + + +/* These are operand prefixes for input/output semantic. + + % input + = output + & both + {} optional operand + + Field table for operands and bit-fields. */ + +static const field_t operand_fields[] = +{ + {"rt", 20, 5, 0, HW_GPR, NULL}, + {"ra", 15, 5, 0, HW_GPR, NULL}, + {"rb", 10, 5, 0, HW_GPR, NULL}, + {"rd", 5, 5, 0, HW_GPR, NULL}, + {"fst", 20, 5, 0, HW_FSR, NULL}, + {"fsa", 15, 5, 0, HW_FSR, NULL}, + {"fsb", 10, 5, 0, HW_FSR, NULL}, + {"fdt", 20, 5, 0, HW_FDR, NULL}, + {"fda", 15, 5, 0, HW_FDR, NULL}, + {"fdb", 10, 5, 0, HW_FDR, NULL}, + {"cprt", 20, 5, 0, HW_CPR, NULL}, + {"cp", 13, 2, 0, HW_CP, NULL}, + {"sh", 5, 5, 0, HW_UINT, NULL}, /* sh in ALU instructions. */ + {"sv", 8, 2, 0, HW_UINT, NULL}, /* sv in MEM instructions. */ + {"dt", 21, 1, 0, HW_DXR, NULL}, + {"usr", 10, 10, 0, HW_USR, NULL}, /* User Special Registers. */ + {"sr", 10, 10, 0, HW_SR, NULL}, /* System Registers. */ + {"ridx", 10, 10, 0, HW_UINT, NULL}, /* Raw value for mfusr/mfsr. */ + {"enb4", 6, 9, 0, HW_UINT, NULL}, /* Enable4 for LSMW. */ + {"swid", 5, 15, 0, HW_UINT, NULL}, + {"stdby_st", 5, 2, 0, HW_STANDBY_ST, NULL}, + {"tlbop_st", 5, 5, 0, HW_TLBOP_ST, NULL}, + {"tlbop_stx", 5, 5, 0, HW_UINT, NULL}, + {"cctl_st0", 5, 5, 0, HW_CCTL_ST0, NULL}, + {"cctl_st1", 5, 5, 0, HW_CCTL_ST1, NULL}, + {"cctl_st2", 5, 5, 0, HW_CCTL_ST2, NULL}, + {"cctl_st3", 5, 5, 0, HW_CCTL_ST3, NULL}, + {"cctl_st4", 5, 5, 0, HW_CCTL_ST4, NULL}, + {"cctl_st5", 5, 5, 0, HW_CCTL_ST5, NULL}, + {"cctl_stx", 5, 5, 0, HW_UINT, NULL}, + {"cctl_lv", 10, 1, 0, HW_CCTL_LV, NULL}, + {"msync_st", 5, 3, 0, HW_MSYNC_ST, NULL}, + {"msync_stx", 5, 3, 0, HW_UINT, NULL}, + {"dpref_st", 20, 5, 0, HW_DPREF_ST, NULL}, + {"rt5", 5, 5, 0, HW_GPR, NULL}, + {"ra5", 0, 5, 0, HW_GPR, NULL}, + {"rt4", 5, 4, 0, HW_GPR, NULL}, + {"rt3", 6, 3, 0, HW_GPR, NULL}, + {"rt38", 8, 3, 0, HW_GPR, NULL}, /* rt3 used in 38 form. */ + {"ra3", 3, 3, 0, HW_GPR, NULL}, + {"rb3", 0, 3, 0, HW_GPR, NULL}, + {"rt5e", 4, 4, 1, HW_GPR, NULL}, /* movd44 */ + {"ra5e", 0, 4, 1, HW_GPR, NULL}, /* movd44 */ + {"re2", 5, 2, 0, HW_GPR, parse_re2}, /* re in push25/pop25. */ + {"fe5", 0, 5, 2, HW_UINT, parse_fe5}, /* imm5u in lwi45.fe. */ + {"pi5", 0, 5, 0, HW_UINT, parse_pi5}, /* imm5u in movpi45. */ + {"abdim", 2, 3, 0, HW_ABDIM, NULL}, /* Flags for LSMW. */ + {"abm", 2, 3, 0, HW_ABM, NULL}, /* Flags for LSMWZB. */ + {"dtiton", 8, 2, 0, HW_DTITON, NULL}, + {"dtitoff", 8, 2, 0, HW_DTITOFF, NULL}, + + {"i5s", 0, 5, 0, HW_INT, NULL}, + {"i10s", 0, 10, 0, HW_INT, NULL}, + {"i15s", 0, 15, 0, HW_INT, NULL}, + {"i19s", 0, 19, 0, HW_INT, NULL}, + {"i20s", 0, 20, 0, HW_INT, NULL}, + {"i8s1", 0, 8, 1, HW_INT, NULL}, + {"i11br3", 8, 11, 0, HW_INT, NULL}, + {"i14s1", 0, 14, 1, HW_INT, NULL}, + {"i15s1", 0, 15, 1, HW_INT, NULL}, + {"i16s1", 0, 16, 1, HW_INT, NULL}, + {"i18s1", 0, 18, 1, HW_INT, NULL}, + {"i24s1", 0, 24, 1, HW_INT, NULL}, + {"i8s2", 0, 8, 2, HW_INT, NULL}, + {"i12s2", 0, 12, 2, HW_INT, NULL}, + {"i15s2", 0, 15, 2, HW_INT, NULL}, + {"i17s2", 0, 17, 2, HW_INT, NULL}, + {"i19s2", 0, 19, 2, HW_INT, NULL}, + {"i3u", 0, 3, 0, HW_UINT, NULL}, + {"i5u", 0, 5, 0, HW_UINT, NULL}, + {"ib5u", 10, 5, 0, HW_UINT, NULL}, /* imm5 field in ALU. */ + {"ib5s", 10, 5, 0, HW_INT, NULL}, /* imm5 field in ALU. */ + {"i9u", 0, 9, 0, HW_UINT, NULL}, /* break16/ex9.it */ + {"ia3u", 3, 3, 0, HW_UINT, NULL}, /* bmski33, fexti33 */ + {"i8u", 0, 8, 0, HW_UINT, NULL}, + {"i15u", 0, 15, 0, HW_UINT, NULL}, + {"i20u", 0, 20, 0, HW_UINT, NULL}, + {"i3u1", 0, 3, 1, HW_UINT, NULL}, + {"i9u1", 0, 9, 1, HW_UINT, NULL}, + {"i3u2", 0, 3, 2, HW_UINT, NULL}, + {"i6u2", 0, 6, 2, HW_UINT, NULL}, + {"i7u2", 0, 7, 2, HW_UINT, NULL}, + {"i5u3", 0, 5, 3, HW_UINT, NULL}, /* pop25/pop25 */ + {"i15s3", 0, 15, 3, HW_UINT, NULL}, /* dprefi.d */ + + {NULL, 0, 0, 0, 0, NULL} +}; + + +#define OP6(op6) (N32_OP6_ ## op6 << 25) +#define DEF_REG(r) (__BIT (r)) +#define USE_REG(r) (__BIT (r)) +#define RT(r) (r << 20) +#define RA(r) (r << 15) +#define RB(r) (r << 10) +#define RA5(r) (r) + +static struct nds32_opcode nds32_opcodes[] = +{ + /* ALU1 */ +#define ALU1(sub) (OP6 (ALU1) | N32_ALU1_ ## sub) + {"add", "=rt,%ra,%rb", ALU1 (ADD), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"sub", "=rt,%ra,%rb", ALU1 (SUB), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"and", "=rt,%ra,%rb", ALU1 (AND), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"xor", "=rt,%ra,%rb", ALU1 (XOR), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"or", "=rt,%ra,%rb", ALU1 (OR), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"nor", "=rt,%ra,%rb", ALU1 (NOR), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"slt", "=rt,%ra,%rb", ALU1 (SLT), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"slts", "=rt,%ra,%rb", ALU1 (SLTS), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"slli", "=rt,%ra,%ib5u", ALU1 (SLLI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"srli", "=rt,%ra,%ib5u", ALU1 (SRLI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"srai", "=rt,%ra,%ib5u", ALU1 (SRAI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"rotri", "=rt,%ra,%ib5u", ALU1 (ROTRI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"sll", "=rt,%ra,%rb", ALU1 (SLL), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"srl", "=rt,%ra,%rb", ALU1 (SRL), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"sra", "=rt,%ra,%rb", ALU1 (SRA), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"rotr", "=rt,%ra,%rb", ALU1 (ROTR), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"seb", "=rt,%ra", ALU1 (SEB), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"seh", "=rt,%ra", ALU1 (SEH), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"bitc", "=rt,%ra,%rb", ALU1 (BITC), 4, ATTR_V3, 0, NULL, 0, NULL}, + {"zeh", "=rt,%ra", ALU1 (ZEH), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"wsbh", "=rt,%ra", ALU1 (WSBH), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"divsr", "=rt,=rd,%ra,%rb", ALU1 (DIVSR), 4, ATTR (DIV) | ATTR_V2UP, 0, NULL, 0, NULL}, + {"divr", "=rt,=rd,%ra,%rb", ALU1 (DIVR), 4, ATTR (DIV) | ATTR_V2UP, 0, NULL, 0, NULL}, + {"sva", "=rt,%ra,%rb", ALU1 (SVA), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"svs", "=rt,%ra,%rb", ALU1 (SVS), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"cmovz", "=rt,%ra,%rb", ALU1 (CMOVZ), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"cmovn", "=rt,%ra,%rb", ALU1 (CMOVN), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"add_slli", "=rt,%ra,%rb,%sh", ALU1 (ADD), 4, ATTR_V3, 0, NULL, 0, NULL}, + {"sub_slli", "=rt,%ra,%rb,%sh", ALU1 (SUB), 4, ATTR_V3, 0, NULL, 0, NULL}, + {"and_slli", "=rt,%ra,%rb,%sh", ALU1 (AND), 4, ATTR_V3, 0, NULL, 0, NULL}, + {"xor_slli", "=rt,%ra,%rb,%sh", ALU1 (XOR), 4, ATTR_V3, 0, NULL, 0, NULL}, + {"or_slli", "=rt,%ra,%rb,%sh", ALU1 (OR), 4, ATTR_V3, 0, NULL, 0, NULL}, + {"or_srli", "=rt,%ra,%rb,%sh", ALU1 (OR_SRLI), 4, ATTR_V3, 0, NULL, 0, NULL}, + {"add_srli", "=rt,%ra,%rb,%sh", ALU1 (ADD_SRLI), 4, ATTR_V3, 0, NULL, 0, NULL}, + {"sub_srli", "=rt,%ra,%rb,%sh", ALU1 (SUB_SRLI), 4, ATTR_V3, 0, NULL, 0, NULL}, + {"and_srli", "=rt,%ra,%rb,%sh", ALU1 (AND_SRLI), 4, ATTR_V3, 0, NULL, 0, NULL}, + {"xor_srli", "=rt,%ra,%rb,%sh", ALU1 (XOR_SRLI), 4, ATTR_V3, 0, NULL, 0, NULL}, + + /* ALU2 */ +#define ALU2(sub) (OP6 (ALU2) | N32_ALU2_ ## sub) + {"max", "=rt,%ra,%rb", ALU2 (MAX), 4, ATTR (PERF_EXT), 0, NULL, 0, NULL}, + {"min", "=rt,%ra,%rb", ALU2 (MIN), 4, ATTR (PERF_EXT), 0, NULL, 0, NULL}, + {"ave", "=rt,%ra,%rb", ALU2 (AVE), 4, ATTR (PERF_EXT), 0, NULL, 0, NULL}, + {"abs", "=rt,%ra", ALU2 (ABS), 4, ATTR (PERF_EXT), 0, NULL, 0, NULL}, + {"clips", "=rt,%ra,%ib5s", ALU2 (CLIPS), 4, ATTR (PERF_EXT), 0, NULL, 0, NULL}, + {"clip", "=rt,%ra,%ib5u", ALU2 (CLIP), 4, ATTR (PERF_EXT), 0, NULL, 0, NULL}, + {"clo", "=rt,%ra", ALU2 (CLO), 4, ATTR (PERF_EXT), 0, NULL, 0, NULL}, + {"clz", "=rt,%ra", ALU2 (CLZ), 4, ATTR (PERF_EXT), 0, NULL, 0, NULL}, + {"bset", "=rt,%ra,%ib5u", ALU2 (BSET), 4, ATTR (PERF_EXT), 0, NULL, 0, NULL}, + {"bclr", "=rt,%ra,%ib5u", ALU2 (BCLR), 4, ATTR (PERF_EXT), 0, NULL, 0, NULL}, + {"btgl", "=rt,%ra,%ib5u", ALU2 (BTGL), 4, ATTR (PERF_EXT), 0, NULL, 0, NULL}, + {"btst", "=rt,%ra,%ib5u", ALU2 (BTST), 4, ATTR (PERF_EXT), 0, NULL, 0, NULL}, + {"bse", "=rt,%ra,=rb", ALU2 (BSE), 4, ATTR (PERF2_EXT), 0, NULL, 0, NULL}, + {"bsp", "=rt,%ra,=rb", ALU2 (BSP), 4, ATTR (PERF2_EXT), 0, NULL, 0, NULL}, + {"ffb", "=rt,%ra,%rb", ALU2 (FFB), 4, ATTR (STR_EXT), 0, NULL, 0, NULL}, + {"ffmism", "=rt,%ra,%rb", ALU2 (FFMISM), 4, ATTR (STR_EXT), 0, NULL, 0, NULL}, + {"ffzmism", "=rt,%ra,%rb", ALU2 (FFZMISM), 4, ATTR (STR_EXT), 0, NULL, 0, NULL}, + {"mfusr", "=rt,%usr", ALU2 (MFUSR), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"mtusr", "%rt,%usr", ALU2 (MTUSR), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"mfusr", "=rt,%ridx", ALU2 (MFUSR), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"mtusr", "%rt,%ridx", ALU2 (MTUSR), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"mul", "=rt,%ra,%rb", ALU2 (MUL), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"mults64", "=dt,%ra,%rb", ALU2 (MULTS64), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"mult64", "=dt,%ra,%rb", ALU2 (MULT64), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"madds64", "=dt,%ra,%rb", ALU2 (MADDS64), 4, ATTR (MAC) | ATTR_ALL, 0, NULL, 0, NULL}, + {"madd64", "=dt,%ra,%rb", ALU2 (MADD64), 4, ATTR (MAC) | ATTR_ALL, 0, NULL, 0, NULL}, + {"msubs64", "=dt,%ra,%rb", ALU2 (MSUBS64), 4, ATTR (MAC) | ATTR_ALL, 0, NULL, 0, NULL}, + {"msub64", "=dt,%ra,%rb", ALU2 (MSUB64), 4, ATTR (MAC) | ATTR_ALL, 0, NULL, 0, NULL}, + {"divs", "=dt,%ra,%rb", ALU2 (DIVS), 4, ATTR (DIV) | ATTR (DXREG), 0, NULL, 0, NULL}, + {"div", "=dt,%ra,%rb", ALU2 (DIV), 4, ATTR (DIV) | ATTR (DXREG), 0, NULL, 0, NULL}, + {"mult32", "=dt,%ra,%rb", ALU2 (MULT32), 4, ATTR (DXREG) | ATTR_ALL, 0, NULL, 0, NULL}, + {"madd32", "=dt,%ra,%rb", ALU2 (MADD32), 4, ATTR (MAC) | ATTR (DXREG) | ATTR_ALL, 0, NULL, 0, NULL}, + {"msub32", "=dt,%ra,%rb", ALU2 (MSUB32), 4, ATTR (MAC) | ATTR (DXREG) | ATTR_ALL, 0, NULL, 0, NULL}, + {"ffbi", "=rt,%ra,%ib5u", ALU2 (FFBI) | __BIT (6), 4, ATTR (STR_EXT), 0, NULL, 0, NULL}, + {"flmism", "=rt,%ra,%rb", ALU2 (FLMISM) | __BIT (6), 4, ATTR (STR_EXT), 0, NULL, 0, NULL}, + {"mulsr64", "=rt,%ra,%rb", ALU2 (MULSR64)| __BIT (6), 4, ATTR_V3MEX_V2, 0, NULL, 0, NULL}, + {"mulr64", "=rt,%ra,%rb", ALU2 (MULR64) | __BIT (6), 4, ATTR_V3MEX_V2, 0, NULL, 0, NULL}, + {"maddr32", "=rt,%ra,%rb", ALU2 (MADDR32) | __BIT (6), 4, ATTR (MAC) | ATTR_V2UP, 0, NULL, 0, NULL}, + {"msubr32", "=rt,%ra,%rb", ALU2 (MSUBR32) | __BIT (6), 4, ATTR (MAC) | ATTR_V2UP, 0, NULL, 0, NULL}, + + /* MISC */ +#define MISC(sub) (OP6 (MISC) | N32_MISC_ ## sub) + {"standby", "%stdby_st", MISC (STANDBY), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"cctl", "%ra,%cctl_st0", MISC (CCTL), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"cctl", "%ra,%cctl_st1{,%cctl_lv}", MISC (CCTL), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"cctl", "=rt,%ra,%cctl_st2", MISC (CCTL), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"cctl", "%rt,%ra,%cctl_st3", MISC (CCTL), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"cctl", "%cctl_st4", MISC (CCTL), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"cctl", "%cctl_st5{,%cctl_lv}", MISC (CCTL), 4, ATTR_V3, 0, NULL, 0, NULL}, + {"cctl", "=rt,%ra,%cctl_stx,%cctl_lv", MISC (CCTL), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"mfsr", "=rt,%sr", MISC (MFSR), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"mtsr", "%rt,%sr", MISC (MTSR), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"mfsr", "=rt,%ridx", MISC (MFSR), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"mtsr", "%rt,%ridx", MISC (MTSR), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"iret", "", MISC (IRET), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"trap", "%swid", MISC (TRAP), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"trap", "", MISC (TRAP), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"teqz", "%rt,%swid", MISC (TEQZ), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"tnez", "%rt,%swid", MISC (TNEZ), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"dsb", "", MISC (DSB), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"isb", "", MISC (ISB), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"break", "%swid", MISC (BREAK), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"break", "", MISC (BREAK), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"syscall", "%swid", MISC (SYSCALL), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"msync", "%msync_st", MISC (MSYNC), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"msync", "%msync_stx", MISC (MSYNC), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"isync", "%rt", MISC (ISYNC), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"tlbop", "%ra,%tlbop_st", MISC (TLBOP), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"tlbop", "%ra,%tlbop_stx", MISC (TLBOP), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"tlbop", "%rt,%ra,pb", MISC (TLBOP) | (5 << 5), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"tlbop", "flua", MISC (TLBOP) | (7 << 5), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + + {"setend.l", "", MISC (MTSR) + | (SRIDX (1, 0, 0) << 10) | __BIT (5), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"setend.b", "", MISC (MTSR) + | (SRIDX (1, 0, 0) << 10) | __BIT (5) | __BIT (20), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"setgie.d", "", MISC (MTSR) + | (SRIDX (1, 0, 0) << 10) | __BIT (6), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"setgie.e", "", MISC (MTSR) + | (SRIDX (1, 0, 0) << 10) | __BIT (6) | __BIT (20), 4, ATTR_ALL, 0, NULL, 0, NULL}, + + /* JI */ + {"jal", "%i24s1", OP6 (JI) | __BIT (24), 4, ATTR_PCREL | ATTR_ALL, 0, NULL, 0, NULL}, + {"j", "%i24s1", OP6 (JI), 4, ATTR_PCREL | ATTR_ALL, 0, NULL, 0, NULL}, + + /* BR1 */ + {"beq", "%rt,%ra,%i14s1", OP6 (BR1), 4, ATTR_PCREL | ATTR_ALL, 0, NULL, 0, NULL}, + {"bne", "%rt,%ra,%i14s1", OP6 (BR1) | __BIT (14), 4, ATTR_PCREL | ATTR_ALL, 0, NULL, 0, NULL}, + + /* BR2 */ +#define BR2(sub) (OP6 (BR2) | (N32_BR2_ ## sub << 16)) + {"beqz", "%rt,%i16s1", BR2 (BEQZ), 4, ATTR_PCREL | ATTR_ALL, 0, NULL, 0, NULL}, + {"bnez", "%rt,%i16s1", BR2 (BNEZ), 4, ATTR_PCREL | ATTR_ALL, 0, NULL, 0, NULL}, + {"bgez", "%rt,%i16s1", BR2 (BGEZ), 4, ATTR_PCREL | ATTR_ALL, 0, NULL, 0, NULL}, + {"bltz", "%rt,%i16s1", BR2 (BLTZ), 4, ATTR_PCREL | ATTR_ALL, 0, NULL, 0, NULL}, + {"bgtz", "%rt,%i16s1", BR2 (BGTZ), 4, ATTR_PCREL | ATTR_ALL, 0, NULL, 0, NULL}, + {"blez", "%rt,%i16s1", BR2 (BLEZ), 4, ATTR_PCREL | ATTR_ALL, 0, NULL, 0, NULL}, + {"bgezal", "%rt,%i16s1", BR2 (BGEZAL), 4, ATTR_PCREL | ATTR_ALL, 0, NULL, 0, NULL}, + {"bltzal", "%rt,%i16s1", BR2 (BLTZAL), 4, ATTR_PCREL | ATTR_ALL, 0, NULL, 0, NULL}, + + /* BR3 */ + {"beqc", "%rt,%i11br3,%i8s1", OP6 (BR3), 4, ATTR_PCREL | ATTR_V3MUP, 0, NULL, 0, NULL}, + {"bnec", "%rt,%i11br3,%i8s1", OP6 (BR3) | __BIT (19), 4, ATTR_PCREL | ATTR_V3MUP, 0, NULL, 0, NULL}, + +#define JREG(sub) (OP6 (JREG) | N32_JREG_ ## sub) + /* JREG */ + {"jr", "%rb", JREG (JR), 4, ATTR (BRANCH) | ATTR_ALL, 0, NULL, 0, NULL}, + {"jral", "%rt,%rb", JREG (JRAL), 4, ATTR (BRANCH) | ATTR_ALL, 0, NULL, 0, NULL}, + {"jral", "%rb", JREG (JRAL) | RT (30), 4, ATTR (BRANCH) | ATTR_ALL, 0, NULL, 0, NULL}, + {"jrnez", "%rb", JREG (JRNEZ), 4, ATTR (BRANCH) | ATTR_V3, 0, NULL, 0, NULL}, + {"jralnez", "%rt,%rb", JREG (JRALNEZ), 4, ATTR (BRANCH) | ATTR_V3, 0, NULL, 0, NULL}, + {"jralnez", "%rb", JREG (JRALNEZ) | RT (30), 4, ATTR (BRANCH) | ATTR_V3, 0, NULL, 0, NULL}, + +#define JREG_RET (1 << 5) +#define JREG_IFC (1 << 6) + {"ret", "%rb", JREG (JR) | JREG_RET, 4, ATTR (BRANCH) | ATTR_ALL, 0, NULL, 0, NULL}, + {"ret", "", JREG (JR) | JREG_RET | RB (30), 4, ATTR (BRANCH) | ATTR_ALL, 0, NULL, 0, NULL}, + {"jral", "%dtiton %rt,%rb", JREG (JRAL), 4, ATTR (BRANCH) | ATTR_ALL, 0, NULL, 0, NULL}, + {"jral", "%dtiton %rb", JREG (JRAL) | RT (30), 4, ATTR (BRANCH) | ATTR_ALL, 0, NULL, 0, NULL}, + {"jr", "%dtitoff %rb", JREG (JR), 4, ATTR (BRANCH) | ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"ret", "%dtitoff %rb", JREG (JR) | JREG_RET, 4, ATTR (BRANCH) | ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"ifret", "", JREG (JR) | JREG_IFC | JREG_RET, 4, ATTR (BRANCH) | ATTR (IFC_EXT), 0, NULL, 0, NULL}, + + /* MEM */ +#define MEM(sub) (OP6 (MEM) | N32_MEM_ ## sub) + {"lb", "=rt,[%ra+(%rb<<%sv)]", MEM (LB), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lb", "=rt,[%ra+%rb{<<%sv}]", MEM (LB), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lh", "=rt,[%ra+(%rb<<%sv)]", MEM (LH), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lh", "=rt,[%ra+%rb{<<%sv}]", MEM (LH), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lw", "=rt,[%ra+(%rb<<%sv)]", MEM (LW), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lw", "=rt,[%ra+%rb{<<%sv}]", MEM (LW), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"sb", "=rt,[%ra+(%rb<<%sv)]", MEM (SB), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"sb", "%rt,[%ra+%rb{<<%sv}]", MEM (SB), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"sh", "=rt,[%ra+(%rb<<%sv)]", MEM (SH), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"sh", "%rt,[%ra+%rb{<<%sv}]", MEM (SH), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"sw", "=rt,[%ra+(%rb<<%sv)]", MEM (SW), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"sw", "%rt,[%ra+%rb{<<%sv}]", MEM (SW), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lb.bi", "=rt,[%ra],(%rb<<%sv)", MEM (LB_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lb.bi", "=rt,[%ra],%rb{<<%sv}", MEM (LB_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lh.bi", "=rt,[%ra],(%rb<<%sv)", MEM (LH_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lh.bi", "=rt,[%ra],%rb{<<%sv}", MEM (LH_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lw.bi", "=rt,[%ra],(%rb<<%sv)", MEM (LW_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lw.bi", "=rt,[%ra],%rb{<<%sv}", MEM (LW_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"sb.bi", "=rt,[%ra],(%rb<<%sv)", MEM (SB_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"sb.bi", "%rt,[%ra],%rb{<<%sv}", MEM (SB_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"sh.bi", "=rt,[%ra],(%rb<<%sv)", MEM (SH_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"sh.bi", "%rt,[%ra],%rb{<<%sv}", MEM (SH_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"sw.bi", "=rt,[%ra],(%rb<<%sv)", MEM (SW_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"sw.bi", "%rt,[%ra],%rb{<<%sv}", MEM (SW_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lbs", "=rt,[%ra+(%rb<<%sv)]", MEM (LBS), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lbs", "=rt,[%ra+%rb{<<%sv}]", MEM (LBS), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lhs", "=rt,[%ra+(%rb<<%sv)]", MEM (LHS), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lhs", "=rt,[%ra+%rb{<<%sv}]", MEM (LHS), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lbs.bi", "=rt,[%ra],(%rb<<%sv)", MEM (LBS_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lbs.bi", "=rt,[%ra],%rb{<<%sv}", MEM (LBS_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lhs.bi", "=rt,[%ra],(%rb<<%sv)", MEM (LHS_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lhs.bi", "=rt,[%ra],%rb{<<%sv}", MEM (LHS_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"llw", "=rt,[%ra+(%rb<<%sv)]", MEM (LLW), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"llw", "=rt,[%ra+%rb{<<%sv}]", MEM (LLW), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"scw", "%rt,[%ra+(%rb<<%sv)]", MEM (SCW), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"scw", "%rt,[%ra+%rb{<<%sv}]", MEM (SCW), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"lbup", "=rt,[%ra+(%rb<<%sv)]", MEM (LBUP), 4, ATTR_V3MEX_V2, 0, NULL, 0, NULL}, + {"lbup", "=rt,[%ra+%rb{<<%sv}]", MEM (LBUP), 4, ATTR_V3MEX_V2, 0, NULL, 0, NULL}, + {"lwup", "=rt,[%ra+(%rb<<%sv)]", MEM (LWUP), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"lwup", "=rt,[%ra+%rb{<<%sv}]", MEM (LWUP), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"sbup", "%rt,[%ra+(%rb<<%sv)]", MEM (SBUP), 4, ATTR_V3MEX_V2, 0, NULL, 0, NULL}, + {"sbup", "%rt,[%ra+%rb{<<%sv}]", MEM (SBUP), 4, ATTR_V3MEX_V2, 0, NULL, 0, NULL}, + {"swup", "%rt,[%ra+(%rb<<%sv)]", MEM (SWUP), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"swup", "%rt,[%ra+%rb{<<%sv}]", MEM (SWUP), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"dpref", "%dpref_st,[%ra+(%rb<<%sv)]", MEM (DPREF), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"dpref", "%dpref_st,[%ra+%rb{<<%sv}]", MEM (DPREF), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + + /* LBGP */ + {"lbi.gp", "=rt,[+%i19s]", OP6 (LBGP), 4, ATTR (GPREL) | ATTR_V2UP, USE_REG (29), NULL, 0, NULL}, + {"lbsi.gp", "=rt,[+%i19s]", OP6 (LBGP) | __BIT (19), 4, ATTR (GPREL) | ATTR_V2UP, USE_REG (29), NULL, 0, NULL}, + + /* SBGP */ + {"sbi.gp", "%rt,[+%i19s]", OP6 (SBGP), 4, ATTR (GPREL) | ATTR_V2UP, USE_REG (29), NULL, 0, NULL}, + {"addi.gp", "=rt,%i19s", OP6 (SBGP) | __BIT (19), 4, ATTR (GPREL) | ATTR_V2UP, USE_REG (29), NULL, 0, NULL}, + + /* HWGP */ + {"lhi.gp", "=rt,[+%i18s1]", OP6 (HWGP), 4, ATTR (GPREL) | ATTR_V2UP, USE_REG (29), NULL, 0, NULL}, + {"lhsi.gp", "=rt,[+%i18s1]", OP6 (HWGP) | (2 << 17), 4, ATTR (GPREL) | ATTR_V2UP, USE_REG (29), NULL, 0, NULL}, + {"shi.gp", "%rt,[+%i18s1]", OP6 (HWGP) | (4 << 17), 4, ATTR (GPREL) | ATTR_V2UP, USE_REG (29), NULL, 0, NULL}, + {"lwi.gp", "=rt,[+%i17s2]", OP6 (HWGP) | (6 << 17), 4, ATTR (GPREL) | ATTR_V2UP, USE_REG (29), NULL, 0, NULL}, + {"swi.gp", "%rt,[+%i17s2]", OP6 (HWGP) | (7 << 17), 4, ATTR (GPREL) | ATTR_V2UP, USE_REG (29), NULL, 0, NULL}, + +#define LSMW(sub) (OP6 (LSMW) | N32_LSMW_ ## sub) + {"lmw", "%abdim %rt,[%ra],%rb{,%enb4}", LSMW (LSMW), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"smw", "%abdim %rt,[%ra],%rb{,%enb4}", LSMW (LSMW) | __BIT (5), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lmwa", "%abdim %rt,[%ra],%rb{,%enb4}", LSMW (LSMWA), 4, ATTR_V3MEX_V2, 0, NULL, 0, NULL}, + {"smwa", "%abdim %rt,[%ra],%rb{,%enb4}", LSMW (LSMWA) | __BIT (5), 4, ATTR_V3MEX_V2, 0, NULL, 0, NULL}, + {"lmwzb", "%abm %rt,[%ra],%rb{,%enb4}", LSMW (LSMWZB), 4, ATTR (STR_EXT), 0, NULL, 0, NULL}, + {"smwzb", "%abm %rt,[%ra],%rb{,%enb4}", LSMW (LSMWZB) | __BIT (5), 4, ATTR (STR_EXT), 0, NULL, 0, NULL}, + + +#define SIMD(sub) (OP6 (SIMD) | N32_SIMD_ ## sub) + {"pbsad", "%rt,%rb,%ra", SIMD (PBSAD), 4, ATTR (PERF2_EXT), 0, NULL, 0, NULL}, + {"pbsada", "%rt,%rb,%ra", SIMD (PBSADA), 4, ATTR (PERF2_EXT), 0, NULL, 0, NULL}, + + /* COP */ +#if 0 + {"cpe1", 0, 0, NULL, 0, NULL}, + {"mfcp", 0, 0, NULL, 0, NULL}, + {"cplw", 0, 0, NULL, 0, NULL}, + {"cplw.bi", 0, 0, NULL, 0, NULL}, + {"cpld", 0, 0, NULL, 0, NULL}, + {"cpld.bi", 0, 0, NULL, 0, NULL}, + {"cpe2", 0, 0, NULL, 0, NULL}, + + {"cpe3", 0, 0, NULL, 0, NULL}, + {"mtcp", 0, 0, NULL, 0, NULL}, + {"cpsw", 0, 0, NULL, 0, NULL}, + {"cpsw.bi", 0, 0, NULL, 0, NULL}, + {"cpsd", 0, 0, NULL, 0, NULL}, + {"cpsd.bi", 0, 0, NULL, 0, NULL}, + {"cpe4", 0, 0, NULL, 0, NULL}, +#endif + + /* FPU */ +#define FS1(sub) (OP6 (COP) | N32_FPU_FS1 | (N32_FPU_FS1_ ## sub << 6)) + {"fadds", "=fst,%fsa,%fsb", FS1 (FADDS), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fsubs", "=fst,%fsa,%fsb", FS1 (FSUBS), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fcpynss", "=fst,%fsa,%fsb", FS1 (FCPYNSS), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fcpyss", "=fst,%fsa,%fsb", FS1 (FCPYSS), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fmadds", "=fst,%fsa,%fsb", FS1 (FMADDS), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fmsubs", "=fst,%fsa,%fsb", FS1 (FMSUBS), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fcmovns", "=fst,%fsa,%fsb", FS1 (FCMOVNS), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fcmovzs", "=fst,%fsa,%fsb", FS1 (FCMOVZS), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fnmadds", "=fst,%fsa,%fsb", FS1 (FNMADDS), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fnmsubs", "=fst,%fsa,%fsb", FS1 (FNMSUBS), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fmuls", "=fst,%fsa,%fsb", FS1 (FMULS), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fdivs", "=fst,%fsa,%fsb", FS1 (FDIVS), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + +#define FS1_F2OP(sub) (OP6 (COP) | N32_FPU_FS1 | (N32_FPU_FS1_F2OP << 6) \ + | (N32_FPU_FS1_F2OP_ ## sub << 10)) + {"fs2d", "=fdt,%fsa", FS1_F2OP (FS2D), 4, ATTR (FPU) | ATTR (FPU_SP_EXT) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fsqrts", "=fst,%fsa", FS1_F2OP (FSQRTS), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fabss", "=fst,%fsa", FS1_F2OP (FABSS), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fui2s", "=fst,%fsa", FS1_F2OP (FUI2S), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fsi2s", "=fst,%fsa", FS1_F2OP (FSI2S), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fs2ui", "=fst,%fsa", FS1_F2OP (FS2UI), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fs2ui.z", "=fst,%fsa", FS1_F2OP (FS2UI_Z), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fs2si", "=fst,%fsa", FS1_F2OP (FS2SI), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fs2si.z", "=fst,%fsa", FS1_F2OP (FS2SI_Z), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + +#define FS2(sub) (OP6 (COP) | N32_FPU_FS2 | (N32_FPU_FS2_ ## sub << 6)) + {"fcmpeqs", "=fst,%fsa,%fsb", FS2 (FCMPEQS), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fcmplts", "=fst,%fsa,%fsb", FS2 (FCMPLTS), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fcmples", "=fst,%fsa,%fsb", FS2 (FCMPLES), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fcmpuns", "=fst,%fsa,%fsb", FS2 (FCMPUNS), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fcmpeqs.e", "=fst,%fsa,%fsb", FS2 (FCMPEQS_E), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fcmplts.e", "=fst,%fsa,%fsb", FS2 (FCMPLTS_E), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fcmples.e", "=fst,%fsa,%fsb", FS2 (FCMPLES_E), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + {"fcmpuns.e", "=fst,%fsa,%fsb", FS2 (FCMPUNS_E), 4, ATTR (FPU) | ATTR (FPU_SP_EXT), 0, NULL, 0, NULL}, + +#define FD1(sub) (OP6 (COP) | N32_FPU_FD1 | (N32_FPU_FD1_ ## sub << 6)) + {"faddd", "=fdt,%fda,%fdb", FD1 (FADDD), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fsubd", "=fdt,%fda,%fdb", FD1 (FSUBD), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fcpynsd", "=fdt,%fda,%fdb", FD1 (FCPYNSD), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fcpysd", "=fdt,%fda,%fdb", FD1 (FCPYSD), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fmaddd", "=fdt,%fda,%fdb", FD1 (FMADDD), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fmsubd", "=fdt,%fda,%fdb", FD1 (FMSUBD), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fcmovnd", "=fdt,%fda,%fsb", FD1 (FCMOVND), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fcmovzd", "=fdt,%fda,%fsb", FD1 (FCMOVZD), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fnmaddd", "=fdt,%fda,%fdb", FD1 (FNMADDD), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fnmsubd", "=fdt,%fda,%fdb", FD1 (FNMSUBD), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fmuld", "=fdt,%fda,%fdb", FD1 (FMULD), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fdivd", "=fdt,%fda,%fdb", FD1 (FDIVD), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + +#define FD1_F2OP(sub) (OP6 (COP) | N32_FPU_FD1 | (N32_FPU_FD1_F2OP << 6) \ + | (N32_FPU_FD1_F2OP_ ## sub << 10)) + {"fd2s", "=fst,%fda", FD1_F2OP (FD2S), 4, ATTR (FPU) | ATTR (FPU_SP_EXT) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fsqrtd", "=fdt,%fda", FD1_F2OP (FSQRTD), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fabsd", "=fdt,%fda", FD1_F2OP (FABSD), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fui2d", "=fdt,%fsa", FD1_F2OP (FUI2D), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fsi2d", "=fdt,%fsa", FD1_F2OP (FSI2D), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fd2ui", "=fst,%fda", FD1_F2OP (FD2UI), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fd2ui.z", "=fst,%fda", FD1_F2OP (FD2UI_Z), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fd2si", "=fst,%fda", FD1_F2OP (FD2SI), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fd2si.z", "=fst,%fda", FD1_F2OP (FD2SI_Z), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + +#define FD2(sub) (OP6 (COP) | N32_FPU_FD2 | (N32_FPU_FD2_ ## sub << 6)) + {"fcmpeqd", "=fst,%fda,%fdb", FD2 (FCMPEQD), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fcmpltd", "=fst,%fda,%fdb", FD2 (FCMPLTD), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fcmpled", "=fst,%fda,%fdb", FD2 (FCMPLED), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fcmpund", "=fst,%fda,%fdb", FD2 (FCMPUND), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fcmpeqd.e", "=fst,%fda,%fdb", FD2 (FCMPEQD_E), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fcmpltd.e", "=fst,%fda,%fdb", FD2 (FCMPLTD_E), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fcmpled.e", "=fst,%fda,%fdb", FD2 (FCMPLED_E), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + {"fcmpund.e", "=fst,%fda,%fdb", FD2 (FCMPUND_E), 4, ATTR (FPU) | ATTR (FPU_DP_EXT), 0, NULL, 0, NULL}, + +#define MFCP(sub) (OP6 (COP) | N32_FPU_MFCP | (N32_FPU_MFCP_ ## sub << 6)) + {"fmfsr", "=rt,%fsa", MFCP (FMFSR), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fmfdr", "=rt,%fda", MFCP (FMFDR), 4, ATTR (FPU), 0, NULL, 0, NULL}, + +#define MFCP_XR(sub) (OP6 (COP) | N32_FPU_MFCP | (N32_FPU_MFCP_XR << 6) \ + | (N32_FPU_MFCP_XR_ ## sub << 10)) + {"fmfcfg", "=rt" , MFCP_XR(FMFCFG), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fmfcsr", "=rt" , MFCP_XR(FMFCSR), 4, ATTR (FPU), 0, NULL, 0, NULL}, + +#define MTCP(sub) (OP6 (COP) | N32_FPU_MTCP | (N32_FPU_MTCP_ ## sub << 6)) + {"fmtsr", "%rt,=fsa", MTCP (FMTSR), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fmtdr", "%rt,=fda", MTCP (FMTDR), 4, ATTR (FPU), 0, NULL, 0, NULL}, + +#define MTCP_XR(sub) (OP6 (COP) | N32_FPU_MTCP | (N32_FPU_MTCP_XR << 6) \ + | (N32_FPU_MTCP_XR_ ## sub << 10)) + {"fmtcsr", "%rt" , MTCP_XR(FMTCSR), 4, ATTR (FPU), 0, NULL, 0, NULL}, + +#define FPU_MEM(sub) (OP6 (COP) | N32_FPU_ ## sub) +#define FPU_MEMBI(sub) (OP6 (COP) | N32_FPU_ ## sub | 0x2 << 6) +#define FPU_RA_IMMBI(sub) (OP6 (sub) | __BIT (12)) + {"fls", "=fst,[%ra+(%rb<<%sv)]", FPU_MEM (FLS), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fls", "=fst,[%ra+%rb{<<%sv}]", FPU_MEM (FLS), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fls.bi", "=fst,[%ra],(%rb<<%sv)", FPU_MEMBI (FLS), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fls.bi", "=fst,[%ra],%rb{<<%sv}", FPU_MEMBI (FLS), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fss", "=fst,[%ra+(%rb<<%sv)]", FPU_MEM (FSS), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fss", "=fst,[%ra+%rb{<<%sv}]", FPU_MEM (FSS), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fss.bi", "=fst,[%ra],(%rb<<%sv)", FPU_MEMBI (FSS), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fss.bi", "=fst,[%ra],%rb{<<%sv}", FPU_MEMBI (FSS), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fld", "=fdt,[%ra+(%rb<<%sv)]", FPU_MEM (FLD), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fld", "=fdt,[%ra+%rb{<<%sv}]", FPU_MEM (FLD), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fld.bi", "=fdt,[%ra],(%rb<<%sv)", FPU_MEMBI (FLD), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fld.bi", "=fdt,[%ra],%rb{<<%sv}", FPU_MEMBI (FLD), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fsd", "=fdt,[%ra+(%rb<<%sv)]", FPU_MEM (FSD), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fsd", "=fdt,[%ra+%rb{<<%sv}]", FPU_MEM (FSD), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fsd.bi", "=fdt,[%ra],(%rb<<%sv)", FPU_MEMBI (FSD), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fsd.bi", "=fdt,[%ra],%rb{<<%sv}", FPU_MEMBI (FSD), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"flsi", "=fst,[%ra{+%i12s2}]", OP6 (LWC), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"flsi.bi", "=fst,[%ra],%i12s2", FPU_RA_IMMBI (LWC),4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fssi", "=fst,[%ra{+%i12s2}]", OP6 (SWC), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fssi.bi", "=fst,[%ra],%i12s2", FPU_RA_IMMBI (SWC),4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fldi", "=fdt,[%ra{+%i12s2}]", OP6 (LDC), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fldi.bi", "=fdt,[%ra],%i12s2", FPU_RA_IMMBI (LDC),4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fsdi", "=fdt,[%ra{+%i12s2}]", OP6 (SDC), 4, ATTR (FPU), 0, NULL, 0, NULL}, + {"fsdi.bi", "=fdt,[%ra],%i12s2", FPU_RA_IMMBI (SDC),4, ATTR (FPU), 0, NULL, 0, NULL}, + + /* AEXT */ + + {"lbi", "=rt,[%ra{+%i15s}]", OP6 (LBI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lhi", "=rt,[%ra{+%i15s1}]", OP6 (LHI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lwi", "=rt,[%ra{+%i15s2}]", OP6 (LWI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lbi.bi", "=rt,[%ra],%i15s", OP6 (LBI_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lhi.bi", "=rt,[%ra],%i15s1", OP6 (LHI_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lwi.bi", "=rt,[%ra],%i15s2", OP6 (LWI_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"sbi", "%rt,[%ra{+%i15s}]", OP6 (SBI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"shi", "%rt,[%ra{+%i15s1}]", OP6 (SHI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"swi", "%rt,[%ra{+%i15s2}]", OP6 (SWI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"sbi.bi", "%rt,[%ra],%i15s", OP6 (SBI_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"shi.bi", "%rt,[%ra],%i15s1", OP6 (SHI_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"swi.bi", "%rt,[%ra],%i15s2", OP6 (SWI_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lbsi", "=rt,[%ra{+%i15s}]", OP6 (LBSI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lhsi", "=rt,[%ra{+%i15s1}]", OP6 (LHSI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lwsi", "=rt,[%ra{+%i15s2}]", OP6 (LWSI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lbsi.bi", "=rt,[%ra],%i15s", OP6 (LBSI_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lhsi.bi", "=rt,[%ra],%i15s1", OP6 (LHSI_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"lwsi.bi", "=rt,[%ra],%i15s2", OP6 (LWSI_BI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"cplwi", "%cp,=cprt,[%ra{+%i12s2}]", OP6 (LWC), 4, 0, 0, NULL, 0, NULL}, + {"cpswi", "%cp,=cprt,[%ra{+%i12s2}]", OP6 (SWC), 4, 0, 0, NULL, 0, NULL}, + {"cpldi", "%cp,%cprt,[%ra{+%i12s2}]", OP6 (LDC), 4, 0, 0, NULL, 0, NULL}, + {"cpsdi", "%cp,%cprt,[%ra{+%i12s2}]", OP6 (SDC), 4, 0, 0, NULL, 0, NULL}, + {"cplwi.bi", "%cp,=cprt,[%ra],%i12s2", OP6 (LWC) | __BIT (12), 4, 0, 0, NULL, 0, NULL}, + {"cpswi.bi", "%cp,=cprt,[%ra],%i12s2", OP6 (SWC) | __BIT (12), 4, 0, 0, NULL, 0, NULL}, + {"cpldi.bi", "%cp,%cprt,[%ra],%i12s2", OP6 (LDC) | __BIT (12), 4, 0, 0, NULL, 0, NULL}, + {"cpsdi.bi", "%cp,%cprt,[%ra],%i12s2", OP6 (SDC) | __BIT (12), 4, 0, 0, NULL, 0, NULL}, + {"movi", "=rt,%i20s", OP6 (MOVI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"sethi", "=rt,%i20u", OP6 (SETHI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"addi", "=rt,%ra,%i15s", OP6 (ADDI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"subri", "=rt,%ra,%i15s", OP6 (SUBRI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"andi", "=rt,%ra,%i15u", OP6 (ANDI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"xori", "=rt,%ra,%i15u", OP6 (XORI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"ori", "=rt,%ra,%i15u", OP6 (ORI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"slti", "=rt,%ra,%i15s", OP6 (SLTI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"sltsi", "=rt,%ra,%i15s", OP6 (SLTSI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"bitci", "=rt,%ra,%i15u", OP6 (BITCI), 4, ATTR_V3, 0, NULL, 0, NULL}, + {"dprefi.w", "%dpref_st,[%ra{+%i15s2]}", OP6 (DPREFI), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + {"dprefi.d", "%dpref_st,[%ra{+%i15s3]}", OP6 (DPREFI) | __BIT (24), 4, ATTR_V3MEX_V1, 0, NULL, 0, NULL}, + + /* 16-bit instructions. */ + {"mov55", "=rt5,%ra5", 0x8000, 2, ATTR_ALL, 0, NULL, 0, NULL}, /* mov55, $sp, $sp == ifret */ + {"ifret16", "", 0x83ff, 2, ATTR (IFC_EXT), 0, NULL, 0, NULL}, + {"movi55", "=rt5,%i5s", 0x8400, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"add45", "=rt4,%ra5", 0x8800, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"sub45", "=rt4,%ra5", 0x8a00, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"addi45", "=rt4,%i5u", 0x8c00, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"subi45", "=rt4,%i5u", 0x8e00, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"srai45", "=rt4,%i5u", 0x9000, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"srli45", "=rt4,%i5u", 0x9200, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"slli333", "=rt3,%ra3,%i3u", 0x9400, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"zeb33", "=rt3,%ra3", 0x9600, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"zeh33", "=rt3,%ra3", 0x9601, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"seb33", "=rt3,%ra3", 0x9602, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"seh33", "=rt3,%ra3", 0x9603, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"xlsb33", "=rt3,%ra3", 0x9604, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"x11b33", "=rt3,%ra3", 0x9605, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"bmski33", "=rt3,%ia3u", 0x9606, 2, ATTR_V3MUP, 0, NULL, 0, NULL}, + {"fexti33", "=rt3,%ia3u", 0x9607, 2, ATTR_V3MUP, 0, NULL, 0, NULL}, + {"add333", "=rt3,%ra3,%rb3", 0x9800, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"sub333", "=rt3,%ra3,%rb3", 0x9a00, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"addi333", "=rt3,%ra3,%i3u", 0x9c00, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"subi333", "=rt3,%ra3,%i3u", 0x9e00, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"lwi333", "=rt3,[%ra3{+%i3u2}]", 0xa000, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"lwi333.bi", "=rt3,[%ra3],%i3u2", 0xa200, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"lhi333", "=rt3,[%ra3{+%i3u1}]", 0xa400, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"lbi333", "=rt3,[%ra3{+%i3u}]", 0xa600, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"swi333", "%rt3,[%ra3{+%i3u2}]", 0xa800, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"swi333.bi", "%rt3,[%ra3],%i3u2", 0xaa00, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"shi333", "%rt3,[%ra3{+%i3u1}]", 0xac00, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"sbi333", "%rt3,[%ra3{+%i3u}]", 0xae00, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"addri36.sp", "%rt3,%i6u2", 0xb000, 2, ATTR_V3MUP, USE_REG (31), NULL, 0, NULL}, + {"lwi45.fe", "=rt4,%fe5", 0xb200, 2, ATTR_V3MUP, USE_REG (8), NULL, 0, NULL}, + {"lwi450", "=rt4,[%ra5]", 0xb400, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"swi450", "%rt4,[%ra5]", 0xb600, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"lwi37", "=rt38,[$fp{+%i7u2}]", 0xb800, 2, ATTR_ALL, USE_REG (28), NULL, 0, NULL}, + {"swi37", "%rt38,[$fp{+%i7u2}]", 0xb880, 2, ATTR_ALL, USE_REG (28), NULL, 0, NULL}, + {"beqz38", "%rt38,%i8s1", 0xc000, 2, ATTR_PCREL | ATTR_ALL, 0, NULL, 0, NULL}, + {"bnez38", "%rt38,%i8s1", 0xc800, 2, ATTR_PCREL | ATTR_ALL, 0, NULL, 0, NULL}, + {"beqs38", "%rt38,%i8s1", 0xd000, 2, ATTR_PCREL | ATTR_ALL, USE_REG (5), NULL, 0, NULL}, + {"j8", "%i8s1", 0xd500, 2, ATTR_PCREL | ATTR_ALL, 0, NULL, 0, NULL}, + {"bnes38", "%rt38,%i8s1", 0xd800, 2, ATTR_PCREL | ATTR_ALL, USE_REG (5), NULL, 0, NULL}, + {"jr5", "%ra5", 0xdd00, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"ex9.it", "%i5u", 0xdd40, 2, ATTR (EX9_EXT), 0, NULL, 0, NULL}, + {"ret5", "%ra5", 0xdd80, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"ret5", "", 0xdd80 | RA5 (30), 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"jral5", "%ra5", 0xdd20, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"add5.pc", "%ra5", 0xdda0, 2, ATTR_V3, 0, NULL, 0, NULL}, + {"slts45", "%rt4,%ra5", 0xe000, 2, ATTR_ALL, DEF_REG (15), NULL, 0, NULL}, + {"slt45", "%rt4,%ra5", 0xe200, 2, ATTR_ALL, DEF_REG (15), NULL, 0, NULL}, + {"sltsi45", "%rt4,%i5u", 0xe400, 2, ATTR_ALL, DEF_REG (15), NULL, 0, NULL}, + {"slti45", "%rt4,%i5u", 0xe600, 2, ATTR_ALL, DEF_REG (15), NULL, 0, NULL}, + {"beqzs8", "%i8s1", 0xe800, 2, ATTR_PCREL | ATTR_ALL, USE_REG (5), NULL, 0, NULL}, + {"bnezs8", "%i8s1", 0xe900, 2, ATTR_PCREL | ATTR_ALL, USE_REG (5), NULL, 0, NULL}, + {"ex9.it", "%i9u", 0xea00, 2, ATTR (EX9_EXT), 0, NULL, 0, NULL}, + {"break16", "%i9u", 0xea00, 2, ATTR_ALL, 0, NULL, 0, NULL}, + {"addi10.sp", "%i10s", 0xec00, 2, ATTR_V2UP, USE_REG (31) | DEF_REG (31), NULL, 0, NULL}, + {"lwi37.sp", "=rt38,[+%i7u2]", 0xf000, 2, ATTR_V2UP, USE_REG (31), NULL, 0, NULL}, + {"swi37.sp", "%rt38,[+%i7u2]", 0xf080, 2, ATTR_V2UP, USE_REG (31), NULL, 0, NULL}, + {"ifcall9", "%i9u1", 0xf800, 2, ATTR (IFC_EXT), 0, NULL, 0, NULL}, + {"movpi45", "=rt4,%pi5", 0xfa00, 2, ATTR_V3MUP, 0, NULL, 0, NULL}, + {"push25", "%re2,%i5u3", 0xfc00, 2, ATTR_V3MUP, USE_REG (31) | DEF_REG (31), NULL, 0, NULL}, + {"pop25", "%re2,%i5u3", 0xfc80, 2, ATTR_V3MUP, USE_REG (31) | DEF_REG (31), NULL, 0, NULL}, + {"movd44", "=rt5e,%ra5e", 0xfd00, 2, ATTR_V3MUP, 0, NULL, 0, NULL}, + {"neg33", "=rt3,%ra3", 0xfe02, 2, ATTR_V3MUP, 0, NULL, 0, NULL}, + {"not33", "=rt3,%ra3", 0xfe03, 2, ATTR_V3MUP, 0, NULL, 0, NULL}, + {"mul33", "=rt3,%ra3", 0xfe04, 2, ATTR_V3MUP, 0, NULL, 0, NULL}, + {"xor33", "=rt3,%ra3", 0xfe05, 2, ATTR_V3MUP, 0, NULL, 0, NULL}, + {"and33", "=rt3,%ra3", 0xfe06, 2, ATTR_V3MUP, 0, NULL, 0, NULL}, + {"or33", "=rt3,%ra3", 0xfe07, 2, ATTR_V3MUP, 0, NULL, 0, NULL}, + + /* Alias instructions. */ + {"neg", "=rt,%ra", OP6 (SUBRI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"zeb", "=rt,%ra", OP6 (ANDI) | 0xff, 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"nop", "", ALU1 (SRLI), 4, ATTR_ALL, 0, NULL, 0, NULL}, + {"nop16", "", 0x9200, 2, ATTR_ALL, 0, NULL, 0, NULL}, + + /* TODO: For some instruction, an operand may refer to a pair of + register, e.g., mulsr64 or movd44. + + Some instruction need special constrain, e.g., movpi45, + break16, ex9.it. */ +}; + +static const keyword_t keyword_gpr[] = +{ + {"r0", 0, ATTR (RDREG)}, {"r1", 1, ATTR (RDREG)}, {"r2", 2, ATTR (RDREG)}, + {"r3", 3, ATTR (RDREG)}, {"r4", 4, ATTR (RDREG)}, {"r5", 5, ATTR (RDREG)}, + {"r6", 6, ATTR (RDREG)}, {"r7", 7, ATTR (RDREG)}, {"r8", 8, ATTR (RDREG)}, + {"r9", 9, ATTR (RDREG)}, {"r10", 10, ATTR (RDREG)}, + {"r11", 11, 0}, {"r12", 12, 0}, {"r13", 13, 0}, {"r14", 14, 0}, + {"r15", 15, ATTR (RDREG)}, + {"r16", 16, 0}, {"r17", 17, 0}, {"r18", 18, 0}, {"r19", 19, 0}, {"r20", 20, 0}, + {"r21", 21, 0}, {"r22", 22, 0}, {"r23", 23, 0}, {"r24", 24, 0}, {"r25", 25, 0}, + {"r26", 26, 0}, {"r27", 27, 0}, + {"r28", 28, ATTR (RDREG)}, {"r29", 29, ATTR (RDREG)}, + {"r30", 30, ATTR (RDREG)}, {"r31", 31, ATTR (RDREG)}, + + {"ta", 15, ATTR (RDREG)}, {"p0", 26, 0}, {"p1", 27, 0}, + {"fp", 28, ATTR (RDREG)}, {"gp", 29, ATTR (RDREG)}, + {"lp", 30, ATTR (RDREG)}, {"sp", 31, ATTR (RDREG)}, + + {NULL, 0, 0} +}; + +static const keyword_t keyword_usr[] = +{ + {"d0.lo", USRIDX (0, 0), 0}, + {"d0.hi", USRIDX (0, 1), 0}, + {"d1.lo", USRIDX (0, 2), 0}, + {"d1.hi", USRIDX (0, 3), 0}, + {"itb", USRIDX (0, 28), 0}, + {"ifc_lp", USRIDX (0, 29), 0}, + {"pc", USRIDX (0, 31), 0}, + + {"dma_cfg", USRIDX (1, 0), 0}, + {"dma_gcsw", USRIDX (1, 1), 0}, + {"dma_chnsel", USRIDX (1, 2), 0}, + {"dma_act", USRIDX (1, 3), 0}, + {"dma_setup", USRIDX (1, 4), 0}, + {"dma_isaddr", USRIDX (1, 5), 0}, + {"dma_esaddr", USRIDX (1, 6), 0}, + {"dma_tcnt", USRIDX (1, 7), 0}, + {"dma_status", USRIDX (1, 8), 0}, + {"dma_2dset", USRIDX (1, 9), 0}, + {"dma_rcnt", USRIDX (1, 23), 0}, + {"dma_hstatus", USRIDX (1, 24), 0}, + {"dma_2dsctl", USRIDX (1, 25), 0}, + + {"pfmc0", USRIDX (2, 0), 0}, + {"pfmc1", USRIDX (2, 1), 0}, + {"pfmc2", USRIDX (2, 2), 0}, + {"pfm_ctl", USRIDX (2, 4), 0}, + + {NULL, 0, 0} +}; + +static const keyword_t keyword_dxr[] = +{ + {"d0", 0, 0}, {"d1", 1, 0}, {NULL, 0, 0} +}; + +static const keyword_t keyword_sr[] = +{ + {"cr0", SRIDX (0, 0, 0), 0}, {"cpu_ver", SRIDX (0, 0, 0), 0}, + {"cr1", SRIDX (0, 1, 0), 0}, {"icm_cfg", SRIDX (0, 1, 0), 0}, + {"cr2", SRIDX (0, 2, 0), 0}, {"dcm_cfg", SRIDX (0, 2, 0), 0}, + {"cr3", SRIDX (0, 3, 0), 0}, {"mmu_cfg", SRIDX (0, 3, 0), 0}, + {"cr4", SRIDX (0, 4, 0), 0}, {"msc_cfg", SRIDX (0, 4, 0), 0}, + {"cr5", SRIDX (0, 0, 1), 0}, {"core_id", SRIDX (0, 0, 1), 0}, + {"cr6", SRIDX (0, 5, 0), 0}, {"fucop_exist", SRIDX (0, 5, 0), 0}, + + {"ir0", SRIDX (1, 0, 0), 0}, {"psw", SRIDX (1, 0, 0), 0}, + {"ir1", SRIDX (1, 0, 1), 0}, {"ipsw", SRIDX (1, 0, 1), 0}, + {"ir2", SRIDX (1, 0, 2), 0}, {"p_ipsw", SRIDX (1, 0, 2), 0}, + {"ir3", SRIDX (1, 1, 1), 0}, {"ivb", SRIDX (1, 1, 1), 0}, + {"ir4", SRIDX (1, 2, 1), 0}, {"p_eva", SRIDX (1, 2, 2), 0}, + {"ir5", SRIDX (1, 2, 2), 0}, {"eva", SRIDX (1, 2, 1), 0}, + {"ir6", SRIDX (1, 3, 1), 0}, {"itype", SRIDX (1, 3, 1), 0}, + {"ir7", SRIDX (1, 3, 2), 0}, {"p_itype", SRIDX (1, 3, 2), 0}, + {"ir8", SRIDX (1, 4, 1), 0}, {"merr", SRIDX (1, 4, 1), 0}, + {"ir9", SRIDX (1, 5, 1), 0}, {"ipc", SRIDX (1, 5, 1), 0}, + {"ir10", SRIDX (1, 5, 2), 0}, {"p_ipc", SRIDX (1, 5, 2), 0}, + {"ir11", SRIDX (1, 5, 3), 0}, {"oipc", SRIDX (1, 5, 3), 0}, + {"ir12", SRIDX (1, 6, 2), 0}, {"p_p0", SRIDX (1, 6, 2), 0}, + {"ir13", SRIDX (1, 7, 2), 0}, {"p_p1", SRIDX (1, 7, 2), 0}, + {"ir14", SRIDX (1, 8, 0), 0}, {"int_mask", SRIDX (1, 8, 0), 0}, + {"ir15", SRIDX (1, 9, 0), 0}, {"int_pend", SRIDX (1, 9, 0), 0}, + {"ir16", SRIDX (1, 10, 0), 0}, {"sp_usr", SRIDX (1, 10, 0), 0}, + {"ir17", SRIDX (1, 10, 1), 0}, {"sp_priv", SRIDX (1, 10, 1), 0}, + {"ir18", SRIDX (1, 11, 0), 0}, {"int_pri", SRIDX (1, 11, 0), 0}, + {"ir19", SRIDX (1, 1, 2), 0}, {"int_ctrl", SRIDX (1, 1, 2), 0}, + {"ir20", SRIDX (1, 10, 2), 0}, {"sp_usr1", SRIDX (1, 10, 2), 0}, + {"ir21", SRIDX (1, 10, 3), 0}, {"sp_priv1", SRIDX (1, 10, 3), 0}, + {"ir22", SRIDX (1, 10, 4), 0}, {"sp_usr2", SRIDX (1, 10, 4), 0}, + {"ir23", SRIDX (1, 10, 5), 0}, {"sp_priv2", SRIDX (1, 10, 5), 0}, + {"ir24", SRIDX (1, 10, 6), 0}, {"sp_usr3", SRIDX (1, 10, 6), 0}, + {"ir25", SRIDX (1, 10, 7), 0}, {"sp_priv3", SRIDX (1, 10, 7), 0}, + {"ir26", SRIDX (1, 8, 1), 0}, {"int_mask2", SRIDX (1, 8, 1), 0}, + {"ir27", SRIDX (1, 9, 1), 0}, {"int_pend2", SRIDX (1, 9, 1), 0}, + {"ir28", SRIDX (1, 11, 1), 0}, {"int_pri2", SRIDX (1, 11, 1), 0}, + {"ir29", SRIDX (1, 9, 4), 0}, {"int_trigger", SRIDX (1, 9, 4), 0}, + {"ir30", SRIDX (1, 1, 3), 0}, + + {"mr0", SRIDX (2, 0, 0), 0}, {"mmu_ctl", SRIDX (2, 0, 0), 0}, + {"mr1", SRIDX (2, 1, 0), 0}, {"l1_pptb", SRIDX (2, 1, 0), 0}, + {"mr2", SRIDX (2, 2, 0), 0}, {"tlb_vpn", SRIDX (2, 2, 0), 0}, + {"mr3", SRIDX (2, 3, 0), 0}, {"tlb_data", SRIDX (2, 3, 0), 0}, + {"mr4", SRIDX (2, 4, 0), 0}, {"tlb_misc", SRIDX (2, 4, 0), 0}, + {"mr5", SRIDX (2, 5, 0), 0}, {"vlpt_idx", SRIDX (2, 5, 0), 0}, + {"mr6", SRIDX (2, 6, 0), 0}, {"ilmb", SRIDX (2, 6, 0), 0}, + {"mr7", SRIDX (2, 7, 0), 0}, {"dlmb", SRIDX (2, 7, 0), 0}, + {"mr8", SRIDX (2, 8, 0), 0}, {"cache_ctl", SRIDX (2, 8, 0), 0}, + {"mr9", SRIDX (2, 9, 0), 0}, {"hsmp_saddr", SRIDX (2, 9, 0), 0}, + {"mr10", SRIDX (2, 9, 1), 0}, {"hsmp_eaddr", SRIDX (2, 9, 1), 0}, + {"mr11", SRIDX (2, 0, 1), 0}, {"bg_region", SRIDX (2, 0, 1), 0}, + + {"pfr0", SRIDX (4, 0, 0), 0}, {"pfmc0", SRIDX (4, 0, 0), 0}, + {"pfr1", SRIDX (4, 0, 1), 0}, {"pfmc1", SRIDX (4, 0, 1), 0}, + {"pfr2", SRIDX (4, 0, 2), 0}, {"pfmc2", SRIDX (4, 0, 2), 0}, + {"pfr3", SRIDX (4, 1, 0), 0}, {"pfm_ctl", SRIDX (4, 1, 0), 0}, + + {"dmar0", SRIDX (5, 0, 0), 0}, {"dma_cfg", SRIDX (5, 0, 0), 0}, + {"dmar1", SRIDX (5, 1, 0), 0}, {"dma_gcsw", SRIDX (5, 1, 0), 0}, + {"dmar2", SRIDX (5, 2, 0), 0}, {"dma_chnsel", SRIDX (5, 2, 0), 0}, + {"dmar3", SRIDX (5, 3, 0), 0}, {"dma_act", SRIDX (5, 3, 0), 0}, + {"dmar4", SRIDX (5, 4, 0), 0}, {"dma_setup", SRIDX (5, 4, 0), 0}, + {"dmar5", SRIDX (5, 5, 0), 0}, {"dma_isaddr", SRIDX (5, 5, 0), 0}, + {"dmar6", SRIDX (5, 6, 0), 0}, {"dma_esaddr", SRIDX (5, 6, 0), 0}, + {"dmar7", SRIDX (5, 7, 0), 0}, {"dma_tcnt", SRIDX (5, 7, 0), 0}, + {"dmar8", SRIDX (5, 8, 0), 0}, {"dma_status", SRIDX (5, 8, 0), 0}, + {"dmar9", SRIDX (5, 9, 0), 0}, {"dma_2dset", SRIDX (5, 9, 0), 0}, + {"dmar10", SRIDX (5, 9, 1), 0}, {"dma_2dsctl", SRIDX (5, 9, 1), 0}, + {"dmar11", SRIDX (5, 7, 1), 0}, {"dma_rcnt", SRIDX (5, 7, 1), 0}, + {"dmar12", SRIDX (5, 8, 1), 0}, {"dma_hstatus", SRIDX (5, 8, 1), 0}, + + {"idr0", SRIDX (2, 15, 0), 0}, {"sdz_ctl", SRIDX (2, 15, 0), 0}, + {"idr1", SRIDX (2, 15, 1), 0}, {"n12misc_ctl", SRIDX (2, 15, 1), 0}, + {"misc_ctl", SRIDX (2, 15, 1), 0}, + + {"secur0", SRIDX (6, 0, 0), 0}, {"sfcr", SRIDX (6, 0, 0), 0}, + + {"prusr_acc_ctl", SRIDX (4, 4, 0), 0}, + {"fucpr", SRIDX (4, 5, 0), 0}, {"fucop_ctl", SRIDX (4, 5, 0), 0}, + + {NULL,0 ,0} +}; + +static const keyword_t keyword_cp[] = +{ + {"cp0", 0, 0}, {"cp1", 1, 0}, {"cp2", 2, 0}, {"cp3", 3, 0}, {NULL, 0, 0} +}; + +static const keyword_t keyword_cpr[] = +{ + {"cpr0", 0, 0}, {"cpr1", 1, 0}, {"cpr2", 2, 0}, {"cpr3", 3, 0}, {"cpr4", 4, 0}, + {"cpr5", 5, 0}, {"cpr6", 6, 0}, {"cpr7", 7, 0}, {"cpr8", 8, 0}, {"cpr9", 9, 0}, + {"cpr10", 10, 0}, {"cpr11", 11, 0}, {"cpr12", 12, 0}, {"cpr13", 13, 0}, + {"cpr14", 14, 0}, {"cpr15", 15, 0}, {"cpr16", 16, 0}, {"cpr17", 17, 0}, + {"cpr18", 18, 0}, {"cpr19", 19, 0}, {"cpr20", 20, 0}, {"cpr21", 21, 0}, + {"cpr22", 22, 0}, {"cpr23", 23, 0}, {"cpr24", 24, 0}, {"cpr25", 25, 0}, + {"cpr26", 26, 0}, {"cpr27", 27, 0}, {"cpr28", 28, 0}, {"cpr29", 29, 0}, + {"cpr30", 30, 0}, {"cpr31", 31, 0}, {NULL, 0, 0} +}; + +static const keyword_t keyword_fsr[] = +{ + {"fs0", 0, 0}, {"fs1", 1, 0}, {"fs2", 2, 0}, {"fs3", 3, 0}, {"fs4", 4, 0}, + {"fs5", 5, 0}, {"fs6", 6, 0}, {"fs7", 7, 0}, {"fs8", 8, 0}, {"fs9", 9, 0}, + {"fs10", 10, 0}, {"fs11", 11, 0}, {"fs12", 12, 0}, {"fs13", 13, 0}, + {"fs14", 14, 0}, {"fs15", 15, 0}, {"fs16", 16, 0}, {"fs17", 17, 0}, + {"fs18", 18, 0}, {"fs19", 19, 0}, {"fs20", 20, 0}, {"fs21", 21, 0}, + {"fs22", 22, 0}, {"fs23", 23, 0}, {"fs24", 24, 0}, {"fs25", 25, 0}, + {"fs26", 26, 0}, {"fs27", 27, 0}, {"fs28", 28, 0}, {"fs29", 29, 0}, + {"fs30", 30, 0}, {"fs31", 31, 0}, {NULL, 0 ,0} +}; + +static const keyword_t keyword_fdr[] = +{ + {"fd0", 0, 0}, {"fd1", 1, 0}, {"fd2", 2, 0}, {"fd3", 3, 0}, {"fd4", 4, 0}, + {"fd5", 5, 0}, {"fd6", 6, 0}, {"fd7", 7, 0}, {"fd8", 8, 0}, {"fd9", 9, 0}, + {"fd10", 10, 0}, {"fd11", 11, 0}, {"fd12", 12, 0}, {"fd13", 13, 0}, + {"fd14", 14, 0}, {"fd15", 15, 0}, {"fd16", 16, 0}, {"fd17", 17, 0}, + {"fd18", 18, 0}, {"fd19", 19, 0}, {"fd20", 20, 0}, {"fd21", 21, 0}, + {"fd22", 22, 0}, {"fd23", 23, 0}, {"fd24", 24, 0}, {"fd25", 25, 0}, + {"fd26", 26, 0}, {"fd27", 27, 0}, {"fd28", 28, 0}, {"fd29", 29, 0}, + {"fd30", 30, 0}, {"fd31", 31, 0}, {NULL, 0, 0} +}; + +static const keyword_t keyword_abdim[] = +{ + {"bi", 0, 0}, {"bim", 1, 0}, {"bd", 2, 0}, {"bdm", 3, 0}, + {"ai", 4, 0}, {"aim", 5, 0}, {"ad", 6, 0}, {"adm", 7, 0}, + {NULL, 0, 0} +}; + +static const keyword_t keyword_abm[] = +{ + {"b", 0, 0}, {"bm", 1, 0}, {"a", 4, 0}, {"am", 5, 0}, {NULL, 0, 0} +}; + +static const keyword_t keyword_dtiton[] = +{ + {"iton", 1, 0}, {"ton", 3, 0}, {NULL, 0, 0} +}; + +static const keyword_t keyword_dtitoff[] = +{ + {"itoff", 1, 0}, {"toff", 3, 0}, {NULL, 0, 0} +}; + +static const keyword_t keyword_dpref_st[] = +{ + {"srd", 0, 0}, {"mrd", 1, 0}, {"swr", 2, 0}, {"mwr", 3, 0}, + {"pte", 4, 0}, {"clwr", 5, 0}, {NULL, 0, 0} +}; + +/* CCTL Ra, SubType */ +static const keyword_t keyword_cctl_st0[] = +{ + {"l1d_ix_inval", 0X0, 0}, {"l1d_ix_wb", 0X1, 0}, {"l1d_ix_wbinval", 0X2, 0}, + {"l1d_va_fillck", 0XB, 0}, {"l1d_va_ulck", 0XC, 0}, {"l1i_ix_inval", 0X10, 0}, + {"l1i_va_fillck", 0X1B, 0}, {"l1i_va_ulck", 0X1C, 0}, + {NULL, 0, 0} +}; + +/* CCTL Ra, SubType, level */ +static const keyword_t keyword_cctl_st1[] = +{ + {"l1d_va_inval", 0X8, 0}, {"l1d_va_wb", 0X9, 0}, + {"l1d_va_wbinval", 0XA, 0}, {"l1i_va_inval", 0X18, 0}, + {NULL, 0, 0} +}; + +/* CCTL Rt, Ra, SubType */ +static const keyword_t keyword_cctl_st2[] = +{ + {"l1d_ix_rtag", 0X3, 0}, {"l1d_ix_rwd", 0X4, 0}, + {"l1i_ix_rtag", 0X13, 0}, {"l1i_ix_rwd", 0X14, 0}, + {NULL, 0, 0} +}; + +/* CCTL Rb, Ra, SubType */ +static const keyword_t keyword_cctl_st3[] = +{ + {"l1d_ix_wtag", 0X5, 0}, {"l1d_ix_wwd", 0X6, 0}, + {"l1i_ix_wtag", 0X15, 0}, {"l1i_ix_wwd", 0X16, 0}, + {NULL, 0, 0} +}; + +/* CCTL L1D_INVALALL */ +static const keyword_t keyword_cctl_st4[] = +{ + {"l1d_invalall", 0x7, 0}, {NULL, 0, 0} +}; + +/* CCTL L1D_WBALL, level */ +static const keyword_t keyword_cctl_st5[] = +{ + {"l1d_wball", 0xf, 0}, {NULL, 0, 0} +}; + +static const keyword_t keyword_cctl_lv[] = +{ + {"1level", 0, 0}, {"alevel", 1, 0}, {"0", 0, 0}, {"1", 1, 0}, + {NULL, 0, 0}, +}; + +static const keyword_t keyword_tlbop_st[] = +{ + {"trd", 0, 0}, {"targetread", 0, 0}, + {"twr", 1, 0}, {"targetwrite", 1, 0}, + {"rwr", 2, 0}, {"rwrite", 2, 0}, + {"rwlk", 3, 0}, {"rwritelock", 3, 0}, + {"unlk", 4, 0}, {"unlock", 4, 0}, + {"inv", 6, 0}, {"invalidate", 6, 0}, + {NULL, 0, 0}, + /* "pb" requries two operand and "flua" requires none. */ + /* {"pb", 5, 0}, {"probe", 5, 0}, + {"flua", 7, 0}, {"flushall", 0}, */ +}; + +static const keyword_t keyword_standby_st[] = +{ + {"no_wake_grant", 0, 0}, + {"wake_grant", 1, 0}, + {"wait_done", 2, 0}, + {"0", 0, 0}, + {"1", 1, 0}, + {"2", 2, 0}, + {"3", 3, 0}, + {NULL, 0, 0}, +}; + +static const keyword_t keyword_msync_st[] = +{ + {"all", 0, 0}, {"store", 1, 0}, + {NULL, 0, 0} +}; + + +/* Hash table for syntax lex. */ +static htab_t field_htab; +/* Hash table for opcodes. */ +static htab_t opcode_htab; +/* Hash table for hardware resources. */ +static htab_t hw_ktabs[_HW_LAST]; + +static hashval_t +htab_hash_hash (const void *p) +{ + struct nds32_hash_entry *h = (struct nds32_hash_entry *) p; + + return htab_hash_string (h->name); +} + +static int +htab_hash_eq (const void *p, const void *q) +{ + struct nds32_hash_entry *h = (struct nds32_hash_entry *) p; + const char *name = (const char *) q; + + return strcmp (name, h->name) == 0; +} + + +/* Build a hash table for array BASE. Each element is in size of SIZE, + and it's first element is a pointer to the key of string. + It stops inserting elements until reach an NULL key. */ + +static htab_t +build_hash_table (const void *base, size_t size) +{ + htab_t htab; + hashval_t hash; + const char *p; + + htab = htab_create_alloc (128, htab_hash_hash, htab_hash_eq, + NULL, xcalloc, free); + + p = base; + while (1) + { + struct nds32_hash_entry **slot; + struct nds32_hash_entry *h; + + h = (struct nds32_hash_entry *) p; + + if (h->name == NULL) + break; + + hash = htab_hash_string (h->name); + slot = (struct nds32_hash_entry **) + htab_find_slot_with_hash (htab, h->name, hash, INSERT); + + assert (slot != NULL && *slot == NULL); + + *slot = h; + + p = p + size; + } + + return htab; +} + +/* Build the syntax for a given opcode OPC. It parses the string + pointed by INSTRUCTION and store the result on SYNTAX, so + when we assemble an instruction, we don't have to parse the syntax + again. */ + +static void +build_opcode_syntax (struct nds32_opcode *opc) +{ + char odstr[MAX_LEX_LEN]; + const char *str; + const char *end; + lex_t *plex; + int len; + hashval_t hash; + field_t *fd; + int opt = 0; + + /* Check whether it has been initialized. */ + if (opc->syntax) + return; + + opc->syntax = xmalloc (MAX_LEX_NUM * sizeof (lex_t)); + + str = opc->instruction; + plex = opc->syntax; + while (*str) + { + int fidx; + + switch (*str) + { + case '%': *plex = SYN_INPUT; break; + case '=': *plex = SYN_OUTPUT; break; + case '&': *plex = SYN_INPUT | SYN_OUTPUT; break; + case '{': + *plex++ = SYN_LOPT; + opt++; + str++; + continue; + case '}': + *plex++ = SYN_ROPT; + str++; + continue; + default: + *plex++ = *str++; + continue; + } + str++; + + /* Extract operand. */ + end = str; + while (ISALNUM (*end) || *end == '_') + end++; + len = end - str; + memcpy (odstr, str, len); + odstr[len] = '\0'; + + hash = htab_hash_string (odstr); + fd = (field_t *) htab_find_with_hash (field_htab, odstr, hash); + fidx = fd - operand_fields; + + if (fd == NULL) + { + fprintf (stderr, "Internal error: Unknown operand, %s\n", str); + } + assert (fd && fidx >= 0 && fidx < (int) ARRAY_SIZE (operand_fields)); + *plex |= LEX_SET_FIELD (fidx); + + str += len; + plex++; + } + + *plex = 0; + opc->variant = opt; + return; + + fprintf (stderr, "Unknown lex in assembly syntax, %s.\n", str); + abort (); +} + +/* Initialize the assembler. It must be called before assembling. */ + +void +nds32_asm_init (nds32_asm_desc_t *pdesc, int flags) +{ + int i; + hashval_t hash; + const keyword_t *keywords[_HW_LAST] = + { + keyword_gpr, keyword_usr, keyword_dxr, keyword_sr, keyword_fsr, + keyword_fdr, keyword_cp, keyword_cpr, keyword_abdim, keyword_abm, + keyword_dtiton, keyword_dtitoff, keyword_dpref_st, + keyword_cctl_st0, keyword_cctl_st1, keyword_cctl_st2, + keyword_cctl_st3, keyword_cctl_st4, keyword_cctl_st5, + keyword_cctl_lv, keyword_tlbop_st, keyword_standby_st, + keyword_msync_st, + }; + + pdesc->flags = flags; + pdesc->mach = flags & NASM_OPEN_ARCH_MASK; + + /* Build keyword tables. */ + field_htab = build_hash_table (operand_fields, + sizeof (operand_fields[0])); + + for (i = 0; i < _HW_LAST; i++) + hw_ktabs[i] = build_hash_table (keywords[i], sizeof (keyword_t)); + + /* Build opcode table. */ + opcode_htab = htab_create_alloc (128, htab_hash_hash, htab_hash_eq, + NULL, xcalloc, free); + + for (i = 0; i < (int) ARRAY_SIZE (nds32_opcodes); i++) + { + struct nds32_opcode **slot; + struct nds32_opcode *opc; + + opc = &nds32_opcodes[i]; + + hash = htab_hash_string (opc->opcode); + slot = (struct nds32_opcode **) + htab_find_slot_with_hash (opcode_htab, opc->opcode, hash, INSERT); + +#define NDS32_PREINIT_SYNTAX +#if defined (NDS32_PREINIT_SYNTAX) + /* Initial SYNTAX when build opcode table, so bug in syntax can be + found when initialized rather than used. */ + build_opcode_syntax (opc); +#endif + + if (*slot == NULL) + { + /* This is the new one. */ + *slot = opc; + } + else + { + /* Already exists. Append to the list. */ + opc = *slot; + while (opc->next) + opc = opc->next; + opc->next = &nds32_opcodes[i]; + } + } +} + +/* Parse the input and store operand keyword string in ODSTR. + This function is only used for parsing keywords, + HW_INT/HW_UINT are parsed parse_operand callback handler. */ + +static char * +parse_to_delimiter (char *str, char odstr[MAX_KEYWORD_LEN]) +{ + char *outp = odstr; + + while (ISALNUM (*str) || *str == '.' || *str == '_') + *outp++ = TOLOWER (*str++); + + *outp = '\0'; + return str; +} + +/* Parse the operand of push25/pop25. */ + +static int +parse_re2 (struct nds32_asm_desc *pdesc ATTRIBUTE_UNUSED, + struct nds32_asm_insn *pinsn ATTRIBUTE_UNUSED, + char **pstr, int64_t *value) +{ + char *end = *pstr; + char odstr[MAX_KEYWORD_LEN]; + keyword_t *k; + hashval_t hash; + + if (*end == '$') + end++; + end = parse_to_delimiter (end, odstr); + + hash = htab_hash_string (odstr); + k = htab_find_with_hash (hw_ktabs[HW_GPR], odstr, hash); + + if (k == NULL) + return NASM_ERR_OPERAND; + + if (k->value == 6) + *value = 0; + else if (k->value == 8) + *value = 1; + else if (k->value == 10) + *value = 2; + else if (k->value == 14) + *value = 3; + else + return NASM_ERR_OPERAND; + + *pstr = end; + return NASM_R_CONST; +} + +/* Parse the operand of lwi45.fe. */ + +static int +parse_fe5 (struct nds32_asm_desc *pdesc, struct nds32_asm_insn *pinsn, + char **pstr, int64_t *value) +{ + int r; + + r = pdesc->parse_operand (pdesc, pinsn, pstr, value); + if (r != NASM_R_CONST) + return r; + + /* 128 == 32 << 2. Leave the shift to parse_opreand, + so it can check whether it is a multiple of 4. */ + *value = 128 + *value; + return r; +} + +/* Parse the operand of movpi45. */ + +static int +parse_pi5 (struct nds32_asm_desc *pdesc, struct nds32_asm_insn *pinsn, + char **pstr, int64_t *value) +{ + int r; + + r = pdesc->parse_operand (pdesc, pinsn, pstr, value); + if (r != NASM_R_CONST) + return r; + + *value -= 16; + return r; +} + +/* Generic operand parse base on the information provided by the field. */ + +static int +parse_operand (nds32_asm_desc_t *pdesc, nds32_asm_insn_t *pinsn, + char **str, int syn) +{ + char odstr[MAX_KEYWORD_LEN]; + char *end; + hashval_t hash; + const field_t *fld = &LEX_GET_FIELD (syn); + keyword_t *k; + int64_t value = 0x100000000; /* Big enough to overflow. */ + int r; + uint64_t modifier = 0; + + end = *str; + + if (fld->parse) + { + r = fld->parse (pdesc, pinsn, &end, &value); + goto done; + } + + if (fld->hw_res < _HW_LAST) + { + /* Parse the operand in assembly code. */ + if (*end == '$') + end++; + end = parse_to_delimiter (end, odstr); + + hash = htab_hash_string (odstr); + k = htab_find_with_hash (hw_ktabs[fld->hw_res], odstr, hash); + + if (k == NULL) + { + pdesc->result = NASM_ERR_OPERAND; + return 0; + } + + if (fld->hw_res == HW_GPR && (pdesc->flags & NASM_OPEN_REDUCED_REG) + && (k->attr & ATTR (RDREG)) == 0) + { + /* Register not allowed in reduced register. */ + pdesc->result = NASM_ERR_REG_REDUCED; + return 0; + } + + if (fld->hw_res == HW_GPR) + { + if (syn & SYN_INPUT) + pinsn->defuse |= USE_REG (k->value); + if (syn & SYN_OUTPUT) + pinsn->defuse |= DEF_REG (k->value); + } + + value = k->value; + if (fld->hw_res == HW_GPR && (fld->bitsize + fld->shift) == 4) + value = nds32_r54map[value]; + } + else if (fld->hw_res == HW_INT || fld->hw_res == HW_UINT) + { + if (*end == '#') + end++; + + /* Handle modifiers. Do we need to make a table for modifiers? + Do we need to check unknown modifier? */ + if (strncasecmp (end, "hi20(", 5) == 0) + { + modifier |= NASM_ATTR_HI20; + end += 5; + } + else if (strncasecmp (end, "lo12(", 5) == 0) + { + modifier |= NASM_ATTR_LO12; + end += 5; + } + else if (strncasecmp (end, "lo20(", 5) == 0) + { + /* e.g., movi */ + modifier |= NASM_ATTR_LO20; + end += 5; + } + + r = pdesc->parse_operand (pdesc, pinsn, &end, &value); + if (modifier) + { + /* Consume the ')' of modifier. */ + end++; + pinsn->attr |= modifier; + } + + switch (r) + { + case NASM_R_ILLEGAL: + pdesc->result = NASM_ERR_OPERAND; + return 0; + case NASM_R_SYMBOL: + /* This field needs special fix-up. */ + pinsn->field = fld; + break; + case NASM_R_CONST: + if (modifier & NASM_ATTR_HI20) + value = (value >> 12) & 0xfffff; + else if (modifier & NASM_ATTR_LO12) + value = value & 0xfff; + else if (modifier & NASM_ATTR_LO20) + value = value & 0xfffff; + break; + default: + fprintf (stderr, "Internal error: Don't know how to handle " + "parsing results.\n"); + abort (); + } + } + else + { + fprintf (stderr, "Internal error: Unknown hardware resource.\n"); + abort (); + } + +done: + /* Don't silently discarding bits. */ + if (value & __MASK (fld->shift)) + { + pdesc->result = NASM_ERR_OUT_OF_RANGE; + return 0; + } + + /* Check the range of signed or unsigned result. */ + if (fld->hw_res != HW_INT && (value >> (fld->bitsize + fld->shift))) + { + pdesc->result = NASM_ERR_OUT_OF_RANGE; + return 0; + } + else if (fld->hw_res == HW_INT) + { + /* Sign-ext the value. */ + if (((value >> 32) == 0) && (value & 0x80000000)) + value |= (int64_t) -1 << 31; + + + /* Shift the value to positive domain. */ + if ((value + (1 << (fld->bitsize + fld->shift - 1))) + >> (fld->bitsize + fld->shift)) + { + pdesc->result = NASM_ERR_OUT_OF_RANGE; + return 0; + } + } + + pinsn->insn |= (((value >> fld->shift) & __MASK (fld->bitsize)) << fld->bitpos); + *str = end; + return 1; +} + +/* Try to parse an instruction string based on opcode syntax. */ + +static int +parse_insn (nds32_asm_desc_t *pdesc, nds32_asm_insn_t *pinsn, + char *str, struct nds32_opcode *opc) +{ + int variant = 0; + char *p = NULL; + + /* A syntax may has optional operands, so we have to try each possible + combination to see if the input is accepted. In order to do so, + bit-N represent whether optional-operand-N is used in this combination. + That is, if bit-N is set, optional-operand-N is not used. + + For example, there are 2 optional operands in this syntax, + + "a{,b}{,c}" + + we can try it 4 times (i.e., 1 << 2) + + 0 (b00): "a,b,c" + 1 (b01): "a,c" + 2 (b10): "a,b" + 3 (b11): "a" + */ + + /* The outer do-while loop is used to try each possible optional + operand combination, and VARIANT is the bit mask. The inner loop + iterates each lexeme in the syntax. */ + + do + { + /* OPT is the number of optional operands we've seen. */ + int opt = 0; + lex_t *plex; + + /* PLEX is the syntax iterator and P is the iterator for input + string. */ + plex = opc->syntax; + p = str; + /* Initial the base value. */ + pinsn->insn = opc->value; + + while (*plex) + { + if (IS_LEX_CHAR (*plex)) + { + /* If it's a plain char, just compare it. */ + if (LEX_CHAR (*plex) != *p) + { + pdesc->result = NASM_ERR_SYNTAX; + goto reject; + } + p++; + } + else if (*plex & SYN_LOPT) + { + /* If it's '{' and it's not used in this iteration, + just skip the whole optional operand. */ + if ((1 << (opt++)) & variant) + { + while ((*plex & SYN_ROPT) == 0) + plex++; + } + } + else if (*plex & SYN_ROPT) + { + /* ignore */ + } + else + { + /* If it's a operand, parse the input operand from input. */ + if (!parse_operand (pdesc, pinsn, &p, *plex)) + goto reject; + } + plex++; + } + + /* Check whether this syntax is accepted. */ + if (*plex == 0 && (*p == '\0' || *p == '!' || *p == '#')) + return 1; + +reject: + /* If not accepted, try another combination. */ + variant++; + } + while (variant < (1 << opc->variant)); + + return 0; +} + +void +nds32_assemble (nds32_asm_desc_t *pdesc, nds32_asm_insn_t *pinsn, + char *str) +{ + struct nds32_opcode *opc; + char *s; + char *mnemoic; + char *dot; + hashval_t hash; + + /* Duplicate the string, so we can modify it for convenience. */ + s = strdup (str); + mnemoic = s; + str = s; + + /* Find opcode mnemoic. */ + while (*s != ' ' && *s != '\t' && *s != '\0') + s++; + if (*s != '\0') + *s++ = '\0'; + dot = strchr (mnemoic, '.'); + +retry_dot: + /* Lookup the opcode syntax. */ + hash = htab_hash_string (mnemoic); + opc = (struct nds32_opcode *) + htab_find_with_hash (opcode_htab, mnemoic, hash); + + /* If we cannot find a match syntax, try it again without `.'. + For example, try "lmw.adm" first and then try "lmw" again. */ + if (opc == NULL && dot != NULL) + { + *dot = '\0'; + s[-1] = ' '; + s = dot + 1; + dot = NULL; + goto retry_dot; + } + else if (opc == NULL) + { + pdesc->result = NASM_ERR_UNKNOWN_OP; + goto out; + } + + /* There may be multiple syntaxes for a given opcode. + Try each one until a match is found. */ + for (; opc; opc = opc->next) + { + /* Build opcode syntax, if it's not been initialized yet. */ + if (opc->syntax == NULL) + build_opcode_syntax (opc); + + /* Reset status before assemble. */ + pinsn->defuse = opc->defuse; + pinsn->insn = 0; + pinsn->field = NULL; + /* Use opcode attributes to initial instruction attributes. */ + pinsn->attr = opc->attr; + if (parse_insn (pdesc, pinsn, s, opc)) + break; + } + + pinsn->opcode = opc; + if (opc == NULL) + { + pdesc->result = NASM_ERR_SYNTAX; + goto out; + } + + /* A matched opcode is found. Write the result to instruction buffer. */ + pdesc->result = NASM_OK; + +out: + free (str); +} diff --git a/opcodes/nds32-asm.h b/opcodes/nds32-asm.h new file mode 100644 index 0000000..21d7348 --- /dev/null +++ b/opcodes/nds32-asm.h @@ -0,0 +1,190 @@ +/* NDS32-specific support for 32-bit ELF. + Copyright (C) 2012-2013 Free Software Foundation, Inc. + Contributed by Andes Technology Corporation. + + 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.*/ + + +#ifndef NDS32_ASM_H +#define NDS32_ASM_H + +/* Constant values for assembler. */ +enum +{ + /* Error code for assembling an instruction. */ + NASM_OK = 0, + NASM_ERR_UNKNOWN_OP, + NASM_ERR_SYNTAX, + NASM_ERR_OPERAND, + NASM_ERR_OUT_OF_RANGE, + NASM_ERR_REG_REDUCED, + NASM_ERR_JUNK_EOL, + + /* Results of parse_operand. */ + NASM_R_CONST, + NASM_R_SYMBOL, + NASM_R_ILLEGAL, + + /* Flags for open description. */ + NASM_OPEN_ARCH_V1 = 0x0, + NASM_OPEN_ARCH_V2 = 0x1, + NASM_OPEN_ARCH_V3 = 0x2, + NASM_OPEN_ARCH_V3M = 0x3, + NASM_OPEN_ARCH_MASK = 0xf, + NASM_OPEN_REDUCED_REG = 0x10, + + /* Common attributes. */ + NASM_ATTR_ISA_V1 = 0x01, + NASM_ATTR_ISA_V2 = 0x02, + NASM_ATTR_ISA_V3 = 0x04, + NASM_ATTR_ISA_V3M = 0x08, + NASM_ATTR_ISA_ALL = 0x0f, + + /* Attributes for instructions. */ + NASM_ATTR_MAC = 0x0000100, + NASM_ATTR_DIV = 0x0000200, + NASM_ATTR_FPU = 0x0000400, + NASM_ATTR_FPU_SP_EXT = 0x0000800, + NASM_ATTR_FPU_DP_EXT = 0x0001000, + NASM_ATTR_STR_EXT = 0x0002000, + NASM_ATTR_PERF_EXT = 0x0004000, + NASM_ATTR_PERF2_EXT = 0x0008000, + NASM_ATTR_AUDIO_ISAEXT = 0x0010000, + NASM_ATTR_IFC_EXT = 0x0020000, + NASM_ATTR_EX9_EXT = 0x0040000, + NASM_ATTR_FPU_FMA = 0x0080000, + NASM_ATTR_DXREG = 0x0100000, + NASM_ATTR_BRANCH = 0x0200000, + NASM_ATTR_RELAXABLE = 0x0400000, + NASM_ATTR_PCREL = 0x0800000, + NASM_ATTR_GPREL = 0x1000000, + + /* Attributes for relocations. */ + NASM_ATTR_HI20 = 0x10000000, + NASM_ATTR_LO12 = 0x20000000, + NASM_ATTR_LO20 = 0x40000000, + + /* Attributes for registers. */ + NASM_ATTR_RDREG = 0x000100 +}; + +/* Macro for instruction attribute. */ +#define ATTR(attr) NASM_ATTR_ ## attr +#define ATTR_NONE 0 +#define ATTR_PCREL (ATTR (PCREL) | ATTR (BRANCH)) + +#define ATTR_ALL (ATTR (ISA_ALL)) +#define ATTR_V2UP (ATTR_ALL & ~(ATTR (ISA_V1))) +#define ATTR_V3MUP (ATTR (ISA_V3) | ATTR (ISA_V3M)) +#define ATTR_V3 (ATTR (ISA_V3)) +#define ATTR_V3MEX_V1 (ATTR_ALL & ~(ATTR (ISA_V3M))) +#define ATTR_V3MEX_V2 (ATTR_V2UP & ~(ATTR (ISA_V3M))) + +/* Lexical element in parsed syntax. */ +typedef int lex_t; + +/* Common header for hash entries. */ +struct nds32_hash_entry +{ + const char *name; +}; + +typedef struct nds32_keyword +{ + const char *name; + int value; + uint64_t attr; +} keyword_t; + +typedef struct nds32_opcode +{ + /* Opcode for the instruction. */ + const char *opcode; + /* Human readable string of this instruction. */ + const char *instruction; + /* Base value of this instruction. */ + uint32_t value; + /* The byte-size of the instruction. */ + int isize; + /* Attributes of this instruction. */ + uint64_t attr; + /* Implicit define/use. */ + uint64_t defuse; + /* Parsed string for assembling. */ + lex_t *syntax; + /* Number of variant. */ + int variant; + /* Next form of the same mnemonic. */ + struct nds32_opcode *next; + /* TODO: Extra constrains and verification. + For example, `mov55 $sp, $sp' is not allowed in v3. */ +} opcode_t; + +typedef struct nds32_asm_insn +{ + /* Assembled instruction bytes. */ + uint32_t insn; + /* The opcode structure for this instruction. */ + struct nds32_opcode *opcode; + /* The field need special fix-up, used for relocation. */ + const struct nds32_field *field; + /* Attributes for relocation. */ + uint64_t attr; + /* Application-dependent data, e.g., expression. */ + void *info; + /* Input/output registers. */ + uint64_t defuse; +} nds32_asm_insn_t; + +typedef struct nds32_asm_desc +{ + /* The callback provided by assembler user for parse an operand, + e.g., parse integer. */ + int (*parse_operand) (struct nds32_asm_desc *, + struct nds32_asm_insn *, + char **, int64_t *); + + /* Result of assembling. */ + int result; + + /* The mach for this assembling. */ + int mach; + + int flags; +} nds32_asm_desc_t; + +/* The field information for an operand. */ +typedef struct nds32_field +{ + /* Name of the field. */ + const char *name; + + int bitpos; + int bitsize; + int shift; + int hw_res; + + int (*parse) (struct nds32_asm_desc *, + struct nds32_asm_insn *, + char **, int64_t *); +} field_t; + +extern void nds32_assemble (nds32_asm_desc_t *, nds32_asm_insn_t *, char *); +extern void nds32_asm_init (nds32_asm_desc_t *, int); + +#endif diff --git a/opcodes/nds32-dis.c b/opcodes/nds32-dis.c new file mode 100644 index 0000000..f4bf779 --- /dev/null +++ b/opcodes/nds32-dis.c @@ -0,0 +1,1196 @@ +/* NDS32-specific support for 32-bit ELF. + Copyright (C) 2012-2013 Free Software Foundation, Inc. + Contributed by Andes Technology Corporation. + + 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 <stdio.h> +#include "ansidecl.h" +#include "dis-asm.h" +#include "bfd.h" +#include "symcat.h" +#include "libiberty.h" +#include "opintl.h" +#include "bfd_stdint.h" + +#define __MF(v, off, bs) ((v & ((1 << (bs)) - 1)) << (off)) +#define __GF(v, off, bs) ((v >> (off)) & ((1 << (bs)) - 1)) +#define __PF(v, off, bs, val) do { v = __put_field (v, off, bs, val); } while (0) +/* #define __SEXT(v, bs) ((v ^ (1 << (bs - 1))) - (1 << (bs - 1))) */ +#define __SEXT(v, bs) (((v & ((1 << bs) - 1)) ^ (1 << (bs - 1))) - (1 << (bs - 1))) +#define __BIT(n) (1 << n) + +/* Get fields */ +#define OP6(insn) ((insn >> 25) & 0x3F) +#define RT5(insn) ((insn >> 20) & 0x1F) +#define RA5(insn) ((insn >> 15) & 0x1F) +#define RB5(insn) ((insn >> 10) & 0x1F) +#define RD5(insn) ((insn >> 5) & 0x1F) +#define SUB5(insn) ((insn >> 0) & 0x1F) +#define SUB10(insn) ((insn >> 0) & 0x3FF) +#define IMMU(insn, bs) (insn & ((1 << bs) - 1)) +#define IMMS(insn, bs) __SEXT ((insn & ((1 << bs) - 1)), bs) +#define IMM1U(insn) IMMU ((insn >> 10), 5) +#define IMM1S(insn) IMMS ((insn >> 10), 5) +#define IMM2U(insn) IMMU ((insn >> 5), 5) +#define IMM2S(insn) IMMS ((insn >> 5), 5) + +/* Default text to print if an instruction isn't recognized. */ +#define UNKNOWN_INSN_MSG _("*unknown*") + +static const char *mnemonic_op6[] = +{ + "lbi", "lhi", "lwi", "ldi", "lbi.bi", "lhi.bi", "lwi.bi", "ldi.bi", + "sbi", "shi", "swi", "sdi", "sbi.bi", "shi.bi", "swi.bi", "sdi.bi", + "lbsi", "lhsi", "lwsi", "dprefi", "lbsi.bi", "lhsi.bi", "lwsi.bi", "lbgp", + "lwc", "swc", "ldc", "sdc", "mem", "lsmw", "hwgp", "sbgp", + "alu1", "alu2", "movi", "sethi", "ji", "jreg", "br1", "br2", + "addi", "subri", "andi", "xori", "ori", "br3", "slti", "sltsi", + "aext", "cext", "misc", "bitci", "op_64", "cop" +}; + +static const char *mnemonic_mem[] = +{ + "lb", "lh", "lw", "ld", "lb.bi", "lh.bi", "lw.bi", "ld.bi", + "sb", "sh", "sw", "sd", "sb.bi", "sh.bi", "sw.bi", "sd.bi", + "lbs", "lhs", "lws", "dpref", "lbs.bi", "lhs.bi", "lws.bi", "27", + "llw", "scw", "32", "33", "34", "35", "36", "37", + "lbup", "41", "lwup", "43", "44", "45", "46", "47", + "sbup", "51", "swup" +}; + +static const char *mnemonic_alu1[] = +{ + "add", "sub", "and", "xor", "or", "nor", "slt", "slts", + "slli", "srli", "srai", "rotri", "sll", "srl", "sra", "rotr", + "seb", "seh", "bitc", "zeh", "wsbh", "or_srli", "divsr", "divr", + "sva", "svs", "cmovz", "cmovn", "add_srli", "sub_srli", "and_srli", "xor_srli" +}; + + +static const char *mnemonic_alu20[] = +{ + "max", "min", "ave", "abs", "clips", "clip", "clo", "clz", + "bset", "bclr", "btgl", "btst", "bse", "bsp", "ffb", "ffmism", + "add.sc", "sub.sc", "add.wc", "sub.wc", "24", "25", "26", "ffzmism", + "qadd", "qsub", "32", "33", "34", "35", "36", "37", + "mfusr", "mtusr", "42", "43", "mul", "45", "46", "47", + "mults64", "mult64", "madds64", "madd64", "msubs64", "msub64", "divs", "div", + "60", "mult32", "62", "madd32", "64", "msub32", "65", "66", + "dmadd", "dmaddc", "dmsub", "dmsubc", "rmfhi", "qmflo" +}; + +static const char *mnemonic_alu21[] = +{ + "00", "01", "02", "03", "04", "05", "06", "07", + "10", "11", "12", "13", "14", "15", "ffbi", "flmism", + "20", "21", "22", "23", "24", "25", "26", "27", + "30", "31", "32", "33", "34", "35", "36", "37", + "40", "41", "42", "43", "44", "45", "46", "47", + "mulsr64", "mulr64", "52", "53", "54", "55", "56", "57", + "60", "61", "62", "maddr32", "64", "msubr32", "66", "67", + "70", "71", "72", "73", "74", "75", "76", "77" +}; + +static const char *mnemonic_br2[] = +{ + "ifcall", "01", "beqz", "bnez", "bgez", "bltz", "bgtz", "blez", + "10", "11", "12", "13", "bgezal", "bltzal" +}; + +static const char *mnemonic_misc[] = +{ + "standby", "cctl", "mfsr", "mtsr", "iret", "trap", "teqz", "tnez", + "dsb", "isb", "break", "syscall", "msync", "isync", "tlbop" +}; + +static const char *mnemonic_hwgp[] = +{ + "lhi.gp", "lhi.gp", "lhsi.gp", "lhsi.gp", + "shi.gp", "shi.gp", "lwi.gp", "swi.gp" +}; + +static const char *keyword_dpref[] = +{ + "SRD", "MRD", "SWR", "MWR", "PTE", "CLWR", "6", "7", + "8", "9", "10", "11", "12", "13", "14", "15" +}; + +static const char *mnemonic_alu[] = +{ + "fadds", "fsubs", "fcpynss", "fcpyss", "fmadds", + "fmsubs", "fcmovns", "fcmovzs", "fnmadds", "fnmsubs", + "10", "11", "fmuls", "fdivs", "faddd", + "fsubd", "fcpynsd", "fcpysd", "fmaddd", "fmsubd", + "fcmovnd", "fcmovzd", "fnmaddd", "fnmsubd", "24", + "25", "fmuld", "fdivd" +}; + +static const char *mnemonic_fpu_2op[] = +{ + "fs2d", "fsqrts", "2", "3", "4", "fabss", "6", "7", + "fui2s", "9", "10", "11", "fsi2s", "13", "14", "15", + "fs2ui", "17", "18", "19", "fs2ui.z", "21", "22", "23", + "fs2si", "25", "26", "27", "fs2si.z", "fd2s", "fsqrtd", "31", + "32", "33", "fabsd", "35", "36", "fui2d", "38", "39", + "40", "fsi2d", "42", "43", "44", "fd2ui", "46", "47", + "48", "fd2ui.z", "50", "51", "52", "fd2si", "54", "55", + "56", "fd2si.z" +}; + +static const char *mnemonic_fs2_cmp[] = +{ + "fcmpeqs", "fcmpeqs.e", "fcmplts", "fcmplts.e", + "fcmples", "fcmples.e", "fcmpuns", "fcmpuns.e" +}; + +static const char *mnemonic_fd2_cmp[] = +{ + "fcmpeqd", "fcmpeqd.e", "fcmpltd", "fcmpltd.e", + "fcmpled", "fcmpled.e", "fcmpund", "fcmpund.e" +}; + +static const char *gpr_map[] = +{ + "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", + "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", + "$r16", "$r17", "$r18", "$r19", "$r20", "$r21", "$r22", "$r23", + "$r24", "$r25", "$r26", "$r27", "$fp", "$gp", "$lp", "$sp" +}; + +static const char *usr_map[][32] = +{ + { + "d0,lo", "d0.hi", "d1,lo", "d1,hi", "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", "pc" + }, + { + "DMA_CFG", "DMA_GCSW", "DMA_CHNSEL", "DMA_ACT", "DMA_SETUP", + "DMA_ISADDR", "DMA_ESADDR", "DMA_TCNT", "DMA_STATUS", "DMA_2DSET", + "10", "11", "12", "13", "14", + "15", "16,", "17", "18", "19", + "20", "21", "22", "23", "24,", + "DMA_2DSCTL" + }, + { + "PFMC0", "PFMC1", "PFMC2", "3", "PFMCTL" + } +}; + +static void +print_insn16 (bfd_vma pc, disassemble_info *info, uint32_t insn) +{ + static char r4map[] = + { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 16, 17, 18, 19 + }; + const int rt5 = __GF (insn, 5, 5); + const int ra5 = __GF (insn, 0, 5); + const int rt4 = r4map[__GF (insn, 5, 4)]; + const int imm5u = IMMU (insn, 5); + const int imm9u = IMMU (insn, 9); + const int rt3 = __GF (insn, 6, 3); + const int ra3 = __GF (insn, 3, 3); + const int rb3 = __GF (insn, 0, 3); + const int rt38 = __GF (insn, 8, 3); + const int imm3u = rb3; + fprintf_ftype func = info->fprintf_func; + void *stream = info->stream; + + static const char *mnemonic_96[] = + { + "0x1", "0x1", "0x2", "0x3", + "add45", "sub45", "addi45", "subi45", + "srai45", "srli45", "slli333", "0xb", + "add333", "sub333", "addi333", "subi333", + "lwi333", "lwi333.bi", "lhi333", "lbi333", + "swi333", "swi333.bi", "shi333", "sbi333", + "addri36.sp", "lwi45.fe", "lwi450", "swi450", + "0x1c", "0x1d", "0x1e", "0x1f", + "0x20", "0x21", "0x22", "0x23", + "0x24", "0x25", "0x26", "0x27," + "0x28", "0x29", "0x2a", "0x2b", + "0x2c", "0x2d", "0x2e", "0x2f," + "slts45", "slt45", "sltsi45", "slti45", + "0x34", "0x35", "0x36", "0x37", + "0x38", "0x39", "0x3a", "0x3b", + "ifcall9", "movpi45" + }; + + static const char *mnemonic_misc33[] = + { + "misc33_0", "misc33_1", "neg33", "not33", "mul33", "xor33", "and33", "or33", + }; + + static const char *mnemonic_0xb[] = + { + "zeb33", "zeh33", "seb33", "seh33", "xlsb33", "x11b33", "bmski33", "fexti33" + }; + + static const char *mnemonic_bnes38[] = + { + "jr5", "jral5", "ex9.it", "?", "ret5", "add5.pc" + }; + + switch (__GF (insn, 7, 8)) + { + case 0xf8: /* push25 */ + case 0xf9: /* pop25 */ + { + uint32_t res[] = { 6, 8, 10, 14 }; + uint32_t re = res[__GF (insn, 5, 2)]; + + func (stream, "%s\t%s, %d", (insn & __BIT (7)) ? "pop25" : "push25", + gpr_map[re], imm5u << 3); + } + return; + } + + if (__GF (insn, 8, 7) == 0x7d) /* movd44 */ + { + int rt5e = __GF (insn, 4, 4) << 1; + int ra5e = IMMU (insn, 4) << 1; + + func (stream, "movd44\t%s, %d", gpr_map[rt5e], ra5e); + return; + } + + switch (__GF (insn, 9, 6)) + { + case 0x4: /* add45 */ + case 0x5: /* sub45 */ + case 0x30: /* slts45 */ + case 0x31: /* slt45 */ + func (stream, "%s\t%s, %s", mnemonic_96[__GF (insn, 9, 6)], + gpr_map[rt4], gpr_map[ra5]); + return; + case 0x6: /* addi45 */ + case 0x7: /* subi45 */ + case 0x8: /* srai45 */ + case 0x9: /* srli45 */ + case 0x32: /* sltsi45 */ + case 0x33: /* slti45 */ + func (stream, "%s\t%s, %d", mnemonic_96[__GF (insn, 9, 6)], + gpr_map[rt4], ra5); + return; + case 0xc: /* add333 */ + case 0xd: /* sub333 */ + func (stream, "%s\t%s, %s, %s", mnemonic_96[__GF (insn, 9, 6)], + gpr_map[rt3], gpr_map[ra3], gpr_map[rb3]); + return; + case 0xa: /* slli333 */ + case 0xe: /* addi333 */ + case 0xf: /* subi333 */ + func (stream, "%s\t%s, %s, %d", mnemonic_96[__GF (insn, 9, 6)], + gpr_map[rt3], gpr_map[ra3], imm3u); + return; + case 0x10: /* lwi333 */ + case 0x14: /* swi333 */ + func (stream, "%s\t%s, [%s + %d]", mnemonic_96[__GF (insn, 9, 6)], + gpr_map[rt3], gpr_map[ra3], imm3u << 2); + return; + case 0x12: /* lhi333 */ + case 0x16: /* shi333 */ + func (stream, "%s\t%s, [%s + %d]", mnemonic_96[__GF (insn, 9, 6)], + gpr_map[rt3], gpr_map[ra3], imm3u << 1); + return; + case 0x13: /* lbi333 */ + case 0x17: /* sbi333 */ + func (stream, "%s\t%s, [%s + %d]", mnemonic_96[__GF (insn, 9, 6)], + gpr_map[rt3], gpr_map[ra3], imm3u); + return; + case 0x11: /* lwi333.bi */ + case 0x15: /* swi333.bi */ + func (stream, "%s\t%s, [%s], %d", mnemonic_96[__GF (insn, 9, 6)], + gpr_map[rt3], gpr_map[ra3], imm3u << 2); + return; + case 0x18: /* addri36.sp */ + func (stream, "%s\t%s, %d", mnemonic_96[__GF (insn, 9, 6)], + gpr_map[rt3], IMMU (insn, 6) << 2); + return; + case 0x19: /* lwi45.fe */ + func (stream, "%s\t%s, %d", mnemonic_96[__GF (insn, 9, 6)], + gpr_map[rt4], -((32 - imm5u) << 2)); + return; + case 0x1a: /* lwi450 */ + case 0x1b: /* swi450 */ + func (stream, "%s\t%s, [%s]", mnemonic_96[__GF (insn, 9, 6)], + gpr_map[rt4], gpr_map[ra5]); + return; + case 0x34: /* beqzs8, bnezs8 */ + func (stream, "%s\t", ((insn & __BIT (8)) ? "bnezs8" : "beqzs8")); + info->print_address_func ((IMMS (insn, 8) << 1) + pc, info); + return; + case 0x35: /* break16, ex9.it */ + /* FIXME: Check bfd_mach. */ + if (imm9u < 32) /* break16 */ + func (stream, "break16\t%d", imm9u); + else + func (stream, "ex9.it\t%d", imm9u); + return; + case 0x3c: /* ifcall9 */ + func (stream, "%s\t", mnemonic_96[__GF (insn, 9, 6)]); + info->print_address_func ((IMMU (insn, 9) << 1) + pc, info); + return; + case 0x3d: /* movpi45 */ + func (stream, "%s\t%s, %d", mnemonic_96[__GF (insn, 9, 6)], + gpr_map[rt4], ra5 + 16); + return; + case 0x3f: /* MISC33 */ + func (stream, "%s\t%s, %s", mnemonic_misc33[rb3], + gpr_map[rt3], gpr_map[ra3]); + return; + case 0xb: /* ... */ + func (stream, "%s\t%s, %s", mnemonic_0xb[rb3], + gpr_map[rt3], gpr_map[ra3]); + return; + } + + switch (__GF (insn, 10, 5)) + { + case 0x0: /* mov55 or ifret16 */ + /* FIXME: Check bfd_mach. */ + if (rt5 == ra5 && rt5 == 31) + func (stream, "ifret16"); + else + func (stream, "mov55\t%s, %s", gpr_map[rt5], gpr_map[ra5]); + return; + case 0x1: /* movi55 */ + func (stream, "movi55\t%s, %d", gpr_map[rt5], IMMS (insn, 5)); + return; + case 0x1b: /* addi10s (V2) */ + func (stream, "addi10s\t%d", IMMS (insn, 10)); + return; + } + + switch (__GF (insn, 11, 4)) + { + case 0x7: /* lwi37.fp/swi37.fp */ + func (stream, "%s\t%s, [$fp + 0x%x]", + ((insn & __BIT (7)) ? "swi37" : "lwi37"), + gpr_map[rt38], IMMU (insn, 7) << 2); + return; + case 0x8: /* beqz38 */ + case 0x9: /* bnez38 */ + func (stream, "%s\t%s, ", + ((__GF (insn, 11, 4) & 1) ? "bnez38" : "beqz38"), gpr_map[rt38]); + info->print_address_func ((IMMS (insn, 8) << 1) + pc, info); + return; + case 0xa: /* beqs38/j8, implied r5 */ + if (rt38 == 5) + { + func (stream, "j8\t"); + info->print_address_func ((IMMS (insn, 8) << 1) + pc, info); + } + else + { + func (stream, "beqs38\t%s, ", gpr_map[rt38]); + info->print_address_func ((IMMS (insn, 8) << 1) + pc, info); + } + return; + case 0xb: /* bnes38 and others */ + if (rt38 == 5) + { + switch (__GF (insn, 5, 3)) + { + case 0: /* jr5 */ + case 1: /* jral5 */ + case 4: /* ret5 */ + func (stream, "%s\t%s", mnemonic_bnes38[__GF (insn, 5, 3)], + gpr_map[ra5]); + return; + case 2: /* ex9.it imm5 */ + case 5: /* add5.pc */ + func (stream, "%s\t%d", mnemonic_bnes38[__GF (insn, 5, 3)], ra5); + return; + default: + func (stream, UNKNOWN_INSN_MSG); + return; + } + } + else + { + func (stream, "bnes38\t%s", gpr_map[rt3]); + info->print_address_func ((IMMS (insn, 8) << 1) + pc, info); + } + return; + case 0xe: /* lwi37/swi37 */ + func (stream, "%s\t%s, [+ 0x%x]", + ((insn & __BIT (7)) ? "swi37.sp" : "lwi37.sp"), + gpr_map[rt38], IMMU (insn, 7) << 2); + return; + } +} + + +static void +print_insn32_mem (bfd_vma pc ATTRIBUTE_UNUSED, disassemble_info *info, + uint32_t insn) +{ + const int rt = RT5 (insn); + const int ra = RA5 (insn); + const int rb = RB5 (insn); + const int sv = __GF (insn, 8, 2); + const int op = insn & 0xFF; + fprintf_ftype func = info->fprintf_func; + void *stream = info->stream; + + switch (op) + { + case 0x0: /* lb */ + case 0x1: /* lh */ + case 0x2: /* lw */ + case 0x3: /* ld */ + case 0x8: /* sb */ + case 0x9: /* sh */ + case 0xa: /* sw */ + case 0xb: /* sd */ + case 0x10: /* lbs */ + case 0x11: /* lhs */ + case 0x12: /* lws */ + case 0x18: /* llw */ + case 0x19: /* scw */ + case 0x20: /* lbup */ + case 0x22: /* lwup */ + case 0x28: /* sbup */ + case 0x2a: /* swup */ + func (stream, "%s\t%s, [%s + (%s << %d)]", + mnemonic_mem[op], gpr_map[rt], gpr_map[ra], gpr_map[rb], sv); + break; + case 0x4: /* lb.bi */ + case 0x5: /* lh.bi */ + case 0x6: /* lw.bi */ + case 0x7: /* ld.bi */ + case 0xc: /* sb.bi */ + case 0xd: /* sh.bi */ + case 0xe: /* sw.bi */ + case 0xf: /* sd.bi */ + case 0x14: /* lbs.bi */ + case 0x15: /* lhs.bi */ + case 0x16: /* lws.bi */ + func (stream, "%s\t%s, [%s], (%s << %d)", + mnemonic_mem[op], gpr_map[rt], gpr_map[ra], gpr_map[rb], sv); + break; + case 0x13: /* dpref */ + { + const char *subtype = "???"; + + if ((rt & 0xf) < ARRAY_SIZE (keyword_dpref)) + subtype = keyword_dpref[rt & 0xf]; + + func (stream, "%s\t%s, [%s + (%s << %d)]", + "dpref", subtype, gpr_map[ra], gpr_map[rb], sv); + } + break; + default: + func (stream, UNKNOWN_INSN_MSG); + return; + } +} + +static void +print_insn32_alu1 (bfd_vma pc ATTRIBUTE_UNUSED, disassemble_info *info, uint32_t insn) +{ + int op = insn & 0x1f; + const int rt = RT5 (insn); + const int ra = RA5 (insn); + const int rb = RB5 (insn); + const int rd = RD5 (insn); + fprintf_ftype func = info->fprintf_func; + void *stream = info->stream; + + switch (op) + { + case 0x0: /* add, add_slli */ + case 0x1: /* sub, sub_slli */ + case 0x2: /* and, add_slli */ + case 0x3: /* xor, xor_slli */ + case 0x4: /* or, or_slli */ + if (rd != 0) + { + func (stream, "%s_slli\t%s, %s, %s, #%d", + mnemonic_alu1[op], gpr_map[rt], gpr_map[ra], gpr_map[rb], rd); + } + else + { + func (stream, "%s\t%s, %s, %s", + mnemonic_alu1[op], gpr_map[rt], gpr_map[ra], gpr_map[rb]); + } + return; + case 0x1c: /* add_srli */ + case 0x1d: /* sub_srli */ + case 0x1e: /* and_srli */ + case 0x1f: /* xor_srli */ + case 0x15: /* or_srli */ + func (stream, "%s\t%s, %s, %s, #%d", + mnemonic_alu1[op], gpr_map[rt], gpr_map[ra], gpr_map[rb], rd); + return; + case 0x5: /* nor */ + case 0x6: /* slt */ + case 0x7: /* slts */ + case 0xc: /* sll */ + case 0xd: /* srl */ + case 0xe: /* sra */ + case 0xf: /* rotr */ + case 0x12: /* bitc */ + case 0x18: /* sva */ + case 0x19: /* svs */ + case 0x1a: /* cmovz */ + case 0x1b: /* cmovn */ + func (stream, "%s\t%s, %s, %s", + mnemonic_alu1[op], gpr_map[rt], gpr_map[ra], gpr_map[rb]); + return; + case 0x9: /* srli */ + if (ra ==0 && rb == 0 && rb==0) + { + func (stream, "nop"); + return; + } + case 0x8: /* slli */ + case 0xa: /* srai */ + case 0xb: /* rotri */ + func (stream, "%s\t%s, %s, #%d", + mnemonic_alu1[op], gpr_map[rt], gpr_map[ra], rb); + return; + case 0x10: /* seb */ + case 0x11: /* seh */ + case 0x13: /* zeh */ + case 0x14: /* wsbh */ + func (stream, "%s\t%s, %s", + mnemonic_alu1[op], gpr_map[rt], gpr_map[ra]); + return; + case 0x16: /* divsr */ + case 0x17: /* divr */ + func (stream, "%s\t%s, %s, %s, %s", + mnemonic_alu1[op], gpr_map[rt], gpr_map[rd], gpr_map[ra], gpr_map[rb]); + return; + default: + func (stream, UNKNOWN_INSN_MSG); + return; + } + + return; +} + +static void +print_insn32_alu2 (bfd_vma pc ATTRIBUTE_UNUSED, + disassemble_info *info, + uint32_t insn) +{ + int op = insn & 0x3ff; + const int rt = RT5 (insn); + const int ra = RA5 (insn); + const int rb = RB5 (insn); + fprintf_ftype func = info->fprintf_func; + void *stream = info->stream; + + if ((insn & 0x7f) == 0x4e) /* ffbi */ + { + func (stream, "ffbi\t%s, %s, #0x%x", + gpr_map[rt], gpr_map[ra], __GF (insn, 7, 8)); + return; + } + + switch (op) + { + case 0x0: /* max */ + case 0x1: /* min */ + case 0x2: /* ave */ + case 0xc: /* bse */ + case 0xd: /* bsp */ + case 0xe: /* ffb */ + case 0xf: /* ffmism */ + case 0x17: /* ffzmism */ + case 0x24: /* mul */ + func (stream, "%s\t%s, %s, %s", mnemonic_alu20[op], + gpr_map[rt], gpr_map[ra], gpr_map[rb]); + return; + + case 0x3: /* abs */ + case 0x6: /* clo */ + case 0x7: /* clz */ + func (stream, "%s\t%s, %s", mnemonic_alu20[op], gpr_map[rt], gpr_map[ra]); + return; + + case 0x4: /* clips */ + case 0x5: /* clip */ + case 0x8: /* bset */ + case 0x9: /* bclr */ + case 0xa: /* btgl */ + case 0xb: /* btst */ + func (stream, "%s\t%s, %s, #%d", mnemonic_alu20[op], + gpr_map[rt], gpr_map[ra], IMM1U (insn)); + return; + + case 0x20: /* mfusr */ + case 0x21: /* mtusr */ + func (stream, "%s\t%s, $%s", mnemonic_alu20[op], + gpr_map[rt], usr_map[__GF (insn, 10, 5)][__GF (insn, 15, 5)]); + return; + case 0x28: /* mults64 */ + case 0x29: /* mult64 */ + case 0x2a: /* madds64 */ + case 0x2b: /* madd64 */ + case 0x2c: /* msubs64 */ + case 0x2d: /* msub64 */ + case 0x2e: /* divs */ + case 0x2f: /* div */ + case 0x31: /* mult32 */ + case 0x33: /* madd32 */ + case 0x35: /* msub32 */ + func (stream, "%s\t$d%d, %s, %s", mnemonic_alu20[op], + rt >> 1, gpr_map[ra], gpr_map[rb]); + return; + + case 0x4f: /* flmism */ + case 0x68: /* mulsr64 */ + case 0x69: /* mulr64 */ + case 0x73: /* maddr32 */ + case 0x75: /* msubr32 */ + op = insn & 0x3f; + func (stream, "%s\t%s, %s, %s", mnemonic_alu21[op], + gpr_map[rt], gpr_map[ra], gpr_map[rb]); + return; + default: + func (stream, UNKNOWN_INSN_MSG); + return; + } +} + +static void +print_insn32_jreg (bfd_vma pc ATTRIBUTE_UNUSED, disassemble_info *info, uint32_t insn) +{ + int op = insn & 0xff; + const int rt = RT5 (insn); + const int rb = RB5 (insn); + const char *dtit_on[] = { "", ".iton", ".dton", ".ton" }; + const char *dtit_off[] = { "", ".itoff", ".dtoff", ".toff" }; + const char *mnemonic_jreg[] = { "jr", "jral", "jrnez", "jralnez" }; + const char *mnemonic_ret[] = { "jr", "ret", NULL, "ifret" }; + const int dtit = __GF (insn, 8, 2); + fprintf_ftype func = info->fprintf_func; + void *stream = info->stream; + + switch (op) + { + case 0: /* jr */ + func (stream, "%s%s\t%s", mnemonic_ret[op >> 5], + dtit_on[dtit], gpr_map[rb]); + return; + + case 0x20: /* ret */ + func (stream, "%s%s\t%s", mnemonic_ret[op >> 5], + dtit_off[dtit], gpr_map[rb]); + return; + case 0x60: /* ifret */ + break; + case 1: /* jral */ + case 2: /* jrnez */ + case 3: /* jralnez */ + func (stream, "%s%s\t%s, %s", mnemonic_jreg[op], + dtit_on[dtit], gpr_map[rt], gpr_map[rb]); + return; + default: /* unknown */ + func (stream, UNKNOWN_INSN_MSG); + break; + } +} + +static void +print_insn32_misc (bfd_vma pc ATTRIBUTE_UNUSED, disassemble_info *info, + uint32_t insn) +{ + int op = insn & 0x1f; + int rt = RT5 (insn); + unsigned int id; + fprintf_ftype func = info->fprintf_func; + void *stream = info->stream; + + static const char *keyword_standby[] = + { + "no_wake_grant", "wake_grant", "wait_done", + }; + static const char *keyword_tlbop[] = + { + "TRD", "TWR", "RWR", "RWLK", "UNLK", "PB", "INV", "FLUA" + }; + + switch (op) + { + case 0x0: /* standby */ + id = __GF (insn, 5, 20); + if (id < ARRAY_SIZE (keyword_standby)) + func (stream, "standby\t%s", keyword_standby[id]); + else + func (stream, "standby\t%d", id); + return; + case 0x1: /* cctl */ + func (stream, "cctl\t!FIXME"); + return; + case 0x8: /* dsb */ + case 0x9: /* isb */ + case 0xd: /* isync */ + case 0xc: /* msync */ + case 0x4: /* iret */ + func (stream, "%s", mnemonic_misc[op]); + return; + case 0x5: /* trap */ + case 0xa: /* break */ + case 0xb: /* syscall */ + id = __GF (insn, 5, 15); + func (stream, "%s\t%d", mnemonic_misc[op], id); + return; + case 0x2: /* mfsr */ + case 0x3: /* mtsr */ + /* FIXME: setend, setgie. */ + id = __GF (insn, 10, 10); + func (stream, "%s\t%s, %d", mnemonic_misc[op], gpr_map[rt], id); + return; + case 0x6: /* teqz */ + case 0x7: /* tnez */ + id = __GF (insn, 5, 15); + func (stream, "%s\t%s, %d", mnemonic_misc[op], gpr_map[rt], id); + return; + case 0xe: /* tlbop */ + id = __GF (insn, 5, 5); + if (id < ARRAY_SIZE (keyword_tlbop)) + func (stream, "tlbop\t%s", keyword_tlbop[id]); + else + func (stream, "tlbop\t%d", id); + return; + } +} + +static void +print_insn32_fpu (bfd_vma pc ATTRIBUTE_UNUSED, disassemble_info *info, + uint32_t insn) +{ + int op = insn & 0xf; + int mask_sub_op = (insn & 0x3c0) >> 6; + int mask_bi = (insn & 0x80) >> 7; + int mask_cfg = (insn & 0x7c00) >> 10; + int mask_f2op = (insn & 0x7c00) >> 10; + int dp = 0; + int dp_insn = 0; + char wd = 's'; + const int rt = RT5 (insn); + const int ra = RA5 (insn); + const int rb = RB5 (insn); + const int sv = __GF (insn, 8, 2); + fprintf_ftype func = info->fprintf_func; + void *stream = info->stream; + + switch (op) + { + case 0x0: /* fs1 */ + case 0x8: /* fd1 */ + dp = (op & 0x8) ? 1 : 0; + if (dp) + { + wd = 'd'; + dp_insn = 14; + } + else + { + wd = 's'; + dp_insn = 0; + } + switch (mask_sub_op) + { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + case 0x4: + case 0x5: + case 0x8: + case 0x9: + case 0xc: + case 0xd: + func (stream, "%s\t$f%c%d, $f%c%d, $f%c%d", + mnemonic_alu[mask_sub_op + dp_insn], + wd, rt, wd, ra, wd, rb); + return; + case 0x6: + case 0x7: + func (stream, "%s\t$f%c%d, $f%c%d, $fs%d", + mnemonic_alu[mask_sub_op + dp_insn], + wd, rt, wd, ra, rb); + return; + case 0xf: + if (dp) + { + wd = 'd'; + dp_insn = 0x1d; + } + else + { + wd = 's'; + dp_insn = 0; + } + + switch (mask_f2op) + { + case 0x0: + if (dp) + func (stream, "%s\t$fs%d, $fd%d", + mnemonic_fpu_2op[mask_f2op + dp_insn], rt, ra); + else + func (stream, "%s\t$fd%d, $fs%d", + mnemonic_fpu_2op[mask_f2op + dp_insn], rt, ra); + return; + case 0x1: + case 0x5: + func (stream, "%s\t$f%c%d, $f%c%d", + mnemonic_fpu_2op[mask_f2op + dp_insn], wd, rt, wd, ra); + return; + case 0x8: + case 0xc: + func (stream, "%s\t$f%c%d, $fs%d", + mnemonic_fpu_2op[mask_f2op + dp_insn], wd, rt, ra); + return; + case 0x10: + case 0x14: + case 0x18: + case 0x1c: + func (stream, "%s\t$fs%d, $f%c%d", + mnemonic_fpu_2op[mask_f2op + dp_insn], rt, wd, ra); + return; + } + } + case 0x1: /* mfcp */ + switch (mask_sub_op) + { + case 0x0: + func (stream, "fmfsr\t%s, $fs%d", gpr_map[rt], ra); + return; + case 0x1: + func (stream, "fmfdr\t%s, $fd%d", gpr_map[rt], ra); + return; + case 0xc: + if (mask_cfg) + func (stream, "fmfcsr\t%s", gpr_map[rt]); + else + func (stream, "fmfcfg\t%s", gpr_map[rt]); + return; + } + case 0x2: /* fls */ + if (mask_bi) + func (stream, "fls.bi\t$fs%d, [%s], (%s << %d)", + rt, gpr_map[ra], gpr_map[rb], sv); + else + func (stream, "fls\t$fs%d, [%s + (%s << %d)]", + rt, gpr_map[ra], gpr_map[rb], sv); + return; + case 0x3: /* fld */ + if (mask_bi) + func (stream, "fld.bi\t$fd%d, [%s], (%s << %d)", + rt, gpr_map[ra], gpr_map[rb], sv); + else + func (stream, "fld\t$fd%d, [%s + (%s << %d)]", + rt, gpr_map[ra], gpr_map[rb], sv); + return; + case 0x4: /* fs2 */ + func (stream, "%s\t$fs%d, $fs%d, $fs%d", + mnemonic_fs2_cmp[mask_sub_op], rt, ra, rb); + return; + case 0x9: /* mtcp */ + switch (mask_sub_op) + { + case 0x0: + func (stream, "fmtsr\t%s, $fs%d", gpr_map[rt], ra); + return; + case 0x1: + func (stream, "fmtdr\t%s, $fd%d", gpr_map[rt], ra); + return; + case 0xc: + func (stream, "fmtcsr\t%s", gpr_map[rt]); + return; + } + case 0xa: /* fss */ + if (mask_bi) + func (stream, "fss.bi\t$fs%d, [%s], (%s << %d)", + rt, gpr_map[ra], gpr_map[rb], sv); + else + func (stream, "fss\t$fs%d, [%s + (%s << %d)]", + rt, gpr_map[ra], gpr_map[rb], sv); + return; + case 0xb: /* fsd */ + if (mask_bi) + func (stream, "fsd.bi\t$fd%d, [%s], (%s << %d)", + rt, gpr_map[ra], gpr_map[rb], sv); + else + func (stream, "fsd\t$fd%d, [%s + (%s << %d)]", + rt, gpr_map[ra], gpr_map[rb], sv); + return; + case 0xc: /* fd2 */ + func (stream, "%s\t$fs%d, $fd%d, $fd%d", + mnemonic_fd2_cmp[mask_sub_op], rt, ra, rb); + return; + } +} + +static void +print_insn32 (bfd_vma pc, disassemble_info *info, uint32_t insn) +{ + int op = OP6 (insn); + const int rt = RT5 (insn); + const int ra = RA5 (insn); + const int rb = RB5 (insn); + const int imm15s = IMMS (insn, 15); + const int imm15u = IMMU (insn, 15); + uint32_t shift; + fprintf_ftype func = info->fprintf_func; + void *stream = info->stream; + + switch (op) + { + case 0x0: /* lbi */ + case 0x1: /* lhi */ + case 0x2: /* lwi */ + case 0x3: /* ldi */ + case 0x8: /* sbi */ + case 0x9: /* shi */ + case 0xa: /* swi */ + case 0xb: /* sdi */ + case 0x10: /* lbsi */ + case 0x11: /* lhsi */ + case 0x12: /* lwsi */ + shift = op & 0x3; + func (stream, "%s\t%s, [%s + #%d]", + mnemonic_op6[op], gpr_map[rt], gpr_map[ra], imm15s << shift); + return; + case 0x4: /* lbi.bi */ + case 0x5: /* lhi.bi */ + case 0x6: /* lwi.bi */ + case 0x7: /* ldi.bi */ + case 0xc: /* sbi.bi */ + case 0xd: /* shi.bi */ + case 0xe: /* swi.bi */ + case 0xf: /* sdi.bi */ + case 0x14: /* lbsi.bi */ + case 0x15: /* lhsi.bi */ + case 0x16: /* lwsi.bi */ + shift = op & 0x3; + func (stream, "%s\t%s, [%s], #%d", + mnemonic_op6[op], gpr_map[rt], gpr_map[ra], imm15s << shift); + return; + case 0x13: /* dprefi */ + { + const char *subtype = "???"; + char wd = 'w'; + + shift = 2; + + /* d-bit */ + if (rt & 0x10) + { + wd = 'd'; + shift = 3; + } + + if ((rt & 0xf) < ARRAY_SIZE (keyword_dpref)) + subtype = keyword_dpref[rt & 0xf]; + + func (stream, "%s.%c\t%s, [%s + #%d]", + mnemonic_op6[op], wd, subtype, gpr_map[ra], imm15s << shift); + } + return; + case 0x17: /* LBGP */ + func (stream, "%s\t%s, [+ %d]", + ((insn & __BIT (19)) ? "lbsi.gp" : "lbi.gp"), + gpr_map[rt], IMMS (insn, 19)); + return; + case 0x18: /* LWC */ + case 0x19: /* SWC */ + case 0x1a: /* LDC */ + case 0x1b: /* SDC */ + if (__GF (insn, 13, 2) == 0) + { + char ls = (op & 1) ? 's' : 'l'; + char wd = (op & 2) ? 'd' : 's'; + + if (insn & __BIT (12)) + { + func (stream, "f%c%ci.bi\t$f%c%d, [%s], %d", ls, wd, + wd, rt, gpr_map[ra], IMMS (insn, 12) << 2); + } + else + { + func (stream, "f%c%ci\t$f%c%d, [%s + %d]", ls, wd, + wd, rt, gpr_map[ra], IMMS (insn, 12) << 2); + } + } + else + { + char ls = (op & 1) ? 's' : 'l'; + char wd = (op & 2) ? 'd' : 'w'; + int cp = __GF (insn, 13, 2); + + if (insn & __BIT (12)) + { + func (stream, "cp%c%ci\tcp%d, $cpr%d, [%s], %d", ls, wd, + cp, rt, gpr_map[ra], IMMS (insn, 12) << 2); + } + else + { + func (stream, "cp%c%ci\tcp%d, $cpr%d, [%s + %d]", ls, wd, + cp, rt, gpr_map[ra], IMMS (insn, 12) << 2); + } + } + return; + case 0x1c: /* MEM */ + print_insn32_mem (pc, info, insn); + return; + case 0x1d: /* LSMW */ + { + int enb4 = __GF (insn, 6, 4); + char ls = (insn & __BIT (5)) ? 's' : 'l'; + char ab = (insn & __BIT (4)) ? 'a' : 'b'; + char *di = (insn & __BIT (3)) ? "d" : "i"; + char *m = (insn & __BIT (2)) ? "m" : ""; + static const char *s[] = {"", "a", "zb", "?"}; + + /* lsmwzb only always increase. */ + if ((insn & 0x3) == 2) + di = ""; + + func (stream, "%cmw%s.%c%s%s\t%s, [%s], %s, 0x%x", + ls, s[insn & 0x3], ab, di, m, gpr_map[rt], + gpr_map[ra], gpr_map[rb], enb4); + } + return; + case 0x1e: /* HWGP */ + op = __GF (insn, 17, 3); + switch (op) + { + case 0: case 1: /* lhi.gp */ + case 2: case 3: /* lhsi.gp */ + case 4: case 5: /* shi.gp */ + func (stream, "%s\t%s, [+ %d]", + mnemonic_hwgp[op], gpr_map[rt], IMMS (insn, 18) << 1); + return; + case 6: /* lwi.gp */ + case 7: /* swi.gp */ + func (stream, "%s\t%s, [+ %d]", + mnemonic_hwgp[op], gpr_map[rt], IMMS (insn, 17) << 2); + return; + } + return; + case 0x1f: /* SBGP */ + if (insn & __BIT (19)) + func (stream, "addi.gp\t%s, %d", + gpr_map[rt], IMMS (insn, 19)); + else + func (stream, "sbi.gp\t%s, [+ %d]", + gpr_map[rt], IMMS (insn, 19)); + return; + case 0x20: /* ALU_1 */ + print_insn32_alu1 (pc, info, insn); + return; + case 0x21: /* ALU_2 */ + print_insn32_alu2 (pc, info, insn); + return; + case 0x22: /* movi */ + func (stream, "movi\t%s, %d", gpr_map[rt], IMMS (insn, 20)); + return; + case 0x23: /* sethi */ + func (stream, "sethi\t%s, 0x%x", gpr_map[rt], IMMU (insn, 20)); + return; + case 0x24: /* ji, jal */ + /* FIXME: Handle relocation. */ + if (info->flags & INSN_HAS_RELOC) + pc = 0; + func (stream, "%s\t", ((insn & __BIT (24)) ? "jal" : "j")); + info->print_address_func ((IMMS (insn, 24) << 1) + pc, info); + return; + case 0x25: /* jreg */ + print_insn32_jreg (pc, info, insn); + return; + case 0x26: /* br1 */ + func (stream, "%s\t%s, %s, ", ((insn & __BIT (14)) ? "bne" : "beq"), + gpr_map[rt], gpr_map[ra]); + info->print_address_func ((IMMS (insn, 14) << 1) + pc, info); + return; + case 0x27: /* br2 */ + func (stream, "%s\t%s, ", mnemonic_br2[__GF (insn, 16, 4)], + gpr_map[rt]); + info->print_address_func ((IMMS (insn, 16) << 1) + pc, info); + return; + case 0x28: /* addi */ + case 0x2e: /* slti */ + case 0x2f: /* sltsi */ + case 0x29: /* subri */ + func (stream, "%s\t%s, %s, %d", + mnemonic_op6[op], gpr_map[rt], gpr_map[ra], imm15s); + return; + case 0x2a: /* andi */ + case 0x2b: /* xori */ + case 0x2c: /* ori */ + case 0x33: /* bitci */ + func (stream, "%s\t%s, %s, %d", + mnemonic_op6[op], gpr_map[rt], gpr_map[ra], imm15u); + return; + case 0x2d: /* br3, beqc, bnec */ + func (stream, "%s\t%s, %d, ", ((insn & __BIT (19)) ? "bnec" : "beqc"), + gpr_map[rt], __SEXT (__GF (insn, 8, 11), 11)); + info->print_address_func ((IMMS (insn, 8) << 1) + pc, info); + return; + case 0x32: /* misc */ + print_insn32_misc (pc, info, insn); + return; + case 0x35: /* FPU */ + print_insn32_fpu (pc, info, insn); + return; + } +} + +int +print_insn_nds32 (bfd_vma pc, disassemble_info *info) +{ + int status; + bfd_byte buf[4]; + uint32_t insn; + + status = info->read_memory_func (pc, (bfd_byte *) buf, 2, info); + if (status) + return -1; + + /* 16-bit instruction. */ + if (buf[0] & 0x80) + { + insn = bfd_getb16 (buf); + print_insn16 (pc, info, insn); + return 2; + } + + /* 32-bit instructions. */ + status = info->read_memory_func (pc + 2, (bfd_byte *) buf + 2, 2, info); + if (status) + return -1; + + insn = bfd_getb32 (buf); + print_insn32 (pc, info, insn); + + return 4; +} diff --git a/opcodes/nds32-opc.h b/opcodes/nds32-opc.h new file mode 100644 index 0000000..4ab0d48 --- /dev/null +++ b/opcodes/nds32-opc.h @@ -0,0 +1,209 @@ +/* NDS32-specific support for 32-bit ELF. + Copyright (C) 2012-2013 Free Software Foundation, Inc. + Contributed by Andes Technology Corporation. + + 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.*/ + + +#ifndef NDS32_OPC_H +#define NDS32_OPC_H + +/* This was the enum used for 32/16 conversion. */ + +enum +{ + NDS32_INSN_INVALID, NDS32_INSN_MOVI, NDS32_INSN_SETHI, NDS32_INSN_ADDI, + NDS32_INSN_ADD, NDS32_INSN_SLTSI, NDS32_INSN_SLTS, NDS32_INSN_SLTI, + NDS32_INSN_SLT, NDS32_INSN_SUBRI, NDS32_INSN_SUB, NDS32_INSN_ANDI, + NDS32_INSN_AND, NDS32_INSN_XORI, NDS32_INSN_XOR, NDS32_INSN_ORI, + NDS32_INSN_OR, NDS32_INSN_NOR, NDS32_INSN_SVA, NDS32_INSN_SVS, + NDS32_INSN_SEB, NDS32_INSN_SEH, NDS32_INSN_ZEH, NDS32_INSN_WSBH, + NDS32_INSN_SLLI, NDS32_INSN_SLL, NDS32_INSN_SRAI, NDS32_INSN_SRA, + NDS32_INSN_ROTRI, NDS32_INSN_ROTR, NDS32_INSN_SRLI, NDS32_INSN_SRL, + NDS32_INSN_MUL, NDS32_INSN_MULTS64, NDS32_INSN_MULT64, NDS32_INSN_MADDS64, + NDS32_INSN_MADD64, NDS32_INSN_MSUBS64, NDS32_INSN_MSUB64, + NDS32_INSN_MULT32, NDS32_INSN_MADD32, NDS32_INSN_MSUB32, NDS32_INSN_MFUSR, + NDS32_INSN_MTUSR, NDS32_INSN_LBI, NDS32_INSN_LBI_BI, NDS32_INSN_LB, + NDS32_INSN_LB_BI, NDS32_INSN_LHI, NDS32_INSN_LHI_BI, NDS32_INSN_LH, + NDS32_INSN_LH_BI, NDS32_INSN_LWI, NDS32_INSN_LWI_BI, NDS32_INSN_LW, + NDS32_INSN_LW_BI, NDS32_INSN_LWUP, NDS32_INSN_SWUP, NDS32_INSN_LBSI, + NDS32_INSN_LBSI_BI, NDS32_INSN_LBS, NDS32_INSN_LBS_BI, NDS32_INSN_LHSI, + NDS32_INSN_LHSI_BI, NDS32_INSN_LHS, NDS32_INSN_LHS_BI, NDS32_INSN_SBI, + NDS32_INSN_SBI_BI, NDS32_INSN_SB, NDS32_INSN_SB_BI, NDS32_INSN_SHI, + NDS32_INSN_SHI_BI, NDS32_INSN_SH, NDS32_INSN_SH_BI, NDS32_INSN_SWI, + NDS32_INSN_SWI_BI, NDS32_INSN_SW, NDS32_INSN_SW_BI, NDS32_INSN_LMW_BI, + NDS32_INSN_LMW_BIM, NDS32_INSN_LMW_BD, NDS32_INSN_LMW_BDM, + NDS32_INSN_LMW_AI, NDS32_INSN_LMW_AIM, NDS32_INSN_LMW_AD, + NDS32_INSN_LMW_ADM, NDS32_INSN_SMW_BI, NDS32_INSN_SMW_BIM, + NDS32_INSN_SMW_BD, NDS32_INSN_SMW_BDM, NDS32_INSN_SMW_AI, + NDS32_INSN_SMW_AIM, NDS32_INSN_SMW_AD, NDS32_INSN_SMW_ADM, NDS32_INSN_LLW, + NDS32_INSN_SCW, NDS32_INSN_J, NDS32_INSN_JAL, NDS32_INSN_JR, + NDS32_INSN_RET, NDS32_INSN_JR_ITOFF, NDS32_INSN_JR_TOFF, + NDS32_INSN_RET_ITOFF, NDS32_INSN_RET_TOFF, NDS32_INSN_JRAL, + NDS32_INSN_JRAL_ITON, NDS32_INSN_JRAL_TON, NDS32_INSN_BEQ, NDS32_INSN_BNE, + NDS32_INSN_BEQZ, NDS32_INSN_BNEZ, NDS32_INSN_BGEZ, NDS32_INSN_BLTZ, + NDS32_INSN_BGTZ, NDS32_INSN_BLEZ, NDS32_INSN_BGEZAL, NDS32_INSN_BLTZAL, + NDS32_INSN_MFSR, NDS32_INSN_MTSR, NDS32_INSN_SETEND_L, + NDS32_INSN_SETEND_B, NDS32_INSN_SETGIE_D, NDS32_INSN_SETGIE_E, + NDS32_INSN_CMOVZ, NDS32_INSN_CMOVN, NDS32_INSN_DPREFI_D, + NDS32_INSN_DPREFI_W, NDS32_INSN_DPREF, NDS32_INSN_ISYNC, NDS32_INSN_MSYNC, + NDS32_INSN_ISB, NDS32_INSN_DSB, NDS32_INSN_STANDBY, NDS32_INSN_TRAP, + NDS32_INSN_TEQZ, NDS32_INSN_TNEZ, NDS32_INSN_BREAK, NDS32_INSN_SYSCALL, + NDS32_INSN_IRET, NDS32_INSN_TLBOP, NDS32_INSN_CCTL, NDS32_INSN_DIVS, + NDS32_INSN_DIV, NDS32_INSN_ABS, NDS32_INSN_AVE, NDS32_INSN_MIN, + NDS32_INSN_MAX, NDS32_INSN_BSET, NDS32_INSN_BTGL, NDS32_INSN_BCLR, + NDS32_INSN_BTST, NDS32_INSN_CLIPS, NDS32_INSN_CLIP, NDS32_INSN_CLZ, + NDS32_INSN_CLO, NDS32_INSN_BSE, NDS32_INSN_BSP, NDS32_INSN_PBSAD, + NDS32_INSN_PBSADA, NDS32_INSN_MOV55, NDS32_INSN_MOVI55, NDS32_INSN_ADDI45, + NDS32_INSN_ADD45, NDS32_INSN_SUBI45, NDS32_INSN_SUB45, NDS32_INSN_SRAI45, + NDS32_INSN_SRLI45, NDS32_INSN_SLLI333, NDS32_INSN_SEB33, NDS32_INSN_SEH33, + NDS32_INSN_ZEB33, NDS32_INSN_ZEH33, NDS32_INSN_XLSB33, NDS32_INSN_X11B33, + NDS32_INSN_ADDI333, NDS32_INSN_ADD333, NDS32_INSN_SUBI333, + NDS32_INSN_SUB333, NDS32_INSN_LWI333, NDS32_INSN_LWI333_BI, + NDS32_INSN_LHI333, NDS32_INSN_LBI333, NDS32_INSN_SWI333, + NDS32_INSN_SWI333_BI, NDS32_INSN_SHI333, NDS32_INSN_SBI333, + NDS32_INSN_LWI450, NDS32_INSN_SWI450, NDS32_INSN_LWI37, NDS32_INSN_SWI37, + NDS32_INSN_BEQZ38, NDS32_INSN_BNEZ38, NDS32_INSN_BEQS38, + NDS32_INSN_BNES38, NDS32_INSN_J8, NDS32_INSN_JR5, NDS32_INSN_RET5, + NDS32_INSN_JRAL5, NDS32_INSN_SLTI45, NDS32_INSN_SLTSI45, NDS32_INSN_SLT45, + NDS32_INSN_SLTS45, NDS32_INSN_BEQZS8, NDS32_INSN_BNEZS8, + NDS32_INSN_BREAK16, NDS32_INSN_ADDI10_SP, NDS32_INSN_LWI37_SP, + NDS32_INSN_SWI37_SP, NDS32_INSN_BMSKI33, NDS32_INSN_FEXTI33, + NDS32_INSN_ADDRI36_SP, NDS32_INSN_LWI45_FE, NDS32_INSN_NEG33, + NDS32_INSN_NOT33, NDS32_INSN_MUL33, NDS32_INSN_XOR33, NDS32_INSN_AND33, + NDS32_INSN_OR33, NDS32_INSN_MOVPI45, NDS32_INSN_PUSH25, NDS32_INSN_POP25, + NDS32_INSN_MOVD44, NDS32_INSN_ADD5_PC, NDS32_INSN_BREAK16V3, + NDS32_INSN_ADDI_GP, NDS32_INSN_MADDR32, NDS32_INSN_MSUBR32, + NDS32_INSN_MULR64, NDS32_INSN_MULSR64, NDS32_INSN_SBI_GP, + NDS32_INSN_SHI_GP, NDS32_INSN_SWI_GP, NDS32_INSN_LBI_GP, + NDS32_INSN_LBSI_GP, NDS32_INSN_LHI_GP, NDS32_INSN_LHSI_GP, + NDS32_INSN_LWI_GP, NDS32_INSN_DIVR, NDS32_INSN_DIVSR, NDS32_INSN_LMWA_BI, + NDS32_INSN_LMWA_BIM, NDS32_INSN_LMWA_BD, NDS32_INSN_LMWA_BDM, + NDS32_INSN_LMWA_AI, NDS32_INSN_LMWA_AIM, NDS32_INSN_LMWA_AD, + NDS32_INSN_LMWA_ADM, NDS32_INSN_SMWA_BI, NDS32_INSN_SMWA_BIM, + NDS32_INSN_SMWA_BD, NDS32_INSN_SMWA_BDM, NDS32_INSN_SMWA_AI, + NDS32_INSN_SMWA_AIM, NDS32_INSN_SMWA_AD, NDS32_INSN_SMWA_ADM, + NDS32_INSN_LBUP, NDS32_INSN_SBUP, NDS32_INSN_LMWZB_B, NDS32_INSN_LMWZB_BM, + NDS32_INSN_LMWZB_A, NDS32_INSN_LMWZB_AM, NDS32_INSN_SMWZB_B, + NDS32_INSN_SMWZB_BM, NDS32_INSN_SMWZB_A, NDS32_INSN_SMWZB_AM, + NDS32_INSN_BEQC, NDS32_INSN_BNEC, NDS32_INSN_JRALNEZ, NDS32_INSN_JRNEZ, + NDS32_INSN_ADD_SLLI, NDS32_INSN_ADD_SRLI, NDS32_INSN_SUB_SLLI, + NDS32_INSN_SUB_SRLI, NDS32_INSN_AND_SLLI, NDS32_INSN_AND_SRLI, + NDS32_INSN_OR_SLLI, NDS32_INSN_OR_SRLI, NDS32_INSN_XOR_SLLI, + NDS32_INSN_XOR_SRLI, NDS32_INSN_BITC, NDS32_INSN_BITCI, NDS32_INSN_AADDL, + NDS32_INSN_ASUBL, NDS32_INSN_ALA, NDS32_INSN_ALR, NDS32_INSN_ALR2, + NDS32_INSN_ASA, NDS32_INSN_ASR, NDS32_INSN_AUPI, NDS32_INSN_AMFAR, + NDS32_INSN_AMTAR, NDS32_INSN_AMTARI, NDS32_INSN_ASATS48, NDS32_INSN_AWEXT, + NDS32_INSN_AMADD, NDS32_INSN_AMADDL_S, NDS32_INSN_AMADDL2_S, + NDS32_INSN_AMADDL_L, NDS32_INSN_AMADDL2_L, NDS32_INSN_AMADDSA, + NDS32_INSN_AMSUB, NDS32_INSN_AMSUBL_S, NDS32_INSN_AMSUBL2_S, + NDS32_INSN_AMSUBL_L, NDS32_INSN_AMSUBL2_L, NDS32_INSN_AMSUBSA, + NDS32_INSN_AMADDS, NDS32_INSN_AMADDSL_S, NDS32_INSN_AMADDSL2_S, + NDS32_INSN_AMADDSL_L, NDS32_INSN_AMADDSL2_L, NDS32_INSN_AMADDSSA, + NDS32_INSN_AMSUBS, NDS32_INSN_AMSUBSL_S, NDS32_INSN_AMSUBSL2_S, + NDS32_INSN_AMSUBSL_L, NDS32_INSN_AMSUBSL2_L, NDS32_INSN_AMSUBSSA, + NDS32_INSN_AMNEGS, NDS32_INSN_AMNEGSL_S, NDS32_INSN_AMNEGSL2_S, + NDS32_INSN_AMNEGSL_L, NDS32_INSN_AMNEGSL2_L, NDS32_INSN_AMNEGSSA, + NDS32_INSN_AMULTS, NDS32_INSN_AMULTSL_S, NDS32_INSN_AMULTSL2_S, + NDS32_INSN_AMULTSL_L, NDS32_INSN_AMULTSL2_L, NDS32_INSN_AMULTSSA, + NDS32_INSN_AMULT, NDS32_INSN_AMULTL_S, NDS32_INSN_AMULTL2_S, + NDS32_INSN_AMULTL_L, NDS32_INSN_AMULTL2_L, NDS32_INSN_AMULTSA, + NDS32_INSN_AZOL, NDS32_INSN_AMABBS, NDS32_INSN_AMABTS, NDS32_INSN_AMATBS, + NDS32_INSN_AMATTS, NDS32_INSN_AMBBS, NDS32_INSN_AMBTS, NDS32_INSN_AMTBS, + NDS32_INSN_AMTTS, NDS32_INSN_AMABBSL_S, NDS32_INSN_AMABBSL_L, + NDS32_INSN_AMABBSL2_S, NDS32_INSN_AMABBSL2_L, NDS32_INSN_AMABBSSA, + NDS32_INSN_AMABTSL_S, NDS32_INSN_AMABTSL_L, NDS32_INSN_AMABTSL2_S, + NDS32_INSN_AMABTSL2_L, NDS32_INSN_AMABTSSA, NDS32_INSN_AMATBSL_S, + NDS32_INSN_AMATBSL_L, NDS32_INSN_AMATBSL2_S, NDS32_INSN_AMATBSL2_L, + NDS32_INSN_AMATBSSA, NDS32_INSN_AMATTSL_S, NDS32_INSN_AMATTSL_L, + NDS32_INSN_AMATTSL2_S, NDS32_INSN_AMATTSL2_L, NDS32_INSN_AMATTSSA, + NDS32_INSN_AMBBSL_S, NDS32_INSN_AMBBSL_L, NDS32_INSN_AMBBSL2_S, + NDS32_INSN_AMBBSL2_L, NDS32_INSN_AMBBSSA, NDS32_INSN_AMBTSL_S, + NDS32_INSN_AMBTSL_L, NDS32_INSN_AMBTSL2_S, NDS32_INSN_AMBTSL2_L, + NDS32_INSN_AMBTSSA, NDS32_INSN_AMTBSL_S, NDS32_INSN_AMTBSL_L, + NDS32_INSN_AMTBSL2_S, NDS32_INSN_AMTBSL2_L, NDS32_INSN_AMTBSSA, + NDS32_INSN_AMTTSL_S, NDS32_INSN_AMTTSL_L, NDS32_INSN_AMTTSL2_S, + NDS32_INSN_AMTTSL2_L, NDS32_INSN_AMTTSSA, NDS32_INSN_AMAWBS, + NDS32_INSN_AMAWTS, NDS32_INSN_AMWBS, NDS32_INSN_AMWTS, + NDS32_INSN_AMAWBSL_S, NDS32_INSN_AMAWBSL_L, NDS32_INSN_AMAWBSL2_S, + NDS32_INSN_AMAWBSL2_L, NDS32_INSN_AMAWBSSA, NDS32_INSN_AMAWTSL_S, + NDS32_INSN_AMAWTSL_L, NDS32_INSN_AMAWTSL2_S, NDS32_INSN_AMAWTSL2_L, + NDS32_INSN_AMAWTSSA, NDS32_INSN_AMWBSL_S, NDS32_INSN_AMWBSL_L, + NDS32_INSN_AMWBSL2_S, NDS32_INSN_AMWBSL2_L, NDS32_INSN_AMWBSSA, + NDS32_INSN_AMWTSL_S, NDS32_INSN_AMWTSL_L, NDS32_INSN_AMWTSL2_S, + NDS32_INSN_AMWTSL2_L, NDS32_INSN_AMWTSSA, NDS32_INSN_AMFAR2, + NDS32_INSN_AMTAR2, NDS32_INSN_FLS, NDS32_INSN_FLS_BI, NDS32_INSN_FLSI, + NDS32_INSN_FLSI_BI, NDS32_INSN_FMFCFG, NDS32_INSN_FMFCSR, + NDS32_INSN_FMTCSR, NDS32_INSN_FMFSR, NDS32_INSN_FMTSR, NDS32_INSN_FSS, + NDS32_INSN_FSS_BI, NDS32_INSN_FSSI, NDS32_INSN_FSSI_BI, NDS32_INSN_FS2D, + NDS32_INSN_FABSS, NDS32_INSN_FADDS, NDS32_INSN_FCMOVNS, + NDS32_INSN_FCMOVZS, NDS32_INSN_FCMPEQS, NDS32_INSN_FCMPEQS_E, + NDS32_INSN_FCMPLTS, NDS32_INSN_FCMPLTS_E, NDS32_INSN_FCMPLES, + NDS32_INSN_FCMPLES_E, NDS32_INSN_FCMPUNS, NDS32_INSN_FCMPUNS_E, + NDS32_INSN_FCPYNSS, NDS32_INSN_FCPYSS, NDS32_INSN_FDIVS, + NDS32_INSN_FMADDS, NDS32_INSN_FMULS, NDS32_INSN_FMSUBS, + NDS32_INSN_FNMADDS, NDS32_INSN_FNMSUBS, NDS32_INSN_FS2SI, + NDS32_INSN_FS2SI_Z, NDS32_INSN_FS2UI, NDS32_INSN_FS2UI_Z, + NDS32_INSN_FSI2S, NDS32_INSN_FSQRTS, NDS32_INSN_FSUBS, NDS32_INSN_FUI2S, + NDS32_INSN_FABSD, NDS32_INSN_FADDD, NDS32_INSN_FCMOVND, + NDS32_INSN_FCMOVZD, NDS32_INSN_FCMPEQD, NDS32_INSN_FCMPEQD_E, + NDS32_INSN_FCMPLTD, NDS32_INSN_FCMPLTD_E, NDS32_INSN_FCMPLED, + NDS32_INSN_FCMPLED_E, NDS32_INSN_FCMPUND, NDS32_INSN_FCMPUND_E, + NDS32_INSN_FCPYNSD, NDS32_INSN_FCPYSD, NDS32_INSN_FD2S, NDS32_INSN_FD2SI, + NDS32_INSN_FD2SI_Z, NDS32_INSN_FD2UI, NDS32_INSN_FD2UI_Z, + NDS32_INSN_FDIVD, NDS32_INSN_FLD, NDS32_INSN_FLD_BI, NDS32_INSN_FLDI, + NDS32_INSN_FLDI_BI, NDS32_INSN_FMADDD, NDS32_INSN_FMFDR, + NDS32_INSN_FMSUBD, NDS32_INSN_FMTDR, NDS32_INSN_FMULD, NDS32_INSN_FNMADDD, + NDS32_INSN_FNMSUBD, NDS32_INSN_FSD, NDS32_INSN_FSD_BI, NDS32_INSN_FSDI, + NDS32_INSN_FSDI_BI, NDS32_INSN_FSI2D, NDS32_INSN_FSQRTD, NDS32_INSN_FSUBD, + NDS32_INSN_FUI2D, NDS32_INSN_CPE1_CP1, NDS32_INSN_CPE1_CP2, + NDS32_INSN_CPE1_CP3, NDS32_INSN_CPE2_CP1, NDS32_INSN_CPE2_CP2, + NDS32_INSN_CPE2_CP3, NDS32_INSN_CPE3_CP1, NDS32_INSN_CPE3_CP2, + NDS32_INSN_CPE3_CP3, NDS32_INSN_CPE4_CP1, NDS32_INSN_CPE4_CP2, + NDS32_INSN_CPE4_CP3, NDS32_INSN_CPLD_CP1, NDS32_INSN_CPLD_BI_CP1, + NDS32_INSN_CPLDI_CP1, NDS32_INSN_CPLDI_BI_CP1, NDS32_INSN_CPLD_CP2, + NDS32_INSN_CPLD_BI_CP2, NDS32_INSN_CPLDI_CP2, NDS32_INSN_CPLDI_BI_CP2, + NDS32_INSN_CPLD_CP3, NDS32_INSN_CPLD_BI_CP3, NDS32_INSN_CPLDI_CP3, + NDS32_INSN_CPLDI_BI_CP3, NDS32_INSN_CPLW_CP1, NDS32_INSN_CPLW_BI_CP1, + NDS32_INSN_CPLWI_CP1, NDS32_INSN_CPLWI_BI_CP1, NDS32_INSN_CPLW_CP2, + NDS32_INSN_CPLW_BI_CP2, NDS32_INSN_CPLWI_CP2, NDS32_INSN_CPLWI_BI_CP2, + NDS32_INSN_CPLW_CP3, NDS32_INSN_CPLW_BI_CP3, NDS32_INSN_CPLWI_CP3, + NDS32_INSN_CPLWI_BI_CP3, NDS32_INSN_CPSD_CP1, NDS32_INSN_CPSD_BI_CP1, + NDS32_INSN_CPSDI_CP1, NDS32_INSN_CPSDI_BI_CP1, NDS32_INSN_CPSD_CP2, + NDS32_INSN_CPSD_BI_CP2, NDS32_INSN_CPSDI_CP2, NDS32_INSN_CPSDI_BI_CP2, + NDS32_INSN_CPSD_CP3, NDS32_INSN_CPSD_BI_CP3, NDS32_INSN_CPSDI_CP3, + NDS32_INSN_CPSDI_BI_CP3, NDS32_INSN_CPSW_CP1, NDS32_INSN_CPSW_BI_CP1, + NDS32_INSN_CPSWI_CP1, NDS32_INSN_CPSWI_BI_CP1, NDS32_INSN_CPSW_CP2, + NDS32_INSN_CPSW_BI_CP2, NDS32_INSN_CPSWI_CP2, NDS32_INSN_CPSWI_BI_CP2, + NDS32_INSN_CPSW_CP3, NDS32_INSN_CPSW_BI_CP3, NDS32_INSN_CPSWI_CP3, + NDS32_INSN_CPSWI_BI_CP3, NDS32_INSN_MFCPD_CP1, NDS32_INSN_MTCPD_CP1, + NDS32_INSN_MFCPD_CP2, NDS32_INSN_MTCPD_CP2, NDS32_INSN_MFCPD_CP3, + NDS32_INSN_MTCPD_CP3, NDS32_INSN_MFCPW_CP1, NDS32_INSN_MTCPW_CP1, + NDS32_INSN_MFCPW_CP2, NDS32_INSN_MTCPW_CP2, NDS32_INSN_MFCPW_CP3, + NDS32_INSN_MTCPW_CP3, NDS32_INSN_MFCPPW_CP1, NDS32_INSN_MTCPPW_CP1, + NDS32_INSN_MFCPPW_CP2, NDS32_INSN_MTCPPW_CP2, NDS32_INSN_MFCPPW_CP3, + NDS32_INSN_MTCPPW_CP3, NDS32_INSN_FFB, NDS32_INSN_FFBI, NDS32_INSN_FFMISM, + NDS32_INSN_FLMISM, NDS32_INSN_FFZMISM, NDS32_INSN_KADDW, NDS32_INSN_KSUBW, + NDS32_INSN_KSLRAW, NDS32_INSN_KADDH, NDS32_INSN_KSUBH, NDS32_INSN_KDMBB, + NDS32_INSN_KDMBT, NDS32_INSN_KDMTB, NDS32_INSN_KDMTT, NDS32_INSN_KHMBB, + NDS32_INSN_KHMBT, NDS32_INSN_KHMTB, NDS32_INSN_KHMTT, NDS32_INSN_RDOV, + NDS32_INSN_CLROV, NDS32_INSN_IFCALL9, NDS32_INSN_IFCALL, NDS32_INSN_IFRET, + NDS32_INSN_EX5_IT, NDS32_INSN_EX9_IT +}; + +#endif |