From: Sandra Loosemore Date: Wed, 6 Feb 2013 23:22:26 +0000 (+0000) Subject: 2013-02-06 Sandra Loosemore X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=36591ba149edf6c6608d931a6d53bb99849fdc99;p=binutils-gdb.git 2013-02-06 Sandra Loosemore Andrew Jenner Based on patches from Altera Corporation. bfd/ * Makefile.am (ALL_MACHINES): Add cpu-nios2.lo. (ALL_MACHINES_CFILES): Add cpu-nios2.c. (BFD_BACKENDS): Add elf32-nios2.lo. (BFD32_BACKENDS_CFILES): Add elf32-nios2.c. * Makefile.in: Regenerated. * configure.in: Add entries for bfd_elf32_bignios2_vec and bfd_elf32_littlenios2_vec. * configure: Regenerated. * config.bfd: Add cases for nios2. * archures.c (enum bfd_architecture): Add bfd_arch_nios2. (bfd_mach_nios2): Define. (bfd_nios2_arch): Declare. (bfd_archures_list): Add bfd_nios2_arch. * targets.c (bfd_elf32_bignios2_vec): Declare. (bfd_elf32_littlenios2_vec): Declare. (_bfd_target_vector): Add entries for bfd_elf32_bignios2_vec and bfd_elf32_littlenios2_vec. * elf-bfd.h (enum elf_target_id): Add NIOS2_ELF_DATA. * reloc.c (enum bfd_reloc_code_real): Add Nios II relocations. * bfd-in2.h: Regenerated. * libbfd.h: Regenerated. * cpu-nios2.c: New file. * elf32-nios2.c: New file. opcodes/ * Makefile.am (TARGET_LIBOPCODES_CFILES): Add nios2-dis.c and nios2-opc.c. * Makefile.in: Regenerated. * configure.in: Add case for bfd_nios2_arch. * configure: Regenerated. * disassemble.c (ARCH_nios2): Define. (disassembler): Add case for bfd_arch_nios2. * nios2-dis.c: New file. * nios2-opc.c: New file. include/ * dis-asm.h (print_insn_big_nios2): Declare. (print_insn_little_nios2): Declare. include/elf * nios2.h: New file. include/opcode/ * nios2.h: New file. gas/ * Makefile.am (TARGET_CPU_CFILES): Add config/tc-nios2.c. (TARGET_CPU_HFILES): Add config/tc-nios2.h. * Makefile.in: Regenerated. * configure.tgt: Add case for nios2*-linux*. * config/obj-elf.c: Conditionally include elf/nios2.h. * config/tc-nios2.c: New file. * config/tc-nios2.h: New file. * doc/Makefile.am (CPU_DOCS): Add c-nios2.texi. * doc/Makefile.in: Regenerated. * doc/all.texi: Set NIOSII. * doc/as.texinfo (Overview): Add Nios II options. (Machine Dependencies): Include c-nios2.texi. * doc/c-nios2.texi: New file. * NEWS: Note Altera Nios II support. gas/testsuite/ * gas/nios2/add.d: New. * gas/nios2/add.s: New. * gas/nios2/align_fill.d: New. * gas/nios2/align_fill.s: New. * gas/nios2/align_text.d: New. * gas/nios2/align_text.s: New. * gas/nios2/and.d: New. * gas/nios2/and.s: New. * gas/nios2/branch.d: New. * gas/nios2/branch.s: New. * gas/nios2/break.d: New. * gas/nios2/break.s: New. * gas/nios2/bret.d: New. * gas/nios2/bret.s: New. * gas/nios2/cache.d: New. * gas/nios2/cache.s: New. * gas/nios2/call26.d: New. * gas/nios2/call26.s: New. * gas/nios2/call.d: New. * gas/nios2/call.s: New. * gas/nios2/cmp.d: New. * gas/nios2/cmp.s: New. * gas/nios2/comments.d: New. * gas/nios2/comments.s: New. * gas/nios2/complex.d: New. * gas/nios2/complex.s: New. * gas/nios2/ctl.d: New. * gas/nios2/ctl.s: New. * gas/nios2/custom.d: New. * gas/nios2/custom.s: New. * gas/nios2/etbt.d: New. * gas/nios2/etbt.s: New. * gas/nios2/flushda.d: New. * gas/nios2/flushda.s: New. * gas/nios2/illegal.l: New. * gas/nios2/illegal.s: New. * gas/nios2/jmp.d: New. * gas/nios2/jmp.s: New. * gas/nios2/ldb.d: New. * gas/nios2/ldb.s: New. * gas/nios2/ldh.d: New. * gas/nios2/ldh.s: New. * gas/nios2/ldw.d: New. * gas/nios2/ldw.s: New. * gas/nios2/lineseparator.d: New. * gas/nios2/lineseparator.s: New. * gas/nios2/mov.d: New. * gas/nios2/movia.d: New. * gas/nios2/movia.s: New. * gas/nios2/movi.d: New. * gas/nios2/movi.s: New. * gas/nios2/mov.s: New. * gas/nios2/mul.d: New. * gas/nios2/mul.s: New. * gas/nios2/nios2.exp: New. * gas/nios2/nor.d: New. * gas/nios2/nor.s: New. * gas/nios2/or.d: New. * gas/nios2/or.s: New. * gas/nios2/ret.d: New. * gas/nios2/ret.s: New. * gas/nios2/rol.d: New. * gas/nios2/rol.s: New. * gas/nios2/rotate.d: New. * gas/nios2/rotate.s: New. * gas/nios2/stb.d: New. * gas/nios2/stb.s: New. * gas/nios2/sth.d: New. * gas/nios2/sth.s: New. * gas/nios2/stw.d: New. * gas/nios2/stw.s: New. * gas/nios2/sub.d: New. * gas/nios2/sub.s: New. * gas/nios2/sync.d: New. * gas/nios2/sync.s: New. * gas/nios2/trap.d: New. * gas/nios2/trap.s: New. * gas/nios2/tret.d: New. * gas/nios2/tret.s: New. * gas/nios2/warn_noat.l: New. * gas/nios2/warn_noat.s: New. * gas/nios2/warn_nobreak.l: New. * gas/nios2/warn_nobreak.s: New. * gas/nios2/xor.d: New. * gas/nios2/xor.s: New. ld/ * Makefile.am (enios2elf.c): New rule. * Makefile.in: Regenerated. * configure.tgt: Add case for nios2*-*-*. * emulparams/nios2elf.sh: New file. * NEWS: Note Altera Nios II support. ld/testsuite/ * ld-nios2/emit-relocs-1a.s: New. * ld-nios2/emit-relocs-1b.s: New. * ld-nios2/emit-relocs-1.d: New. * ld-nios2/emit-relocs-1.ld: New. * ld-nios2/gprel.d: New. * ld-nios2/gprel.s: New. * ld-nios2/hilo16.d: New. * ld-nios2/hilo16.s: New. * ld-nios2/hilo16_symbol.s: New. * ld-nios2/imm5.d: New. * ld-nios2/imm5.s: New. * ld-nios2/imm5_symbol.s: New. * ld-nios2/nios2.exp: New. * ld-nios2/pcrel16.d: New. * ld-nios2/pcrel16_label.s: New. * ld-nios2/pcrel16.s: New. * ld-nios2/relax_callr.d: New. * ld-nios2/relax_callr.ld: New. * ld-nios2/relax_callr.s: New. * ld-nios2/relax_cjmp.d: New. * ld-nios2/relax_cjmp.s: New. * ld-nios2/relax_jmp.ld: New. * ld-nios2/relax_section.d: New. * ld-nios2/relax_section.s: New. * ld-nios2/relax_ujmp.d: New. * ld-nios2/relax_ujmp.s: New. * ld-nios2/reloc.d: New. * ld-nios2/reloc.s: New. * ld-nios2/reloc_symbol.s: New. * ld-nios2/s16.d: New. * ld-nios2/s16.s: New. * ld-nios2/s16_symbol.s: New. * ld-nios2/u16.d: New. * ld-nios2/u16.s: New. * ld-nios2/u16_symbol.s: New. * ld-elf/indirect.exp: Skip on targets that don't support -shared -fPIC. * ld-elfcomm/elfcomm.exp: Build with -G0 for nios2. * ld-plugin/lto.exp: Skip shared library tests on targets that don't support them. Skip execution tests on non-native targets. binutils/ * readelf.c: Include elf/nios2.h. (dump_relocations): Add case for EM_ALTERA_NIOS2. (get_nios2_dynamic_type): New. (get_dynamic_type): Add case for EM_ALTERA_NIOS2. (is_32bit_abs_reloc): Fix EM_ALTERA_NIOS2 case. (is_16bit_abs_reloc): Likewise. (is_none_reloc): Add EM_ALTERA_NIOS2 and EM_NIOS32 cases. * NEWS: Note Altera Nios II support. * MAINTAINERS: Add Nios II maintainers. --- diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 4b66949bcf4..7565d924a9a 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,32 @@ +2013-02-06 Sandra Loosemore + Andrew Jenner + + Based on patches from Altera Corporation. + + * Makefile.am (ALL_MACHINES): Add cpu-nios2.lo. + (ALL_MACHINES_CFILES): Add cpu-nios2.c. + (BFD_BACKENDS): Add elf32-nios2.lo. + (BFD32_BACKENDS_CFILES): Add elf32-nios2.c. + * Makefile.in: Regenerated. + * configure.in: Add entries for bfd_elf32_bignios2_vec and + bfd_elf32_littlenios2_vec. + * configure: Regenerated. + * config.bfd: Add cases for nios2. + * archures.c (enum bfd_architecture): Add bfd_arch_nios2. + (bfd_mach_nios2): Define. + (bfd_nios2_arch): Declare. + (bfd_archures_list): Add bfd_nios2_arch. + * targets.c (bfd_elf32_bignios2_vec): Declare. + (bfd_elf32_littlenios2_vec): Declare. + (_bfd_target_vector): Add entries for bfd_elf32_bignios2_vec and + bfd_elf32_littlenios2_vec. + * elf-bfd.h (enum elf_target_id): Add NIOS2_ELF_DATA. + * reloc.c (enum bfd_reloc_code_real): Add Nios II relocations. + * bfd-in2.h: Regenerated. + * libbfd.h: Regenerated. + * cpu-nios2.c: New file. + * elf32-nios2.c: New file. + 2013-02-06 Alan Modra * elf32-arm.c (elf32_arm_final_link_relocate): Only test for diff --git a/bfd/Makefile.am b/bfd/Makefile.am index 8c348f32ffb..6efe20fc0e9 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-nios2.lo \ cpu-ns32k.lo \ cpu-openrisc.lo \ cpu-or32.lo \ @@ -220,6 +221,7 @@ ALL_MACHINES_CFILES = \ cpu-msp430.c \ cpu-mt.c \ cpu-ns32k.c \ + cpu-nios2.c \ cpu-openrisc.c \ cpu-or32.c \ cpu-pdp11.c \ @@ -348,6 +350,7 @@ BFD32_BACKENDS = \ elf32-moxie.lo \ elf32-msp430.lo \ elf32-mt.lo \ + elf32-nios2.lo \ elf32-openrisc.lo \ elf32-or32.lo \ elf32-pj.lo \ @@ -537,6 +540,7 @@ BFD32_BACKENDS_CFILES = \ elf32-moxie.c \ elf32-msp430.c \ elf32-mt.c \ + elf32-nios2.c \ elf32-openrisc.c \ elf32-or32.c \ elf32-pj.c \ diff --git a/bfd/Makefile.in b/bfd/Makefile.in index a74aaa12a6c..e54cf57bf98 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-nios2.lo \ cpu-ns32k.lo \ cpu-openrisc.lo \ cpu-or32.lo \ @@ -521,6 +522,7 @@ ALL_MACHINES_CFILES = \ cpu-msp430.c \ cpu-mt.c \ cpu-ns32k.c \ + cpu-nios2.c \ cpu-openrisc.c \ cpu-or32.c \ cpu-pdp11.c \ @@ -650,6 +652,7 @@ BFD32_BACKENDS = \ elf32-moxie.lo \ elf32-msp430.lo \ elf32-mt.lo \ + elf32-nios2.lo \ elf32-openrisc.lo \ elf32-or32.lo \ elf32-pj.lo \ @@ -839,6 +842,7 @@ BFD32_BACKENDS_CFILES = \ elf32-moxie.c \ elf32-msp430.c \ elf32-mt.c \ + elf32-nios2.c \ elf32-openrisc.c \ elf32-or32.c \ elf32-pj.c \ @@ -1345,6 +1349,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-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@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-or32.Plo@am__quote@ @@ -1433,6 +1438,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-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@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-pj.Plo@am__quote@ diff --git a/bfd/archures.c b/bfd/archures.c index a1b7868a39c..0be72da5284 100644 --- a/bfd/archures.c +++ b/bfd/archures.c @@ -468,6 +468,8 @@ DESCRIPTION .#define bfd_mach_tilegx32 2 . bfd_arch_aarch64, {* AArch64 *} .#define bfd_mach_aarch64 0 +. bfd_arch_nios2, +.#define bfd_mach_nios2 0 . bfd_arch_last . }; */ @@ -560,6 +562,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_nios2_arch; extern const bfd_arch_info_type bfd_ns32k_arch; extern const bfd_arch_info_type bfd_openrisc_arch; extern const bfd_arch_info_type bfd_or32_arch; @@ -648,6 +651,7 @@ static const bfd_arch_info_type * const bfd_archures_list[] = &bfd_moxie_arch, &bfd_msp430_arch, &bfd_mt_arch, + &bfd_nios2_arch, &bfd_ns32k_arch, &bfd_openrisc_arch, &bfd_or32_arch, diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index 5b041763423..6dfd17fa9b0 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -2199,6 +2199,8 @@ enum bfd_architecture #define bfd_mach_tilegx32 2 bfd_arch_aarch64, /* AArch64 */ #define bfd_mach_aarch64 0 + bfd_arch_nios2, +#define bfd_mach_nios2 0 bfd_arch_last }; @@ -4913,6 +4915,42 @@ a matching LO8XG part. */ BFD_RELOC_MSP430_2X_PCREL, BFD_RELOC_MSP430_RL_PCREL, +/* Relocations used by the Altera Nios II core. */ + BFD_RELOC_NIOS2_S16, + BFD_RELOC_NIOS2_U16, + BFD_RELOC_NIOS2_CALL26, + BFD_RELOC_NIOS2_IMM5, + BFD_RELOC_NIOS2_CACHE_OPX, + BFD_RELOC_NIOS2_IMM6, + BFD_RELOC_NIOS2_IMM8, + BFD_RELOC_NIOS2_HI16, + BFD_RELOC_NIOS2_LO16, + BFD_RELOC_NIOS2_HIADJ16, + BFD_RELOC_NIOS2_GPREL, + BFD_RELOC_NIOS2_UJMP, + BFD_RELOC_NIOS2_CJMP, + BFD_RELOC_NIOS2_CALLR, + BFD_RELOC_NIOS2_ALIGN, + BFD_RELOC_NIOS2_GOT16, + BFD_RELOC_NIOS2_CALL16, + BFD_RELOC_NIOS2_GOTOFF_LO, + BFD_RELOC_NIOS2_GOTOFF_HA, + BFD_RELOC_NIOS2_PCREL_LO, + BFD_RELOC_NIOS2_PCREL_HA, + BFD_RELOC_NIOS2_TLS_GD16, + BFD_RELOC_NIOS2_TLS_LDM16, + BFD_RELOC_NIOS2_TLS_LDO16, + BFD_RELOC_NIOS2_TLS_IE16, + BFD_RELOC_NIOS2_TLS_LE16, + BFD_RELOC_NIOS2_TLS_DTPMOD, + BFD_RELOC_NIOS2_TLS_DTPREL, + BFD_RELOC_NIOS2_TLS_TPREL, + BFD_RELOC_NIOS2_COPY, + BFD_RELOC_NIOS2_GLOB_DAT, + BFD_RELOC_NIOS2_JUMP_SLOT, + BFD_RELOC_NIOS2_RELATIVE, + BFD_RELOC_NIOS2_GOTOFF, + /* IQ2000 Relocations. */ BFD_RELOC_IQ2000_OFFSET_16, BFD_RELOC_IQ2000_OFFSET_21, diff --git a/bfd/config.bfd b/bfd/config.bfd index cd1993696d8..0f75b0b0ce0 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 ;; +nios2*) targ_archs=bfd_nios2_arch ;; or32*) targ_archs=bfd_or32_arch ;; pdp11*) targ_archs=bfd_pdp11_arch ;; pj*) targ_archs="bfd_pj_arch bfd_i386_arch";; @@ -1144,6 +1145,21 @@ case "${targ}" in targ_underscore=yes ;; + nios2eb-*-*) + targ_defvec=bfd_elf32_bignios2_vec + targ_selvecs=bfd_elf32_littlenios2_vec + ;; + + nios2el-*-*) + targ_defvec=bfd_elf32_littlenios2_vec + targ_selvecs=bfd_elf32_bignios2_vec + ;; + + nios2-*-*) + targ_defvec=bfd_elf32_littlenios2_vec + targ_selvecs=bfd_elf32_bignios2_vec + ;; + openrisc-*-elf) targ_defvec=bfd_elf32_openrisc_vec ;; diff --git a/bfd/configure b/bfd/configure index 1279e563d8c..426c18c2f45 100755 --- a/bfd/configure +++ b/bfd/configure @@ -15228,6 +15228,7 @@ do bfd_elf32_bigmips_vxworks_vec) tb="$tb elf32-mips.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo" ;; bfd_elf32_bigmoxie_vec) tb="$tb elf32-moxie.lo elf32.lo $elf" ;; + bfd_elf32_bignios2_vec) tb="$tb elf32-nios2.lo elf32.lo $elf" ;; bfd_elf32_cr16_vec) tb="$tb elf32-cr16.lo elf32.lo $elf" ;; bfd_elf32_cr16c_vec) tb="$tb elf32-cr16c.lo elf32.lo $elf" ;; bfd_elf32_cris_vec) tb="$tb elf32-cris.lo elf32.lo $elf" ;; @@ -15270,6 +15271,7 @@ do bfd_elf32_littlemips_vxworks_vec) tb="$tb elf32-mips.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo" ;; bfd_elf32_littlemoxie_vec) tb="$tb elf32-moxie.lo elf32.lo $elf" ;; + bfd_elf32_littlenios2_vec) tb="$tb elf32-nios2.lo elf32.lo $elf" ;; bfd_elf32_m32c_vec) tb="$tb elf32-m32c.lo elf32.lo $elf" ;; bfd_elf32_m32r_vec) tb="$tb elf32-m32r.lo elf32.lo $elf" ;; bfd_elf32_m32rle_vec) tb="$tb elf32-m32r.lo elf32.lo $elf" ;; diff --git a/bfd/configure.in b/bfd/configure.in index 33f62e3ef24..960dc4eb46d 100644 --- a/bfd/configure.in +++ b/bfd/configure.in @@ -725,6 +725,7 @@ do bfd_elf32_bigmips_vxworks_vec) tb="$tb elf32-mips.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo" ;; bfd_elf32_bigmoxie_vec) tb="$tb elf32-moxie.lo elf32.lo $elf" ;; + bfd_elf32_bignios2_vec) tb="$tb elf32-nios2.lo elf32.lo $elf" ;; bfd_elf32_cr16_vec) tb="$tb elf32-cr16.lo elf32.lo $elf" ;; bfd_elf32_cr16c_vec) tb="$tb elf32-cr16c.lo elf32.lo $elf" ;; bfd_elf32_cris_vec) tb="$tb elf32-cris.lo elf32.lo $elf" ;; @@ -767,6 +768,7 @@ do bfd_elf32_littlemips_vxworks_vec) tb="$tb elf32-mips.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo" ;; bfd_elf32_littlemoxie_vec) tb="$tb elf32-moxie.lo elf32.lo $elf" ;; + bfd_elf32_littlenios2_vec) tb="$tb elf32-nios2.lo elf32.lo $elf" ;; bfd_elf32_m32c_vec) tb="$tb elf32-m32c.lo elf32.lo $elf" ;; bfd_elf32_m32r_vec) tb="$tb elf32-m32r.lo elf32.lo $elf" ;; bfd_elf32_m32rle_vec) tb="$tb elf32-m32r.lo elf32.lo $elf" ;; diff --git a/bfd/cpu-nios2.c b/bfd/cpu-nios2.c new file mode 100644 index 00000000000..fa4c859d496 --- /dev/null +++ b/bfd/cpu-nios2.c @@ -0,0 +1,44 @@ +/* BFD support for the Altera Nios II processor. + Copyright (C) 2012, 2013 Free Software Foundation, Inc. + Contributed by Nigel Gray (ngray@altera.com). + Contributed by Mentor Graphics, Inc. + + This file is part of BFD, the Binary File Descriptor library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "sysdep.h" +#include "bfd.h" +#include "libbfd.h" + +#define N(BITS_WORD, BITS_ADDR, NUMBER, PRINT, DEFAULT, NEXT) \ + { \ + BITS_WORD, /* bits in a word */ \ + BITS_ADDR, /* bits in an address */ \ + 8, /* 8 bits in a byte */ \ + bfd_arch_nios2, \ + NUMBER, \ + "nios2", \ + PRINT, \ + 3, \ + DEFAULT, \ + bfd_default_compatible, \ + bfd_default_scan, \ + bfd_arch_default_fill, \ + NEXT \ + } + +const bfd_arch_info_type bfd_nios2_arch = N (32, 32, 0, "nios2", TRUE, NULL); diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index 1fd73cf5dc9..9b383175823 100644 --- a/bfd/elf-bfd.h +++ b/bfd/elf-bfd.h @@ -421,6 +421,7 @@ enum elf_target_id MICROBLAZE_ELF_DATA, MIPS_ELF_DATA, MN10300_ELF_DATA, + NIOS2_ELF_DATA, PPC32_ELF_DATA, PPC64_ELF_DATA, S390_ELF_DATA, diff --git a/bfd/elf32-nios2.c b/bfd/elf32-nios2.c new file mode 100644 index 00000000000..2d6f07a32cd --- /dev/null +++ b/bfd/elf32-nios2.c @@ -0,0 +1,4141 @@ +/* 32-bit ELF support for Nios II. + Copyright (C) 2012, 2013 Free Software Foundation, Inc. + Contributed by Nigel Gray (ngray@altera.com). + Contributed by Mentor Graphics, Inc. + + This file is part of BFD, the Binary File Descriptor library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* This file handles Altera Nios II ELF targets. */ + +#include "sysdep.h" +#include "bfd.h" +#include "libbfd.h" +#include "bfdlink.h" +#include "genlink.h" +#include "elf-bfd.h" +#include "elf/nios2.h" +#include "opcode/nios2.h" + +/* Use RELA relocations. */ +#ifndef USE_RELA +#define USE_RELA +#endif + +#ifdef USE_REL +#undef USE_REL +#endif + +/* Forward declarations. */ +static bfd_reloc_status_type nios2_elf32_ignore_reloc + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_hi16_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_lo16_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_hiadj16_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_pcrel_lo16_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_pcrel_hiadj16_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_pcrel16_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_call26_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_gprel_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_ujmp_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_cjmp_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type nios2_elf32_callr_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); + +/* Target vector. */ +extern const bfd_target bfd_elf32_littlenios2_vec; +extern const bfd_target bfd_elf32_bignios2_vec; + +/* Offset of tp and dtp pointers from start of TLS block. */ +#define TP_OFFSET 0x7000 +#define DTP_OFFSET 0x8000 + +/* The relocation table used for SHT_REL sections. */ +static reloc_howto_type elf_nios2_howto_table_rel[] = { + /* No relocation. */ + HOWTO (R_NIOS2_NONE, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 0, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_NIOS2_NONE", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* 16-bit signed immediate relocation. */ + HOWTO (R_NIOS2_S16, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 6, /* bitpos */ + complain_overflow_signed, /* complain on overflow */ + bfd_elf_generic_reloc, /* special function */ + "R_NIOS2_S16", /* name */ + FALSE, /* partial_inplace */ + 0x003fffc0, /* src_mask */ + 0x003fffc0, /* dest_mask */ + FALSE), /* pcrel_offset */ + + /* 16-bit unsigned immediate relocation. */ + HOWTO (R_NIOS2_U16, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 6, /* bitpos */ + complain_overflow_unsigned, /* complain on overflow */ + bfd_elf_generic_reloc, /* special function */ + "R_NIOS2_U16", /* name */ + FALSE, /* partial_inplace */ + 0x003fffc0, /* src_mask */ + 0x003fffc0, /* dest_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_NIOS2_PCREL16, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + TRUE, /* pc_relative */ + 6, /* bitpos */ + complain_overflow_signed, /* complain on overflow */ + nios2_elf32_pcrel16_relocate, /* special function */ + "R_NIOS2_PCREL16", /* name */ + FALSE, /* partial_inplace */ + 0x003fffc0, /* src_mask */ + 0x003fffc0, /* dest_mask */ + TRUE), /* pcrel_offset */ + + HOWTO (R_NIOS2_CALL26, /* type */ + 2, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 26, /* bitsize */ + FALSE, /* pc_relative */ + 6, /* bitpos */ + complain_overflow_dont, /* complain on overflow */ + nios2_elf32_call26_relocate, /* special function */ + "R_NIOS2_CALL26", /* name */ + FALSE, /* partial_inplace */ + 0xffffffc0, /* src_mask */ + 0xffffffc0, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_NIOS2_IMM5, + 0, + 2, + 5, + FALSE, + 6, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_IMM5", + FALSE, + 0x000007c0, + 0x000007c0, + FALSE), + + HOWTO (R_NIOS2_CACHE_OPX, + 0, + 2, + 5, + FALSE, + 22, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_CACHE_OPX", + FALSE, + 0x07c00000, + 0x07c00000, + FALSE), + + HOWTO (R_NIOS2_IMM6, + 0, + 2, + 6, + FALSE, + 6, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_IMM6", + FALSE, + 0x00000fc0, + 0x00000fc0, + FALSE), + + HOWTO (R_NIOS2_IMM8, + 0, + 2, + 8, + FALSE, + 6, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_IMM8", + FALSE, + 0x00003fc0, + 0x00003fc0, + FALSE), + + HOWTO (R_NIOS2_HI16, + 0, + 2, + 32, + FALSE, + 6, + complain_overflow_dont, + nios2_elf32_hi16_relocate, + "R_NIOS2_HI16", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_LO16, + 0, + 2, + 32, + FALSE, + 6, + complain_overflow_dont, + nios2_elf32_lo16_relocate, + "R_NIOS2_LO16", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_HIADJ16, + 0, + 2, + 32, + FALSE, + 6, + complain_overflow_dont, + nios2_elf32_hiadj16_relocate, + "R_NIOS2_HIADJ16", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_BFD_RELOC_32, + 0, + 2, /* long */ + 32, + FALSE, + 0, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_BFD_RELOC32", + FALSE, + 0xffffffff, + 0xffffffff, + FALSE), + + HOWTO (R_NIOS2_BFD_RELOC_16, + 0, + 1, /* short */ + 16, + FALSE, + 0, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_BFD_RELOC16", + FALSE, + 0x0000ffff, + 0x0000ffff, + FALSE), + + HOWTO (R_NIOS2_BFD_RELOC_8, + 0, + 0, /* byte */ + 8, + FALSE, + 0, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_BFD_RELOC8", + FALSE, + 0x000000ff, + 0x000000ff, + FALSE), + + HOWTO (R_NIOS2_GPREL, + 0, + 2, + 32, + FALSE, + 6, + complain_overflow_dont, + nios2_elf32_gprel_relocate, + "R_NIOS2_GPREL", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_GNU_VTINHERIT, + 0, + 2, /* short */ + 0, + FALSE, + 0, + complain_overflow_dont, + NULL, + "R_NIOS2_GNU_VTINHERIT", + FALSE, + 0, + 0, + FALSE), + + HOWTO (R_NIOS2_GNU_VTENTRY, + 0, + 2, /* byte */ + 0, + FALSE, + 0, + complain_overflow_dont, + _bfd_elf_rel_vtable_reloc_fn, + "R_NIOS2_GNU_VTENTRY", + FALSE, + 0, + 0, + FALSE), + + HOWTO (R_NIOS2_UJMP, + 0, + 2, + 32, + FALSE, + 6, + complain_overflow_dont, + nios2_elf32_ujmp_relocate, + "R_NIOS2_UJMP", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_CJMP, + 0, + 2, + 32, + FALSE, + 6, + complain_overflow_dont, + nios2_elf32_cjmp_relocate, + "R_NIOS2_CJMP", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_CALLR, + 0, + 2, + 32, + FALSE, + 6, + complain_overflow_dont, + nios2_elf32_callr_relocate, + "R_NIOS2_CALLR", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_ALIGN, + 0, + 2, + 0, + FALSE, + 0, + complain_overflow_dont, + nios2_elf32_ignore_reloc, + "R_NIOS2_ALIGN", + FALSE, + 0, + 0, + TRUE), + + + HOWTO (R_NIOS2_GOT16, + 0, + 2, + 16, + FALSE, + 6, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_GOT16", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_CALL16, + 0, + 2, + 16, + FALSE, + 6, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_CALL16", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_GOTOFF_LO, + 0, + 2, + 16, + FALSE, + 6, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_GOTOFF_LO", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_GOTOFF_HA, + 0, + 2, + 16, + FALSE, + 6, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_GOTOFF_HA", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_PCREL_LO, + 0, + 2, + 16, + TRUE, + 6, + complain_overflow_dont, + nios2_elf32_pcrel_lo16_relocate, + "R_NIOS2_PCREL_LO", + FALSE, + 0x003fffc0, + 0x003fffc0, + TRUE), + + HOWTO (R_NIOS2_PCREL_HA, + 0, + 2, + 16, + FALSE, /* This is a PC-relative relocation, but we need to subtract + PC ourselves before the HIADJ. */ + 6, + complain_overflow_dont, + nios2_elf32_pcrel_hiadj16_relocate, + "R_NIOS2_PCREL_HA", + FALSE, + 0x003fffc0, + 0x003fffc0, + TRUE), + + HOWTO (R_NIOS2_TLS_GD16, + 0, + 2, + 16, + FALSE, + 6, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_TLS_GD16", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_TLS_LDM16, + 0, + 2, + 16, + FALSE, + 6, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_TLS_LDM16", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_TLS_LDO16, + 0, + 2, + 16, + FALSE, + 6, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_TLS_LDO16", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_TLS_IE16, + 0, + 2, + 16, + FALSE, + 6, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_TLS_IE16", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_TLS_LE16, + 0, + 2, + 16, + FALSE, + 6, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_NIOS2_TLS_LE16", + FALSE, + 0x003fffc0, + 0x003fffc0, + FALSE), + + HOWTO (R_NIOS2_TLS_DTPMOD, + 0, + 2, + 32, + FALSE, + 0, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_TLS_DTPMOD", + FALSE, + 0xffffffff, + 0xffffffff, + FALSE), + + HOWTO (R_NIOS2_TLS_DTPREL, + 0, + 2, + 32, + FALSE, + 0, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_TLS_DTPREL", + FALSE, + 0xffffffff, + 0xffffffff, + FALSE), + + HOWTO (R_NIOS2_TLS_TPREL, + 0, + 2, + 32, + FALSE, + 0, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_TLS_TPREL", + FALSE, + 0xffffffff, + 0xffffffff, + FALSE), + + HOWTO (R_NIOS2_COPY, + 0, + 2, + 32, + FALSE, + 0, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_COPY", + FALSE, + 0, + 0, + FALSE), + + HOWTO (R_NIOS2_GLOB_DAT, + 0, + 2, + 32, + FALSE, + 0, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_GLOB_DAT", + FALSE, + 0xffffffff, + 0xffffffff, + FALSE), + + HOWTO (R_NIOS2_JUMP_SLOT, + 0, + 2, + 32, + FALSE, + 0, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_JUMP_SLOT", + FALSE, + 0xffffffff, + 0xffffffff, + FALSE), + + HOWTO (R_NIOS2_RELATIVE, + 0, + 2, + 32, + FALSE, + 0, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_RELATIVE", + FALSE, + 0xffffffff, + 0xffffffff, + FALSE), + + HOWTO (R_NIOS2_GOTOFF, + 0, + 2, + 32, + FALSE, + 0, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_NIOS2_GOTOFF", + FALSE, + 0xffffffff, + 0xffffffff, + FALSE), + +/* Add other relocations here. */ +}; + +static unsigned char elf_code_to_howto_index[R_NIOS2_ILLEGAL + 1]; + +/* Return the howto for relocation RTYPE. */ +static reloc_howto_type * +lookup_howto (unsigned int rtype) +{ + static int initialized = 0; + int i; + int howto_tbl_size = (int) (sizeof (elf_nios2_howto_table_rel) + / sizeof (elf_nios2_howto_table_rel[0])); + + if (!initialized) + { + initialized = 1; + memset (elf_code_to_howto_index, 0xff, + sizeof (elf_code_to_howto_index)); + for (i = 0; i < howto_tbl_size; i++) + elf_code_to_howto_index[elf_nios2_howto_table_rel[i].type] = i; + } + + BFD_ASSERT (rtype <= R_NIOS2_ILLEGAL); + i = elf_code_to_howto_index[rtype]; + if (i >= howto_tbl_size) + return 0; + return elf_nios2_howto_table_rel + i; +} + +/* Map for converting BFD reloc types to Nios II reloc types. */ +struct elf_reloc_map +{ + bfd_reloc_code_real_type bfd_val; + enum elf_nios2_reloc_type elf_val; +}; + +static const struct elf_reloc_map nios2_reloc_map[] = { + {BFD_RELOC_NIOS2_S16, R_NIOS2_S16}, + {BFD_RELOC_NIOS2_U16, R_NIOS2_U16}, + {BFD_RELOC_16_PCREL, R_NIOS2_PCREL16}, + {BFD_RELOC_NIOS2_CALL26, R_NIOS2_CALL26}, + {BFD_RELOC_NIOS2_IMM5, R_NIOS2_IMM5}, + {BFD_RELOC_NIOS2_CACHE_OPX, R_NIOS2_CACHE_OPX}, + {BFD_RELOC_NIOS2_IMM6, R_NIOS2_IMM6}, + {BFD_RELOC_NIOS2_IMM8, R_NIOS2_IMM8}, + {BFD_RELOC_NIOS2_HI16, R_NIOS2_HI16}, + {BFD_RELOC_NIOS2_LO16, R_NIOS2_LO16}, + {BFD_RELOC_NIOS2_HIADJ16, R_NIOS2_HIADJ16}, + {BFD_RELOC_32, R_NIOS2_BFD_RELOC_32}, + {BFD_RELOC_16, R_NIOS2_BFD_RELOC_16}, + {BFD_RELOC_8, R_NIOS2_BFD_RELOC_8}, + {BFD_RELOC_NIOS2_GPREL, R_NIOS2_GPREL}, + {BFD_RELOC_VTABLE_INHERIT, R_NIOS2_GNU_VTINHERIT}, + {BFD_RELOC_VTABLE_ENTRY, R_NIOS2_GNU_VTENTRY}, + {BFD_RELOC_NIOS2_UJMP, R_NIOS2_UJMP}, + {BFD_RELOC_NIOS2_CJMP, R_NIOS2_CJMP}, + {BFD_RELOC_NIOS2_CALLR, R_NIOS2_CALLR}, + {BFD_RELOC_NIOS2_ALIGN, R_NIOS2_ALIGN}, + {BFD_RELOC_NIOS2_GOT16, R_NIOS2_GOT16}, + {BFD_RELOC_NIOS2_CALL16, R_NIOS2_CALL16}, + {BFD_RELOC_NIOS2_GOTOFF_LO, R_NIOS2_GOTOFF_LO}, + {BFD_RELOC_NIOS2_GOTOFF_HA, R_NIOS2_GOTOFF_HA}, + {BFD_RELOC_NIOS2_PCREL_LO, R_NIOS2_PCREL_LO}, + {BFD_RELOC_NIOS2_PCREL_HA, R_NIOS2_PCREL_HA}, + {BFD_RELOC_NIOS2_TLS_GD16, R_NIOS2_TLS_GD16}, + {BFD_RELOC_NIOS2_TLS_LDM16, R_NIOS2_TLS_LDM16}, + {BFD_RELOC_NIOS2_TLS_LDO16, R_NIOS2_TLS_LDO16}, + {BFD_RELOC_NIOS2_TLS_IE16, R_NIOS2_TLS_IE16}, + {BFD_RELOC_NIOS2_TLS_LE16, R_NIOS2_TLS_LE16}, + {BFD_RELOC_NIOS2_TLS_DTPMOD, R_NIOS2_TLS_DTPMOD}, + {BFD_RELOC_NIOS2_TLS_DTPREL, R_NIOS2_TLS_DTPREL}, + {BFD_RELOC_NIOS2_TLS_TPREL, R_NIOS2_TLS_TPREL}, + {BFD_RELOC_NIOS2_COPY, R_NIOS2_COPY}, + {BFD_RELOC_NIOS2_GLOB_DAT, R_NIOS2_GLOB_DAT}, + {BFD_RELOC_NIOS2_JUMP_SLOT, R_NIOS2_JUMP_SLOT}, + {BFD_RELOC_NIOS2_RELATIVE, R_NIOS2_RELATIVE}, + {BFD_RELOC_NIOS2_GOTOFF, R_NIOS2_GOTOFF} +}; + +/* The Nios II 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 elf32_nios2_dyn_relocs +{ + struct elf32_nios2_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; +}; + +/* Nios II ELF linker hash entry. */ + +struct elf32_nios2_link_hash_entry +{ + struct elf_link_hash_entry root; + + /* Track dynamic relocs copied for this symbol. */ + struct elf32_nios2_dyn_relocs *dyn_relocs; + +#define GOT_UNKNOWN 0 +#define GOT_NORMAL 1 +#define GOT_TLS_GD 2 +#define GOT_TLS_IE 4 + unsigned char tls_type; + + /* We need to detect and take special action for symbols which are only + referenced with %call() and not with %got(). Such symbols do not need + a dynamic GOT reloc in shared objects, only a dynamic PLT reloc. Lazy + linking will not work if the dynamic GOT reloc exists. + To check for this condition efficiently, we compare got_types_used against + CALL16_USED, meaning + (got_types_used & (GOT16_USED | CALL16_USED)) == CALL16_USED. */ +#define GOT16_USED 1 +#define CALL16_USED 2 + unsigned char got_types_used; +}; + +#define elf32_nios2_hash_entry(ent) \ + ((struct elf32_nios2_link_hash_entry *) (ent)) + +/* Get the Nios II elf linker hash table from a link_info structure. */ +#define elf32_nios2_hash_table(info) \ + ((struct elf32_nios2_link_hash_table *) ((info)->hash)) + +/* Nios II ELF linker hash table. */ +struct elf32_nios2_link_hash_table + { + /* The main hash table. */ + struct elf_link_hash_table root; + + /* Short-cuts to get to dynamic linker sections. */ + asection *sdynbss; + asection *srelbss; + asection *sbss; + + union { + bfd_signed_vma refcount; + bfd_vma offset; + } tls_ldm_got; + + /* Small local sym cache. */ + struct sym_cache sym_cache; + + bfd_vma res_n_size; + }; + +struct nios2_elf32_obj_tdata +{ + struct elf_obj_tdata root; + + /* tls_type for each local got entry. */ + char *local_got_tls_type; + + /* TRUE if TLS GD relocs have been seen for this object. */ + bfd_boolean has_tlsgd; +}; + +#define elf32_nios2_tdata(abfd) \ + ((struct nios2_elf32_obj_tdata *) (abfd)->tdata.any) + +#define elf32_nios2_local_got_tls_type(abfd) \ + (elf32_nios2_tdata (abfd)->local_got_tls_type) + +/* The name of the dynamic interpreter. This is put in the .interp + section. */ +#define ELF_DYNAMIC_INTERPRETER "/lib/ld.so.1" + +/* PLT implementation for position-dependent code. */ +static const bfd_vma nios2_plt_entry[] = { /* .PLTn: */ + 0x03c00034, /* movhi r15, %hiadj(plt_got_slot_address) */ + 0x7bc00017, /* ldw r15, %lo(plt_got_slot_address)(r15) */ + 0x7800683a /* jmp r15 */ +}; + +static const bfd_vma nios2_plt0_entry[] = { /* .PLTresolve */ + 0x03800034, /* movhi r14, %hiadj(res_0) */ + 0x73800004, /* addi r14, r14, %lo(res_0) */ + 0x7b9fc83a, /* sub r15, r15, r14 */ + 0x03400034, /* movhi r13, %hiadj(_GLOBAL_OFFSET_TABLE_) */ + 0x6b800017, /* ldw r14, %lo(_GLOBAL_OFFSET_TABLE_+4)(r13) */ + 0x6b400017, /* ldw r13, %lo(_GLOBAL_OFFSET_TABLE_+8)(r13) */ + 0x6800683a /* jmp r13 */ +}; + +/* PLT implementation for position-independent code. */ +static const bfd_vma nios2_so_plt_entry[] = { /* .PLTn */ + 0x03c00034, /* movhi r15, %hiadj(index * 4) */ + 0x7bc00004, /* addi r15, r15, %lo(index * 4) */ + 0x00000006 /* br .PLTresolve */ +}; + +static const bfd_vma nios2_so_plt0_entry[] = { /* .PLTresolve */ + 0x001ce03a, /* nextpc r14 */ + 0x03400034, /* movhi r13, %hiadj(_GLOBAL_OFFSET_TABLE_) */ + 0x6b9b883a, /* add r13, r13, r14 */ + 0x6b800017, /* ldw r14, %lo(_GLOBAL_OFFSET_TABLE_+4)(r13) */ + 0x6b400017, /* ldw r13, %lo(_GLOBAL_OFFSET_TABLE_+8)(r13) */ + 0x6800683a /* jmp r13 */ +}; + +/* Implement elf_backend_grok_prstatus: + Support for core dump NOTE sections. */ +static bfd_boolean +nios2_grok_prstatus (bfd *abfd, Elf_Internal_Note *note) +{ + int offset; + size_t size; + + switch (note->descsz) + { + default: + return FALSE; + + case 212: /* Linux/Nios II */ + /* 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 = 136; + + break; + } + + /* Make a ".reg/999" section. */ + return _bfd_elfcore_make_pseudosection (abfd, ".reg", + size, note->descpos + offset); +} + +/* Implement elf_backend_grok_psinfo. */ +static bfd_boolean +nios2_grok_psinfo (bfd *abfd, Elf_Internal_Note *note) +{ + switch (note->descsz) + { + default: + return FALSE; + + case 124: /* Linux/Nios II elf_prpsinfo */ + 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); + } + + /* 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; +} + +/* Create an entry in a Nios II ELF linker hash table. */ +static struct bfd_hash_entry * +link_hash_newfunc (struct bfd_hash_entry *entry, + struct bfd_hash_table *table, const char *string) +{ + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (entry == NULL) + { + entry = bfd_hash_allocate (table, + sizeof (struct elf32_nios2_link_hash_entry)); + if (entry == NULL) + return entry; + } + + /* Call the allocation method of the superclass. */ + entry = _bfd_elf_link_hash_newfunc (entry, table, string); + if (entry) + { + struct elf32_nios2_link_hash_entry *eh; + + eh = (struct elf32_nios2_link_hash_entry *) entry; + eh->dyn_relocs = NULL; + eh->tls_type = GOT_UNKNOWN; + eh->got_types_used = 0; + } + + return entry; +} + +/* Implement bfd_elf32_bfd_reloc_type_lookup: + Given a BFD reloc type, return a howto structure. */ +static reloc_howto_type * +nios2_elf32_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, + bfd_reloc_code_real_type code) +{ + int i; + for (i = 0; + i < (int) (sizeof (nios2_reloc_map) / sizeof (struct elf_reloc_map)); + ++i) + if (nios2_reloc_map[i].bfd_val == code) + return &elf_nios2_howto_table_rel[(int) nios2_reloc_map[i].elf_val]; + return NULL; +} + +/* Implement bfd_elf32_bfd_reloc_name_lookup: + Given a reloc name, return a howto structure. */ +static reloc_howto_type * +nios2_elf32_bfd_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, + const char *r_name) +{ + unsigned int i; + for (i = 0; + i < (sizeof (elf_nios2_howto_table_rel) + / sizeof (elf_nios2_howto_table_rel[0])); + i++) + if (elf_nios2_howto_table_rel[i].name + && strcasecmp (elf_nios2_howto_table_rel[i].name, r_name) == 0) + return &elf_nios2_howto_table_rel[i]; + + return NULL; +} + +/* Implement elf_info_to_howto: + Given a ELF32 relocation, fill in a arelent structure. */ +static void +nios2_elf32_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED, arelent *cache_ptr, + Elf_Internal_Rela *dst) +{ + unsigned int r_type; + + r_type = ELF32_R_TYPE (dst->r_info); + BFD_ASSERT (r_type < R_NIOS2_ILLEGAL); + cache_ptr->howto = &elf_nios2_howto_table_rel[r_type]; +} + +/* Return the base VMA address which should be subtracted from real addresses + when resolving @dtpoff relocation. + This is PT_TLS segment p_vaddr. */ +static bfd_vma +dtpoff_base (struct bfd_link_info *info) +{ + /* If tls_sec is NULL, we should have signalled an error already. */ + if (elf_hash_table (info)->tls_sec == NULL) + return 0; + return elf_hash_table (info)->tls_sec->vma; +} + +/* Return the relocation value for @tpoff relocation + if STT_TLS virtual address is ADDRESS. */ +static bfd_vma +tpoff (struct bfd_link_info *info, bfd_vma address) +{ + struct elf_link_hash_table *htab = elf_hash_table (info); + + /* If tls_sec is NULL, we should have signalled an error already. */ + if (htab->tls_sec == NULL) + return 0; + return address - htab->tls_sec->vma; +} + +/* Set the GP value for OUTPUT_BFD. Returns FALSE if this is a + dangerous relocation. */ +static bfd_boolean +nios2_elf_assign_gp (bfd *output_bfd, bfd_vma *pgp, struct bfd_link_info *info) +{ + + bfd_boolean gp_found; + struct bfd_hash_entry *h; + struct bfd_link_hash_entry *lh; + + /* If we've already figured out what GP will be, just return it. */ + *pgp = _bfd_get_gp_value (output_bfd); + if (*pgp) + return TRUE; + + h = bfd_hash_lookup (&info->hash->table, "_gp", FALSE, FALSE); + lh = (struct bfd_link_hash_entry *) h; +lookup: + if (lh) + { + switch (lh->type) + { + case bfd_link_hash_undefined: + case bfd_link_hash_undefweak: + case bfd_link_hash_common: + gp_found = FALSE; + break; + case bfd_link_hash_defined: + case bfd_link_hash_defweak: + gp_found = TRUE; + *pgp = lh->u.def.value; + break; + case bfd_link_hash_indirect: + case bfd_link_hash_warning: + lh = lh->u.i.link; + /* @@FIXME ignoring warning for now */ + goto lookup; + case bfd_link_hash_new: + default: + abort (); + } + } + else + gp_found = FALSE; + + if (!gp_found) + { + /* Only get the error once. */ + *pgp = 4; + _bfd_set_gp_value (output_bfd, *pgp); + return FALSE; + } + + _bfd_set_gp_value (output_bfd, *pgp); + + return TRUE; +} + +/* Retrieve the previously cached _gp pointer, returning bfd_reloc_dangerous + if it's not available as we don't have a link_info pointer available here + to look it up in the output symbol table. We don't need to adjust the + symbol value for an external symbol if we are producing relocatable + output. */ +static bfd_reloc_status_type +nios2_elf_final_gp (bfd *output_bfd, asymbol *symbol, bfd_boolean relocatable, + char **error_message, bfd_vma *pgp) +{ + if (bfd_is_und_section (symbol->section) && !relocatable) + { + *pgp = 0; + return bfd_reloc_undefined; + } + + *pgp = _bfd_get_gp_value (output_bfd); + if (*pgp == 0 && (!relocatable || (symbol->flags & BSF_SECTION_SYM) != 0)) + { + if (relocatable) + { + /* Make up a value. */ + *pgp = symbol->section->output_section->vma + 0x4000; + _bfd_set_gp_value (output_bfd, *pgp); + } + else + { + *error_message + = (char *) _("global pointer relative relocation when _gp not defined"); + return bfd_reloc_dangerous; + } + } + + return bfd_reloc_ok; +} + +/* The usual way of loading a 32-bit constant into a Nios II register is to + load the high 16 bits in one instruction and then add the low 16 bits with + a signed add. This means that the high halfword needs to be adjusted to + compensate for the sign bit of the low halfword. This function returns the + adjusted high halfword for a given 32-bit constant. */ +static +bfd_vma hiadj (bfd_vma symbol_value) +{ + return ((symbol_value + 0x8000) >> 16) & 0xffff; +} + +/* Do the relocations that require special handling. */ +static bfd_reloc_status_type +nios2_elf32_do_hi16_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + symbol_value = symbol_value + addend; + addend = 0; + symbol_value = (symbol_value >> 16) & 0xffff; + return _bfd_final_link_relocate (howto, abfd, input_section, + data, offset, symbol_value, addend); +} + +static bfd_reloc_status_type +nios2_elf32_do_lo16_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + symbol_value = symbol_value + addend; + addend = 0; + symbol_value = symbol_value & 0xffff; + return _bfd_final_link_relocate (howto, abfd, input_section, + data, offset, symbol_value, addend); +} + +static bfd_reloc_status_type +nios2_elf32_do_hiadj16_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + symbol_value = symbol_value + addend; + addend = 0; + symbol_value = hiadj(symbol_value); + return _bfd_final_link_relocate (howto, abfd, input_section, data, offset, + symbol_value, addend); +} + +static bfd_reloc_status_type +nios2_elf32_do_pcrel_lo16_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + symbol_value = symbol_value + addend; + addend = 0; + symbol_value = symbol_value & 0xffff; + return _bfd_final_link_relocate (howto, abfd, input_section, + data, offset, symbol_value, addend); +} + +static bfd_reloc_status_type +nios2_elf32_do_pcrel_hiadj16_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section + ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + symbol_value = symbol_value + addend; + symbol_value -= (input_section->output_section->vma + + input_section->output_offset); + symbol_value -= offset; + addend = 0; + symbol_value = hiadj(symbol_value); + return _bfd_final_link_relocate (howto, abfd, input_section, data, offset, + symbol_value, addend); +} + +static bfd_reloc_status_type +nios2_elf32_do_pcrel16_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + /* NIOS2 pc relative relocations are relative to the next 32-bit instruction + so we need to subtract 4 before doing a final_link_relocate. */ + symbol_value = symbol_value + addend - 4; + addend = 0; + return _bfd_final_link_relocate (howto, abfd, input_section, + data, offset, symbol_value, addend); +} + +static bfd_reloc_status_type +nios2_elf32_do_call26_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + /* Check that the relocation is in the same page as the current address. */ + if (((symbol_value + addend) & 0xf0000000) + != ((input_section->output_section->vma + offset) & 0xf0000000)) + return bfd_reloc_overflow; + + return _bfd_final_link_relocate (howto, abfd, input_section, + data, offset, symbol_value, addend); +} + +static bfd_reloc_status_type +nios2_elf32_do_gprel_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + /* Because we need the output_bfd, the special handling is done + in nios2_elf32_relocate_section or in nios2_elf32_gprel_relocate. */ + return _bfd_final_link_relocate (howto, abfd, input_section, + data, offset, symbol_value, addend); +} + +static bfd_reloc_status_type +nios2_elf32_do_ujmp_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + bfd_vma symbol_lo16, symbol_hi16; + bfd_reloc_status_type r; + symbol_value = symbol_value + addend; + addend = 0; + symbol_hi16 = (symbol_value >> 16) & 0xffff; + symbol_lo16 = symbol_value & 0xffff; + + r = _bfd_final_link_relocate (howto, abfd, input_section, + data, offset, symbol_hi16, addend); + + if (r == bfd_reloc_ok) + return _bfd_final_link_relocate (howto, abfd, input_section, + data, offset + 4, symbol_lo16, addend); + + return r; +} + +static bfd_reloc_status_type +nios2_elf32_do_cjmp_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + bfd_vma symbol_lo16, symbol_hi16; + bfd_reloc_status_type r; + symbol_value = symbol_value + addend; + addend = 0; + symbol_hi16 = (symbol_value >> 16) & 0xffff; + symbol_lo16 = symbol_value & 0xffff; + + r = _bfd_final_link_relocate (howto, abfd, input_section, + data, offset, symbol_hi16, addend); + + if (r == bfd_reloc_ok) + return _bfd_final_link_relocate (howto, abfd, input_section, + data, offset + 4, symbol_lo16, addend); + + return r; +} + +static bfd_reloc_status_type +nios2_elf32_do_callr_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section ATTRIBUTE_UNUSED, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + bfd_vma symbol_lo16, symbol_hi16; + bfd_reloc_status_type r; + symbol_value = symbol_value + addend; + addend = 0; + symbol_hi16 = (symbol_value >> 16) & 0xffff; + symbol_lo16 = symbol_value & 0xffff; + + r = _bfd_final_link_relocate (howto, abfd, input_section, + data, offset, symbol_hi16, addend); + + if (r == bfd_reloc_ok) + return _bfd_final_link_relocate (howto, abfd, input_section, + data, offset + 4, symbol_lo16, addend); + + return r; +} + +/* HOWTO handlers for relocations that require special handling. */ + +/* This is for relocations used only when relaxing to ensure + changes in size of section don't screw up .align. */ +static bfd_reloc_status_type +nios2_elf32_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; +} + +static bfd_reloc_status_type +nios2_elf32_hi16_relocate (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 != 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 nios2_elf32_do_hi16_relocate (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +static bfd_reloc_status_type +nios2_elf32_lo16_relocate (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 != 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 nios2_elf32_do_lo16_relocate (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +static bfd_reloc_status_type +nios2_elf32_hiadj16_relocate (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 != 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 nios2_elf32_do_hiadj16_relocate (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +static bfd_reloc_status_type +nios2_elf32_pcrel_lo16_relocate (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 != 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 nios2_elf32_do_pcrel_lo16_relocate ( + abfd, reloc_entry->howto, input_section, data, reloc_entry->address, + (symbol->value + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +static bfd_reloc_status_type +nios2_elf32_pcrel_hiadj16_relocate (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 != 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 nios2_elf32_do_pcrel_hiadj16_relocate ( + abfd, reloc_entry->howto, input_section, data, reloc_entry->address, + (symbol->value + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +static bfd_reloc_status_type +nios2_elf32_pcrel16_relocate (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 != 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 nios2_elf32_do_pcrel16_relocate (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +static bfd_reloc_status_type +nios2_elf32_call26_relocate (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 != 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 nios2_elf32_do_call26_relocate (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +static bfd_reloc_status_type +nios2_elf32_gprel_relocate (bfd *abfd, arelent *reloc_entry, asymbol *symbol, + void *data, asection *input_section, + bfd *output_bfd, char **msg) +{ + bfd_vma relocation; + bfd_vma gp; + bfd_reloc_status_type r; + + + /* This part is from bfd_elf_generic_reloc. */ + if (output_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; + + relocation = (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset); + + /* This assumes we've already cached the _gp symbol. */ + r = nios2_elf_final_gp (abfd, symbol, FALSE, msg, &gp); + if (r == bfd_reloc_ok) + { + relocation = relocation + reloc_entry->addend - gp; + reloc_entry->addend = 0; + if ((signed) relocation < -32768 || (signed) relocation > 32767) + { + *msg = _("global pointer relative address out of range"); + r = bfd_reloc_outofrange; + } + else + r = nios2_elf32_do_gprel_relocate (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + relocation, reloc_entry->addend); + } + + return r; +} + +static bfd_reloc_status_type +nios2_elf32_ujmp_relocate (bfd *abfd, arelent *reloc_entry, asymbol *symbol, + void *data, asection *input_section, + bfd *output_bfd, char **msg ATTRIBUTE_UNUSED) +{ + /* This part is from bfd_elf_generic_reloc. */ + if (output_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 nios2_elf32_do_ujmp_relocate (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +static bfd_reloc_status_type +nios2_elf32_cjmp_relocate (bfd *abfd, arelent *reloc_entry, asymbol *symbol, + void *data, asection *input_section, + bfd *output_bfd, char **msg ATTRIBUTE_UNUSED) +{ + /* This part is from bfd_elf_generic_reloc. */ + if (output_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 nios2_elf32_do_cjmp_relocate (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +static bfd_reloc_status_type +nios2_elf32_callr_relocate (bfd *abfd, arelent *reloc_entry, asymbol *symbol, + void *data, asection *input_section, + bfd *output_bfd, char **msg ATTRIBUTE_UNUSED) +{ + /* This part is from bfd_elf_generic_reloc. */ + if (output_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 nios2_elf32_do_callr_relocate (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + + +/* Implement elf_backend_relocate_section. */ +static bfd_boolean +nios2_elf32_relocate_section (bfd *output_bfd, + struct bfd_link_info *info, + bfd *input_bfd, + asection *input_section, + bfd_byte *contents, + Elf_Internal_Rela *relocs, + Elf_Internal_Sym *local_syms, + asection **local_sections) +{ + Elf_Internal_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes; + Elf_Internal_Rela *rel; + Elf_Internal_Rela *relend; + struct elf32_nios2_link_hash_table *htab; + asection *sgot; + asection *splt; + asection *sreloc = NULL; + bfd_vma *local_got_offsets; + + symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (input_bfd); + relend = relocs + input_section->reloc_count; + + htab = elf32_nios2_hash_table (info); + sgot = htab->root.sgot; + splt = htab->root.splt; + local_got_offsets = elf_local_got_offsets (input_bfd); + + for (rel = relocs; rel < relend; rel++) + { + reloc_howto_type *howto; + unsigned long r_symndx; + Elf_Internal_Sym *sym; + asection *sec; + struct elf_link_hash_entry *h; + struct elf32_nios2_link_hash_entry *eh; + bfd_vma relocation; + bfd_vma gp; + bfd_vma reloc_address; + bfd_reloc_status_type r = bfd_reloc_ok; + const char *name = NULL; + int r_type; + const char *format; + char msgbuf[256]; + const char* msg = (const char*) NULL; + bfd_boolean unresolved_reloc; + bfd_vma off; + int use_plt; + + r_type = ELF32_R_TYPE (rel->r_info); + r_symndx = ELF32_R_SYM (rel->r_info); + + howto = lookup_howto ((unsigned) ELF32_R_TYPE (rel->r_info)); + h = NULL; + sym = NULL; + sec = NULL; + + if (r_symndx < symtab_hdr->sh_info) + { + sym = local_syms + r_symndx; + sec = local_sections[r_symndx]; + relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel); + } + else + { + bfd_boolean warned; + + RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel, + r_symndx, symtab_hdr, sym_hashes, + h, sec, relocation, + unresolved_reloc, warned); + } + + if (sec && discarded_section (sec)) + RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section, + rel, 1, relend, howto, 0, contents); + + /* Nothing more to do unless this is a final link. */ + if (info->relocatable) + continue; + + if (sec && sec->output_section) + reloc_address = (sec->output_section->vma + sec->output_offset + + rel->r_offset); + else + reloc_address = 0; + + if (howto) + { + switch (howto->type) + { + case R_NIOS2_HI16: + r = nios2_elf32_do_hi16_relocate (input_bfd, howto, + input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + break; + case R_NIOS2_LO16: + r = nios2_elf32_do_lo16_relocate (input_bfd, howto, + input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + break; + case R_NIOS2_PCREL_LO: + r = nios2_elf32_do_pcrel_lo16_relocate (input_bfd, howto, + input_section, + contents, + rel->r_offset, + relocation, + rel->r_addend); + break; + case R_NIOS2_HIADJ16: + r = nios2_elf32_do_hiadj16_relocate (input_bfd, howto, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + break; + case R_NIOS2_PCREL_HA: + r = nios2_elf32_do_pcrel_hiadj16_relocate (input_bfd, howto, + input_section, + contents, + rel->r_offset, + relocation, + rel->r_addend); + break; + case R_NIOS2_PCREL16: + r = nios2_elf32_do_pcrel16_relocate (input_bfd, howto, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + break; + case R_NIOS2_GPREL: + /* Turns an absolute address into a gp-relative address. */ + if (!nios2_elf_assign_gp (output_bfd, &gp, info)) + { + format = _("global pointer relative relocation at address " + "0x%08x when _gp not defined\n"); + sprintf (msgbuf, format, reloc_address); + msg = msgbuf; + r = bfd_reloc_dangerous; + } + else + { + bfd_vma symbol_address = rel->r_addend + relocation; + relocation = relocation + rel->r_addend - gp; + rel->r_addend = 0; + if (((signed) relocation < -32768 + || (signed) relocation > 32767) + && (!h + || h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak)) + { + format = _("Unable to reach %s (at 0x%08x) from the " + "global pointer (at 0x%08x) because the " + "offset (%d) is out of the allowed range, " + "-32678 to 32767.\n" ); + sprintf (msgbuf, format, name, symbol_address, gp, + (signed)relocation); + msg = msgbuf; + r = bfd_reloc_outofrange; + } + else + r = _bfd_final_link_relocate (howto, input_bfd, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + } + + break; + case R_NIOS2_UJMP: + r = nios2_elf32_do_ujmp_relocate (input_bfd, howto, + input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + break; + case R_NIOS2_CJMP: + r = nios2_elf32_do_cjmp_relocate (input_bfd, howto, + input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + break; + case R_NIOS2_CALLR: + r = nios2_elf32_do_callr_relocate (input_bfd, howto, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + break; + case R_NIOS2_CALL26: + /* If we have a call to an undefined weak symbol, we just want + to stuff a zero in the bits of the call instruction and + bypass the normal call26 relocation handling, because it'll + diagnose an overflow error if address 0 isn't in the same + 256MB segment as the call site. Presumably the call + should be guarded by a null check anyway. */ + if (h != NULL && h->root.type == bfd_link_hash_undefweak) + { + BFD_ASSERT (relocation == 0 && rel->r_addend == 0); + r = _bfd_final_link_relocate (howto, input_bfd, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + break; + } + /* Handle relocations which should use the PLT entry. + NIOS2_BFD_RELOC_32 relocations will use the symbol's value, + which may point to a PLT entry, but we don't need to handle + that here. If we created a PLT entry, all branches in this + object should go to it. */ + if (h != NULL && splt != NULL && h->plt.offset != (bfd_vma) -1) + { + /* If we've created a .plt section, and assigned a PLT entry + to this function, it should not be known to bind locally. + If it were, we would have cleared the PLT entry. */ + BFD_ASSERT (!SYMBOL_CALLS_LOCAL (info, h)); + + relocation = (splt->output_section->vma + + splt->output_offset + + h->plt.offset); + + unresolved_reloc = FALSE; + } + r = nios2_elf32_do_call26_relocate (input_bfd, howto, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + break; + case R_NIOS2_ALIGN: + r = bfd_reloc_ok; + /* For symmetry this would be + r = nios2_elf32_do_ignore_reloc (input_bfd, howto, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + but do_ignore_reloc would do no more than return + bfd_reloc_ok. */ + break; + + case R_NIOS2_GOT16: + case R_NIOS2_CALL16: + /* Relocation is to the entry for this symbol in the + global offset table. */ + if (sgot == NULL) + { + r = bfd_reloc_notsupported; + break; + } + + use_plt = 0; + + if (h != NULL) + { + bfd_boolean dyn; + + eh = (struct elf32_nios2_link_hash_entry *)h; + use_plt = (eh->got_types_used == CALL16_USED + && h->plt.offset != (bfd_vma) -1); + + off = h->got.offset; + BFD_ASSERT (off != (bfd_vma) -1); + dyn = elf_hash_table (info)->dynamic_sections_created; + if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) + || (info->shared + && SYMBOL_REFERENCES_LOCAL (info, h)) + || (ELF_ST_VISIBILITY (h->other) + && h->root.type == bfd_link_hash_undefweak)) + { + /* This is actually a static link, or it is a -Bsymbolic + link and the symbol is defined locally. 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 + unresolved_reloc = FALSE; + } + else + { + BFD_ASSERT (local_got_offsets != NULL + && local_got_offsets[r_symndx] != (bfd_vma) -1); + + off = local_got_offsets[r_symndx]; + + /* The offset must always be a multiple of 4. We use the + least significant bit to record whether we have already + generated the necessary reloc. */ + 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; + bfd_byte *loc; + + srelgot = htab->root.srelgot; + BFD_ASSERT (srelgot != NULL); + + outrel.r_addend = relocation; + outrel.r_offset = (sgot->output_section->vma + + sgot->output_offset + + off); + outrel.r_info = ELF32_R_INFO (0, R_NIOS2_RELATIVE); + loc = srelgot->contents; + loc += (srelgot->reloc_count++ * + sizeof (Elf32_External_Rela)); + bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); + } + + local_got_offsets[r_symndx] |= 1; + } + } + + if (use_plt && info->shared) + { + off = ((h->plt.offset - 24) / 12 + 3) * 4; + relocation = htab->root.sgotplt->output_offset + off; + } + else + relocation = sgot->output_offset + off; + + /* This relocation does not use the addend. */ + rel->r_addend = 0; + + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + break; + + case R_NIOS2_GOTOFF_LO: + case R_NIOS2_GOTOFF_HA: + case R_NIOS2_GOTOFF: + /* Relocation is relative to the start of the + global offset table. */ + + BFD_ASSERT (sgot != NULL); + if (sgot == NULL) + { + r = bfd_reloc_notsupported; + break; + } + + /* Note that sgot->output_offset is not involved in this + calculation. We always want the start of .got. If we + define _GLOBAL_OFFSET_TABLE in a different way, as is + permitted by the ABI, we might have to change this + calculation. */ + relocation -= sgot->output_section->vma; + switch (howto->type) + { + case R_NIOS2_GOTOFF_LO: + r = nios2_elf32_do_lo16_relocate (input_bfd, howto, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + break; + case R_NIOS2_GOTOFF_HA: + r = nios2_elf32_do_hiadj16_relocate (input_bfd, howto, + input_section, contents, + rel->r_offset, + relocation, + rel->r_addend); + break; + default: + r = _bfd_final_link_relocate (howto, input_bfd, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + break; + } + break; + + case R_NIOS2_TLS_LDO16: + relocation -= dtpoff_base (info) + DTP_OFFSET; + + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + break; + case R_NIOS2_TLS_LDM16: + if (htab->root.sgot == NULL) + abort (); + + off = htab->tls_ldm_got.offset; + + if ((off & 1) != 0) + off &= ~1; + else + { + /* If we don't know the module number, create a relocation + for it. */ + if (info->shared) + { + Elf_Internal_Rela outrel; + bfd_byte *loc; + + if (htab->root.srelgot == NULL) + abort (); + + outrel.r_addend = 0; + outrel.r_offset = (htab->root.sgot->output_section->vma + + htab->root.sgot->output_offset + + off); + outrel.r_info = ELF32_R_INFO (0, R_NIOS2_TLS_DTPMOD); + + loc = htab->root.srelgot->contents; + loc += (htab->root.srelgot->reloc_count++ + * sizeof (Elf32_External_Rela)); + bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); + } + else + bfd_put_32 (output_bfd, 1, + htab->root.sgot->contents + off); + + htab->tls_ldm_got.offset |= 1; + } + + relocation = (htab->root.sgot->output_offset + off); + + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + + break; + case R_NIOS2_TLS_GD16: + case R_NIOS2_TLS_IE16: + { + int indx; + char tls_type; + + if (htab->root.sgot == NULL) + abort (); + + indx = 0; + if (h != NULL) + { + bfd_boolean dyn; + dyn = htab->root.dynamic_sections_created; + if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) + && (!info->shared + || !SYMBOL_REFERENCES_LOCAL (info, h))) + { + unresolved_reloc = FALSE; + indx = h->dynindx; + } + off = h->got.offset; + tls_type = (((struct elf32_nios2_link_hash_entry *) h) + ->tls_type); + } + else + { + if (local_got_offsets == NULL) + abort (); + off = local_got_offsets[r_symndx]; + tls_type = (elf32_nios2_local_got_tls_type (input_bfd) + [r_symndx]); + } + + if (tls_type == GOT_UNKNOWN) + abort (); + + if ((off & 1) != 0) + off &= ~1; + else + { + bfd_boolean need_relocs = FALSE; + Elf_Internal_Rela outrel; + bfd_byte *loc = NULL; + int cur_off = off; + + /* The GOT entries have not been initialized yet. Do it + now, and emit any relocations. If both an IE GOT and a + GD GOT are necessary, we emit the GD first. */ + + if ((info->shared || indx != 0) + && (h == NULL + || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT + || h->root.type != bfd_link_hash_undefweak)) + { + need_relocs = TRUE; + if (htab->root.srelgot == NULL) + abort (); + loc = htab->root.srelgot->contents; + loc += (htab->root.srelgot->reloc_count * + sizeof (Elf32_External_Rela)); + } + + if (tls_type & GOT_TLS_GD) + { + if (need_relocs) + { + outrel.r_addend = 0; + outrel.r_offset = (htab->root.sgot->output_section->vma + + htab->root.sgot->output_offset + + cur_off); + outrel.r_info = ELF32_R_INFO (indx, + R_NIOS2_TLS_DTPMOD); + + bfd_elf32_swap_reloca_out (output_bfd, &outrel, + loc); + htab->root.srelgot->reloc_count++; + loc += sizeof (Elf32_External_Rela); + + if (indx == 0) + bfd_put_32 (output_bfd, + (relocation - dtpoff_base (info) - + DTP_OFFSET), + htab->root.sgot->contents + cur_off + 4); + else + { + outrel.r_addend = 0; + outrel.r_info = ELF32_R_INFO (indx, + R_NIOS2_TLS_DTPREL); + outrel.r_offset += 4; + + bfd_elf32_swap_reloca_out (output_bfd, &outrel, + loc); + htab->root.srelgot->reloc_count++; + loc += sizeof (Elf32_External_Rela); + } + } + else + { + /* If we are not emitting relocations for a + general dynamic reference, then we must be in a + static link or an executable link with the + symbol binding locally. Mark it as belonging + to module 1, the executable. */ + bfd_put_32 (output_bfd, 1, + htab->root.sgot->contents + cur_off); + bfd_put_32 (output_bfd, (relocation - + dtpoff_base (info) - + DTP_OFFSET), + htab->root.sgot->contents + cur_off + 4); + } + + cur_off += 8; + } + + if (tls_type & GOT_TLS_IE) + { + if (need_relocs) + { + if (indx == 0) + outrel.r_addend = (relocation - + dtpoff_base (info)); + else + outrel.r_addend = 0; + outrel.r_offset = (htab->root.sgot->output_section->vma + + htab->root.sgot->output_offset + + cur_off); + outrel.r_info = ELF32_R_INFO (indx, + R_NIOS2_TLS_TPREL); + + bfd_elf32_swap_reloca_out (output_bfd, &outrel, + loc); + htab->root.srelgot->reloc_count++; + loc += sizeof (Elf32_External_Rela); + } + else + bfd_put_32 (output_bfd, (tpoff (info, relocation) + - TP_OFFSET), + htab->root.sgot->contents + cur_off); + cur_off += 4; + } + + if (h != NULL) + h->got.offset |= 1; + else + local_got_offsets[r_symndx] |= 1; + } + + if ((tls_type & GOT_TLS_GD) && r_type != R_NIOS2_TLS_GD16) + off += 8; + relocation = (htab->root.sgot->output_offset + off); + + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + } + + break; + case R_NIOS2_TLS_LE16: + if (info->shared && !info->pie) + { + (*_bfd_error_handler) + (_("%B(%A+0x%lx): R_NIOS2_TLS_LE16 relocation not " + "permitted in shared object"), + input_bfd, input_section, + (long) rel->r_offset, howto->name); + return FALSE; + } + else + relocation = tpoff (info, relocation) - TP_OFFSET; + + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + break; + + case R_NIOS2_BFD_RELOC_32: + if (info->shared + && (input_section->flags & SEC_ALLOC) != 0 + && (h == NULL + || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT + || h->root.type != bfd_link_hash_undefweak)) + { + Elf_Internal_Rela outrel; + bfd_byte *loc; + bfd_boolean skip, relocate; + + /* When generating a shared object, these relocations + are copied into the output file to be resolved at run + time. */ + + 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 (h != NULL + && h->dynindx != -1 + && (!info->shared + || !info->symbolic + || !h->def_regular)) + { + outrel.r_info = ELF32_R_INFO (h->dynindx, r_type); + outrel.r_addend = rel->r_addend; + } + else + { + /* This symbol is local, or marked to become local. */ + outrel.r_addend = relocation + rel->r_addend; + relocate = TRUE; + outrel.r_info = ELF32_R_INFO (0, R_NIOS2_RELATIVE); + } + + sreloc = elf_section_data (input_section)->sreloc; + if (sreloc == NULL) + abort (); + + loc = sreloc->contents; + loc += sreloc->reloc_count++ * sizeof (Elf32_External_Rela); + bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); + + /* This reloc will be computed at runtime, so there's no + need to do anything now, except for R_NIOS2_BFD_RELOC_32 + relocations that have been turned into + R_NIOS2_RELATIVE. */ + if (!relocate) + break; + } + + r = _bfd_final_link_relocate (howto, input_bfd, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + break; + + case R_NIOS2_TLS_DTPREL: + relocation -= dtpoff_base (info); + /* Fall through. */ + + default: + r = _bfd_final_link_relocate (howto, input_bfd, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + break; + } + } + else + r = bfd_reloc_notsupported; + + if (r != bfd_reloc_ok) + { + if (h != NULL) + name = h->root.root.string; + else + { + name = bfd_elf_string_from_elf_section (input_bfd, + symtab_hdr->sh_link, + sym->st_name); + if (name == NULL || *name == '\0') + name = bfd_section_name (input_bfd, sec); + } + + switch (r) + { + case bfd_reloc_overflow: + r = info->callbacks->reloc_overflow (info, NULL, name, + howto->name, (bfd_vma) 0, + input_bfd, input_section, + rel->r_offset); + break; + + case bfd_reloc_undefined: + r = info->callbacks->undefined_symbol (info, name, input_bfd, + input_section, + rel->r_offset, TRUE); + break; + + case bfd_reloc_outofrange: + if (msg == NULL) + msg = _("relocation out of range"); + break; + + case bfd_reloc_notsupported: + if (msg == NULL) + msg = _("unsupported relocation"); + break; + + case bfd_reloc_dangerous: + if (msg == NULL) + msg = _("dangerous relocation"); + break; + + default: + if (msg == NULL) + msg = _("unknown error"); + break; + } + + if (msg) + { + r = info->callbacks->warning + (info, msg, name, input_bfd, input_section, rel->r_offset); + return FALSE; + } + } + } + return TRUE; +} + +/* Implement elf-backend_section_flags: + Convert NIOS2 specific section flags to bfd internal section flags. */ +static bfd_boolean +nios2_elf32_section_flags (flagword *flags, const Elf_Internal_Shdr *hdr) +{ + if (hdr->sh_flags & SHF_NIOS2_GPREL) + *flags |= SEC_SMALL_DATA; + + return TRUE; +} + +/* Implement elf_backend_fake_sections: + Set the correct type for an NIOS2 ELF section. We do this by the + section name, which is a hack, but ought to work. */ +static bfd_boolean +nios2_elf32_fake_sections (bfd *abfd ATTRIBUTE_UNUSED, + Elf_Internal_Shdr *hdr, asection *sec) +{ + register const char *name = bfd_get_section_name (abfd, sec); + + if ((sec->flags & SEC_SMALL_DATA) + || strcmp (name, ".sdata") == 0 + || strcmp (name, ".sbss") == 0 + || strcmp (name, ".lit4") == 0 || strcmp (name, ".lit8") == 0) + hdr->sh_flags |= SHF_NIOS2_GPREL; + + return TRUE; +} + +/* 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 elf32_nios2_link_hash_table *htab; + + htab = elf32_nios2_hash_table (info); + + if (! _bfd_elf_create_got_section (dynobj, info)) + return FALSE; + + /* In order for the two loads in .PLTresolve to share the same %hiadj, + _GLOBAL_OFFSET_TABLE_ must be aligned to a 16-byte boundary. */ + if (!bfd_set_section_alignment (dynobj, htab->root.sgotplt, 4)) + return FALSE; + + return TRUE; +} + +/* Implement elf_backend_create_dynamic_sections: + Create .plt, .rela.plt, .got, .got.plt, .rela.got, .dynbss, and + .rela.bss sections in DYNOBJ, and set up shortcuts to them in our + hash table. */ +static bfd_boolean +nios2_elf32_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info) +{ + struct elf32_nios2_link_hash_table *htab; + + htab = elf32_nios2_hash_table (info); + if (!htab->root.sgot && !create_got_section (dynobj, info)) + return FALSE; + + _bfd_elf_create_dynamic_sections (dynobj, info); + + /* In order for the two loads in a shared object .PLTresolve to share the + same %hiadj, the start of the PLT (as well as the GOT) must be aligned + to a 16-byte boundary. This is because the addresses for these loads + include the -(.plt+4) PIC correction. */ + if (!bfd_set_section_alignment (dynobj, htab->root.splt, 4)) + return FALSE; + + htab->sdynbss = bfd_get_linker_section (dynobj, ".dynbss"); + if (!htab->sdynbss) + return FALSE; + if (!info->shared) + { + htab->srelbss = bfd_get_linker_section (dynobj, ".rela.bss"); + if (!htab->srelbss) + return FALSE; + } + + return TRUE; +} + +/* Implement elf_backend_copy_indirect_symbol: + Copy the extra info we tack onto an elf_link_hash_entry. */ +static void +nios2_elf32_copy_indirect_symbol (struct bfd_link_info *info, + struct elf_link_hash_entry *dir, + struct elf_link_hash_entry *ind) +{ + struct elf32_nios2_link_hash_entry *edir, *eind; + + edir = (struct elf32_nios2_link_hash_entry *) dir; + eind = (struct elf32_nios2_link_hash_entry *) ind; + + if (eind->dyn_relocs != NULL) + { + if (edir->dyn_relocs != NULL) + { + struct elf32_nios2_dyn_relocs **pp; + struct elf32_nios2_dyn_relocs *p; + + /* Add reloc counts against the indirect sym to the direct sym + list. Merge any entries against the same section. */ + for (pp = &eind->dyn_relocs; (p = *pp) != NULL; ) + { + struct elf32_nios2_dyn_relocs *q; + + for (q = edir->dyn_relocs; q != NULL; q = q->next) + if (q->sec == p->sec) + { + q->pc_count += p->pc_count; + q->count += p->count; + *pp = p->next; + break; + } + if (q == NULL) + pp = &p->next; + } + *pp = edir->dyn_relocs; + } + + edir->dyn_relocs = eind->dyn_relocs; + eind->dyn_relocs = NULL; + } + + if (ind->root.type == bfd_link_hash_indirect + && dir->got.refcount <= 0) + { + edir->tls_type = eind->tls_type; + eind->tls_type = GOT_UNKNOWN; + } + + edir->got_types_used |= eind->got_types_used; + + _bfd_elf_link_hash_copy_indirect (info, dir, ind); +} + +/* Implement elf_backend_check_relocs: + Look through the relocs for a section during the first phase. */ +static bfd_boolean +nios2_elf32_check_relocs (bfd *abfd, struct bfd_link_info *info, + asection *sec, const Elf_Internal_Rela *relocs) +{ + bfd *dynobj; + 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 elf32_nios2_link_hash_table *htab; + asection *sgot; + asection *srelgot; + asection *sreloc = NULL; + bfd_signed_vma *local_got_refcounts; + + if (info->relocatable) + return TRUE; + + dynobj = elf_hash_table (info)->dynobj; + 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; + local_got_refcounts = elf_local_got_refcounts (abfd); + + htab = elf32_nios2_hash_table (info); + sgot = htab->root.sgot; + srelgot = htab->root.srelgot; + + rel_end = relocs + sec->reloc_count; + for (rel = relocs; rel < rel_end; rel++) + { + unsigned int r_type; + struct elf_link_hash_entry *h; + unsigned long r_symndx; + + r_symndx = ELF32_R_SYM (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; + } + + r_type = ELF32_R_TYPE (rel->r_info); + + switch (r_type) + { + case R_NIOS2_GOT16: + case R_NIOS2_CALL16: + case R_NIOS2_TLS_GD16: + case R_NIOS2_TLS_IE16: + /* This symbol requires a global offset table entry. */ + { + int tls_type, old_tls_type; + + switch (r_type) + { + default: + case R_NIOS2_GOT16: + case R_NIOS2_CALL16: + tls_type = GOT_NORMAL; + break; + case R_NIOS2_TLS_GD16: + tls_type = GOT_TLS_GD; + break; + case R_NIOS2_TLS_IE16: + tls_type = GOT_TLS_IE; + break; + } + + if (dynobj == NULL) + { + /* Create the .got section. */ + elf_hash_table (info)->dynobj = dynobj = abfd; + nios2_elf32_create_dynamic_sections (dynobj, info); + } + + if (sgot == NULL) + { + sgot = htab->root.sgot; + BFD_ASSERT (sgot != NULL); + } + + if (srelgot == NULL + && (h != NULL || info->shared)) + { + srelgot = htab->root.srelgot; + BFD_ASSERT (srelgot != NULL); + } + + if (h != NULL) + { + struct elf32_nios2_link_hash_entry *eh + = (struct elf32_nios2_link_hash_entry *)h; + h->got.refcount++; + old_tls_type = elf32_nios2_hash_entry(h)->tls_type; + if (r_type == R_NIOS2_CALL16) + { + /* Make sure a plt entry is created for this symbol if + it turns out to be a function defined by a dynamic + object. */ + h->plt.refcount++; + h->needs_plt = 1; + h->type = STT_FUNC; + eh->got_types_used |= CALL16_USED; + } + else + eh->got_types_used |= GOT16_USED; + } + else + { + /* This is a global offset table entry for a local symbol. */ + if (local_got_refcounts == NULL) + { + bfd_size_type size; + + size = symtab_hdr->sh_info; + size *= (sizeof (bfd_signed_vma) + sizeof (char)); + 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; + elf32_nios2_local_got_tls_type (abfd) + = (char *) (local_got_refcounts + symtab_hdr->sh_info); + } + local_got_refcounts[r_symndx]++; + old_tls_type = elf32_nios2_local_got_tls_type (abfd) [r_symndx]; + } + + /* We will already have issued an error message if there is a + TLS / non-TLS mismatch, based on the symbol type. We don't + support any linker relaxations. So just combine any TLS + types needed. */ + if (old_tls_type != GOT_UNKNOWN && old_tls_type != GOT_NORMAL + && tls_type != GOT_NORMAL) + tls_type |= old_tls_type; + + if (old_tls_type != tls_type) + { + if (h != NULL) + elf32_nios2_hash_entry (h)->tls_type = tls_type; + else + elf32_nios2_local_got_tls_type (abfd) [r_symndx] = tls_type; + } + } + /* Fall through */ + case R_NIOS2_TLS_LDM16: + if (r_type == R_NIOS2_TLS_LDM16) + htab->tls_ldm_got.refcount++; + + if (htab->root.sgot == NULL) + { + if (htab->root.dynobj == NULL) + htab->root.dynobj = abfd; + if (!create_got_section (htab->root.dynobj, info)) + return FALSE; + } + break; + + /* This relocation describes the C++ object vtable hierarchy. + Reconstruct it for later use during GC. */ + case R_NIOS2_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_NIOS2_GNU_VTENTRY: + if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend)) + return FALSE; + break; + + case R_NIOS2_BFD_RELOC_32: + case R_NIOS2_CALL26: + case R_NIOS2_HIADJ16: + case R_NIOS2_LO16: + + if (h != NULL) + { + /* If this reloc is in a read-only section, we might + need a copy reloc. We can't check reliably at this + stage whether the section is read-only, as input + sections have not yet been mapped to output sections. + Tentatively set the flag for now, and correct in + adjust_dynamic_symbol. */ + if (!info->shared) + h->non_got_ref = 1; + + /* Make sure a plt entry is created for this symbol if it + turns out to be a function defined by a dynamic object. */ + h->plt.refcount++; + + if (r_type == R_NIOS2_CALL26) + h->needs_plt = 1; + } + + /* If we are creating a shared library, we need to copy the + reloc into the shared library. */ + if (info->shared + && (sec->flags & SEC_ALLOC) != 0 + && (r_type == R_NIOS2_BFD_RELOC_32 + || (h != NULL && ! h->needs_plt + && (! info->symbolic || ! h->def_regular)))) + { + struct elf32_nios2_dyn_relocs *p; + struct elf32_nios2_dyn_relocs **head; + + /* When creating a shared object, we must copy these + reloc types into the output file. We create a reloc + section in dynobj and make room for this reloc. */ + if (sreloc == NULL) + { + sreloc = _bfd_elf_make_dynamic_reloc_section + (sec, dynobj, 2, abfd, TRUE); + if (sreloc == NULL) + return FALSE; + } + + /* If this is a global symbol, we count the number of + relocations we need for this symbol. */ + if (h != NULL) + head = &((struct elf32_nios2_link_hash_entry *) h)->dyn_relocs; + else + { + /* Track dynamic relocs needed for local syms too. + We really need local syms available to do this + easily. Oh well. */ + + asection *s; + void *vpp; + Elf_Internal_Sym *isym; + + isym = bfd_sym_from_r_symndx (&htab->sym_cache, + abfd, r_symndx); + if (isym == NULL) + return FALSE; + + s = bfd_section_from_elf_index (abfd, isym->st_shndx); + if (s == NULL) + s = sec; + + vpp = &elf_section_data (s)->local_dynrel; + head = (struct elf32_nios2_dyn_relocs **) vpp; + } + + p = *head; + if (p == NULL || p->sec != sec) + { + bfd_size_type amt = sizeof *p; + p = ((struct elf32_nios2_dyn_relocs *) + bfd_alloc (htab->root.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; + + } + break; + } + } + + return TRUE; +} + + +/* Implement elf_backend_gc_mark_hook: + Return the section that should be marked against GC for a given + relocation. */ +static asection * +nios2_elf32_gc_mark_hook (asection *sec, + struct bfd_link_info *info ATTRIBUTE_UNUSED, + 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_NIOS2_GNU_VTINHERIT: + case R_NIOS2_GNU_VTENTRY: + return NULL; + } + return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym); +} + +/* Implement elf_backend_gc_sweep_hook: + Update the got entry reference counts for the section being removed. */ +static bfd_boolean +nios2_elf32_gc_sweep_hook (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; + bfd_signed_vma *local_got_refcounts; + const Elf_Internal_Rela *rel, *relend; + bfd *dynobj; + + if (info->relocatable) + return TRUE; + + elf_section_data (sec)->local_dynrel = NULL; + + dynobj = elf_hash_table (info)->dynobj; + if (dynobj == NULL) + return TRUE; + + 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; + int r_type; + + r_symndx = ELF32_R_SYM (rel->r_info); + if (r_symndx >= symtab_hdr->sh_info) + { + 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; + } + + r_type = ELF32_R_TYPE (rel->r_info); + switch (r_type) + { + case R_NIOS2_GOT16: + case R_NIOS2_CALL16: + if (h != NULL) + { + if (h->got.refcount > 0) + --h->got.refcount; + } + else if (local_got_refcounts != NULL) + { + if (local_got_refcounts[r_symndx] > 0) + --local_got_refcounts[r_symndx]; + } + break; + + case R_NIOS2_PCREL_LO: + case R_NIOS2_PCREL_HA: + case R_NIOS2_BFD_RELOC_32: + case R_NIOS2_CALL26: + if (h != NULL) + { + struct elf32_nios2_link_hash_entry *eh; + struct elf32_nios2_dyn_relocs **pp; + struct elf32_nios2_dyn_relocs *p; + + eh = (struct elf32_nios2_link_hash_entry *) h; + + if (h->plt.refcount > 0) + --h->plt.refcount; + + if (r_type == R_NIOS2_PCREL_LO || r_type == R_NIOS2_PCREL_HA + || r_type == R_NIOS2_BFD_RELOC_32) + { + for (pp = &eh->dyn_relocs; (p = *pp) != NULL; + pp = &p->next) + if (p->sec == sec) + { + p->count -= 1; + if (p->count == 0) + *pp = p->next; + break; + } + } + } + break; + + default: + break; + } + } + + return TRUE; +} + +/* Install 16-bit immediate value VALUE at offset OFFSET into section SEC. */ +static void +nios2_elf32_install_imm16 (asection *sec, bfd_vma offset, bfd_vma value) +{ + bfd_vma word = bfd_get_32 (sec->owner, sec->contents + offset); + + BFD_ASSERT(value <= 0xffff); + + bfd_put_32 (sec->owner, word | ((value & 0xffff) << 6), + sec->contents + offset); +} + +/* Install COUNT 32-bit values DATA starting at offset OFFSET into + section SEC. */ +static void +nios2_elf32_install_data (asection *sec, const bfd_vma *data, bfd_vma offset, + int count) +{ + while (count--) + { + bfd_put_32 (sec->owner, *data, sec->contents + offset); + offset += 4; + ++data; + } +} + +/* Implement elf_backend_finish_dynamic_symbols: + Finish up dynamic symbol handling. We set the contents of various + dynamic sections here. */ +static bfd_boolean +nios2_elf32_finish_dynamic_symbol (bfd *output_bfd, + struct bfd_link_info *info, + struct elf_link_hash_entry *h, + Elf_Internal_Sym *sym) +{ + struct elf32_nios2_link_hash_table *htab; + struct elf32_nios2_link_hash_entry *eh + = (struct elf32_nios2_link_hash_entry *)h; + int use_plt; + + htab = elf32_nios2_hash_table (info); + + if (h->plt.offset != (bfd_vma) -1) + { + asection *splt; + asection *sgotplt; + asection *srela; + bfd_vma plt_index; + bfd_vma got_offset; + Elf_Internal_Rela rela; + bfd_byte *loc; + bfd_vma got_address; + + /* This symbol has an entry in the procedure linkage table. Set + it up. */ + BFD_ASSERT (h->dynindx != -1); + splt = htab->root.splt; + sgotplt = htab->root.sgotplt; + srela = htab->root.srelplt; + BFD_ASSERT (splt != NULL && sgotplt != NULL && srela != NULL); + + /* Emit the PLT entry. */ + if (info->shared) + { + nios2_elf32_install_data (splt, nios2_so_plt_entry, h->plt.offset, + 3); + plt_index = (h->plt.offset - 24) / 12; + got_offset = (plt_index + 3) * 4; + nios2_elf32_install_imm16 (splt, h->plt.offset, + hiadj(plt_index * 4)); + nios2_elf32_install_imm16 (splt, h->plt.offset + 4, + (plt_index * 4) & 0xffff); + nios2_elf32_install_imm16 (splt, h->plt.offset + 8, + 0xfff4 - h->plt.offset); + got_address = (sgotplt->output_section->vma + sgotplt->output_offset + + got_offset); + + /* Fill in the entry in the global offset table. There are no + res_n slots for a shared object PLT, instead the .got.plt entries + point to the PLT entries. */ + bfd_put_32 (output_bfd, + splt->output_section->vma + splt->output_offset + + h->plt.offset, sgotplt->contents + got_offset); + } + else + { + plt_index = (h->plt.offset - 28 - htab->res_n_size) / 12; + got_offset = (plt_index + 3) * 4; + + nios2_elf32_install_data (splt, nios2_plt_entry, h->plt.offset, 3); + got_address = (sgotplt->output_section->vma + sgotplt->output_offset + + got_offset); + nios2_elf32_install_imm16 (splt, h->plt.offset, hiadj(got_address)); + nios2_elf32_install_imm16 (splt, h->plt.offset + 4, + got_address & 0xffff); + + /* Fill in the entry in the global offset table. */ + bfd_put_32 (output_bfd, + splt->output_section->vma + splt->output_offset + + plt_index * 4, sgotplt->contents + got_offset); + } + + /* Fill in the entry in the .rela.plt section. */ + rela.r_offset = got_address; + rela.r_info = ELF32_R_INFO (h->dynindx, R_NIOS2_JUMP_SLOT); + rela.r_addend = 0; + loc = srela->contents + 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 the symbol is weak, we do need to clear the value. + Otherwise, the PLT entry would provide a definition for + the symbol even if the symbol wasn't defined anywhere, + and so the symbol would never be NULL. */ + if (!h->ref_regular_nonweak) + sym->st_value = 0; + } + } + + use_plt = (eh->got_types_used == CALL16_USED + && h->plt.offset != (bfd_vma) -1); + + if (!use_plt && h->got.offset != (bfd_vma) -1 + && (elf32_nios2_hash_entry (h)->tls_type & GOT_TLS_GD) == 0 + && (elf32_nios2_hash_entry (h)->tls_type & GOT_TLS_IE) == 0) + { + asection *sgot; + asection *srela; + Elf_Internal_Rela rela; + bfd_byte *loc; + bfd_vma offset; + + /* This symbol has an entry in the global offset table. Set it + up. */ + sgot = htab->root.sgot; + srela = htab->root.srelgot; + BFD_ASSERT (sgot != NULL && srela != NULL); + + offset = (h->got.offset & ~(bfd_vma) 1); + rela.r_offset = (sgot->output_section->vma + + sgot->output_offset + offset); + + /* 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 && SYMBOL_REFERENCES_LOCAL (info, h)) + { + rela.r_info = ELF32_R_INFO (0, R_NIOS2_RELATIVE); + rela.r_addend = bfd_get_signed_32 (output_bfd, + (sgot->contents + offset)); + bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + offset); + } + else + { + bfd_put_32 (output_bfd, (bfd_vma) 0, + sgot->contents + offset); + rela.r_info = ELF32_R_INFO (h->dynindx, R_NIOS2_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); + } + + if (use_plt && h->got.offset != (bfd_vma) -1) + { + bfd_vma offset = (h->got.offset & ~(bfd_vma) 1); + asection *sgot = htab->root.sgot; + asection *splt = htab->root.splt; + bfd_put_32 (output_bfd, (splt->output_section->vma + splt->output_offset + + h->plt.offset), + sgot->contents + offset); + } + + if (h->needs_copy) + { + asection *s; + Elf_Internal_Rela rela; + bfd_byte *loc; + + /* This symbol 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 = htab->srelbss; + 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_NIOS2_COPY); + rela.r_addend = 0; + loc = s->contents + s->reloc_count++ * sizeof (Elf32_External_Rela); + bfd_elf32_swap_reloca_out (output_bfd, &rela, loc); + } + + /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. */ + if (strcmp (h->root.root.string, "_DYNAMIC") == 0 + || h == elf_hash_table (info)->hgot) + sym->st_shndx = SHN_ABS; + + return TRUE; +} + +/* Implement elf_backend_finish_dynamic_sections. */ +static bfd_boolean +nios2_elf32_finish_dynamic_sections (bfd *output_bfd, + struct bfd_link_info *info) +{ + bfd *dynobj; + asection *sgotplt; + asection *sdyn; + struct elf32_nios2_link_hash_table *htab; + + htab = elf32_nios2_hash_table (info); + dynobj = elf_hash_table (info)->dynobj; + sgotplt = htab->root.sgotplt; + BFD_ASSERT (sgotplt != NULL); + sdyn = bfd_get_linker_section (dynobj, ".dynamic"); + + if (elf_hash_table (info)->dynamic_sections_created) + { + asection *splt; + Elf32_External_Dyn *dyncon, *dynconend; + + splt = htab->root.splt; + BFD_ASSERT (splt != 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: + s = htab->root.sgot; + BFD_ASSERT (s != NULL); + dyn.d_un.d_ptr = s->output_section->vma; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_JMPREL: + s = htab->root.srelplt; + BFD_ASSERT (s != NULL); + dyn.d_un.d_ptr = s->output_section->vma; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_PLTRELSZ: + s = htab->root.srelplt; + BFD_ASSERT (s != NULL); + dyn.d_un.d_val = s->size; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_RELASZ: + /* The procedure linkage table relocs (DT_JMPREL) should + not be included in the overall relocs (DT_RELA). + 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. */ + s = htab->root.srelplt; + if (s != NULL) + dyn.d_un.d_val -= s->size; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_NIOS2_GP: + s = htab->root.sgot; + BFD_ASSERT (s != NULL); + dyn.d_un.d_ptr = s->output_section->vma + 0x7ff0; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + } + } + + /* Fill in the first entry in the procedure linkage table. */ + if (splt->size > 0) + { + bfd_vma got_address = (sgotplt->output_section->vma + + sgotplt->output_offset); + if (info->shared) + { + bfd_vma corrected = got_address - (splt->output_section->vma + + splt->output_offset + 4); + nios2_elf32_install_data (splt, nios2_so_plt0_entry, 0, 6); + nios2_elf32_install_imm16 (splt, 4, hiadj (corrected)); + nios2_elf32_install_imm16 (splt, 12, (corrected & 0xffff) + 4); + nios2_elf32_install_imm16 (splt, 16, (corrected & 0xffff) + 8); + + elf_section_data (splt->output_section)->this_hdr.sh_entsize + = 24; + } + else + { + /* Divide by 4 here, not 3 because we already corrected for the + res_N branches. */ + bfd_vma res_size = (splt->size - 28) / 4; + bfd_vma res_start = (splt->output_section->vma + + splt->output_offset); + bfd_vma res_offset; + + for (res_offset = 0; res_offset < res_size; res_offset += 4) + bfd_put_32 (output_bfd, + 6 | ((res_size - (res_offset + 4)) << 6), + splt->contents + res_offset); + + nios2_elf32_install_data (splt, nios2_plt0_entry, res_size, 7); + nios2_elf32_install_imm16 (splt, res_size, hiadj (res_start)); + nios2_elf32_install_imm16 (splt, res_size + 4, + res_start & 0xffff); + nios2_elf32_install_imm16 (splt, res_size + 12, + hiadj (got_address)); + nios2_elf32_install_imm16 (splt, res_size + 16, + (got_address & 0xffff) + 4); + nios2_elf32_install_imm16 (splt, res_size + 20, + (got_address & 0xffff) + 8); + + elf_section_data (splt->output_section)->this_hdr.sh_entsize + = 28 + res_size; + } + } + } + /* Fill in the first three entries in the global offset table. */ + if (sgotplt->size > 0) + { + if (sdyn == NULL) + bfd_put_32 (output_bfd, (bfd_vma) 0, sgotplt->contents); + else + bfd_put_32 (output_bfd, + sdyn->output_section->vma + sdyn->output_offset, + sgotplt->contents); + bfd_put_32 (output_bfd, (bfd_vma) 0, sgotplt->contents + 4); + bfd_put_32 (output_bfd, (bfd_vma) 0, sgotplt->contents + 8); + } + + elf_section_data (sgotplt->output_section)->this_hdr.sh_entsize = 4; + + return TRUE; +} + +/* Implement elf_backend_adjust_dynamic_symbol: + 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 +nios2_elf32_adjust_dynamic_symbol (struct bfd_link_info *info, + struct elf_link_hash_entry *h) +{ + struct elf32_nios2_link_hash_table *htab; + bfd *dynobj; + asection *s; + unsigned align2; + + htab = elf32_nios2_hash_table (info); + 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 (h->plt.refcount <= 0 + || SYMBOL_CALLS_LOCAL (info, h) + || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT + && h->root.type == bfd_link_hash_undefweak)) + { + /* This case can occur if we saw a PLT reloc in an input + file, but the symbol was never referred to by a dynamic + object, or if all references were garbage collected. In + such a case, we don't actually need to build a procedure + linkage table, and we can just do a PCREL reloc instead. */ + h->plt.offset = (bfd_vma) -1; + h->needs_plt = 0; + } + + return TRUE; + } + + /* Reinitialize the plt offset now that it is not used as a reference + count any more. */ + 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; + } + + /* If there are no non-GOT references, we do not need a copy + relocation. */ + if (!h->non_got_ref) + 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 (h->size == 0) + { + (*_bfd_error_handler) (_("dynamic variable `%s' is zero size"), + h->root.root.string); + 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. */ + s = htab->sdynbss; + BFD_ASSERT (s != NULL); + + /* We must generate a R_NIOS2_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; + } + + align2 = bfd_log2 (h->size); + if (align2 > h->root.u.def.section->alignment_power) + align2 = h->root.u.def.section->alignment_power; + + /* Align dynbss. */ + s->size = BFD_ALIGN (s->size, (bfd_size_type)1 << align2); + if (align2 > bfd_get_section_alignment (dynobj, s) + && !bfd_set_section_alignment (dynobj, s, align2)) + 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; +} + +/* Worker function for nios2_elf32_size_dynamic_sections. */ +static bfd_boolean +adjust_dynrelocs (struct elf_link_hash_entry *h, PTR inf) +{ + struct bfd_link_info *info; + struct elf32_nios2_link_hash_table *htab; + + 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 = elf32_nios2_hash_table (info); + + if (h->plt.offset != (bfd_vma)-1) + h->plt.offset += htab->res_n_size; + if (htab->root.splt == h->root.u.def.section) + h->root.u.def.value += htab->res_n_size; + + return TRUE; +} + +/* Another worker function for nios2_elf32_size_dynamic_sections. + Allocate space in .plt, .got and associated reloc sections for + dynamic relocs. */ +static bfd_boolean +allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf) +{ + struct bfd_link_info *info; + struct elf32_nios2_link_hash_table *htab; + struct elf32_nios2_link_hash_entry *eh; + struct elf32_nios2_dyn_relocs *p; + int use_plt; + + 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 = elf32_nios2_hash_table (info); + + 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 + && !bfd_elf_link_record_dynamic_symbol (info, h)) + return FALSE; + + if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, info->shared, h)) + { + asection *s = htab->root.splt; + + /* Allocate room for the header. */ + if (s->size == 0) + { + if (info->shared) + s->size = 24; + else + s->size = 28; + } + + 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 += 12; + + /* We also need to make an entry in the .rela.plt section. */ + htab->root.srelplt->size += sizeof (Elf32_External_Rela); + + /* And the .got.plt section. */ + htab->root.sgotplt->size += 4; + } + else + { + h->plt.offset = (bfd_vma) -1; + h->needs_plt = 0; + } + } + else + { + h->plt.offset = (bfd_vma) -1; + h->needs_plt = 0; + } + + eh = (struct elf32_nios2_link_hash_entry *) h; + use_plt = (eh->got_types_used == CALL16_USED + && h->plt.offset != (bfd_vma) -1); + + if (h->got.refcount > 0) + { + asection *s; + bfd_boolean dyn; + int tls_type = eh->tls_type; + int indx; + + /* 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 + && !bfd_elf_link_record_dynamic_symbol (info, h)) + return FALSE; + + s = htab->root.sgot; + h->got.offset = s->size; + + if (tls_type == GOT_UNKNOWN) + abort (); + + if (tls_type == GOT_NORMAL) + /* Non-TLS symbols need one GOT slot. */ + s->size += 4; + else + { + if (tls_type & GOT_TLS_GD) + /* R_NIOS2_TLS_GD16 needs 2 consecutive GOT slots. */ + s->size += 8; + if (tls_type & GOT_TLS_IE) + /* R_NIOS2_TLS_IE16 needs one GOT slot. */ + s->size += 4; + } + + dyn = htab->root.dynamic_sections_created; + + indx = 0; + if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) + && (!info->shared + || !SYMBOL_REFERENCES_LOCAL (info, h))) + indx = h->dynindx; + + if (tls_type != GOT_NORMAL + && (info->shared || indx != 0) + && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT + || h->root.type != bfd_link_hash_undefweak)) + { + if (tls_type & GOT_TLS_IE) + htab->root.srelgot->size += sizeof (Elf32_External_Rela); + + if (tls_type & GOT_TLS_GD) + htab->root.srelgot->size += sizeof (Elf32_External_Rela); + + if ((tls_type & GOT_TLS_GD) && indx != 0) + htab->root.srelgot->size += sizeof (Elf32_External_Rela); + } + else if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT + || h->root.type != bfd_link_hash_undefweak) + && !use_plt + && (info->shared + || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h))) + htab->root.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 elf32_nios2_dyn_relocs **pp; + + for (pp = &eh->dyn_relocs; (p = *pp) != NULL; ) + { + p->count -= p->pc_count; + p->pc_count = 0; + if (p->count == 0) + *pp = p->next; + else + pp = &p->next; + } + } + + /* Also discard relocs on undefined weak syms with non-default + visibility. */ + if (eh->dyn_relocs != NULL + && h->root.type == bfd_link_hash_undefweak) + { + if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT) + eh->dyn_relocs = NULL; + + /* Make sure undefined weak symbols are output as a dynamic + symbol in PIEs. */ + else if (h->dynindx == -1 + && !h->forced_local + && !bfd_elf_link_record_dynamic_symbol (info, h)) + return FALSE; + } + } + else + { + /* For the non-shared case, discard space for relocs against + symbols which turn out to need copy relocs or are not + dynamic. */ + + if (!h->non_got_ref + && ((h->def_dynamic && !h->def_regular) + || (htab->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 + && !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; +} + +/* Implement elf_backend_size_dynamic_sections: + Set the sizes of the dynamic sections. */ +static bfd_boolean +nios2_elf32_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, + struct bfd_link_info *info) +{ + bfd *dynobj; + asection *s; + bfd_boolean plt; + bfd_boolean got; + bfd_boolean relocs; + bfd *ibfd; + struct elf32_nios2_link_hash_table *htab; + + htab = elf32_nios2_hash_table (info); + dynobj = elf_hash_table (info)->dynobj; + BFD_ASSERT (dynobj != NULL); + + htab->res_n_size = 0; + if (elf_hash_table (info)->dynamic_sections_created) + { + /* Set the contents of the .interp section to the interpreter. */ + if (info->executable) + { + s = bfd_get_linker_section (dynobj, ".interp"); + BFD_ASSERT (s != NULL); + s->size = sizeof ELF_DYNAMIC_INTERPRETER; + s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER; + } + } + else + { + /* We may have created entries in the .rela.got section. + However, if we are not creating the dynamic sections, we will + not actually use these entries. Reset the size of .rela.got, + which will cause it to get stripped from the output file + below. */ + s = htab->root.srelgot; + if (s != NULL) + s->size = 0; + } + + /* Set up .got offsets for local syms, and space for local dynamic + relocs. */ + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) + { + bfd_signed_vma *local_got; + bfd_signed_vma *end_local_got; + char *local_tls_type; + bfd_size_type locsymcount; + Elf_Internal_Shdr *symtab_hdr; + asection *srel; + + if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour) + continue; + + for (s = ibfd->sections; s != NULL; s = s->next) + { + struct elf32_nios2_dyn_relocs *p; + + for (p = elf_section_data (s)->local_dynrel; p != NULL; p = p->next) + { + if (!bfd_is_abs_section (p->sec) + && bfd_is_abs_section (p->sec->output_section)) + { + /* Input section has been discarded, either because + it is a copy of a linkonce section or due to + linker script /DISCARD/, so we'll be discarding + the relocs too. */ + } + else if (p->count != 0) + { + srel = elf_section_data (p->sec)->sreloc; + srel->size += p->count * sizeof (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; + local_tls_type = elf32_nios2_local_got_tls_type (ibfd); + s = htab->root.sgot; + srel = htab->root.srelgot; + for (; local_got < end_local_got; ++local_got, ++local_tls_type) + { + if (*local_got > 0) + { + *local_got = s->size; + if (*local_tls_type & GOT_TLS_GD) + /* TLS_GD relocs need an 8-byte structure in the GOT. */ + s->size += 8; + if (*local_tls_type & GOT_TLS_IE) + s->size += 4; + if (*local_tls_type == GOT_NORMAL) + s->size += 4; + + if (info->shared || *local_tls_type == GOT_TLS_GD) + srel->size += sizeof (Elf32_External_Rela); + } + else + *local_got = (bfd_vma) -1; + } + } + + if (htab->tls_ldm_got.refcount > 0) + { + /* Allocate two GOT entries and one dynamic relocation (if necessary) + for R_NIOS2_TLS_LDM16 relocations. */ + htab->tls_ldm_got.offset = htab->root.sgot->size; + htab->root.sgot->size += 8; + if (info->shared) + htab->root.srelgot->size += sizeof (Elf32_External_Rela); + } + else + htab->tls_ldm_got.offset = -1; + + /* Allocate global sym .plt and .got entries, and space for global + sym dynamic relocs. */ + elf_link_hash_traverse (& htab->root, allocate_dynrelocs, info); + + /* The check_relocs and adjust_dynamic_symbol entry points have + determined the sizes of the various dynamic sections. Allocate + memory for them. */ + plt = FALSE; + got = FALSE; + relocs = FALSE; + for (s = dynobj->sections; s != NULL; s = s->next) + { + const char *name; + + if ((s->flags & SEC_LINKER_CREATED) == 0) + continue; + + /* It's OK to base decisions on the section name, because none + of the dynobj section names depend upon the input files. */ + name = bfd_get_section_name (dynobj, s); + + if (strcmp (name, ".plt") == 0) + { + /* Remember whether there is a PLT. */ + plt = s->size != 0; + + /* Correct for the number of res_N branches. */ + if (plt && !info->shared) + { + htab->res_n_size = (s->size-28) / 3; + s->size += htab->res_n_size; + } + } + else if (CONST_STRNEQ (name, ".rela")) + { + if (s->size != 0) + { + 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 if (CONST_STRNEQ (name, ".got")) + got = s->size != 0; + else if (strcmp (name, ".dynbss") != 0) + /* 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; + } + + if ((s->flags & SEC_HAS_CONTENTS) == 0) + continue; + + /* Allocate memory for the section contents. */ + /* FIXME: This should be a call to bfd_alloc not bfd_zalloc. + Unused entries should be reclaimed before the section's contents + are written out, but at the moment this does not happen. Thus in + order to prevent writing out garbage, we initialize the section's + contents to zero. */ + s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size); + if (s->contents == NULL) + return FALSE; + } + + /* Adjust dynamic symbols that point to the plt to account for the + now-known number of resN slots. */ + if (htab->res_n_size) + elf_link_hash_traverse (& htab->root, adjust_dynrelocs, info); + + if (elf_hash_table (info)->dynamic_sections_created) + { + /* Add some entries to the .dynamic section. We fill in the + values later, in elf_nios2_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 && !add_dynamic_entry (DT_DEBUG, 0)) + return FALSE; + + if (got && !add_dynamic_entry (DT_PLTGOT, 0)) + return FALSE; + + if (plt + && (!add_dynamic_entry (DT_PLTRELSZ, 0) + || !add_dynamic_entry (DT_PLTREL, DT_RELA) + || !add_dynamic_entry (DT_JMPREL, 0))) + return FALSE; + + if (relocs + && (!add_dynamic_entry (DT_RELA, 0) + || !add_dynamic_entry (DT_RELASZ, 0) + || !add_dynamic_entry (DT_RELAENT, sizeof (Elf32_External_Rela)))) + return FALSE; + + if (!info->shared && !add_dynamic_entry (DT_NIOS2_GP, 0)) + return FALSE; + + if ((info->flags & DF_TEXTREL) != 0 + && !add_dynamic_entry (DT_TEXTREL, 0)) + return FALSE; + } +#undef add_dynamic_entry + + return TRUE; +} + +/* Implement bfd_elf32_bfd_link_hash_table_create. */ +static struct bfd_link_hash_table * +nios2_elf32_link_hash_table_create (bfd *abfd) +{ + struct elf32_nios2_link_hash_table *ret; + bfd_size_type amt = sizeof (struct elf32_nios2_link_hash_table); + + ret = bfd_malloc (amt); + if (ret == NULL) + return NULL; + + if (!_bfd_elf_link_hash_table_init (&ret->root, abfd, + link_hash_newfunc, + sizeof (struct + elf32_nios2_link_hash_entry), + NIOS2_ELF_DATA)) + { + free (ret); + return NULL; + } + + ret->sdynbss = NULL; + ret->srelbss = NULL; + ret->sbss = NULL; + ret->tls_ldm_got.refcount = 0; + ret->sym_cache.abfd = NULL; + return &ret->root.root; +} + +/* Implement elf_backend_reloc_type_class. */ +static enum elf_reloc_type_class +nios2_elf32_reloc_type_class (const Elf_Internal_Rela *rela) +{ + switch ((int) ELF32_R_TYPE (rela->r_info)) + { + case R_NIOS2_RELATIVE: + return reloc_class_relative; + case R_NIOS2_JUMP_SLOT: + return reloc_class_plt; + case R_NIOS2_COPY: + return reloc_class_copy; + default: + return reloc_class_normal; + } +} + +/* Return 1 if target is one of ours. */ +static bfd_boolean +is_nios2_elf_target (const struct bfd_target *targ) +{ + return (targ == &bfd_elf32_littlenios2_vec + || targ == &bfd_elf32_bignios2_vec); +} + +/* Implement elf_backend_add_symbol_hook. + This hook is called by the linker when adding symbols from an object + file. We use it to put .comm items in .sbss, and not .bss. */ +static bfd_boolean +nios2_elf_add_symbol_hook (bfd *abfd, + struct bfd_link_info *info, + Elf_Internal_Sym *sym, + const char **namep ATTRIBUTE_UNUSED, + flagword *flagsp ATTRIBUTE_UNUSED, + asection **secp, + bfd_vma *valp) +{ + bfd *dynobj; + + if (sym->st_shndx == SHN_COMMON + && !info->relocatable + && sym->st_size <= elf_gp_size (abfd) + && is_nios2_elf_target (info->output_bfd->xvec)) + { + /* Common symbols less than or equal to -G nn bytes are automatically + put into .sbss. */ + struct elf32_nios2_link_hash_table *htab; + + htab = elf32_nios2_hash_table (info); + if (htab->sbss == NULL) + { + flagword flags = SEC_IS_COMMON | SEC_LINKER_CREATED; + + dynobj = elf_hash_table (info)->dynobj; + if (!dynobj) + dynobj = abfd; + + htab->sbss = bfd_make_section_anyway_with_flags (dynobj, ".sbss", + flags); + if (htab->sbss == NULL) + return FALSE; + } + + *secp = htab->sbss; + *valp = sym->st_size; + } + + return TRUE; +} + +/* Implement elf_backend_can_make_relative_eh_frame: + Decide whether to attempt to turn absptr or lsda encodings in + shared libraries into pcrel within the given input section. */ +static bfd_boolean +nios2_elf32_can_make_relative_eh_frame (bfd *input_bfd ATTRIBUTE_UNUSED, + struct bfd_link_info *info + ATTRIBUTE_UNUSED, + asection *eh_frame_section + ATTRIBUTE_UNUSED) +{ + /* We can't use PC-relative encodings in the .eh_frame section. */ + return FALSE; +} + +/* Implement elf_backend_special_sections. */ +const struct bfd_elf_special_section elf32_nios2_special_sections[] = +{ + { STRING_COMMA_LEN (".sbss"), -2, SHT_NOBITS, + SHF_ALLOC + SHF_WRITE + SHF_NIOS2_GPREL }, + { STRING_COMMA_LEN (".sdata"), -2, SHT_PROGBITS, + SHF_ALLOC + SHF_WRITE + SHF_NIOS2_GPREL }, + { NULL, 0, 0, 0, 0 } +}; + +#define ELF_ARCH bfd_arch_nios2 +#define ELF_TARGET_ID NIOS2_ELF_DATA +#define ELF_MACHINE_CODE EM_ALTERA_NIOS2 + +/* The Nios II MMU uses a 4K page size. */ + +#define ELF_MAXPAGESIZE 0x1000 + +#define bfd_elf32_bfd_link_hash_table_create \ + nios2_elf32_link_hash_table_create + +/* Relocation table lookup macros. */ + +#define bfd_elf32_bfd_reloc_type_lookup nios2_elf32_bfd_reloc_type_lookup +#define bfd_elf32_bfd_reloc_name_lookup nios2_elf32_bfd_reloc_name_lookup + +/* JUMP_TABLE_LINK macros. */ + +/* elf_info_to_howto (using RELA relocations). */ + +#define elf_info_to_howto nios2_elf32_info_to_howto + +/* elf backend functions. */ + +#define elf_backend_can_gc_sections 1 +#define elf_backend_can_refcount 1 +#define elf_backend_plt_readonly 1 +#define elf_backend_want_got_plt 1 +#define elf_backend_rela_normal 1 + +#define elf_backend_relocate_section nios2_elf32_relocate_section +#define elf_backend_section_flags nios2_elf32_section_flags +#define elf_backend_fake_sections nios2_elf32_fake_sections +#define elf_backend_check_relocs nios2_elf32_check_relocs + +#define elf_backend_gc_mark_hook nios2_elf32_gc_mark_hook +#define elf_backend_gc_sweep_hook nios2_elf32_gc_sweep_hook +#define elf_backend_create_dynamic_sections \ + nios2_elf32_create_dynamic_sections +#define elf_backend_finish_dynamic_symbol nios2_elf32_finish_dynamic_symbol +#define elf_backend_finish_dynamic_sections \ + nios2_elf32_finish_dynamic_sections +#define elf_backend_adjust_dynamic_symbol nios2_elf32_adjust_dynamic_symbol +#define elf_backend_reloc_type_class nios2_elf32_reloc_type_class +#define elf_backend_size_dynamic_sections nios2_elf32_size_dynamic_sections +#define elf_backend_add_symbol_hook nios2_elf_add_symbol_hook +#define elf_backend_copy_indirect_symbol nios2_elf32_copy_indirect_symbol + +#define elf_backend_grok_prstatus nios2_grok_prstatus +#define elf_backend_grok_psinfo nios2_grok_psinfo + +#undef elf_backend_can_make_relative_eh_frame +#define elf_backend_can_make_relative_eh_frame \ + nios2_elf32_can_make_relative_eh_frame + +#define elf_backend_special_sections elf32_nios2_special_sections + +#define TARGET_LITTLE_SYM bfd_elf32_littlenios2_vec +#define TARGET_LITTLE_NAME "elf32-littlenios2" +#define TARGET_BIG_SYM bfd_elf32_bignios2_vec +#define TARGET_BIG_NAME "elf32-bignios2" + +#define elf_backend_got_header_size 12 + +#include "elf32-target.h" diff --git a/bfd/libbfd.h b/bfd/libbfd.h index 20d614751dc..729fd11a815 100644 --- a/bfd/libbfd.h +++ b/bfd/libbfd.h @@ -2370,6 +2370,40 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@", "BFD_RELOC_MSP430_16_BYTE", "BFD_RELOC_MSP430_2X_PCREL", "BFD_RELOC_MSP430_RL_PCREL", + "BFD_RELOC_NIOS2_S16", + "BFD_RELOC_NIOS2_U16", + "BFD_RELOC_NIOS2_CALL26", + "BFD_RELOC_NIOS2_IMM5", + "BFD_RELOC_NIOS2_CACHE_OPX", + "BFD_RELOC_NIOS2_IMM6", + "BFD_RELOC_NIOS2_IMM8", + "BFD_RELOC_NIOS2_HI16", + "BFD_RELOC_NIOS2_LO16", + "BFD_RELOC_NIOS2_HIADJ16", + "BFD_RELOC_NIOS2_GPREL", + "BFD_RELOC_NIOS2_UJMP", + "BFD_RELOC_NIOS2_CJMP", + "BFD_RELOC_NIOS2_CALLR", + "BFD_RELOC_NIOS2_ALIGN", + "BFD_RELOC_NIOS2_GOT16", + "BFD_RELOC_NIOS2_CALL16", + "BFD_RELOC_NIOS2_GOTOFF_LO", + "BFD_RELOC_NIOS2_GOTOFF_HA", + "BFD_RELOC_NIOS2_PCREL_LO", + "BFD_RELOC_NIOS2_PCREL_HA", + "BFD_RELOC_NIOS2_TLS_GD16", + "BFD_RELOC_NIOS2_TLS_LDM16", + "BFD_RELOC_NIOS2_TLS_LDO16", + "BFD_RELOC_NIOS2_TLS_IE16", + "BFD_RELOC_NIOS2_TLS_LE16", + "BFD_RELOC_NIOS2_TLS_DTPMOD", + "BFD_RELOC_NIOS2_TLS_DTPREL", + "BFD_RELOC_NIOS2_TLS_TPREL", + "BFD_RELOC_NIOS2_COPY", + "BFD_RELOC_NIOS2_GLOB_DAT", + "BFD_RELOC_NIOS2_JUMP_SLOT", + "BFD_RELOC_NIOS2_RELATIVE", + "BFD_RELOC_NIOS2_GOTOFF", "BFD_RELOC_IQ2000_OFFSET_16", "BFD_RELOC_IQ2000_OFFSET_21", "BFD_RELOC_IQ2000_UHI16", diff --git a/bfd/reloc.c b/bfd/reloc.c index 7a14be9d588..626c818b091 100644 --- a/bfd/reloc.c +++ b/bfd/reloc.c @@ -5664,6 +5664,77 @@ ENUMX ENUMDOC msp430 specific relocation codes +ENUM + BFD_RELOC_NIOS2_S16 +ENUMX + BFD_RELOC_NIOS2_U16 +ENUMX + BFD_RELOC_NIOS2_CALL26 +ENUMX + BFD_RELOC_NIOS2_IMM5 +ENUMX + BFD_RELOC_NIOS2_CACHE_OPX +ENUMX + BFD_RELOC_NIOS2_IMM6 +ENUMX + BFD_RELOC_NIOS2_IMM8 +ENUMX + BFD_RELOC_NIOS2_HI16 +ENUMX + BFD_RELOC_NIOS2_LO16 +ENUMX + BFD_RELOC_NIOS2_HIADJ16 +ENUMX + BFD_RELOC_NIOS2_GPREL +ENUMX + BFD_RELOC_NIOS2_UJMP +ENUMX + BFD_RELOC_NIOS2_CJMP +ENUMX + BFD_RELOC_NIOS2_CALLR +ENUMX + BFD_RELOC_NIOS2_ALIGN +ENUMX + BFD_RELOC_NIOS2_GOT16 +ENUMX + BFD_RELOC_NIOS2_CALL16 +ENUMX + BFD_RELOC_NIOS2_GOTOFF_LO +ENUMX + BFD_RELOC_NIOS2_GOTOFF_HA +ENUMX + BFD_RELOC_NIOS2_PCREL_LO +ENUMX + BFD_RELOC_NIOS2_PCREL_HA +ENUMX + BFD_RELOC_NIOS2_TLS_GD16 +ENUMX + BFD_RELOC_NIOS2_TLS_LDM16 +ENUMX + BFD_RELOC_NIOS2_TLS_LDO16 +ENUMX + BFD_RELOC_NIOS2_TLS_IE16 +ENUMX + BFD_RELOC_NIOS2_TLS_LE16 +ENUMX + BFD_RELOC_NIOS2_TLS_DTPMOD +ENUMX + BFD_RELOC_NIOS2_TLS_DTPREL +ENUMX + BFD_RELOC_NIOS2_TLS_TPREL +ENUMX + BFD_RELOC_NIOS2_COPY +ENUMX + BFD_RELOC_NIOS2_GLOB_DAT +ENUMX + BFD_RELOC_NIOS2_JUMP_SLOT +ENUMX + BFD_RELOC_NIOS2_RELATIVE +ENUMX + BFD_RELOC_NIOS2_GOTOFF +ENUMDOC + Relocations used by the Altera Nios II core. + ENUM BFD_RELOC_IQ2000_OFFSET_16 ENUMX diff --git a/bfd/targets.c b/bfd/targets.c index 17553ebef5b..c9fbbc2fe31 100644 --- a/bfd/targets.c +++ b/bfd/targets.c @@ -607,6 +607,7 @@ extern const bfd_target bfd_elf32_bigarm_vxworks_vec; extern const bfd_target bfd_elf32_bigmips_vec; extern const bfd_target bfd_elf32_bigmips_vxworks_vec; extern const bfd_target bfd_elf32_bigmoxie_vec; +extern const bfd_target bfd_elf32_bignios2_vec; extern const bfd_target bfd_elf32_cr16_vec; extern const bfd_target bfd_elf32_cr16c_vec; extern const bfd_target bfd_elf32_cris_vec; @@ -646,6 +647,7 @@ extern const bfd_target bfd_elf32_littlearm_vxworks_vec; extern const bfd_target bfd_elf32_littlemips_vec; extern const bfd_target bfd_elf32_littlemips_vxworks_vec; extern const bfd_target bfd_elf32_littlemoxie_vec; +extern const bfd_target bfd_elf32_littlenios2_vec; extern const bfd_target bfd_elf32_m32c_vec; extern const bfd_target bfd_elf32_m32r_vec; extern const bfd_target bfd_elf32_m32rle_vec; @@ -984,6 +986,7 @@ static const bfd_target * const _bfd_target_vector[] = &bfd_elf32_bigmips_vec, &bfd_elf32_bigmips_vxworks_vec, &bfd_elf32_bigmoxie_vec, + &bfd_elf32_bignios2_vec, &bfd_elf32_cr16_vec, &bfd_elf32_cr16c_vec, &bfd_elf32_cris_vec, @@ -1025,6 +1028,7 @@ static const bfd_target * const _bfd_target_vector[] = &bfd_elf32_littlemips_vec, &bfd_elf32_littlemips_vxworks_vec, &bfd_elf32_littlemoxie_vec, + &bfd_elf32_littlenios2_vec, &bfd_elf32_m32c_vec, &bfd_elf32_m32r_vec, &bfd_elf32_m32rle_vec, diff --git a/binutils/ChangeLog b/binutils/ChangeLog index 2eedde76937..9a0701b9108 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,18 @@ +2013-02-06 Sandra Loosemore + Andrew Jenner + + Based on patches from Altera Corporation. + + * readelf.c: Include elf/nios2.h. + (dump_relocations): Add case for EM_ALTERA_NIOS2. + (get_nios2_dynamic_type): New. + (get_dynamic_type): Add case for EM_ALTERA_NIOS2. + (is_32bit_abs_reloc): Fix EM_ALTERA_NIOS2 case. + (is_16bit_abs_reloc): Likewise. + (is_none_reloc): Add EM_ALTERA_NIOS2 and EM_NIOS32 cases. + * NEWS: Note Altera Nios II support. + * MAINTAINERS: Add Nios II maintainers. + 2013-01-29 Xi Wang * readelf.c (process_version_sections): Fix overflow checks to diff --git a/binutils/MAINTAINERS b/binutils/MAINTAINERS index f887dcde62a..2a1ac610a52 100644 --- a/binutils/MAINTAINERS +++ b/binutils/MAINTAINERS @@ -107,6 +107,8 @@ responsibility among the other maintainers. Moxie Anthony Green MSP430 Dmitry Diky NetBSD support Matt Thomas + Nios II Sandra Loosemore + Nios II Andrew Jenner PPC Geoff Keating PPC Alan Modra PPC vector ext Aldy Hernandez diff --git a/binutils/NEWS b/binutils/NEWS index 2d36dbcc5e7..91400435f89 100644 --- a/binutils/NEWS +++ b/binutils/NEWS @@ -1,5 +1,7 @@ -*- text -*- +* Add support for Altera Nios II. + Changes in 2.23: * Add support for the VLE extension to the PowerPC architecture. diff --git a/binutils/readelf.c b/binutils/readelf.c index ae211a7d6f7..77100799875 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -153,6 +153,8 @@ #include "elf/xstormy16.h" #include "elf/xtensa.h" +#include "elf/nios2.h" + #include "getopt.h" #include "libiberty.h" #include "safe-ctype.h" @@ -1288,6 +1290,10 @@ dump_relocations (FILE * file, case EM_XGATE: rtype = elf_xgate_reloc_type (type); break; + + case EM_ALTERA_NIOS2: + rtype = elf_nios2_reloc_type (type); + break; } if (rtype == NULL) @@ -1686,6 +1692,17 @@ get_tic6x_dynamic_type (unsigned long type) } } +static const char * +get_nios2_dynamic_type (unsigned long type) +{ + switch (type) + { + case DT_NIOS2_GP: return "NIOS2_GP"; + default: + return NULL; + } +} + static const char * get_dynamic_type (unsigned long type) { @@ -1800,6 +1817,9 @@ get_dynamic_type (unsigned long type) case EM_TI_C6000: result = get_tic6x_dynamic_type (type); break; + case EM_ALTERA_NIOS2: + result = get_nios2_dynamic_type (type); + break; default: result = NULL; break; @@ -10074,6 +10094,7 @@ is_32bit_abs_reloc (unsigned int reloc_type) case EM_MT: return reloc_type == 2; /* R_MT_32. */ case EM_ALTERA_NIOS2: + return reloc_type == 12; /* R_NIOS2_BFD_RELOC_32. */ case EM_NIOS32: return reloc_type == 1; /* R_NIOS_32. */ case EM_OPENRISC: @@ -10324,6 +10345,7 @@ is_16bit_abs_reloc (unsigned int reloc_type) case EM_MSP430_OLD: return reloc_type == 5; /* R_MSP430_16_BYTE. */ case EM_ALTERA_NIOS2: + return reloc_type == 13; /* R_NIOS2_BFD_RELOC_16. */ case EM_NIOS32: return reloc_type == 9; /* R_NIOS_16. */ case EM_TI_C6000: @@ -10380,6 +10402,8 @@ is_none_reloc (unsigned int reloc_type) case EM_TILEPRO: /* R_TILEPRO_NONE. */ case EM_XC16X: case EM_C166: /* R_XC16X_NONE. */ + case EM_ALTERA_NIOS2: /* R_NIOS2_NONE. */ + case EM_NIOS32: /* R_NIOS_NONE. */ return reloc_type == 0; case EM_AARCH64: return reloc_type == 0 || reloc_type == 256; diff --git a/gas/ChangeLog b/gas/ChangeLog index d064176ebd6..01ac44d01f4 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,23 @@ +2013-02-06 Sandra Loosemore + Andrew Jenner + + Based on patches from Altera Corporation. + + * Makefile.am (TARGET_CPU_CFILES): Add config/tc-nios2.c. + (TARGET_CPU_HFILES): Add config/tc-nios2.h. + * Makefile.in: Regenerated. + * configure.tgt: Add case for nios2*-linux*. + * config/obj-elf.c: Conditionally include elf/nios2.h. + * config/tc-nios2.c: New file. + * config/tc-nios2.h: New file. + * doc/Makefile.am (CPU_DOCS): Add c-nios2.texi. + * doc/Makefile.in: Regenerated. + * doc/all.texi: Set NIOSII. + * doc/as.texinfo (Overview): Add Nios II options. + (Machine Dependencies): Include c-nios2.texi. + * doc/c-nios2.texi: New file. + * NEWS: Note Altera Nios II support. + 2013-02-06 Alan Modra PR gas/14255 diff --git a/gas/Makefile.am b/gas/Makefile.am index 7729d4a7ce9..5ed9d2a7f42 100644 --- a/gas/Makefile.am +++ b/gas/Makefile.am @@ -164,6 +164,7 @@ TARGET_CPU_CFILES = \ config/tc-moxie.c \ config/tc-msp430.c \ config/tc-mt.c \ + config/tc-nios2.c \ config/tc-ns32k.c \ config/tc-openrisc.c \ config/tc-or32.c \ @@ -234,6 +235,7 @@ TARGET_CPU_HFILES = \ config/tc-mn10300.h \ config/tc-msp430.h \ config/tc-mt.h \ + config/tc-nios2.h \ config/tc-ns32k.h \ config/tc-openrisc.h \ config/tc-or32.h \ diff --git a/gas/Makefile.in b/gas/Makefile.in index c2a646ca48f..c71e2844a18 100644 --- a/gas/Makefile.in +++ b/gas/Makefile.in @@ -433,6 +433,7 @@ TARGET_CPU_CFILES = \ config/tc-moxie.c \ config/tc-msp430.c \ config/tc-mt.c \ + config/tc-nios2.c \ config/tc-ns32k.c \ config/tc-openrisc.c \ config/tc-or32.c \ @@ -503,6 +504,7 @@ TARGET_CPU_HFILES = \ config/tc-mn10300.h \ config/tc-msp430.h \ config/tc-mt.h \ + config/tc-nios2.h \ config/tc-ns32k.h \ config/tc-openrisc.h \ config/tc-or32.h \ @@ -855,6 +857,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-moxie.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-msp430.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-mt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-nios2.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-ns32k.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-openrisc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tc-or32.Po@am__quote@ @@ -1468,6 +1471,20 @@ tc-mt.obj: config/tc-mt.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tc-mt.obj `if test -f 'config/tc-mt.c'; then $(CYGPATH_W) 'config/tc-mt.c'; else $(CYGPATH_W) '$(srcdir)/config/tc-mt.c'; fi` +tc-nios2.o: config/tc-nios2.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tc-nios2.o -MD -MP -MF $(DEPDIR)/tc-nios2.Tpo -c -o tc-nios2.o `test -f 'config/tc-nios2.c' || echo '$(srcdir)/'`config/tc-nios2.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/tc-nios2.Tpo $(DEPDIR)/tc-nios2.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/tc-nios2.c' object='tc-nios2.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tc-nios2.o `test -f 'config/tc-nios2.c' || echo '$(srcdir)/'`config/tc-nios2.c + +tc-nios2.obj: config/tc-nios2.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tc-nios2.obj -MD -MP -MF $(DEPDIR)/tc-nios2.Tpo -c -o tc-nios2.obj `if test -f 'config/tc-nios2.c'; then $(CYGPATH_W) 'config/tc-nios2.c'; else $(CYGPATH_W) '$(srcdir)/config/tc-nios2.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/tc-nios2.Tpo $(DEPDIR)/tc-nios2.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config/tc-nios2.c' object='tc-nios2.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tc-nios2.obj `if test -f 'config/tc-nios2.c'; then $(CYGPATH_W) 'config/tc-nios2.c'; else $(CYGPATH_W) '$(srcdir)/config/tc-nios2.c'; fi` + tc-ns32k.o: config/tc-ns32k.c @am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tc-ns32k.o -MD -MP -MF $(DEPDIR)/tc-ns32k.Tpo -c -o tc-ns32k.o `test -f 'config/tc-ns32k.c' || echo '$(srcdir)/'`config/tc-ns32k.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/tc-ns32k.Tpo $(DEPDIR)/tc-ns32k.Po diff --git a/gas/NEWS b/gas/NEWS index dbe88de66dd..f453b4942f9 100644 --- a/gas/NEWS +++ b/gas/NEWS @@ -1,5 +1,7 @@ -*- text -*- +* Add support for Altera Nios II. + * Add support for the Imagination Technologies Meta processor. * Add support for the v850e3v5. diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c index 64503283f52..33772619194 100644 --- a/gas/config/obj-elf.c +++ b/gas/config/obj-elf.c @@ -62,6 +62,10 @@ #include "elf/mep.h" #endif +#ifdef TC_NIOS2 +#include "elf/nios2.h" +#endif + static void obj_elf_line (int); static void obj_elf_size (int); static void obj_elf_type (int); diff --git a/gas/config/tc-nios2.c b/gas/config/tc-nios2.c new file mode 100644 index 00000000000..841c45404d4 --- /dev/null +++ b/gas/config/tc-nios2.c @@ -0,0 +1,3067 @@ +/* Altera Nios II assembler. + Copyright (C) 2012, 2013 Free Software Foundation, Inc. + Contributed by Nigel Gray (ngray@altera.com). + Contributed by Mentor Graphics, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GAS; see the file COPYING. If not, write to the Free + Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "as.h" +#include "opcode/nios2.h" +#include "elf/nios2.h" +#include "tc-nios2.h" +#include "bfd.h" +#include "dwarf2dbg.h" +#include "subsegs.h" +#include "safe-ctype.h" +#include "dw2gencfi.h" + +#ifndef OBJ_ELF +/* We are not supporting any other target so we throw a compile time error. */ +OBJ_ELF not defined +#endif + +/* We can choose our endianness at run-time, regardless of configuration. */ +extern int target_big_endian; + +/* This array holds the chars that always start a comment. If the + pre-processor is disabled, these aren't very useful. */ +const char comment_chars[] = "#"; + +/* This array holds the chars that only start a comment at the beginning of + a line. If the line seems to have the form '# 123 filename' + .line and .file directives will appear in the pre-processed output. */ +/* Note that input_file.c hand checks for '#' at the beginning of the + first line of the input file. This is because the compiler outputs + #NO_APP at the beginning of its output. */ +/* Also note that C style comments are always supported. */ +const char line_comment_chars[] = "#"; + +/* This array holds machine specific line separator characters. */ +const char line_separator_chars[] = ";"; + +/* Chars that can be used to separate mant from exp in floating point nums. */ +const char EXP_CHARS[] = "eE"; + +/* Chars that mean this number is a floating point constant. */ +/* As in 0f12.456 */ +/* or 0d1.2345e12 */ +const char FLT_CHARS[] = "rRsSfFdDxXpP"; + +/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be + changed in read.c. Ideally it shouldn't have to know about it at all, + but nothing is ideal around here. */ + +/* Machine-dependent command-line options. */ + +const char *md_shortopts = "r"; + +struct option md_longopts[] = { +#define OPTION_RELAX_ALL (OPTION_MD_BASE + 0) + {"relax-all", no_argument, NULL, OPTION_RELAX_ALL}, +#define OPTION_NORELAX (OPTION_MD_BASE + 1) + {"no-relax", no_argument, NULL, OPTION_NORELAX}, +#define OPTION_RELAX_SECTION (OPTION_MD_BASE + 2) + {"relax-section", no_argument, NULL, OPTION_RELAX_SECTION}, +#define OPTION_EB (OPTION_MD_BASE + 3) + {"EB", no_argument, NULL, OPTION_EB}, +#define OPTION_EL (OPTION_MD_BASE + 4) + {"EL", no_argument, NULL, OPTION_EL} +}; + +size_t md_longopts_size = sizeof (md_longopts); + +/* The assembler supports three different relaxation modes, controlled by + command-line options. */ +typedef enum +{ + relax_section = 0, + relax_none, + relax_all +} relax_optionT; + +/* Struct contains all assembler options set with .set. */ +struct +{ + /* .set noat -> noat = 1 allows assembly code to use at without warning + and macro expansions generate a warning. + .set at -> noat = 0, assembly code using at warn but macro expansions + do not generate warnings. */ + bfd_boolean noat; + + /* .set nobreak -> nobreak = 1 allows assembly code to use ba,bt without + warning. + .set break -> nobreak = 0, assembly code using ba,bt warns. */ + bfd_boolean nobreak; + + /* .cmd line option -relax-all allows all branches and calls to be replaced + with longer versions. + -no-relax inhibits branch/call conversion. + The default value is relax_section, which relaxes branches within + a section. */ + relax_optionT relax; + +} nios2_as_options = {FALSE, FALSE, relax_section}; + + +typedef struct nios2_insn_reloc +{ + /* Any expression in the instruction is parsed into this field, + which is passed to fix_new_exp() to generate a fixup. */ + expressionS reloc_expression; + + /* The type of the relocation to be applied. */ + bfd_reloc_code_real_type reloc_type; + + /* PC-relative. */ + unsigned int reloc_pcrel; + + /* The next relocation to be applied to the instruction. */ + struct nios2_insn_reloc *reloc_next; +} nios2_insn_relocS; + +/* This struct is used to hold state when assembling instructions. */ +typedef struct nios2_insn_info +{ + /* Assembled instruction. */ + unsigned long insn_code; + /* Pointer to the relevant bit of the opcode table. */ + const struct nios2_opcode *insn_nios2_opcode; + /* After parsing ptrs to the tokens in the instruction fill this array + it is terminated with a null pointer (hence the first +1). + The second +1 is because in some parts of the code the opcode + is not counted as a token, but still placed in this array. */ + const char *insn_tokens[NIOS2_MAX_INSN_TOKENS + 1 + 1]; + + /* This holds information used to generate fixups + and eventually relocations if it is not null. */ + nios2_insn_relocS *insn_reloc; +} nios2_insn_infoS; + +/* This struct associates an argument assemble function with + an argument syntax string. Used by the assembler to find out + how to parse and assemble a set of instruction operands and + return the instruction field values. */ +typedef struct nios2_arg_info +{ + const char *args; + void (*assemble_args_func) (nios2_insn_infoS *insn_info); +} nios2_arg_infoS; + +/* This struct is used to convert Nios II pseudo-ops into the + corresponding real op. */ +typedef struct nios2_ps_insn_info +{ + /* Map this pseudo_op... */ + const char *pseudo_insn; + + /* ...to this real instruction. */ + const char *insn; + + /* Call this function to modify the operands.... */ + void (*arg_modifer_func) (char ** parsed_args, const char *arg, int num, + int start); + + /* ...with these arguments. */ + const char *arg_modifier; + int num; + int index; + + /* If arg_modifier_func allocates new memory, provide this function + to free it afterwards. */ + void (*arg_cleanup_func) (char **parsed_args, int num, int start); +} nios2_ps_insn_infoS; + +/* Opcode hash table. */ +static struct hash_control *nios2_opcode_hash = NULL; +#define nios2_opcode_lookup(NAME) \ + ((struct nios2_opcode *) hash_find (nios2_opcode_hash, (NAME))) + +/* Register hash table. */ +static struct hash_control *nios2_reg_hash = NULL; +#define nios2_reg_lookup(NAME) \ + ((struct nios2_reg *) hash_find (nios2_reg_hash, (NAME))) + +/* Parse args hash table. */ +static struct hash_control *nios2_arg_hash = NULL; +#define nios2_arg_lookup(NAME) \ + ((nios2_arg_infoS *) hash_find (nios2_arg_hash, (NAME))) + +/* Pseudo-op hash table. */ +static struct hash_control *nios2_ps_hash = NULL; +#define nios2_ps_lookup(NAME) \ + ((nios2_ps_insn_infoS *) hash_find (nios2_ps_hash, (NAME))) + +/* The known current alignment of the current section. */ +static int nios2_current_align; +static segT nios2_current_align_seg; + +static int nios2_auto_align_on = 1; + +/* The last seen label in the current section. This is used to auto-align + labels preceeding instructions. */ +static symbolS *nios2_last_label; + +#ifdef OBJ_ELF +/* Pre-defined "_GLOBAL_OFFSET_TABLE_" */ +symbolS *GOT_symbol; +#endif + + +/** Utility routines. */ +/* Function md_chars_to_number takes the sequence of + bytes in buf and returns the corresponding value + in an int. n must be 1, 2 or 4. */ +static valueT +md_chars_to_number (char *buf, int n) +{ + int i; + valueT val; + + gas_assert (n == 1 || n == 2 || n == 4); + + val = 0; + if (target_big_endian) + for (i = 0; i < n; ++i) + val = val | ((buf[i] & 0xff) << 8 * (n - (i + 1))); + else + for (i = 0; i < n; ++i) + val = val | ((buf[i] & 0xff) << 8 * i); + return val; +} + + +/* This function turns a C long int, short int or char + into the series of bytes that represent the number + on the target machine. */ +void +md_number_to_chars (char *buf, valueT val, int n) +{ + gas_assert (n == 1 || n == 2 || n == 4 || n == 8); + if (target_big_endian) + number_to_chars_bigendian (buf, val, n); + else + number_to_chars_littleendian (buf, val, n); +} + +/* Turn a string in input_line_pointer into a floating point constant + of type TYPE, and store the appropriate bytes in *LITP. The number + of LITTLENUMS emitted is stored in *SIZEP. An error message is + returned, or NULL on OK. */ +char * +md_atof (int type, char *litP, int *sizeP) +{ + int prec; + LITTLENUM_TYPE words[4]; + char *t; + int i; + + switch (type) + { + case 'f': + prec = 2; + break; + case 'd': + prec = 4; + break; + default: + *sizeP = 0; + return _("bad call to md_atof"); + } + + t = atof_ieee (input_line_pointer, type, words); + if (t) + input_line_pointer = t; + + *sizeP = prec * 2; + + if (! target_big_endian) + for (i = prec - 1; i >= 0; i--, litP += 2) + md_number_to_chars (litP, (valueT) words[i], 2); + else + for (i = 0; i < prec; i++, litP += 2) + md_number_to_chars (litP, (valueT) words[i], 2); + + return NULL; +} + +/* Return true if STR starts with PREFIX, which should be a string literal. */ +#define strprefix(STR, PREFIX) \ + (strncmp ((STR), PREFIX, strlen (PREFIX)) == 0) + +/* Return true if STR is prefixed with a control register name. */ +static int +nios2_control_register_arg_p (const char *str) +{ + return (strprefix (str, "ctl") + || strprefix (str, "cpuid") + || strprefix (str, "status") + || strprefix (str, "estatus") + || strprefix (str, "bstatus") + || strprefix (str, "ienable") + || strprefix (str, "ipending") + || strprefix (str, "exception") + || strprefix (str, "pteaddr") + || strprefix (str, "tlbacc") + || strprefix (str, "tlbmisc") + || strprefix (str, "fstatus") + || strprefix (str, "config") + || strprefix (str, "mpubase") + || strprefix (str, "mpuacc") + || strprefix (str, "badaddr")); +} + +/* Return true if STR is prefixed with a special relocation operator. */ +static int +nios2_special_relocation_p (const char *str) +{ + return (strprefix (str, "%lo") + || strprefix (str, "%hi") + || strprefix (str, "%hiadj") + || strprefix (str, "%gprel") + || strprefix (str, "%got") + || strprefix (str, "%call") + || strprefix (str, "%gotoff_lo") + || strprefix (str, "%gotoff_hiadj") + || strprefix (str, "%tls_gd") + || strprefix (str, "%tls_ldm") + || strprefix (str, "%tls_ldo") + || strprefix (str, "%tls_ie") + || strprefix (str, "%tls_le") + || strprefix (str, "%gotoff")); +} + +/* Checks whether the register name is a coprocessor + register - returns TRUE if it is, FALSE otherwise. */ +static bfd_boolean +nios2_coproc_reg (const char *reg_name) +{ + gas_assert (reg_name != NULL); + + /* Check that we do have a valid register name and that it is a + coprocessor register. + It must begin with c, not be a control register, and be a valid + register name. */ + if (strprefix (reg_name, "c") + && !strprefix (reg_name, "ctl") + && hash_find (nios2_reg_hash, reg_name) != NULL) + return TRUE; + else + return FALSE; +} + +/* nop fill pattern for text section. */ +static char const nop[4] = { 0x3a, 0x88, 0x01, 0x00 }; + +/* Handles all machine-dependent alignment needs. */ +static void +nios2_align (int log_size, const char *pfill, symbolS *label) +{ + int align; + long max_alignment = 15; + + /* The front end is prone to changing segments out from under us + temporarily when -g is in effect. */ + int switched_seg_p = (nios2_current_align_seg != now_seg); + + align = log_size; + if (align > max_alignment) + { + align = max_alignment; + as_bad (_("Alignment too large: %d. assumed"), align); + } + else if (align < 0) + { + as_warn (_("Alignment negative: 0 assumed")); + align = 0; + } + + if (align != 0) + { + if (subseg_text_p (now_seg) && align >= 2) + { + /* First, make sure we're on a four-byte boundary, in case + someone has been putting .byte values the text section. */ + if (nios2_current_align < 2 || switched_seg_p) + frag_align (2, 0, 0); + + /* Now fill in the alignment pattern. */ + if (pfill != NULL) + frag_align_pattern (align, pfill, sizeof nop, 0); + else + frag_align (align, 0, 0); + } + else + frag_align (align, 0, 0); + + if (!switched_seg_p) + nios2_current_align = align; + + /* If the last label was in a different section we can't align it. */ + if (label != NULL && !switched_seg_p) + { + symbolS *sym; + int label_seen = FALSE; + struct frag *old_frag; + valueT old_value; + valueT new_value; + + gas_assert (S_GET_SEGMENT (label) == now_seg); + + old_frag = symbol_get_frag (label); + old_value = S_GET_VALUE (label); + new_value = (valueT) frag_now_fix (); + + /* It is possible to have more than one label at a particular + address, especially if debugging is enabled, so we must + take care to adjust all the labels at this address in this + fragment. To save time we search from the end of the symbol + list, backwards, since the symbols we are interested in are + almost certainly the ones that were most recently added. + Also to save time we stop searching once we have seen at least + one matching label, and we encounter a label that is no longer + in the target fragment. Note, this search is guaranteed to + find at least one match when sym == label, so no special case + code is necessary. */ + for (sym = symbol_lastP; sym != NULL; sym = symbol_previous (sym)) + if (symbol_get_frag (sym) == old_frag + && S_GET_VALUE (sym) == old_value) + { + label_seen = TRUE; + symbol_set_frag (sym, frag_now); + S_SET_VALUE (sym, new_value); + } + else if (label_seen && symbol_get_frag (sym) != old_frag) + break; + } + record_alignment (now_seg, align); + } +} + + +/** Support for self-check mode. */ + +/* Mode of the assembler. */ +typedef enum +{ + NIOS2_MODE_ASSEMBLE, /* Ordinary operation. */ + NIOS2_MODE_TEST /* Hidden mode used for self testing. */ +} NIOS2_MODE; + +static NIOS2_MODE nios2_mode = NIOS2_MODE_ASSEMBLE; + +/* This function is used to in self-checking mode + to check the assembled instruction + opcode should be the assembled opcode, and exp_opcode + the parsed string representing the expected opcode. */ +static void +nios2_check_assembly (unsigned int opcode, const char *exp_opcode) +{ + if (nios2_mode == NIOS2_MODE_TEST) + { + if (exp_opcode == NULL) + as_bad (_("expecting opcode string in self test mode")); + else if (opcode != strtoul (exp_opcode, NULL, 16)) + as_bad (_("assembly 0x%08x, expected %s"), opcode, exp_opcode); + } +} + + +/** Support for machine-dependent assembler directives. */ +/* Handle the .align pseudo-op. This aligns to a power of two. It + also adjusts any current instruction label. We treat this the same + way the MIPS port does: .align 0 turns off auto alignment. */ +static void +s_nios2_align (int ignore ATTRIBUTE_UNUSED) +{ + int align; + char fill; + const char *pfill = NULL; + long max_alignment = 15; + + align = get_absolute_expression (); + if (align > max_alignment) + { + align = max_alignment; + as_bad (_("Alignment too large: %d. assumed"), align); + } + else if (align < 0) + { + as_warn (_("Alignment negative: 0 assumed")); + align = 0; + } + + if (*input_line_pointer == ',') + { + input_line_pointer++; + fill = get_absolute_expression (); + pfill = (const char *) &fill; + } + else if (subseg_text_p (now_seg)) + pfill = (const char *) &nop; + else + { + pfill = NULL; + nios2_last_label = NULL; + } + + if (align != 0) + { + nios2_auto_align_on = 1; + nios2_align (align, pfill, nios2_last_label); + nios2_last_label = NULL; + } + else + nios2_auto_align_on = 0; + + demand_empty_rest_of_line (); +} + +/* Handle the .text pseudo-op. This is like the usual one, but it + clears the saved last label and resets known alignment. */ +static void +s_nios2_text (int i) +{ + s_text (i); + nios2_last_label = NULL; + nios2_current_align = 0; + nios2_current_align_seg = now_seg; +} + +/* Handle the .data pseudo-op. This is like the usual one, but it + clears the saved last label and resets known alignment. */ +static void +s_nios2_data (int i) +{ + s_data (i); + nios2_last_label = NULL; + nios2_current_align = 0; + nios2_current_align_seg = now_seg; +} + +/* Handle the .section pseudo-op. This is like the usual one, but it + clears the saved last label and resets known alignment. */ +static void +s_nios2_section (int ignore) +{ + obj_elf_section (ignore); + nios2_last_label = NULL; + nios2_current_align = 0; + nios2_current_align_seg = now_seg; +} + +/* Explicitly unaligned cons. */ +static void +s_nios2_ucons (int nbytes) +{ + int hold; + hold = nios2_auto_align_on; + nios2_auto_align_on = 0; + cons (nbytes); + nios2_auto_align_on = hold; +} + +/* Handle the .sdata directive. */ +static void +s_nios2_sdata (int ignore ATTRIBUTE_UNUSED) +{ + get_absolute_expression (); /* Ignored. */ + subseg_new (".sdata", 0); + demand_empty_rest_of_line (); +} + +/* .set sets assembler options eg noat/at and is also used + to set symbol values (.equ, .equiv ). */ +static void +s_nios2_set (int equiv) +{ + char *directive = input_line_pointer; + char delim = get_symbol_end (); + char *endline = input_line_pointer; + *endline = delim; + + /* We only want to handle ".set XXX" if the + user has tried ".set XXX, YYY" they are not + trying a directive. This prevents + us from polluting the name space. */ + SKIP_WHITESPACE (); + if (is_end_of_line[(unsigned char) *input_line_pointer]) + { + bfd_boolean done = TRUE; + *endline = 0; + + if (!strcmp (directive, "noat")) + nios2_as_options.noat = TRUE; + else if (!strcmp (directive, "at")) + nios2_as_options.noat = FALSE; + else if (!strcmp (directive, "nobreak")) + nios2_as_options.nobreak = TRUE; + else if (!strcmp (directive, "break")) + nios2_as_options.nobreak = FALSE; + else if (!strcmp (directive, "norelax")) + nios2_as_options.relax = relax_none; + else if (!strcmp (directive, "relaxsection")) + nios2_as_options.relax = relax_section; + else if (!strcmp (directive, "relaxall")) + nios2_as_options.relax = relax_all; + else + done = FALSE; + + if (done) + { + *endline = delim; + demand_empty_rest_of_line (); + return; + } + } + + /* If we fall through to here, either we have ".set XXX, YYY" + or we have ".set XXX" where XXX is unknown or we have + a syntax error. */ + input_line_pointer = directive; + *endline = delim; + s_set (equiv); +} + +/* Machine-dependent assembler directives. + Format of each entry is: + { "directive", handler_func, param } */ +const pseudo_typeS md_pseudo_table[] = { + {"align", s_nios2_align, 0}, + {"text", s_nios2_text, 0}, + {"data", s_nios2_data, 0}, + {"section", s_nios2_section, 0}, + {"section.s", s_nios2_section, 0}, + {"sect", s_nios2_section, 0}, + {"sect.s", s_nios2_section, 0}, + /* .dword and .half are included for compatibility with MIPS. */ + {"dword", cons, 8}, + {"half", cons, 2}, + /* NIOS2 native word size is 4 bytes, so we override + the GAS default of 2. */ + {"word", cons, 4}, + /* Explicitly unaligned directives. */ + {"2byte", s_nios2_ucons, 2}, + {"4byte", s_nios2_ucons, 4}, + {"8byte", s_nios2_ucons, 8}, + {"16byte", s_nios2_ucons, 16}, +#ifdef OBJ_ELF + {"sdata", s_nios2_sdata, 0}, +#endif + {"set", s_nios2_set, 0}, + {NULL, NULL, 0} +}; + + +/** Relaxation support. */ + +/* We support two relaxation modes: a limited PC-relative mode with + -relax-section (the default), and an absolute jump mode with -relax-all. + + Nios II PC-relative branch instructions only support 16-bit offsets. + And, there's no good way to add a 32-bit constant to the PC without + using two registers. + + To deal with this, for the pc-relative relaxation mode we convert + br label + into a series of 16-bit adds, like: + nextpc at + addi at, at, 32767 + ... + addi at, at, remainder + jmp at + + Similarly, conditional branches are converted from + b(condition) r, s, label + into a series like: + b(opposite condition) r, s, skip + nextpc at + addi at, at, 32767 + ... + addi at, at, remainder + jmp at + skip: + + The compiler can do a better job, either by converting the branch + directly into a JMP (going through the GOT for PIC) or by allocating + a second register for the 32-bit displacement. + + For the -relax-all relaxation mode, the conversions are + movhi at, %hi(symbol+offset) + ori at, %lo(symbol+offset) + jmp at + and + b(opposite condition), r, s, skip + movhi at, %hi(symbol+offset) + ori at, %lo(symbol+offset) + jmp at + skip: + respectively. +*/ + +/* Arbitrarily limit the number of addis we can insert; we need to be able + to specify the maximum growth size for each frag that contains a + relaxable branch. There's no point in specifying a huge number here + since that means the assembler needs to allocate that much extra + memory for every branch, and almost no real code will ever need it. + Plus, as already noted a better solution is to just use a jmp, or + allocate a second register to hold a 32-bit displacement. + FIXME: Rather than making this a constant, it could be controlled by + a command-line argument. */ +#define RELAX_MAX_ADDI 32 + +/* The fr_subtype field represents the target-specific relocation state. + It has type relax_substateT (unsigned int). We use it to track the + number of addis necessary, plus a bit to track whether this is a + conditional branch. + Regardless of the smaller RELAX_MAX_ADDI limit, we reserve 16 bits + in the fr_subtype to encode the number of addis so that the whole + theoretically-valid range is representable. + For the -relax-all mode, N = 0 represents an in-range branch and N = 1 + represents a branch that needs to be relaxed. */ +#define UBRANCH (0 << 16) +#define CBRANCH (1 << 16) +#define IS_CBRANCH(SUBTYPE) ((SUBTYPE) & CBRANCH) +#define IS_UBRANCH(SUBTYPE) (!IS_CBRANCH (SUBTYPE)) +#define UBRANCH_SUBTYPE(N) (UBRANCH | (N)) +#define CBRANCH_SUBTYPE(N) (CBRANCH | (N)) +#define SUBTYPE_ADDIS(SUBTYPE) ((SUBTYPE) & 0xffff) + +/* For the -relax-section mode, unconditional branches require 2 extra i + nstructions besides the addis, conditional branches require 3. */ +#define UBRANCH_ADDIS_TO_SIZE(N) (((N) + 2) * 4) +#define CBRANCH_ADDIS_TO_SIZE(N) (((N) + 3) * 4) + +/* For the -relax-all mode, unconditional branches require 3 instructions + and conditional branches require 4. */ +#define UBRANCH_JUMP_SIZE 12 +#define CBRANCH_JUMP_SIZE 16 + +/* Maximum sizes of relaxation sequences. */ +#define UBRANCH_MAX_SIZE \ + (nios2_as_options.relax == relax_all \ + ? UBRANCH_JUMP_SIZE \ + : UBRANCH_ADDIS_TO_SIZE (RELAX_MAX_ADDI)) +#define CBRANCH_MAX_SIZE \ + (nios2_as_options.relax == relax_all \ + ? CBRANCH_JUMP_SIZE \ + : CBRANCH_ADDIS_TO_SIZE (RELAX_MAX_ADDI)) + +/* Register number of AT, the assembler temporary. */ +#define AT_REGNUM 1 + +/* Determine how many bytes are required to represent the sequence + indicated by SUBTYPE. */ +static int +nios2_relax_subtype_size (relax_substateT subtype) +{ + int n = SUBTYPE_ADDIS (subtype); + if (n == 0) + /* Regular conditional/unconditional branch instruction. */ + return 4; + else if (nios2_as_options.relax == relax_all) + return (IS_CBRANCH (subtype) ? CBRANCH_JUMP_SIZE : UBRANCH_JUMP_SIZE); + else if (IS_CBRANCH (subtype)) + return CBRANCH_ADDIS_TO_SIZE (n); + else + return UBRANCH_ADDIS_TO_SIZE (n); +} + +/* Estimate size of fragp before relaxation. + This could also examine the offset in fragp and adjust + fragp->fr_subtype, but we will do that in nios2_relax_frag anyway. */ +int +md_estimate_size_before_relax (fragS *fragp, segT segment ATTRIBUTE_UNUSED) +{ + return nios2_relax_subtype_size (fragp->fr_subtype); +} + +/* Implement md_relax_frag, returning the change in size of the frag. */ +long +nios2_relax_frag (segT segment, fragS *fragp, long stretch) +{ + addressT target = fragp->fr_offset; + relax_substateT subtype = fragp->fr_subtype; + symbolS *symbolp = fragp->fr_symbol; + + if (symbolp) + { + fragS *sym_frag = symbol_get_frag (symbolp); + offsetT offset; + int n; + + target += S_GET_VALUE (symbolp); + + /* See comments in write.c:relax_frag about handling of stretch. */ + if (stretch != 0 + && sym_frag->relax_marker != fragp->relax_marker) + { + if (stretch < 0 || sym_frag->region == fragp->region) + target += stretch; + else if (target < fragp->fr_address) + target = fragp->fr_next->fr_address + stretch; + } + + /* We subtract 4 because all pc relative branches are + from the next instruction. */ + offset = target - fragp->fr_address - fragp->fr_fix - 4; + if (offset >= -32768 && offset <= 32764) + /* Fits in PC-relative branch. */ + n = 0; + else if (nios2_as_options.relax == relax_all) + /* Convert to jump. */ + n = 1; + else if (nios2_as_options.relax == relax_section + && S_GET_SEGMENT (symbolp) == segment + && S_IS_DEFINED (symbolp)) + /* Attempt a PC-relative relaxation on a branch to a defined + symbol in the same segment. */ + { + /* The relaxation for conditional branches is offset by 4 + bytes because we insert the inverted branch around the + sequence. */ + if (IS_CBRANCH (subtype)) + offset = offset - 4; + if (offset > 0) + n = offset / 32767 + 1; + else + n = offset / -32768 + 1; + + /* Bail out immediately if relaxation has failed. If we try to + defer the diagnostic to md_convert_frag, some pathological test + cases (e.g. gcc/testsuite/gcc.c-torture/compile/20001226-1.c) + apparently never converge. By returning 0 here we could pretend + to the caller that nothing has changed, but that leaves things + in an inconsistent state when we get to md_convert_frag. */ + if (n > RELAX_MAX_ADDI) + { + as_bad_where (fragp->fr_file, fragp->fr_line, + _("branch offset out of range\n")); + as_fatal (_("branch relaxation failed\n")); + } + } + else + /* We cannot handle this case, diagnose overflow later. */ + return 0; + + if (IS_CBRANCH (subtype)) + fragp->fr_subtype = CBRANCH_SUBTYPE (n); + else + fragp->fr_subtype = UBRANCH_SUBTYPE (n); + + return (nios2_relax_subtype_size (fragp->fr_subtype) + - nios2_relax_subtype_size (subtype)); + } + + /* If we got here, it's probably an error. */ + return 0; +} + + +/* Complete fragp using the data from the relaxation pass. */ +void +md_convert_frag (bfd *headers ATTRIBUTE_UNUSED, segT segment ATTRIBUTE_UNUSED, + fragS *fragp) +{ + char *buffer = fragp->fr_literal + fragp->fr_fix; + relax_substateT subtype = fragp->fr_subtype; + int n = SUBTYPE_ADDIS (subtype); + addressT target = fragp->fr_offset; + symbolS *symbolp = fragp->fr_symbol; + offsetT offset; + unsigned int addend_mask, addi_mask; + offsetT addend, remainder; + int i; + + /* If we didn't or can't relax, this is a regular branch instruction. + We just need to generate the fixup for the symbol and offset. */ + if (n == 0) + { + fix_new (fragp, fragp->fr_fix, 4, fragp->fr_symbol, fragp->fr_offset, 1, + BFD_RELOC_16_PCREL); + fragp->fr_fix += 4; + return; + } + + /* Replace the cbranch at fr_fix with one that has the opposite condition + in order to jump around the block of instructions we'll be adding. */ + if (IS_CBRANCH (subtype)) + { + unsigned int br_opcode; + int nbytes; + + /* Account for the nextpc and jmp in the pc-relative case, or the two + load instructions and jump in the absolute case. */ + if (nios2_as_options.relax == relax_section) + nbytes = (n + 2) * 4; + else + nbytes = 12; + + br_opcode = md_chars_to_number (buffer, 4); + switch (br_opcode & OP_MASK_OP) + { + case OP_MATCH_BEQ: + br_opcode = (br_opcode & ~OP_MASK_OP) | OP_MATCH_BNE; + break; + case OP_MATCH_BNE: + br_opcode = (br_opcode & ~OP_MASK_OP) | OP_MATCH_BEQ ; + break; + case OP_MATCH_BGE: + br_opcode = (br_opcode & ~OP_MASK_OP) | OP_MATCH_BLT ; + break; + case OP_MATCH_BGEU: + br_opcode = (br_opcode & ~OP_MASK_OP) | OP_MATCH_BLTU ; + break; + case OP_MATCH_BLT: + br_opcode = (br_opcode & ~OP_MASK_OP) | OP_MATCH_BGE ; + break; + case OP_MATCH_BLTU: + br_opcode = (br_opcode & ~OP_MASK_OP) | OP_MATCH_BGEU ; + break; + default: + as_bad_where (fragp->fr_file, fragp->fr_line, + _("expecting conditional branch for relaxation\n")); + abort (); + } + + br_opcode = br_opcode | (nbytes << OP_SH_IMM16); + md_number_to_chars (buffer, br_opcode, 4); + fragp->fr_fix += 4; + buffer += 4; + } + + /* Load at for the PC-relative case. */ + if (nios2_as_options.relax == relax_section) + { + /* Insert the nextpc instruction. */ + md_number_to_chars (buffer, + OP_MATCH_NEXTPC | (AT_REGNUM << OP_SH_RRD), 4); + fragp->fr_fix += 4; + buffer += 4; + + /* We need to know whether the offset is positive or negative. */ + target += S_GET_VALUE (symbolp); + offset = target - fragp->fr_address - fragp->fr_fix; + if (offset > 0) + addend = 32767; + else + addend = -32768; + addend_mask = (((unsigned int)addend) & 0xffff) << OP_SH_IMM16; + + /* Insert n-1 addi instructions. */ + addi_mask = (OP_MATCH_ADDI + | (AT_REGNUM << OP_SH_IRD) + | (AT_REGNUM << OP_SH_IRS)); + for (i = 0; i < n - 1; i ++) + { + md_number_to_chars (buffer, addi_mask | addend_mask, 4); + fragp->fr_fix += 4; + buffer += 4; + } + + /* Insert the last addi instruction to hold the remainder. */ + remainder = offset - addend * (n - 1); + gas_assert (remainder >= -32768 && remainder <= 32767); + addend_mask = (((unsigned int)remainder) & 0xffff) << OP_SH_IMM16; + md_number_to_chars (buffer, addi_mask | addend_mask, 4); + fragp->fr_fix += 4; + buffer += 4; + } + + /* Load at for the absolute case. */ + else + { + md_number_to_chars (buffer, OP_MATCH_ORHI | 0x00400000, 4); + fix_new (fragp, fragp->fr_fix, 4, fragp->fr_symbol, fragp->fr_offset, + 0, BFD_RELOC_NIOS2_HI16); + fragp->fr_fix += 4; + buffer += 4; + md_number_to_chars (buffer, OP_MATCH_ORI | 0x08400000, 4); + fix_new (fragp, fragp->fr_fix, 4, fragp->fr_symbol, fragp->fr_offset, + 0, BFD_RELOC_NIOS2_LO16); + fragp->fr_fix += 4; + buffer += 4; + } + + /* Insert the jmp instruction. */ + md_number_to_chars (buffer, OP_MATCH_JMP | (AT_REGNUM << OP_SH_RRS), 4); + fragp->fr_fix += 4; + buffer += 4; +} + + +/** Fixups and overflow checking. */ + +/* Check a fixup for overflow. */ +static bfd_boolean +nios2_check_overflow (valueT fixup, reloc_howto_type *howto) +{ + /* Apply the rightshift before checking for overflow. */ + fixup = ((signed)fixup) >> howto->rightshift; + + /* Check for overflow - return TRUE if overflow, FALSE if not. */ + switch (howto->complain_on_overflow) + { + case complain_overflow_dont: + break; + case complain_overflow_bitfield: + if ((fixup >> howto->bitsize) != 0 + && ((signed) fixup >> howto->bitsize) != -1) + return TRUE; + break; + case complain_overflow_signed: + if ((fixup & 0x80000000) > 0) + { + /* Check for negative overflow. */ + if ((signed) fixup < ((signed) 0x80000000 >> howto->bitsize)) + return TRUE; + } + else + { + /* Check for positive overflow. */ + if (fixup >= ((unsigned) 1 << (howto->bitsize - 1))) + return TRUE; + } + break; + case complain_overflow_unsigned: + if ((fixup >> howto->bitsize) != 0) + return TRUE; + break; + default: + as_bad (_("error checking for overflow - broken assembler")); + break; + } + return FALSE; +} + +/* Emit diagnostic for fixup overflow. */ +static void +nios2_diagnose_overflow (valueT fixup, reloc_howto_type *howto, + fixS *fixP, valueT value) +{ + if (fixP->fx_r_type == BFD_RELOC_8 + || fixP->fx_r_type == BFD_RELOC_16 + || fixP->fx_r_type == BFD_RELOC_32) + /* These relocs are against data, not instructions. */ + as_bad_where (fixP->fx_file, fixP->fx_line, + _("immediate value 0x%x truncated to 0x%x"), + (unsigned int) fixup, + (unsigned int) (~(~(valueT) 0 << howto->bitsize) & fixup)); + else + { + /* What opcode is the instruction? This will determine + whether we check for overflow in immediate values + and what error message we get. */ + const struct nios2_opcode *opcode; + enum overflow_type overflow_msg_type; + unsigned int range_min; + unsigned int range_max; + unsigned int address; + gas_assert (fixP->fx_size == 4); + opcode = nios2_find_opcode_hash (value); + gas_assert (opcode); + overflow_msg_type = opcode->overflow_msg; + switch (overflow_msg_type) + { + case call_target_overflow: + range_min + = ((fixP->fx_frag->fr_address + fixP->fx_where) & 0xf0000000); + range_max = range_min + 0x0fffffff; + address = fixup | range_min; + + as_bad_where (fixP->fx_file, fixP->fx_line, + _("call target address 0x%08x out of range 0x%08x to 0x%08x"), + address, range_min, range_max); + break; + case branch_target_overflow: + as_bad_where (fixP->fx_file, fixP->fx_line, + _("branch offset %d out of range %d to %d"), + (int)fixup, -32768, 32767); + break; + case address_offset_overflow: + as_bad_where (fixP->fx_file, fixP->fx_line, + _("%s offset %d out of range %d to %d"), + opcode->name, (int)fixup, -32768, 32767); + break; + case signed_immed16_overflow: + as_bad_where (fixP->fx_file, fixP->fx_line, + _("immediate value %d out of range %d to %d"), + (int)fixup, -32768, 32767); + break; + case unsigned_immed16_overflow: + as_bad_where (fixP->fx_file, fixP->fx_line, + _("immediate value %u out of range %u to %u"), + (unsigned int)fixup, 0, 65535); + break; + case unsigned_immed5_overflow: + as_bad_where (fixP->fx_file, fixP->fx_line, + _("immediate value %u out of range %u to %u"), + (unsigned int)fixup, 0, 31); + break; + case custom_opcode_overflow: + as_bad_where (fixP->fx_file, fixP->fx_line, + _("custom instruction opcode %u out of range %u to %u"), + (unsigned int)fixup, 0, 255); + break; + default: + as_bad_where (fixP->fx_file, fixP->fx_line, + _("overflow in immediate argument")); + break; + } + } +} + +/* Apply a fixup to the object file. */ +void +md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) +{ + /* Assert that the fixup is one we can handle. */ + gas_assert (fixP != NULL && valP != NULL + && (fixP->fx_r_type == BFD_RELOC_8 + || fixP->fx_r_type == BFD_RELOC_16 + || fixP->fx_r_type == BFD_RELOC_32 + || fixP->fx_r_type == BFD_RELOC_64 + || fixP->fx_r_type == BFD_RELOC_NIOS2_S16 + || fixP->fx_r_type == BFD_RELOC_NIOS2_U16 + || fixP->fx_r_type == BFD_RELOC_16_PCREL + || fixP->fx_r_type == BFD_RELOC_NIOS2_CALL26 + || fixP->fx_r_type == BFD_RELOC_NIOS2_IMM5 + || fixP->fx_r_type == BFD_RELOC_NIOS2_CACHE_OPX + || fixP->fx_r_type == BFD_RELOC_NIOS2_IMM6 + || fixP->fx_r_type == BFD_RELOC_NIOS2_IMM8 + || fixP->fx_r_type == BFD_RELOC_NIOS2_HI16 + || fixP->fx_r_type == BFD_RELOC_NIOS2_LO16 + || fixP->fx_r_type == BFD_RELOC_NIOS2_HIADJ16 + || fixP->fx_r_type == BFD_RELOC_NIOS2_GPREL + || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT + || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY + || fixP->fx_r_type == BFD_RELOC_NIOS2_UJMP + || fixP->fx_r_type == BFD_RELOC_NIOS2_CJMP + || fixP->fx_r_type == BFD_RELOC_NIOS2_CALLR + || fixP->fx_r_type == BFD_RELOC_NIOS2_ALIGN + || fixP->fx_r_type == BFD_RELOC_NIOS2_GOT16 + || fixP->fx_r_type == BFD_RELOC_NIOS2_CALL16 + || fixP->fx_r_type == BFD_RELOC_NIOS2_GOTOFF_LO + || fixP->fx_r_type == BFD_RELOC_NIOS2_GOTOFF_HA + || fixP->fx_r_type == BFD_RELOC_NIOS2_TLS_GD16 + || fixP->fx_r_type == BFD_RELOC_NIOS2_TLS_LDM16 + || fixP->fx_r_type == BFD_RELOC_NIOS2_TLS_LDO16 + || fixP->fx_r_type == BFD_RELOC_NIOS2_TLS_IE16 + || fixP->fx_r_type == BFD_RELOC_NIOS2_TLS_LE16 + || fixP->fx_r_type == BFD_RELOC_NIOS2_GOTOFF + || fixP->fx_r_type == BFD_RELOC_NIOS2_TLS_DTPREL + /* Add other relocs here as we generate them. */ + )); + + if (fixP->fx_r_type == BFD_RELOC_64) + { + /* We may reach here due to .8byte directives, but we never output + BFD_RELOC_64; it must be resolved. */ + if (fixP->fx_addsy != NULL) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("cannot create 64-bit relocation")); + else + { + md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, + *valP, 8); + fixP->fx_done = 1; + } + return; + } + + /* The value passed in valP can be the value of a fully + resolved expression, or it can be the value of a partially + resolved expression. In the former case, both fixP->fx_addsy + and fixP->fx_subsy are NULL, and fixP->fx_offset == *valP, and + we can fix up the instruction that fixP relates to. + In the latter case, one or both of fixP->fx_addsy and + fixP->fx_subsy are not NULL, and fixP->fx_offset may or may not + equal *valP. We don't need to check for fixP->fx_subsy being null + because the generic part of the assembler generates an error if + it is not an absolute symbol. */ + if (fixP->fx_addsy != NULL) + /* Partially resolved expression. */ + { + fixP->fx_addnumber = fixP->fx_offset; + fixP->fx_done = 0; + + switch (fixP->fx_r_type) + { + case BFD_RELOC_NIOS2_TLS_GD16: + case BFD_RELOC_NIOS2_TLS_LDM16: + case BFD_RELOC_NIOS2_TLS_LDO16: + case BFD_RELOC_NIOS2_TLS_IE16: + case BFD_RELOC_NIOS2_TLS_LE16: + case BFD_RELOC_NIOS2_TLS_DTPMOD: + case BFD_RELOC_NIOS2_TLS_DTPREL: + case BFD_RELOC_NIOS2_TLS_TPREL: + S_SET_THREAD_LOCAL (fixP->fx_addsy); + break; + default: + break; + } + } + else + /* Fully resolved fixup. */ + { + reloc_howto_type *howto + = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type); + + if (howto == NULL) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("relocation is not supported")); + else + { + valueT fixup = *valP; + valueT value; + char *buf; + + /* If this is a pc-relative relocation, we need to + subtract the current offset within the object file + FIXME : for some reason fixP->fx_pcrel isn't 1 when it should be + so I'm using the howto structure instead to determine this. */ + if (howto->pc_relative == 1) + fixup = fixup - (fixP->fx_frag->fr_address + fixP->fx_where + 4); + + /* Get the instruction or data to be fixed up. */ + buf = fixP->fx_frag->fr_literal + fixP->fx_where; + value = md_chars_to_number (buf, fixP->fx_size); + + /* Check for overflow, emitting a diagnostic if necessary. */ + if (nios2_check_overflow (fixup, howto)) + nios2_diagnose_overflow (fixup, howto, fixP, value); + + /* Apply the right shift. */ + fixup = ((signed)fixup) >> howto->rightshift; + + /* Truncate the fixup to right size. */ + switch (fixP->fx_r_type) + { + case BFD_RELOC_NIOS2_HI16: + fixup = (fixup >> 16) & 0xFFFF; + break; + case BFD_RELOC_NIOS2_LO16: + fixup = fixup & 0xFFFF; + break; + case BFD_RELOC_NIOS2_HIADJ16: + fixup = ((fixup >> 16) & 0xFFFF) + ((fixup >> 15) & 0x01); + break; + default: + { + int n = sizeof (fixup) * 8 - howto->bitsize; + fixup = (fixup << n) >> n; + break; + } + } + + /* Fix up the instruction. */ + value = (value & ~howto->dst_mask) | (fixup << howto->bitpos); + md_number_to_chars (buf, value, fixP->fx_size); + } + + fixP->fx_done = 1; + } + + if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT) + { + fixP->fx_done = 0; + if (fixP->fx_addsy + && !S_IS_DEFINED (fixP->fx_addsy) && !S_IS_WEAK (fixP->fx_addsy)) + S_SET_WEAK (fixP->fx_addsy); + } + else if (fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY) + fixP->fx_done = 0; +} + + + +/** Instruction parsing support. */ + +/* Special relocation directive strings. */ + +struct nios2_special_relocS +{ + const char *string; + bfd_reloc_code_real_type reloc_type; +}; + +struct nios2_special_relocS nios2_special_reloc[] = { + {"%hiadj", BFD_RELOC_NIOS2_HIADJ16}, + {"%hi", BFD_RELOC_NIOS2_HI16}, + {"%lo", BFD_RELOC_NIOS2_LO16}, + {"%gprel", BFD_RELOC_NIOS2_GPREL}, + {"%call", BFD_RELOC_NIOS2_CALL16}, + {"%gotoff_lo", BFD_RELOC_NIOS2_GOTOFF_LO}, + {"%gotoff_hiadj", BFD_RELOC_NIOS2_GOTOFF_HA}, + {"%tls_gd", BFD_RELOC_NIOS2_TLS_GD16}, + {"%tls_ldm", BFD_RELOC_NIOS2_TLS_LDM16}, + {"%tls_ldo", BFD_RELOC_NIOS2_TLS_LDO16}, + {"%tls_ie", BFD_RELOC_NIOS2_TLS_IE16}, + {"%tls_le", BFD_RELOC_NIOS2_TLS_LE16}, + {"%gotoff", BFD_RELOC_NIOS2_GOTOFF}, + {"%got", BFD_RELOC_NIOS2_GOT16} +}; + +#define NIOS2_NUM_SPECIAL_RELOCS \ + (sizeof(nios2_special_reloc)/sizeof(nios2_special_reloc[0])) +const int nios2_num_special_relocs = NIOS2_NUM_SPECIAL_RELOCS; + +/* Creates a new nios2_insn_relocS and returns a pointer to it. */ +static nios2_insn_relocS * +nios2_insn_reloc_new (bfd_reloc_code_real_type reloc_type, unsigned int pcrel) +{ + nios2_insn_relocS *retval; + retval = (nios2_insn_relocS *) malloc (sizeof (nios2_insn_relocS)); + if (retval == NULL) + { + as_bad (_("can't create relocation")); + abort (); + } + + /* Fill out the fields with default values. */ + retval->reloc_next = NULL; + retval->reloc_type = reloc_type; + retval->reloc_pcrel = pcrel; + return retval; +} + +/* Frees up memory previously allocated by nios2_insn_reloc_new(). */ +/* FIXME: this is never called; memory leak? */ +#if 0 +static void +nios2_insn_reloc_destroy (nios2_insn_relocS *reloc) +{ + gas_assert (reloc != NULL); + free (reloc); +} +#endif + +/* The various nios2_assemble_* functions call this + function to generate an expression from a string representing an expression. + It then tries to evaluate the expression, and if it can, returns its value. + If not, it creates a new nios2_insn_relocS and stores the expression and + reloc_type for future use. */ +static unsigned long +nios2_assemble_expression (const char *exprstr, + nios2_insn_infoS *insn, + nios2_insn_relocS *prev_reloc, + bfd_reloc_code_real_type reloc_type, + unsigned int pcrel) +{ + nios2_insn_relocS *reloc; + char *saved_line_ptr; + unsigned short value; + int i; + + gas_assert (exprstr != NULL); + gas_assert (insn != NULL); + + /* Check for relocation operators. + Change the relocation type and advance the ptr to the start of + the expression proper. */ + for (i = 0; i < nios2_num_special_relocs; i++) + if (strstr (exprstr, nios2_special_reloc[i].string) != NULL) + { + reloc_type = nios2_special_reloc[i].reloc_type; + exprstr += strlen (nios2_special_reloc[i].string) + 1; + + /* %lo and %hiadj have different meanings for PC-relative + expressions. */ + if (pcrel) + { + if (reloc_type == BFD_RELOC_NIOS2_LO16) + reloc_type = BFD_RELOC_NIOS2_PCREL_LO; + if (reloc_type == BFD_RELOC_NIOS2_HIADJ16) + reloc_type = BFD_RELOC_NIOS2_PCREL_HA; + } + + break; + } + + /* We potentially have a relocation. */ + reloc = nios2_insn_reloc_new (reloc_type, pcrel); + if (prev_reloc != NULL) + prev_reloc->reloc_next = reloc; + else + insn->insn_reloc = reloc; + + /* Parse the expression string. */ + saved_line_ptr = input_line_pointer; + input_line_pointer = (char *) exprstr; + expression (&reloc->reloc_expression); + input_line_pointer = saved_line_ptr; + + /* This is redundant as the fixup will put this into + the instruction, but it is included here so that + self-test mode (-r) works. */ + value = 0; + if (nios2_mode == NIOS2_MODE_TEST + && reloc->reloc_expression.X_op == O_constant) + value = reloc->reloc_expression.X_add_number; + + return (unsigned long) value; +} + +/* Argument assemble functions. + All take an instruction argument string, and a pointer + to an instruction opcode. Upon return the insn_opcode + has the relevant fields filled in to represent the arg + string. The return value is NULL if successful, or + an error message if an error was detected. + + The naming conventions for these functions match the args template + in the nios2_opcode structure, as documented in include/opcode/nios2.h. + For example, nios2_assemble_args_dst is used for instructions with + "d,s,t" args. + See nios2_arg_info_structs below for the exact correspondence. */ + +static void +nios2_assemble_args_dst (nios2_insn_infoS *insn_info) +{ + if (insn_info->insn_tokens[1] != NULL + && insn_info->insn_tokens[2] != NULL + && insn_info->insn_tokens[3] != NULL) + { + struct nios2_reg *dst = nios2_reg_lookup (insn_info->insn_tokens[1]); + struct nios2_reg *src1 = nios2_reg_lookup (insn_info->insn_tokens[2]); + struct nios2_reg *src2 = nios2_reg_lookup (insn_info->insn_tokens[3]); + + if (dst == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[1]); + else + SET_INSN_FIELD (RRD, insn_info->insn_code, dst->index); + + if (src1 == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[2]); + else + SET_INSN_FIELD (RRS, insn_info->insn_code, src1->index); + + if (src2 == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[3]); + else + SET_INSN_FIELD (RRT, insn_info->insn_code, src2->index); + + nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[4]); + } +} + +static void +nios2_assemble_args_tsi (nios2_insn_infoS *insn_info) +{ + if (insn_info->insn_tokens[1] != NULL && + insn_info->insn_tokens[2] != NULL && insn_info->insn_tokens[3] != NULL) + { + struct nios2_reg *dst = nios2_reg_lookup (insn_info->insn_tokens[1]); + struct nios2_reg *src1 = nios2_reg_lookup (insn_info->insn_tokens[2]); + unsigned int src2 + = nios2_assemble_expression (insn_info->insn_tokens[3], insn_info, + insn_info->insn_reloc, BFD_RELOC_NIOS2_S16, + 0); + + if (dst == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[1]); + else + SET_INSN_FIELD (IRT, insn_info->insn_code, dst->index); + + if (src1 == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[2]); + else + SET_INSN_FIELD (IRS, insn_info->insn_code, src1->index); + + SET_INSN_FIELD (IMM16, insn_info->insn_code, src2); + nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[4]); + SET_INSN_FIELD (IMM16, insn_info->insn_code, 0); + } +} + +static void +nios2_assemble_args_tsu (nios2_insn_infoS *insn_info) +{ + if (insn_info->insn_tokens[1] != NULL + && insn_info->insn_tokens[2] != NULL + && insn_info->insn_tokens[3] != NULL) + { + struct nios2_reg *dst = nios2_reg_lookup (insn_info->insn_tokens[1]); + struct nios2_reg *src1 = nios2_reg_lookup (insn_info->insn_tokens[2]); + unsigned int src2 + = nios2_assemble_expression (insn_info->insn_tokens[3], insn_info, + insn_info->insn_reloc, BFD_RELOC_NIOS2_U16, + 0); + + if (dst == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[1]); + else + SET_INSN_FIELD (IRT, insn_info->insn_code, dst->index); + + if (src1 == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[2]); + else + SET_INSN_FIELD (IRS, insn_info->insn_code, src1->index); + + SET_INSN_FIELD (IMM16, insn_info->insn_code, src2); + nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[4]); + SET_INSN_FIELD (IMM16, insn_info->insn_code, 0); + } +} + +static void +nios2_assemble_args_sto (nios2_insn_infoS *insn_info) +{ + if (insn_info->insn_tokens[1] != NULL + && insn_info->insn_tokens[2] != NULL + && insn_info->insn_tokens[3] != NULL) + { + struct nios2_reg *dst = nios2_reg_lookup (insn_info->insn_tokens[1]); + struct nios2_reg *src1 = nios2_reg_lookup (insn_info->insn_tokens[2]); + unsigned int src2 + = nios2_assemble_expression (insn_info->insn_tokens[3], insn_info, + insn_info->insn_reloc, BFD_RELOC_16_PCREL, + 1); + + if (dst == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[1]); + else + SET_INSN_FIELD (IRS, insn_info->insn_code, dst->index); + + if (src1 == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[2]); + else + SET_INSN_FIELD (IRT, insn_info->insn_code, src1->index); + + SET_INSN_FIELD (IMM16, insn_info->insn_code, src2); + nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[4]); + SET_INSN_FIELD (IMM16, insn_info->insn_code, 0); + } +} + +static void +nios2_assemble_args_o (nios2_insn_infoS *insn_info) +{ + if (insn_info->insn_tokens[1] != NULL) + { + unsigned long immed + = nios2_assemble_expression (insn_info->insn_tokens[1], insn_info, + insn_info->insn_reloc, BFD_RELOC_16_PCREL, + 1); + SET_INSN_FIELD (IMM16, insn_info->insn_code, immed); + nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[2]); + SET_INSN_FIELD (IMM16, insn_info->insn_code, 0); + } +} + +static void +nios2_assemble_args_is (nios2_insn_infoS *insn_info) +{ + if (insn_info->insn_tokens[1] != NULL && insn_info->insn_tokens[2] != NULL) + { + struct nios2_reg *addr_src = nios2_reg_lookup (insn_info->insn_tokens[2]); + unsigned long immed + = nios2_assemble_expression (insn_info->insn_tokens[1], insn_info, + insn_info->insn_reloc, BFD_RELOC_NIOS2_S16, + 0); + + SET_INSN_FIELD (IMM16, insn_info->insn_code, immed); + + if (addr_src == NULL) + as_bad (_("unknown base register %s"), insn_info->insn_tokens[2]); + else + SET_INSN_FIELD (RRS, insn_info->insn_code, addr_src->index); + + nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[3]); + SET_INSN_FIELD (IMM16, insn_info->insn_code, 0); + } +} + +static void +nios2_assemble_args_m (nios2_insn_infoS *insn_info) +{ + if (insn_info->insn_tokens[1] != NULL) + { + unsigned long immed + = nios2_assemble_expression (insn_info->insn_tokens[1], insn_info, + insn_info->insn_reloc, + BFD_RELOC_NIOS2_CALL26, 0); + + SET_INSN_FIELD (IMM26, insn_info->insn_code, immed); + nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[2]); + SET_INSN_FIELD (IMM26, insn_info->insn_code, 0); + } +} + +static void +nios2_assemble_args_s (nios2_insn_infoS *insn_info) +{ + if (insn_info->insn_tokens[1] != NULL) + { + struct nios2_reg *src = nios2_reg_lookup (insn_info->insn_tokens[1]); + if (src == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[1]); + else + SET_INSN_FIELD (RRS, insn_info->insn_code, src->index); + + nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[2]); + } +} + +static void +nios2_assemble_args_tis (nios2_insn_infoS *insn_info) +{ + if (insn_info->insn_tokens[1] != NULL + && insn_info->insn_tokens[2] != NULL + && insn_info->insn_tokens[3] != NULL) + { + struct nios2_reg *dst = nios2_reg_lookup (insn_info->insn_tokens[1]); + struct nios2_reg *addr_src = nios2_reg_lookup (insn_info->insn_tokens[3]); + unsigned long immed + = nios2_assemble_expression (insn_info->insn_tokens[2], insn_info, + insn_info->insn_reloc, BFD_RELOC_NIOS2_S16, + 0); + + if (addr_src == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[3]); + else + SET_INSN_FIELD (RRS, insn_info->insn_code, addr_src->index); + + if (dst == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[1]); + else + SET_INSN_FIELD (RRT, insn_info->insn_code, dst->index); + + SET_INSN_FIELD (IMM16, insn_info->insn_code, immed); + nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[4]); + SET_INSN_FIELD (IMM16, insn_info->insn_code, 0); + } +} + +static void +nios2_assemble_args_dc (nios2_insn_infoS *insn_info) +{ + if (insn_info->insn_tokens[1] != NULL && insn_info->insn_tokens[2] != NULL) + { + struct nios2_reg *ctl = nios2_reg_lookup (insn_info->insn_tokens[2]); + struct nios2_reg *dst = nios2_reg_lookup (insn_info->insn_tokens[1]); + + if (ctl == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[1]); + else + SET_INSN_FIELD (RCTL, insn_info->insn_code, ctl->index); + + if (dst == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[2]); + else + SET_INSN_FIELD (RRD, insn_info->insn_code, dst->index); + + nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[3]); + } +} + +static void +nios2_assemble_args_cs (nios2_insn_infoS *insn_info) +{ + if (insn_info->insn_tokens[1] != NULL && insn_info->insn_tokens[2] != NULL) + { + struct nios2_reg *ctl = nios2_reg_lookup (insn_info->insn_tokens[1]); + struct nios2_reg *src = nios2_reg_lookup (insn_info->insn_tokens[2]); + + if (ctl == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[1]); + else if (ctl->index == 4) + as_bad (_("ipending control register (ctl4) is read-only\n")); + else + SET_INSN_FIELD (RCTL, insn_info->insn_code, ctl->index); + + if (src == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[2]); + else + SET_INSN_FIELD (RRS, insn_info->insn_code, src->index); + + nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[3]); + } +} + +static void +nios2_assemble_args_ldst (nios2_insn_infoS *insn_info) +{ + if (insn_info->insn_tokens[1] != NULL + && insn_info->insn_tokens[2] != NULL + && insn_info->insn_tokens[3] != NULL + && insn_info->insn_tokens[4] != NULL) + { + unsigned long custom_n + = nios2_assemble_expression (insn_info->insn_tokens[1], insn_info, + insn_info->insn_reloc, + BFD_RELOC_NIOS2_IMM8, 0); + + struct nios2_reg *dst = nios2_reg_lookup (insn_info->insn_tokens[2]); + struct nios2_reg *src1 = nios2_reg_lookup (insn_info->insn_tokens[3]); + struct nios2_reg *src2 = nios2_reg_lookup (insn_info->insn_tokens[4]); + + SET_INSN_FIELD (CUSTOM_N, insn_info->insn_code, custom_n); + + if (dst == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[2]); + else + SET_INSN_FIELD (RRD, insn_info->insn_code, dst->index); + + if (src1 == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[3]); + else + SET_INSN_FIELD (RRS, insn_info->insn_code, src1->index); + + if (src2 == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[4]); + else + SET_INSN_FIELD (RRT, insn_info->insn_code, src2->index); + + /* Set or clear the bits to indicate whether coprocessor registers are + used. */ + if (nios2_coproc_reg (insn_info->insn_tokens[2])) + SET_INSN_FIELD (CUSTOM_C, insn_info->insn_code, 0); + else + SET_INSN_FIELD (CUSTOM_C, insn_info->insn_code, 1); + + if (nios2_coproc_reg (insn_info->insn_tokens[3])) + SET_INSN_FIELD (CUSTOM_A, insn_info->insn_code, 0); + else + SET_INSN_FIELD (CUSTOM_A, insn_info->insn_code, 1); + + if (nios2_coproc_reg (insn_info->insn_tokens[4])) + SET_INSN_FIELD (CUSTOM_B, insn_info->insn_code, 0); + else + SET_INSN_FIELD (CUSTOM_B, insn_info->insn_code, 1); + + nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[5]); + } +} + +static void +nios2_assemble_args_none (nios2_insn_infoS *insn_info ATTRIBUTE_UNUSED) +{ + /* Nothing to do. */ +} + +static void +nios2_assemble_args_dsj (nios2_insn_infoS *insn_info) +{ + if (insn_info->insn_tokens[1] != NULL + && insn_info->insn_tokens[2] != NULL + && insn_info->insn_tokens[3] != NULL) + { + struct nios2_reg *dst = nios2_reg_lookup (insn_info->insn_tokens[1]); + struct nios2_reg *src1 = nios2_reg_lookup (insn_info->insn_tokens[2]); + + /* A 5-bit constant expression. */ + unsigned int src2 = + nios2_assemble_expression (insn_info->insn_tokens[3], insn_info, + insn_info->insn_reloc, + BFD_RELOC_NIOS2_IMM5, 0); + + if (dst == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[1]); + else + SET_INSN_FIELD (RRD, insn_info->insn_code, dst->index); + + if (src1 == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[2]); + else + SET_INSN_FIELD (RRS, insn_info->insn_code, src1->index); + + SET_INSN_FIELD (IMM5, insn_info->insn_code, src2); + nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[4]); + SET_INSN_FIELD (IMM5, insn_info->insn_code, 0); + } +} + +static void +nios2_assemble_args_d (nios2_insn_infoS *insn_info) +{ + if (insn_info->insn_tokens[1] != NULL) + { + struct nios2_reg *dst = nios2_reg_lookup (insn_info->insn_tokens[1]); + + if (dst == NULL) + as_bad (_("unknown register %s"), insn_info->insn_tokens[1]); + else + SET_INSN_FIELD (RRD, insn_info->insn_code, dst->index); + + nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[2]); + } +} + +static void +nios2_assemble_args_b (nios2_insn_infoS *insn_info) +{ + unsigned int imm5 = 0; + + if (insn_info->insn_tokens[1] != NULL) + { + /* A 5-bit constant expression. */ + imm5 = nios2_assemble_expression (insn_info->insn_tokens[1], + insn_info, insn_info->insn_reloc, + BFD_RELOC_NIOS2_IMM5, 0); + SET_INSN_FIELD (TRAP_IMM5, insn_info->insn_code, imm5); + nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[2]); + } + + SET_INSN_FIELD (TRAP_IMM5, insn_info->insn_code, imm5); + + nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[2]); +} + +/* This table associates pointers to functions that parse the arguments to an + instruction and fill in the relevant fields of the instruction. */ +const nios2_arg_infoS nios2_arg_info_structs[] = { + /* args, assemble_args_func */ + {"d,s,t", nios2_assemble_args_dst}, + {"d,s,t,E", nios2_assemble_args_dst}, + {"t,s,i", nios2_assemble_args_tsi}, + {"t,s,i,E", nios2_assemble_args_tsi}, + {"t,s,u", nios2_assemble_args_tsu}, + {"t,s,u,E", nios2_assemble_args_tsu}, + {"s,t,o", nios2_assemble_args_sto}, + {"s,t,o,E", nios2_assemble_args_sto}, + {"o", nios2_assemble_args_o}, + {"o,E", nios2_assemble_args_o}, + {"s", nios2_assemble_args_s}, + {"s,E", nios2_assemble_args_s}, + {"", nios2_assemble_args_none}, + {"E", nios2_assemble_args_none}, + {"i(s)", nios2_assemble_args_is}, + {"i(s)E", nios2_assemble_args_is}, + {"m", nios2_assemble_args_m}, + {"m,E", nios2_assemble_args_m}, + {"t,i(s)", nios2_assemble_args_tis}, + {"t,i(s)E", nios2_assemble_args_tis}, + {"d,c", nios2_assemble_args_dc}, + {"d,c,E", nios2_assemble_args_dc}, + {"c,s", nios2_assemble_args_cs}, + {"c,s,E", nios2_assemble_args_cs}, + {"l,d,s,t", nios2_assemble_args_ldst}, + {"l,d,s,t,E", nios2_assemble_args_ldst}, + {"d,s,j", nios2_assemble_args_dsj}, + {"d,s,j,E", nios2_assemble_args_dsj}, + {"d", nios2_assemble_args_d}, + {"d,E", nios2_assemble_args_d}, + {"b", nios2_assemble_args_b}, + {"b,E", nios2_assemble_args_b} +}; + +#define NIOS2_NUM_ARGS \ + ((sizeof(nios2_arg_info_structs)/sizeof(nios2_arg_info_structs[0]))) +const int nios2_num_arg_info_structs = NIOS2_NUM_ARGS; + +/* The function consume_arg takes a pointer into a string + of instruction tokens (args) and a pointer into a string + representing the expected sequence of tokens and separators. + It checks whether the first argument in argstr is of the + expected type, throwing an error if it is not, and returns + the pointer argstr. */ +static char * +nios2_consume_arg (nios2_insn_infoS *insn, char *argstr, const char *parsestr) +{ + char *temp; + int regno = -1; + + switch (*parsestr) + { + case 'c': + if (!nios2_control_register_arg_p (argstr)) + as_bad (_("expecting control register")); + break; + case 'd': + case 's': + case 't': + + /* We check to make sure we don't have a control register. */ + if (nios2_control_register_arg_p (argstr)) + as_bad (_("illegal use of control register")); + + /* And whether coprocessor registers are valid here. */ + if (nios2_coproc_reg (argstr) + && insn->insn_nios2_opcode->match != OP_MATCH_CUSTOM) + as_bad (_("illegal use of coprocessor register\n")); + + /* Extract a register number if the register is of the + form r[0-9]+, if it is a normal register, set + regno to its number (0-31), else set regno to -1. */ + if (argstr[0] == 'r' && ISDIGIT (argstr[1])) + { + char *p = argstr; + + ++p; + regno = 0; + do + { + regno *= 10; + regno += *p - '0'; + ++p; + } + while (ISDIGIT (*p)); + } + else + regno = -1; + + /* And whether we are using at. */ + if (!nios2_as_options.noat + && (regno == 1 || strprefix (argstr, "at"))) + as_warn (_("Register at (r1) can sometimes be corrupted by assembler " + "optimizations.\n" + "Use .set noat to turn off those optimizations (and this " + "warning).")); + + /* And whether we are using oci registers. */ + if (!nios2_as_options.nobreak + && (regno == 25 || strprefix (argstr, "bt"))) + as_warn (_("The debugger will corrupt bt (r25). If you don't need to " + "debug this\n" + "code then use .set nobreak to turn off this warning.")); + + if (!nios2_as_options.nobreak + && (regno == 30 || strprefix (argstr, "ba"))) + as_warn (_("The debugger will corrupt ba (r30). If you don't need to " + "debug this\n" + "code then use .set nobreak to turn off this warning.")); + break; + case 'i': + case 'u': + if (*argstr == '%') + { + if (nios2_special_relocation_p (argstr)) + { + /* We zap the parentheses because we don't want them confused + with separators. */ + temp = strchr (argstr, '('); + if (temp != NULL) + *temp = ' '; + temp = strchr (argstr, ')'); + if (temp != NULL) + *temp = ' '; + } + else + as_bad (_("badly formed expression near %s"), argstr); + } + break; + case 'm': + case 'j': + case 'k': + case 'l': + case 'b': + /* We can't have %hi, %lo or %hiadj here. */ + if (*argstr == '%') + as_bad (_("badly formed expression near %s"), argstr); + break; + default: + break; + } + + return argstr; +} + +/* The function consume_separator takes a pointer into a string + of instruction tokens (args) and a pointer into a string representing + the expected sequence of tokens and separators. It finds the first + instance of the character pointed to by separator in argstr, and + returns a pointer to the next element of argstr, which is the + following token in the sequence. */ +static char * +nios2_consume_separator (char *argstr, const char *separator) +{ + char *p; + + /* If we have a opcode reg, expr(reg) type instruction, and + * we are separating the expr from the (reg), we find the last + * (, just in case the expression has parentheses. */ + + if (*separator == '(') + p = strrchr (argstr, *separator); + else + p = strchr (argstr, *separator); + + if (p != NULL) + *p++ = 0; + else + as_bad (_("expecting %c near %s"), *separator, argstr); + return p; +} + + +/* The principal argument parsing function which takes a string argstr + representing the instruction arguments for insn, and extracts the argument + tokens matching parsestr into parsed_args. */ +static void +nios2_parse_args (nios2_insn_infoS *insn, char *argstr, + const char *parsestr, char **parsed_args) +{ + char *p; + char *end = NULL; + int i; + p = argstr; + i = 0; + bfd_boolean terminate = FALSE; + + /* This rest of this function is it too fragile and it mostly works, + therefore special case this one. */ + if (*parsestr == 0 && argstr != 0) + { + as_bad (_("too many arguments")); + parsed_args[0] = NULL; + return; + } + + while (p != NULL && !terminate && i < NIOS2_MAX_INSN_TOKENS) + { + parsed_args[i] = nios2_consume_arg (insn, p, parsestr); + ++parsestr; + if (*parsestr != '\0') + { + p = nios2_consume_separator (p, parsestr); + ++parsestr; + } + else + { + /* Check that the argument string has no trailing arguments. */ + /* If we've got a %lo etc relocation, we've zapped the parens with + spaces. */ + if (nios2_special_relocation_p (p)) + end = strpbrk (p, ","); + else + end = strpbrk (p, " ,"); + + if (end != NULL) + as_bad (_("too many arguments")); + } + + if (*parsestr == '\0' || (p != NULL && *p == '\0')) + terminate = TRUE; + ++i; + } + + parsed_args[i] = NULL; + + if (*parsestr != '\0' && insn->insn_nios2_opcode->match != OP_MATCH_BREAK) + as_bad (_("missing argument")); +} + + + +/** Support for pseudo-op parsing. These are macro-like opcodes that + expand into real insns by suitable fiddling with the operands. */ + +/* Append the string modifier to the string contained in the argument at + parsed_args[ndx]. */ +static void +nios2_modify_arg (char **parsed_args, const char *modifier, + int unused ATTRIBUTE_UNUSED, int ndx) +{ + char *tmp = parsed_args[ndx]; + + parsed_args[ndx] + = (char *) malloc (strlen (parsed_args[ndx]) + strlen (modifier) + 1); + strcpy (parsed_args[ndx], tmp); + strcat (parsed_args[ndx], modifier); +} + +/* Modify parsed_args[ndx] by negating that argument. */ +static void +nios2_negate_arg (char **parsed_args, const char *modifier ATTRIBUTE_UNUSED, + int unused ATTRIBUTE_UNUSED, int ndx) +{ + char *tmp = parsed_args[ndx]; + + parsed_args[ndx] + = (char *) malloc (strlen ("~(") + strlen (parsed_args[ndx]) + + strlen (")+1") + 1); + + strcpy (parsed_args[ndx], "~("); + strcat (parsed_args[ndx], tmp); + strcat (parsed_args[ndx], ")+1"); +} + +/* The function nios2_swap_args swaps the pointers at indices index_1 and + index_2 in the array parsed_args[] - this is used for operand swapping + for comparison operations. */ +static void +nios2_swap_args (char **parsed_args, const char *unused ATTRIBUTE_UNUSED, + int index_1, int index_2) +{ + char *tmp; + gas_assert (index_1 < NIOS2_MAX_INSN_TOKENS + && index_2 < NIOS2_MAX_INSN_TOKENS); + tmp = parsed_args[index_1]; + parsed_args[index_1] = parsed_args[index_2]; + parsed_args[index_2] = tmp; +} + +/* This function appends the string appnd to the array of strings in + parsed_args num times starting at index start in the array. */ +static void +nios2_append_arg (char **parsed_args, const char *appnd, int num, + int start) +{ + int i, count; + char *tmp; + + gas_assert ((start + num) < NIOS2_MAX_INSN_TOKENS); + + if (nios2_mode == NIOS2_MODE_TEST) + tmp = parsed_args[start]; + else + tmp = NULL; + + for (i = start, count = num; count > 0; ++i, --count) + parsed_args[i] = (char *) appnd; + + gas_assert (i == (start + num)); + parsed_args[i] = tmp; + parsed_args[i + 1] = NULL; +} + +/* This function inserts the string insert num times in the array + parsed_args, starting at the index start. */ +static void +nios2_insert_arg (char **parsed_args, const char *insert, int num, + int start) +{ + int i, count; + + gas_assert ((start + num) < NIOS2_MAX_INSN_TOKENS); + + /* Move the existing arguments up to create space. */ + for (i = NIOS2_MAX_INSN_TOKENS; i - num >= start; --i) + parsed_args[i] = parsed_args[i - num]; + + for (i = start, count = num; count > 0; ++i, --count) + parsed_args[i] = (char *) insert; +} + +/* Cleanup function to free malloc'ed arg strings. */ +static void +nios2_free_arg (char **parsed_args, int num ATTRIBUTE_UNUSED, int start) +{ + if (parsed_args[start]) + { + free (parsed_args[start]); + parsed_args[start] = NULL; + } +} + +/* This function swaps the pseudo-op for a real op. */ +static nios2_ps_insn_infoS* +nios2_translate_pseudo_insn (nios2_insn_infoS *insn) +{ + + nios2_ps_insn_infoS *ps_insn; + + /* Find which real insn the pseudo-op transates to and + switch the insn_info ptr to point to it. */ + ps_insn = nios2_ps_lookup (insn->insn_nios2_opcode->name); + + if (ps_insn != NULL) + { + insn->insn_nios2_opcode = nios2_opcode_lookup (ps_insn->insn); + insn->insn_tokens[0] = insn->insn_nios2_opcode->name; + /* Modify the args so they work with the real insn. */ + ps_insn->arg_modifer_func ((char **) insn->insn_tokens, + ps_insn->arg_modifier, ps_insn->num, + ps_insn->index); + } + else + /* we cannot recover from this. */ + as_fatal (_("unrecognized pseudo-instruction %s"), + ps_insn->pseudo_insn); + return ps_insn; +} + +/* Invoke the cleanup handler for pseudo-insn ps_insn on insn. */ +static void +nios2_cleanup_pseudo_insn (nios2_insn_infoS *insn, + nios2_ps_insn_infoS *ps_insn) +{ + if (ps_insn->arg_cleanup_func) + (ps_insn->arg_cleanup_func) ((char **) insn->insn_tokens, + ps_insn->num, ps_insn->index); +} + +const nios2_ps_insn_infoS nios2_ps_insn_info_structs[] = { + /* pseudo-op, real-op, arg, arg_modifier_func, num, index, arg_cleanup_func */ + {"mov", "add", nios2_append_arg, "zero", 1, 3, NULL}, + {"movi", "addi", nios2_insert_arg, "zero", 1, 2, NULL}, + {"movhi", "orhi", nios2_insert_arg, "zero", 1, 2, NULL}, + {"movui", "ori", nios2_insert_arg, "zero", 1, 2, NULL}, + {"movia", "orhi", nios2_insert_arg, "zero", 1, 2, NULL}, + {"nop", "add", nios2_append_arg, "zero", 3, 1, NULL}, + {"bgt", "blt", nios2_swap_args, "", 1, 2, NULL}, + {"bgtu", "bltu", nios2_swap_args, "", 1, 2, NULL}, + {"ble", "bge", nios2_swap_args, "", 1, 2, NULL}, + {"bleu", "bgeu", nios2_swap_args, "", 1, 2, NULL}, + {"cmpgt", "cmplt", nios2_swap_args, "", 2, 3, NULL}, + {"cmpgtu", "cmpltu", nios2_swap_args, "", 2, 3, NULL}, + {"cmple", "cmpge", nios2_swap_args, "", 2, 3, NULL}, + {"cmpleu", "cmpgeu", nios2_swap_args, "", 2, 3, NULL}, + {"cmpgti", "cmpgei", nios2_modify_arg, "+1", 0, 3, nios2_free_arg}, + {"cmpgtui", "cmpgeui", nios2_modify_arg, "+1", 0, 3, nios2_free_arg}, + {"cmplei", "cmplti", nios2_modify_arg, "+1", 0, 3, nios2_free_arg}, + {"cmpleui", "cmpltui", nios2_modify_arg, "+1", 0, 3, nios2_free_arg}, + {"subi", "addi", nios2_negate_arg, "", 0, 3, nios2_free_arg} + /* Add further pseudo-ops here. */ +}; + +#define NIOS2_NUM_PSEUDO_INSNS \ + ((sizeof(nios2_ps_insn_info_structs)/ \ + sizeof(nios2_ps_insn_info_structs[0]))) +const int nios2_num_ps_insn_info_structs = NIOS2_NUM_PSEUDO_INSNS; + + +/** Assembler output support. */ + +static int +can_evaluate_expr (nios2_insn_infoS *insn) +{ + /* Remove this check for null and the invalid insn "ori r9, 1234" seg faults. */ + if (!insn->insn_reloc) + /* ??? Ideally we should do something other than as_fatal here as we can + continue to assemble. + However this function (actually the output_* functions) should not + have been called in the first place once an illegal instruction had + been encountered. */ + as_fatal (_("Invalid instruction encountered, cannot recover. No assembly attempted.")); + + if (insn->insn_reloc->reloc_expression.X_op == O_constant) + return 1; + + return 0; +} + +static int +get_expr_value (nios2_insn_infoS *insn) +{ + int value = 0; + + if (insn->insn_reloc->reloc_expression.X_op == O_constant) + value = insn->insn_reloc->reloc_expression.X_add_number; + return value; +} + +/* Output a normal instruction. */ +static void +output_insn (nios2_insn_infoS *insn) +{ + char *f; + nios2_insn_relocS *reloc; + + f = frag_more (4); + /* This allocates enough space for the instruction + and puts it in the current frag. */ + md_number_to_chars (f, insn->insn_code, 4); + /* Emit debug info. */ + dwarf2_emit_insn (4); + /* Create any fixups to be acted on later. */ + for (reloc = insn->insn_reloc; reloc != NULL; reloc = reloc->reloc_next) + fix_new_exp (frag_now, f - frag_now->fr_literal, 4, + &reloc->reloc_expression, reloc->reloc_pcrel, + reloc->reloc_type); +} + +/* Output an unconditional branch. */ +static void +output_ubranch (nios2_insn_infoS *insn) +{ + nios2_insn_relocS *reloc = insn->insn_reloc; + + /* If the reloc is NULL, there was an error assembling the branch. */ + if (reloc != NULL) + { + symbolS *symp = reloc->reloc_expression.X_add_symbol; + offsetT offset = reloc->reloc_expression.X_add_number; + char *f; + + /* Tag dwarf2 debug info to the address at the start of the insn. + We must do it before frag_var() below closes off the frag. */ + dwarf2_emit_insn (0); + + /* We create a machine dependent frag which can grow + to accommodate the largest possible instruction sequence + this may generate. */ + f = frag_var (rs_machine_dependent, + UBRANCH_MAX_SIZE, 4, UBRANCH_SUBTYPE (0), + symp, offset, NULL); + + md_number_to_chars (f, insn->insn_code, 4); + + /* We leave fixup generation to md_convert_frag. */ + } +} + +/* Output a conditional branch. */ +static void +output_cbranch (nios2_insn_infoS *insn) +{ + nios2_insn_relocS *reloc = insn->insn_reloc; + + /* If the reloc is NULL, there was an error assembling the branch. */ + if (reloc != NULL) + { + symbolS *symp = reloc->reloc_expression.X_add_symbol; + offsetT offset = reloc->reloc_expression.X_add_number; + char *f; + + /* Tag dwarf2 debug info to the address at the start of the insn. + We must do it before frag_var() below closes off the frag. */ + dwarf2_emit_insn (0); + + /* We create a machine dependent frag which can grow + to accommodate the largest possible instruction sequence + this may generate. */ + f = frag_var (rs_machine_dependent, + CBRANCH_MAX_SIZE, 4, CBRANCH_SUBTYPE (0), + symp, offset, NULL); + + md_number_to_chars (f, insn->insn_code, 4); + + /* We leave fixup generation to md_convert_frag. */ + } +} + +/* Output a call sequence. Since calls are not pc-relative for NIOS2, + but are page-relative, we cannot tell at any stage in assembly + whether a call will be out of range since a section may be linked + at any address. So if we are relaxing, we convert all call instructions + to long call sequences, and rely on the linker to relax them back to + short calls. */ +static void +output_call (nios2_insn_infoS *insn) +{ + /* This allocates enough space for the instruction + and puts it in the current frag. */ + char *f = frag_more (12); + nios2_insn_relocS *reloc = insn->insn_reloc; + + md_number_to_chars (f, OP_MATCH_ORHI | 0x00400000, 4); + dwarf2_emit_insn (4); + fix_new_exp (frag_now, f - frag_now->fr_literal, 4, + &reloc->reloc_expression, 0, BFD_RELOC_NIOS2_HI16); + md_number_to_chars (f + 4, OP_MATCH_ORI | 0x08400000, 4); + dwarf2_emit_insn (4); + fix_new_exp (frag_now, f - frag_now->fr_literal + 4, 4, + &reloc->reloc_expression, 0, BFD_RELOC_NIOS2_LO16); + md_number_to_chars (f + 8, OP_MATCH_CALLR | 0x08000000, 4); + dwarf2_emit_insn (4); +} + +/* Output an addi - will silently convert to + orhi if rA = r0 and (expr & 0xffff0000) == 0. */ +static void +output_addi (nios2_insn_infoS *insn) +{ + if (can_evaluate_expr (insn)) + { + int expr_val = get_expr_value (insn); + if (GET_INSN_FIELD (RRS, insn->insn_code) == 0 + && (expr_val & 0xffff) == 0 + && expr_val != 0) + { + /* We really want a movhi (orhi) here. */ + insn->insn_code = (insn->insn_code & ~OP_MATCH_ADDI) | OP_MATCH_ORHI; + insn->insn_reloc->reloc_expression.X_add_number = + (insn->insn_reloc->reloc_expression.X_add_number >> 16) & 0xffff; + insn->insn_reloc->reloc_type = BFD_RELOC_NIOS2_U16; + } + } + + /* Output an instruction. */ + output_insn (insn); +} + +static void +output_andi (nios2_insn_infoS *insn) +{ + if (can_evaluate_expr (insn)) + { + int expr_val = get_expr_value (insn); + if (expr_val != 0 && (expr_val & 0xffff) == 0) + { + /* We really want a movhi (orhi) here. */ + insn->insn_code = (insn->insn_code & ~OP_MATCH_ANDI) | OP_MATCH_ANDHI; + insn->insn_reloc->reloc_expression.X_add_number = + (insn->insn_reloc->reloc_expression.X_add_number >> 16) & 0xffff; + insn->insn_reloc->reloc_type = BFD_RELOC_NIOS2_U16; + } + } + + /* Output an instruction. */ + output_insn (insn); +} + +static void +output_ori (nios2_insn_infoS *insn) +{ + if (can_evaluate_expr (insn)) + { + int expr_val = get_expr_value (insn); + if (expr_val != 0 && (expr_val & 0xffff) == 0) + { + /* We really want a movhi (orhi) here. */ + insn->insn_code = (insn->insn_code & ~OP_MATCH_ORI) | OP_MATCH_ORHI; + insn->insn_reloc->reloc_expression.X_add_number = + (insn->insn_reloc->reloc_expression.X_add_number >> 16) & 0xffff; + insn->insn_reloc->reloc_type = BFD_RELOC_NIOS2_U16; + } + } + + /* Output an instruction. */ + output_insn (insn); +} + +static void +output_xori (nios2_insn_infoS *insn) +{ + if (can_evaluate_expr (insn)) + { + int expr_val = get_expr_value (insn); + if (expr_val != 0 && (expr_val & 0xffff) == 0) + { + /* We really want a movhi (orhi) here. */ + insn->insn_code = (insn->insn_code & ~OP_MATCH_XORI) | OP_MATCH_XORHI; + insn->insn_reloc->reloc_expression.X_add_number = + (insn->insn_reloc->reloc_expression.X_add_number >> 16) & 0xffff; + insn->insn_reloc->reloc_type = BFD_RELOC_NIOS2_U16; + } + } + + /* Output an instruction. */ + output_insn (insn); +} + + +/* Output a movhi/addi pair for the movia pseudo-op. */ +static void +output_movia (nios2_insn_infoS *insn) +{ + /* This allocates enough space for the instruction + and puts it in the current frag. */ + char *f = frag_more (8); + nios2_insn_relocS *reloc = insn->insn_reloc; + unsigned long reg_index = GET_INSN_FIELD (IRT, insn->insn_code); + + /* If the reloc is NULL, there was an error assembling the movia. */ + if (reloc != NULL) + { + md_number_to_chars (f, insn->insn_code, 4); + dwarf2_emit_insn (4); + md_number_to_chars (f + 4, + (OP_MATCH_ADDI | (reg_index << OP_SH_IRT) + | (reg_index << OP_SH_IRS)), + 4); + dwarf2_emit_insn (4); + fix_new (frag_now, f - frag_now->fr_literal, 4, + reloc->reloc_expression.X_add_symbol, + reloc->reloc_expression.X_add_number, 0, + BFD_RELOC_NIOS2_HIADJ16); + fix_new (frag_now, f + 4 - frag_now->fr_literal, 4, + reloc->reloc_expression.X_add_symbol, + reloc->reloc_expression.X_add_number, 0, BFD_RELOC_NIOS2_LO16); + } +} + + + +/** External interfaces. */ + +/* The following functions are called by machine-independent parts of + the assembler. */ +int +md_parse_option (int c, char *arg ATTRIBUTE_UNUSED) +{ + switch (c) + { + case 'r': + /* Hidden option for self-test mode. */ + nios2_mode = NIOS2_MODE_TEST; + break; + case OPTION_RELAX_ALL: + nios2_as_options.relax = relax_all; + break; + case OPTION_NORELAX: + nios2_as_options.relax = relax_none; + break; + case OPTION_RELAX_SECTION: + nios2_as_options.relax = relax_section; + break; + case OPTION_EB: + target_big_endian = 1; + break; + case OPTION_EL: + target_big_endian = 0; + break; + default: + return 0; + break; + } + + return 1; +} + +/* Implement TARGET_FORMAT. We can choose to be big-endian or + little-endian at runtime based on a switch. */ +const char * +nios2_target_format (void) +{ + return target_big_endian ? "elf32-bignios2" : "elf32-littlenios2"; +} + +/* Machine-dependent usage message. */ +void +md_show_usage (FILE *stream) +{ + fprintf (stream, " NIOS2 options:\n" + " -relax-all replace all branch and call " + "instructions with jmp and callr sequences\n" + " -relax-section replace identified out of range " + "branches with jmp sequences (default)\n" + " -no-relax do not replace any branches or calls\n" + " -EB force big-endian byte ordering\n" + " -EL force little-endian byte ordering\n"); +} + +/* This function is called once, at assembler startup time. + It should set up all the tables, etc. that the MD part of the + assembler will need. */ +void +md_begin (void) +{ + int i; + const char *inserted; + + /* Create and fill a hashtable for the Nios II opcodes, registers and + arguments. */ + nios2_opcode_hash = hash_new (); + nios2_reg_hash = hash_new (); + nios2_arg_hash = hash_new (); + nios2_ps_hash = hash_new (); + + for (i = 0; i < NUMOPCODES; ++i) + { + inserted + = hash_insert (nios2_opcode_hash, nios2_opcodes[i].name, + (PTR) & nios2_opcodes[i]); + if (inserted != NULL) + { + fprintf (stderr, _("internal error: can't hash `%s': %s\n"), + nios2_opcodes[i].name, inserted); + /* Probably a memory allocation problem? Give up now. */ + as_fatal (_("Broken assembler. No assembly attempted.")); + } + } + + for (i = 0; i < nios2_num_regs; ++i) + { + inserted + = hash_insert (nios2_reg_hash, nios2_regs[i].name, + (PTR) & nios2_regs[i]); + if (inserted != NULL) + { + fprintf (stderr, _("internal error: can't hash `%s': %s\n"), + nios2_regs[i].name, inserted); + /* Probably a memory allocation problem? Give up now. */ + as_fatal (_("Broken assembler. No assembly attempted.")); + } + + } + + for (i = 0; i < nios2_num_arg_info_structs; ++i) + { + inserted + = hash_insert (nios2_arg_hash, nios2_arg_info_structs[i].args, + (PTR) & nios2_arg_info_structs[i]); + if (inserted != NULL) + { + fprintf (stderr, _("internal error: can't hash `%s': %s\n"), + nios2_arg_info_structs[i].args, inserted); + /* Probably a memory allocation problem? Give up now. */ + as_fatal (_("Broken assembler. No assembly attempted.")); + } + } + + for (i = 0; i < nios2_num_ps_insn_info_structs; ++i) + { + inserted + = hash_insert (nios2_ps_hash, nios2_ps_insn_info_structs[i].pseudo_insn, + (PTR) & nios2_ps_insn_info_structs[i]); + if (inserted != NULL) + { + fprintf (stderr, _("internal error: can't hash `%s': %s\n"), + nios2_ps_insn_info_structs[i].pseudo_insn, inserted); + /* Probably a memory allocation problem? Give up now. */ + as_fatal (_("Broken assembler. No assembly attempted.")); + } + } + + /* Assembler option defaults. */ + nios2_as_options.noat = FALSE; + nios2_as_options.nobreak = FALSE; + + /* Debug information is incompatible with relaxation. */ + if (debug_type != DEBUG_UNSPECIFIED) + nios2_as_options.relax = relax_none; + + /* Initialize the alignment data. */ + nios2_current_align_seg = now_seg; + nios2_last_label = NULL; + nios2_current_align = 0; +} + + +/* Assembles a single line of Nios II assembly language. */ +void +md_assemble (char *op_str) +{ + char *argstr; + char *op_strdup = NULL; + nios2_arg_infoS *arg_info; + unsigned long saved_pinfo = 0; + nios2_insn_infoS thisinsn; + nios2_insn_infoS *insn = &thisinsn; + + /* Make sure we are aligned on a 4-byte boundary. */ + if (nios2_current_align < 2) + nios2_align (2, NULL, nios2_last_label); + else if (nios2_current_align > 2) + nios2_current_align = 2; + nios2_last_label = NULL; + + /* We don't want to clobber to op_str + because we want to be able to use it in messages. */ + op_strdup = strdup (op_str); + insn->insn_tokens[0] = strtok (op_strdup, " "); + argstr = strtok (NULL, ""); + + /* Assemble the opcode. */ + insn->insn_nios2_opcode = nios2_opcode_lookup (insn->insn_tokens[0]); + insn->insn_reloc = NULL; + + if (insn->insn_nios2_opcode != NULL) + { + nios2_ps_insn_infoS *ps_insn = NULL; + /* Set the opcode for the instruction. */ + insn->insn_code = insn->insn_nios2_opcode->match; + + /* Parse the arguments pointed to by argstr. */ + if (nios2_mode == NIOS2_MODE_ASSEMBLE) + nios2_parse_args (insn, argstr, insn->insn_nios2_opcode->args, + (char **) &insn->insn_tokens[1]); + else + nios2_parse_args (insn, argstr, insn->insn_nios2_opcode->args_test, + (char **) &insn->insn_tokens[1]); + + /* We need to preserve the MOVIA macro as this is clobbered by + translate_pseudo_insn. */ + if (insn->insn_nios2_opcode->pinfo == NIOS2_INSN_MACRO_MOVIA) + saved_pinfo = NIOS2_INSN_MACRO_MOVIA; + /* If the instruction is an pseudo-instruction, we want to replace it + with its real equivalent, and then continue. */ + if ((insn->insn_nios2_opcode->pinfo & NIOS2_INSN_MACRO) + == NIOS2_INSN_MACRO) + ps_insn = nios2_translate_pseudo_insn (insn); + + /* Find the assemble function, and call it. */ + arg_info = nios2_arg_lookup (insn->insn_nios2_opcode->args); + if (arg_info != NULL) + { + arg_info->assemble_args_func (insn); + + if (nios2_as_options.relax != relax_none + && !nios2_as_options.noat + && insn->insn_nios2_opcode->pinfo & NIOS2_INSN_UBRANCH) + output_ubranch (insn); + else if (nios2_as_options.relax != relax_none + && !nios2_as_options.noat + && insn->insn_nios2_opcode->pinfo & NIOS2_INSN_CBRANCH) + output_cbranch (insn); + else if (nios2_as_options.relax == relax_all + && !nios2_as_options.noat + && insn->insn_nios2_opcode->pinfo & NIOS2_INSN_CALL + && insn->insn_reloc + && insn->insn_reloc->reloc_type == BFD_RELOC_NIOS2_CALL26) + output_call (insn); + else if (insn->insn_nios2_opcode->pinfo & NIOS2_INSN_ANDI) + output_andi (insn); + else if (insn->insn_nios2_opcode->pinfo & NIOS2_INSN_ORI) + output_ori (insn); + else if (insn->insn_nios2_opcode->pinfo & NIOS2_INSN_XORI) + output_xori (insn); + else if (insn->insn_nios2_opcode->pinfo & NIOS2_INSN_ADDI) + output_addi (insn); + else if (saved_pinfo == NIOS2_INSN_MACRO_MOVIA) + output_movia (insn); + else + output_insn (insn); + if (ps_insn) + nios2_cleanup_pseudo_insn (insn, ps_insn); + } + else + { + /* The assembler is broken. */ + fprintf (stderr, + _("internal error: %s is not a valid argument syntax\n"), + insn->insn_nios2_opcode->args); + /* Probably a memory allocation problem. Give up now. */ + as_fatal (_("Broken assembler. No assembly attempted.")); + } + } + else + /* Unrecognised instruction - error. */ + as_bad (_("unrecognised instruction %s"), insn->insn_tokens[0]); + + /* Don't leak memory. */ + free (op_strdup); +} + +/* Round up section size. */ +valueT +md_section_align (asection *seg ATTRIBUTE_UNUSED, valueT size) +{ + /* I think byte alignment is fine here. */ + return size; +} + +/* Implement TC_FORCE_RELOCATION. */ +int +nios2_force_relocation (fixS *fixp) +{ + if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT + || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY + || fixp->fx_r_type == BFD_RELOC_NIOS2_ALIGN) + return 1; + + return generic_force_reloc (fixp); +} + +/* Implement tc_fix_adjustable. */ +int +nios2_fix_adjustable (fixS *fixp) +{ + if (fixp->fx_addsy == NULL) + return 1; + +#ifdef OBJ_ELF + /* Prevent all adjustments to global symbols. */ + if (OUTPUT_FLAVOR == bfd_target_elf_flavour + && (S_IS_EXTERNAL (fixp->fx_addsy) || S_IS_WEAK (fixp->fx_addsy))) + return 0; +#endif + if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT + || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) + return 0; + + /* Preserve relocations against symbols with function type. */ + if (symbol_get_bfdsym (fixp->fx_addsy)->flags & BSF_FUNCTION) + return 0; + + /* Don't allow symbols to be discarded on GOT related relocs. */ + if (fixp->fx_r_type == BFD_RELOC_NIOS2_GOT16 + || fixp->fx_r_type == BFD_RELOC_NIOS2_CALL16 + || fixp->fx_r_type == BFD_RELOC_NIOS2_GOTOFF_LO + || fixp->fx_r_type == BFD_RELOC_NIOS2_GOTOFF_HA + || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_GD16 + || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_LDM16 + || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_LDO16 + || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_IE16 + || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_LE16 + || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_DTPMOD + || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_DTPREL + || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_TPREL + || fixp->fx_r_type == BFD_RELOC_NIOS2_GOTOFF) + return 0; + + return 1; +} + +/* Implement tc_frob_symbol. This is called in adjust_reloc_syms; + it is used to remove *ABS* references from the symbol table. */ +int +nios2_frob_symbol (symbolS *symp) +{ + if ((OUTPUT_FLAVOR == bfd_target_elf_flavour + && symp == section_symbol (absolute_section)) + || !S_IS_DEFINED (symp)) + return 1; + else + return 0; +} + +/* The function tc_gen_reloc creates a relocation structure for the + fixup fixp, and returns a pointer to it. This structure is passed + to bfd_install_relocation so that it can be written to the object + file for linking. */ +arelent * +tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp) +{ + arelent *reloc = (arelent *) xmalloc (sizeof (arelent)); + reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); + *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); + + reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; + reloc->addend = fixp->fx_offset; /* fixp->fx_addnumber; */ + + if (fixp->fx_pcrel) + { + switch (fixp->fx_r_type) + { + case BFD_RELOC_16: + fixp->fx_r_type = BFD_RELOC_16_PCREL; + break; + case BFD_RELOC_NIOS2_LO16: + fixp->fx_r_type = BFD_RELOC_NIOS2_PCREL_LO; + break; + case BFD_RELOC_NIOS2_HIADJ16: + fixp->fx_r_type = BFD_RELOC_NIOS2_PCREL_HA; + break; + default: + break; + } + } + + reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type); + if (reloc->howto == NULL) + { + as_bad_where (fixp->fx_file, fixp->fx_line, + _("can't represent relocation type %s"), + bfd_get_reloc_code_name (fixp->fx_r_type)); + + /* Set howto to a garbage value so that we can keep going. */ + reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32); + gas_assert (reloc->howto != NULL); + } + return reloc; +} + +long +md_pcrel_from (fixS *fixP ATTRIBUTE_UNUSED) +{ + return 0; +} + +/* Called just before the assembler exits. */ +void +md_end () +{ + /* FIXME - not yet implemented */ +} + +/* Under ELF we need to default _GLOBAL_OFFSET_TABLE. + Otherwise we have no need to default values of symbols. */ +symbolS * +md_undefined_symbol (char *name ATTRIBUTE_UNUSED) +{ +#ifdef OBJ_ELF + if (name[0] == '_' && name[1] == 'G' + && strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0) + { + if (!GOT_symbol) + { + if (symbol_find (name)) + as_bad ("GOT already in the symbol table"); + + GOT_symbol = symbol_new (name, undefined_section, + (valueT) 0, &zero_address_frag); + } + + return GOT_symbol; + } +#endif + + return 0; +} + +/* Implement tc_frob_label. */ +void +nios2_frob_label (symbolS *lab) +{ + /* Emit dwarf information. */ + dwarf2_emit_label (lab); + + /* Update the label's address with the current output pointer. */ + symbol_set_frag (lab, frag_now); + S_SET_VALUE (lab, (valueT) frag_now_fix ()); + + /* Record this label for future adjustment after we find out what + kind of data it references, and the required alignment therewith. */ + nios2_last_label = lab; +} + +/* Implement md_cons_align. */ +void +nios2_cons_align (int size) +{ + int log_size = 0; + const char *pfill = NULL; + + while ((size >>= 1) != 0) + ++log_size; + + if (subseg_text_p (now_seg)) + pfill = (const char *) &nop; + else + pfill = NULL; + + if (nios2_auto_align_on) + nios2_align (log_size, pfill, NULL); + + nios2_last_label = NULL; +} + +/* Map 's' to SHF_NIOS2_GPREL. */ +/* This is from the Alpha code tc-alpha.c. */ +int +nios2_elf_section_letter (int letter, char **ptr_msg) +{ + if (letter == 's') + return SHF_NIOS2_GPREL; + + *ptr_msg = _("Bad .section directive: want a,s,w,x,M,S,G,T in string"); + return -1; +} + +/* Map SHF_ALPHA_GPREL to SEC_SMALL_DATA. */ +/* This is from the Alpha code tc-alpha.c. */ +flagword +nios2_elf_section_flags (flagword flags, int attr, int type ATTRIBUTE_UNUSED) +{ + if (attr & SHF_NIOS2_GPREL) + flags |= SEC_SMALL_DATA; + return flags; +} + +/* Implement TC_PARSE_CONS_EXPRESSION to handle %tls_ldo(...) */ +static int nios2_tls_ldo_reloc; + +void +nios2_cons (expressionS *exp, int size) +{ + nios2_tls_ldo_reloc = 0; + + SKIP_WHITESPACE (); + if (input_line_pointer[0] == '%') + { + if (strprefix (input_line_pointer + 1, "tls_ldo")) + { + if (size != 4) + as_bad (_("Illegal operands: %%tls_ldo in %d-byte data field"), + size); + else + { + input_line_pointer += 8; + nios2_tls_ldo_reloc = 1; + } + } + if (nios2_tls_ldo_reloc) + { + SKIP_WHITESPACE (); + if (input_line_pointer[0] != '(') + as_bad (_("Illegal operands: %%tls_ldo requires arguments in ()")); + else + { + int c; + char *end = ++input_line_pointer; + int npar = 0; + + for (c = *end; !is_end_of_line[c]; end++, c = *end) + if (c == '(') + npar++; + else if (c == ')') + { + if (!npar) + break; + npar--; + } + + if (c != ')') + as_bad (_("Illegal operands: %%tls_ldo requires arguments in ()")); + else + { + *end = '\0'; + expression (exp); + *end = c; + if (input_line_pointer != end) + as_bad (_("Illegal operands: %%tls_ldo requires arguments in ()")); + else + { + input_line_pointer++; + SKIP_WHITESPACE (); + c = *input_line_pointer; + if (! is_end_of_line[c] && c != ',') + as_bad (_("Illegal operands: garbage after %%tls_ldo()")); + } + } + } + } + } + if (!nios2_tls_ldo_reloc) + expression (exp); +} + +/* Implement TC_CONS_FIX_NEW. */ +void +nios2_cons_fix_new (fragS *frag, int where, unsigned int nbytes, + expressionS *exp) +{ + bfd_reloc_code_real_type r; + + r = (nbytes == 1 ? BFD_RELOC_8 + : (nbytes == 2 ? BFD_RELOC_16 + : (nbytes == 4 ? BFD_RELOC_32 : BFD_RELOC_64))); + + if (nios2_tls_ldo_reloc) + r = BFD_RELOC_NIOS2_TLS_DTPREL; + + fix_new_exp (frag, where, (int) nbytes, exp, 0, r); + nios2_tls_ldo_reloc = 0; +} + +/* Implement HANDLE_ALIGN. */ +void +nios2_handle_align (fragS *fragp) +{ + /* If we are expecting to relax in the linker, then we must output a + relocation to tell the linker we are aligning code. */ + if (nios2_as_options.relax == relax_all + && (fragp->fr_type == rs_align || fragp->fr_type == rs_align_code) + && fragp->fr_address + fragp->fr_fix > 0 + && fragp->fr_offset > 1 + && now_seg != bss_section) + fix_new (fragp, fragp->fr_fix, 0, &abs_symbol, fragp->fr_offset, 0, + BFD_RELOC_NIOS2_ALIGN); +} + +/* Implement tc_regname_to_dw2regnum, to convert REGNAME to a DWARF-2 + register number. */ +int +nios2_regname_to_dw2regnum (char *regname) +{ + struct nios2_reg *r = nios2_reg_lookup (regname); + if (r == NULL) + return -1; + return r->index; +} + +/* Implement tc_cfi_frame_initial_instructions, to initialize the DWARF-2 + unwind information for this procedure. */ +void +nios2_frame_initial_instructions (void) +{ + cfi_add_CFA_def_cfa (27, 0); +} diff --git a/gas/config/tc-nios2.h b/gas/config/tc-nios2.h new file mode 100644 index 00000000000..9e69194126f --- /dev/null +++ b/gas/config/tc-nios2.h @@ -0,0 +1,125 @@ +/* Definitions for Altera Nios II assembler. + Copyright (C) 2012, 2013 Free Software Foundation, Inc. + Contributed by Nigel Gray (ngray@altera.com). + Contributed by Mentor Graphics, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GAS; see the file COPYING. If not, write to the Free + Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef TC_NIOS2 +#define TC_NIOS2 + +/* If unspecified, default to little endian. We can explicitly specify + * a big-endian default by configuring with --target=nios2eb-elf. We + * can override the default with the -EB and -EL options. */ +#ifndef TARGET_BYTES_BIG_ENDIAN +#define TARGET_BYTES_BIG_ENDIAN 0 +#endif + +/* Words are big enough to hold addresses. */ +#define WORKING_DOT_WORD 1 + +#ifdef OBJ_ELF +extern const char *nios2_target_format (void); +#define TARGET_FORMAT nios2_target_format () +#define TARGET_ARCH bfd_arch_nios2 +#endif + +/* A NIOS2 instruction consists of tokens and separator characters + the tokens are things like the instruction name (add, or jmp etc), + the register indices ($5, $7 etc), and constant expressions. The + separator characters are commas, brackets and space. + The instruction name is always separated from other tokens by a space + The maximum number of tokens in an instruction is 5 (the instruction name, + 3 arguments, and a 4th string representing the expected instructin opcode + after assembly. The latter is only used when the assemble is running in + self test mode, otherwise its presence will generate an error. */ +#define NIOS2_MAX_INSN_TOKENS 6 + +/* There are no machine-specific operands so we #define this to nothing. */ +#define md_operand(x) + +/* Function prototypes exported to rest of GAS. */ +extern void md_assemble (char *op_str); +extern void md_end (void); +extern void md_begin (void); + +#define TC_FORCE_RELOCATION(fixp) nios2_force_relocation (fixp) +extern int nios2_force_relocation (struct fix *); + +#define tc_fix_adjustable(fixp) nios2_fix_adjustable (fixp) +extern int nios2_fix_adjustable (struct fix *); + +#define tc_frob_label(lab) nios2_frob_label (lab) +extern void nios2_frob_label (symbolS *); + +#define tc_frob_symbol(symp, punt) punt = nios2_frob_symbol (symp) ? 1 : punt +extern int nios2_frob_symbol (symbolS * symp); + +#define md_cons_align(nbytes) nios2_cons_align (nbytes) +extern void nios2_cons_align (int); + +extern void md_convert_frag (bfd * headers, segT sec, fragS * fragP); + +/* When relaxing, we need to generate relocations for alignment + directives. */ +#define HANDLE_ALIGN(frag) nios2_handle_align (frag) +extern void nios2_handle_align (fragS *); + +#define md_relax_frag nios2_relax_frag +extern long nios2_relax_frag (segT segment, fragS * fragP, long stretch); + +#ifdef OBJ_ELF +#define ELF_TC_SPECIAL_SECTIONS \ + { ".sdata", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_NIOS2_GPREL }, \ + { ".sbss", SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_NIOS2_GPREL }, \ + { ".lit4", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_NIOS2_GPREL }, \ + { ".lit8", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_NIOS2_GPREL }, + +/* Processor-specific section directives. */ +#define md_elf_section_letter nios2_elf_section_letter +extern int nios2_elf_section_letter (int, char **); +#define md_elf_section_flags nios2_elf_section_flags +extern flagword nios2_elf_section_flags (flagword, int, int); +#endif + +#define GLOBAL_OFFSET_TABLE_NAME "_GLOBAL_OFFSET_TABLE_" + +#define DIFF_EXPR_OK + +/* Nios2 ABI doesn't have 32-bit PCREL relocation, and, as relocations for + CFI information will be in section other than .text, we can't use PC-biased + relocs. */ +#define CFI_DIFF_EXPR_OK 0 + +#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) nios2_cons (EXP, NBYTES) +extern void nios2_cons (expressionS *exp, int size); + +#define TC_CONS_FIX_NEW nios2_cons_fix_new +extern void nios2_cons_fix_new (struct frag *frag, int where, + unsigned int nbytes, struct expressionS *exp); + +/* We want .cfi_* pseudo-ops for generating unwind info. */ +#define TARGET_USE_CFIPOP 1 +#define DWARF2_DEFAULT_RETURN_COLUMN 31 +#define DWARF2_CIE_DATA_ALIGNMENT (-4) +#define tc_regname_to_dw2regnum nios2_regname_to_dw2regnum +extern int nios2_regname_to_dw2regnum (char *regname); +#define tc_cfi_frame_initial_instructions nios2_frame_initial_instructions +extern void nios2_frame_initial_instructions (void); + +#endif /* TC_NIOS2 */ diff --git a/gas/configure.tgt b/gas/configure.tgt index 8454ab9a9ef..9d498e81e8b 100644 --- a/gas/configure.tgt +++ b/gas/configure.tgt @@ -350,6 +350,8 @@ case ${generic_target} in msp430-*-*) fmt=elf ;; + nios2*-linux*) fmt=elf em=linux ;; + ns32k-pc532-mach*) fmt=aout em=pc532mach ;; ns32k-pc532-ux*) fmt=aout em=pc532mach ;; ns32k-pc532-lites*) fmt=aout em=nbsd532 ;; diff --git a/gas/doc/Makefile.am b/gas/doc/Makefile.am index b200378ba67..3d1e9339bc8 100644 --- a/gas/doc/Makefile.am +++ b/gas/doc/Makefile.am @@ -74,6 +74,7 @@ CPU_DOCS = \ c-mmix.texi \ c-mt.texi \ c-msp430.texi \ + c-nios2.texi \ c-ns32k.texi \ c-pdp11.texi \ c-pj.texi \ diff --git a/gas/doc/Makefile.in b/gas/doc/Makefile.in index 9969ff4d33c..4c3c4fb8f94 100644 --- a/gas/doc/Makefile.in +++ b/gas/doc/Makefile.in @@ -316,6 +316,7 @@ CPU_DOCS = \ c-mmix.texi \ c-mt.texi \ c-msp430.texi \ + c-nios2.texi \ c-ns32k.texi \ c-pdp11.texi \ c-pj.texi \ @@ -410,17 +411,17 @@ as.info: as.texinfo $(as_TEXINFOS) fi; \ rm -rf $$backupdir; exit $$rc -as.dvi: as.texinfo $(as_TEXINFOS) +as.dvi: as.texinfo $(as_TEXINFOS) TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \ MAKEINFO='$(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir)' \ $(TEXI2DVI) -o $@ `test -f 'as.texinfo' || echo '$(srcdir)/'`as.texinfo -as.pdf: as.texinfo $(as_TEXINFOS) +as.pdf: as.texinfo $(as_TEXINFOS) TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \ MAKEINFO='$(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir)' \ $(TEXI2PDF) -o $@ `test -f 'as.texinfo' || echo '$(srcdir)/'`as.texinfo -as.html: as.texinfo $(as_TEXINFOS) +as.html: as.texinfo $(as_TEXINFOS) rm -rf $(@:.html=.htp) if $(MAKEINFOHTML) $(AM_MAKEINFOHTMLFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) \ -o $(@:.html=.htp) `test -f 'as.texinfo' || echo '$(srcdir)/'`as.texinfo; \ diff --git a/gas/doc/all.texi b/gas/doc/all.texi index b213ad76b5a..99dbf8ff727 100644 --- a/gas/doc/all.texi +++ b/gas/doc/all.texi @@ -58,6 +58,7 @@ @set MMIX @set MS1 @set MSP430 +@set NIOSII @set NS32K @set PDP11 @set PJ diff --git a/gas/doc/as.texinfo b/gas/doc/as.texinfo index c976c05381e..cafcb22c995 100644 --- a/gas/doc/as.texinfo +++ b/gas/doc/as.texinfo @@ -431,6 +431,12 @@ gcc(1), ld(1), and the Info entries for @file{binutils} and @file{ld}. [@b{--no-expand}] [@b{--no-merge-gregs}] [@b{-x}] [@b{--linker-allocated-gregs}] @end ifset +@ifset NIOSII + +@emph{Target Nios II options:} + [@b{-relax-all}] [@b{-relax-section}] [@b{-no-relax}] + [@b{-EB}] [@b{-EL}] +@end ifset @ifset PDP11 @emph{Target PDP11 options:} @@ -1028,6 +1034,24 @@ unit coprocessor. The default is to assume an MMU for 68020 and up. @end table @end ifset +@ifset NIOSII + +@ifclear man +@xref{Nios II Options}, for the options available when @value{AS} is configured +for an Altera Nios II processor. +@end ifclear + +@ifset man +@c man begin OPTIONS +The following options are available when @value{AS} is configured for an +Altera Nios II processor. +@c man end +@c man begin INCLUDE +@include c-nios2.texi +@c ended inside the included file +@end ifset +@end ifset + @ifset PDP11 For details about the PDP-11 machine dependent features options, @@ -7059,6 +7083,9 @@ subject, see the hardware manufacturer's manual. @ifset MSP430 * MSP430-Dependent:: MSP430 Dependent Features @end ifset +@ifset NIOSII +* NiosII-Dependent:: Altera Nios II Dependent Features +@end ifset @ifset NS32K * NS32K-Dependent:: NS32K Dependent Features @end ifset @@ -7270,6 +7297,10 @@ family. @include c-msp430.texi @end ifset +@ifset NIOSII +@include c-nios2.texi +@end ifset + @ifset NS32K @include c-ns32k.texi @end ifset diff --git a/gas/doc/c-nios2.texi b/gas/doc/c-nios2.texi new file mode 100644 index 00000000000..1d45dd20b23 --- /dev/null +++ b/gas/doc/c-nios2.texi @@ -0,0 +1,249 @@ +@c Copyright 2012, 2013 Free Software Foundation, Inc. +@c This is part of the GAS manual. +@c For copying conditions, see the file as.texinfo. +@c man end +@ifset GENERIC +@page +@node NiosII-Dependent +@chapter Nios II Dependent Features +@end ifset +@ifclear GENERIC +@node Machine Dependencies +@chapter Nios II Dependent Features +@end ifclear + +@cindex Altera Nios II support +@cindex Nios support +@cindex Nios II support +@menu +* Nios II Options:: Options +* Nios II Syntax:: Syntax +* Nios II Relocations:: Relocations +* Nios II Directives:: Nios II Machine Directives +* Nios II Opcodes:: Opcodes +@end menu + +@node Nios II Options +@section Options +@cindex Nios II options +@cindex options for Nios II + +@c man begin OPTIONS +@table @gcctabopt + +@cindex @code{relax-section} command line option, Nios II +@item -relax-section +Replace identified out-of-range branches with PC-relative @code{jmp} +sequences when possible. The generated code sequences are suitable +for use in position-independent code, but there is a practical limit +on the extended branch range because of the length of the sequences. +This option is the default. + +@cindex @code{relax-all} command line option, Nios II +@item -relax-all +Replace branch instructions not determinable to be in range +and all call instructions with @code{jmp} and @code{callr} sequences +(respectively). This option generates absolute relocations against the +target symbols and is not appropriate for position-independent code. + +@cindex @code{no-relax} command line option, Nios II +@item -no-relax +Do not replace any branches or calls. + +@cindex @code{EB} command line option, Nios II +@item -EB +Generate big-endian output. + +@cindex @code{EL} command line option, Nios II +@item -EL +Generate little-endian output. This is the default. + +@end table +@c man end + +@node Nios II Syntax +@section Syntax +@menu +* Nios II Chars:: Special Characters +@end menu + + +@node Nios II Chars +@subsection Special Characters + +@cindex line comment character, Nios II +@cindex Nios II line comment character +@cindex line separator character, Nios II +@cindex Nios II line separator character +@samp{#} is the line comment character. +@samp{;} is the line separator character. + + +@node Nios II Relocations +@section Nios II Machine Relocations + +@cindex machine relocations, Nios II +@cindex Nios II machine relocations + +@table @code +@cindex @code{hiadj} directive, Nios II +@item %hiadj(@var{expression}) +Extract the upper 16 bits of @var{expression} and add +one if the 15th bit is set. + +The value of @code{%hiadj(@var{expression})} is: +@smallexample +((@var{expression} >> 16) & 0xffff) + ((@var{expression} >> 15) & 0x01) +@end smallexample + +The @code{%hiadj} relocation is intended to be used with +the @code{addi}, @code{ld} or @code{st} instructions +along with a @code{%lo}, in order to load a 32-bit constant. + +@smallexample +movhi r2, %hiadj(symbol) +addi r2, r2, %lo(symbol) +@end smallexample + +@cindex @code{hi} directive, Nios II +@item %hi(@var{expression}) +Extract the upper 16 bits of @var{expression}. + +@cindex @code{lo} directive, Nios II +@item %lo(@var{expression}) +Extract the lower 16 bits of @var{expression}. + +@cindex @code{gprel} directive, Nios II +@item %gprel(@var{expression}) +Subtract the value of the symbol @code{_gp} from +@var{expression}. + +The intention of the @code{%gprel} relocation is +to have a fast small area of memory which only +takes a 16-bit immediate to access. + +@smallexample + .section .sdata +fastint: + .int 123 + .section .text + ldw r4, %gprel(fastint)(gp) +@end smallexample + +@cindex @code{call} directive, Nios II +@cindex @code{got} directive, Nios II +@cindex @code{gotoff} directive, Nios II +@cindex @code{gotoff_lo} directive, Nios II +@cindex @code{gotoff_hiadj} directive, Nios II +@cindex @code{tls_gd} directive, Nios II +@cindex @code{tls_ie} directive, Nios II +@cindex @code{tls_le} directive, Nios II +@cindex @code{tls_ldm} directive, Nios II +@cindex @code{tls_ldo} directive, Nios II +@item %call(@var{expression}) +@itemx %got(@var{expression}) +@itemx %gotoff(@var{expression}) +@itemx %gotoff_lo(@var{expression}) +@itemx %gotoff_hiadj(@var{expression}) +@itemx %tls_gd(@var{expression}) +@itemx %tls_ie(@var{expression}) +@itemx %tls_le(@var{expression}) +@itemx %tls_ldm(@var{expression}) +@itemx %tls_ldo(@var{expression}) + +These relocations support the ABI for Linux Systems documented in the +@cite{Nios II Processor Reference Handbook}. +@end table + + +@node Nios II Directives +@section Nios II Machine Directives + +@cindex machine directives, Nios II +@cindex Nios II machine directives + +@table @code + +@cindex @code{align} directive, Nios II +@item .align @var{expression} [, @var{expression}] +This is the generic @code{.align} directive, however +this aligns to a power of two. + +@cindex @code{half} directive, Nios II +@item .half @var{expression} +Create an aligned constant 2 bytes in size. + +@cindex @code{word} directive, Nios II +@item .word @var{expression} +Create an aligned constant 4 bytes in size. + +@cindex @code{dword} directive, Nios II +@item .dword @var{expression} +Create an aligned constant 8 bytes in size. + +@cindex @code{2byte} directive, Nios II +@item .2byte @var{expression} +Create an unaligned constant 2 bytes in size. + +@cindex @code{4byte} directive, Nios II +@item .4byte @var{expression} +Create an unaligned constant 4 bytes in size. + +@cindex @code{8byte} directive, Nios II +@item .8byte @var{expression} +Create an unaligned constant 8 bytes in size. + +@cindex @code{16byte} directive, Nios II +@item .16byte @var{expression} +Create an unaligned constant 16 bytes in size. + +@cindex @code{set noat} directive, Nios II +@item .set noat +Allows assembly code to use @code{at} register without +warning. Macro or relaxation expansions +generate warnings. + +@cindex @code{set at} directive, Nios II +@item .set at +Assembly code using @code{at} register generates +warnings, and macro expansion and relaxation are +enabled. + +@cindex @code{set nobreak} directive, Nios II +@item .set nobreak +Allows assembly code to use @code{ba} and @code{bt} +registers without warning. + +@cindex @code{set break} directive, Nios II +@item .set break +Turns warnings back on for using @code{ba} and @code{bt} +registers. + +@cindex @code{set norelax} directive, Nios II +@item .set norelax +Do not replace any branches or calls. + +@cindex @code{set relaxsection} directive, Nios II +@item .set relaxsection +Replace identified out-of-range branches with +@code{jmp} sequences (default). + +@cindex @code{set relaxall} directive, Nios II +@item .set relaxsection +Replace all branch and call instructions with +@code{jmp} and @code{callr} sequences. + +@cindex @code{set} directive, Nios II +@item .set @dots{} +All other @code{.set} are the normal use. + +@end table + +@node Nios II Opcodes +@section Opcodes + +@cindex Nios II opcodes +@cindex opcodes for Nios II +@code{@value{AS}} implements all the standard Nios II opcodes documented in the +@cite{Nios II Processor Reference Handbook}, including the assembler +pseudo-instructions. diff --git a/gas/testsuite/ChangeLog b/gas/testsuite/ChangeLog index ab916891ed5..e165af4136c 100644 --- a/gas/testsuite/ChangeLog +++ b/gas/testsuite/ChangeLog @@ -1,3 +1,94 @@ +2013-02-06 Sandra Loosemore + Andrew Jenner + + Based on patches from Altera Corporation. + + * gas/nios2/add.d: New. + * gas/nios2/add.s: New. + * gas/nios2/align_fill.d: New. + * gas/nios2/align_fill.s: New. + * gas/nios2/align_text.d: New. + * gas/nios2/align_text.s: New. + * gas/nios2/and.d: New. + * gas/nios2/and.s: New. + * gas/nios2/branch.d: New. + * gas/nios2/branch.s: New. + * gas/nios2/break.d: New. + * gas/nios2/break.s: New. + * gas/nios2/bret.d: New. + * gas/nios2/bret.s: New. + * gas/nios2/cache.d: New. + * gas/nios2/cache.s: New. + * gas/nios2/call26.d: New. + * gas/nios2/call26.s: New. + * gas/nios2/call.d: New. + * gas/nios2/call.s: New. + * gas/nios2/cmp.d: New. + * gas/nios2/cmp.s: New. + * gas/nios2/comments.d: New. + * gas/nios2/comments.s: New. + * gas/nios2/complex.d: New. + * gas/nios2/complex.s: New. + * gas/nios2/ctl.d: New. + * gas/nios2/ctl.s: New. + * gas/nios2/custom.d: New. + * gas/nios2/custom.s: New. + * gas/nios2/etbt.d: New. + * gas/nios2/etbt.s: New. + * gas/nios2/flushda.d: New. + * gas/nios2/flushda.s: New. + * gas/nios2/illegal.l: New. + * gas/nios2/illegal.s: New. + * gas/nios2/jmp.d: New. + * gas/nios2/jmp.s: New. + * gas/nios2/ldb.d: New. + * gas/nios2/ldb.s: New. + * gas/nios2/ldh.d: New. + * gas/nios2/ldh.s: New. + * gas/nios2/ldw.d: New. + * gas/nios2/ldw.s: New. + * gas/nios2/lineseparator.d: New. + * gas/nios2/lineseparator.s: New. + * gas/nios2/mov.d: New. + * gas/nios2/movia.d: New. + * gas/nios2/movia.s: New. + * gas/nios2/movi.d: New. + * gas/nios2/movi.s: New. + * gas/nios2/mov.s: New. + * gas/nios2/mul.d: New. + * gas/nios2/mul.s: New. + * gas/nios2/nios2.exp: New. + * gas/nios2/nor.d: New. + * gas/nios2/nor.s: New. + * gas/nios2/or.d: New. + * gas/nios2/or.s: New. + * gas/nios2/ret.d: New. + * gas/nios2/ret.s: New. + * gas/nios2/rol.d: New. + * gas/nios2/rol.s: New. + * gas/nios2/rotate.d: New. + * gas/nios2/rotate.s: New. + * gas/nios2/stb.d: New. + * gas/nios2/stb.s: New. + * gas/nios2/sth.d: New. + * gas/nios2/sth.s: New. + * gas/nios2/stw.d: New. + * gas/nios2/stw.s: New. + * gas/nios2/sub.d: New. + * gas/nios2/sub.s: New. + * gas/nios2/sync.d: New. + * gas/nios2/sync.s: New. + * gas/nios2/trap.d: New. + * gas/nios2/trap.s: New. + * gas/nios2/tret.d: New. + * gas/nios2/tret.s: New. + * gas/nios2/warn_noat.l: New. + * gas/nios2/warn_noat.s: New. + * gas/nios2/warn_nobreak.l: New. + * gas/nios2/warn_nobreak.s: New. + * gas/nios2/xor.d: New. + * gas/nios2/xor.s: New. + 2013-01-31 Tristan Gingold * gas/ppc/test1xcoff32.d: Updated. diff --git a/gas/testsuite/gas/nios2/add.d b/gas/testsuite/gas/nios2/add.d new file mode 100644 index 00000000000..ba3d27fb18e --- /dev/null +++ b/gas/testsuite/gas/nios2/add.d @@ -0,0 +1,16 @@ +#objdump: -dr --prefix-addresses +#name: NIOS2 add + +# Test the add instruction + +.*: +file format elf32-littlenios2 + +Disassembly of section .text: +0+0000 <[^>]*> add r4,r4,r4 +0+0004 <[^>]*> addi r4,r4,32767 +0+0008 <[^>]*> addi r4,r4,-32768 +0+000c <[^>]*> addi r4,r4,0 +0+0010 <[^>]*> addi r4,r4,-1 +0+0014 <[^>]*> addi r4,r4,-1 +0+0018 <[^>]*> addi r4,r4,13398 +0+001c <[^>]*> nop diff --git a/gas/testsuite/gas/nios2/add.s b/gas/testsuite/gas/nios2/add.s new file mode 100644 index 00000000000..5b72a82741d --- /dev/null +++ b/gas/testsuite/gas/nios2/add.s @@ -0,0 +1,13 @@ +# Source file used to test the add and addi instructions. + +foo: + add r4,r4,r4 + addi r4,r4,0x7fff + addi r4,r4,-0x8000 + addi r4,r4,0x0 + addi r4,r4,-0x01 + subi r4,r4,0x01 + addi r4,r4,0x3456 + +# should disassemble to add r0,0,r0 + nop diff --git a/gas/testsuite/gas/nios2/align_fill.d b/gas/testsuite/gas/nios2/align_fill.d new file mode 100644 index 00000000000..90a9e5fbaa3 --- /dev/null +++ b/gas/testsuite/gas/nios2/align_fill.d @@ -0,0 +1,23 @@ +#objdump: -dr --prefix-addresses +#name: NIOS2 align_fill + +# Test the and macro. + +.*: +file format elf32-littlenios2 + +Disassembly of section .text: +0+0000 <[^>]*> addi sp,sp,-8 +0+0004 <[^>]*> stw fp,4\(sp\) +0+0008 <[^>]*> mov fp,sp +0+000c <[^>]*> mov r3,zero +0+0010 <[^>]*> nop +0+0014 <[^>]*> nop +0+0018 <[^>]*> nop +0+001c <[^>]*> nop +0+0020 <[^>]*> addi r3,r3,1 +0+0024 <[^>]*> cmplti r2,r3,100 +0+0028 <[^>]*> bne r2,zero,0+0020 <[^>*]*> +0+002c <[^>]*> ldw fp,4\(sp\) +0+0030 <[^>]*> addi sp,sp,8 +0+0034 <[^>]*> ret + ... diff --git a/gas/testsuite/gas/nios2/align_fill.s b/gas/testsuite/gas/nios2/align_fill.s new file mode 100644 index 00000000000..56838395465 --- /dev/null +++ b/gas/testsuite/gas/nios2/align_fill.s @@ -0,0 +1,20 @@ + .file "a.c" + .section .text + .align 3 + .global x + .type x, @function +x: + addi sp, sp, -8 + stw fp, 4(sp) + mov fp, sp + mov r3, zero + .align 5 +.L6: + addi r3, r3, 1 + cmplti r2, r3, 100 + bne r2, zero, .L6 + ldw fp, 4(sp) + addi sp, sp, 8 + ret + .size x, .-x + .ident "GCC: (GNU) 3.3.3 (Altera Nios II 1.0 b302)" diff --git a/gas/testsuite/gas/nios2/align_text.d b/gas/testsuite/gas/nios2/align_text.d new file mode 100644 index 00000000000..80611e76c61 --- /dev/null +++ b/gas/testsuite/gas/nios2/align_text.d @@ -0,0 +1,22 @@ +#objdump: -dr +#name: NIOS2 align_test + +# Test alignment in text sections. + +.*: +file format elf32-littlenios2 + +Disassembly of section .text: +00000000 : + 0: 00000000 call 0 + 4: 0001883a nop + 8: 0001883a nop + c: 0001883a nop + 10: 0001883a nop + 14: 0001883a nop + 18: 0001883a nop + 1c: 0001883a nop + +00000020