diff options
author | Kuan-Lin Chen <kuanlinchentw@gmail.com> | 2013-12-13 11:52:32 +0000 |
---|---|---|
committer | Nick Clifton <nickc@redhat.com> | 2013-12-13 11:52:32 +0000 |
commit | 35c081572f32263b24554ae40502fb5b51ece8c6 (patch) | |
tree | 800c0d49d0635671e8e28c56635702212c6f7fc1 /bfd | |
parent | 8a48ac9579f34efea9bc4f2d5b02230e2ac3dfc1 (diff) | |
download | binutils-gdb-35c081572f32263b24554ae40502fb5b51ece8c6.tar.gz |
Add support for Andes NDS32:
BFD:
* 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.
BINUTILS:
* 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.
TESTSUITE:
* binutils-all/objdump.exp: Add NDS32 cpu.
* binutils-all/readelf.r: Skip extra reloc created by NDS32.
GAS:
* 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.
TESTSUITE:
* 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.
LD:
* 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.
TESTSUITE:
* 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.
OPCODES:
* 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.
INCLUDE:
* dis-asm.h (print_insn_nds32): Add nds32 target.
* elf/nds32.h: New file for nds32.
* opcode/nds32.h: New file for nds32.
Diffstat (limited to 'bfd')
-rw-r--r-- | bfd/ChangeLog | 29 | ||||
-rw-r--r-- | bfd/Makefile.am | 4 | ||||
-rw-r--r-- | bfd/Makefile.in | 6 | ||||
-rw-r--r-- | bfd/archures.c | 8 | ||||
-rw-r--r-- | bfd/bfd-in2.h | 178 | ||||
-rw-r--r-- | bfd/config.bfd | 21 | ||||
-rwxr-xr-x | bfd/configure | 4 | ||||
-rw-r--r-- | bfd/configure.in | 4 | ||||
-rw-r--r-- | bfd/cpu-nds32.c | 45 | ||||
-rw-r--r-- | bfd/elf-bfd.h | 1 | ||||
-rw-r--r-- | bfd/elf32-nds32.c | 14271 | ||||
-rw-r--r-- | bfd/elf32-nds32.h | 147 | ||||
-rw-r--r-- | bfd/libbfd.h | 94 | ||||
-rw-r--r-- | bfd/reloc.c | 268 | ||||
-rw-r--r-- | bfd/targets.c | 8 |
15 files changed, 15088 insertions, 0 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 7e41ce8806c..ce6fc956deb 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 6f0e4772eed..1183e3eecfb 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 8a38a765450..a81da7c66e2 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 97c540abeec..8a0b23a0761 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 846116b58a9..71996dbc4b9 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 5324d39a21c..4edcc6af51f 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 bdda6dc689d..382dd011f2a 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 748c2935f8d..0d41867c677 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 00000000000..d5b2bf420d0 --- /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 15dab945705..0446725cb63 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 00000000000..c9c372afb21 --- /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 00000000000..7b97f30e780 --- /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 4aaecbffa0e..87605b964df 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 77a04f8c0ca..0d191f194c2 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 b117bfe634c..26fb972ef88 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, |