diff --git a/bfd/Makefile.am b/bfd/Makefile.am index de811af..d55e681 100644 --- a/bfd/Makefile.am +++ b/bfd/Makefile.am @@ -82,6 +82,7 @@ ALL_MACHINES = \ cpu-mips.lo \ cpu-mmix.lo \ cpu-msp430.lo \ + cpu-nios2.lo \ cpu-or32.lo \ cpu-ns32k.lo \ cpu-openrisc.lo \ @@ -139,6 +140,7 @@ ALL_MACHINES_CFILES = \ cpu-msp430.c \ cpu-or32.c \ cpu-ns32k.c \ + cpu-nios2.c \ cpu-openrisc.c \ cpu-pdp11.c \ cpu-pj.c \ @@ -241,6 +243,7 @@ BFD32_BACKENDS = \ elfxx-mips.lo \ elf32-mips.lo \ elf32-msp430.lo \ + elf32-nios2.lo \ elf32-openrisc.lo \ elf32-or32.lo \ elf32-pj.lo \ @@ -405,6 +408,7 @@ BFD32_BACKENDS_CFILES = \ elfxx-mips.c \ elf32-mips.c \ elf32-msp430.c \ + elf32-nios2.c \ elf32-openrisc.c \ elf32-or32.c \ elf32-pj.c \ @@ -943,6 +947,7 @@ cpu-mcore.lo: cpu-mcore.c $(INCDIR)/filenames.h cpu-mips.lo: cpu-mips.c $(INCDIR)/filenames.h cpu-mmix.lo: cpu-mmix.c $(INCDIR)/filenames.h cpu-msp430.lo: cpu-msp430.c $(INCDIR)/filenames.h +cpu-nios2.lo: cpu-nios2.c $(INCDIR)/filenames.h cpu-or32.lo: cpu-or32.c $(INCDIR)/filenames.h cpu-ns32k.lo: cpu-ns32k.c $(INCDIR)/filenames.h ns32k.h cpu-openrisc.lo: cpu-openrisc.c $(INCDIR)/filenames.h @@ -1250,6 +1255,10 @@ elf32-msp430.lo: elf32-msp430.c $(INCDIR)/filenames.h \ $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h \ $(INCDIR)/elf/msp430.h $(INCDIR)/elf/reloc-macros.h \ elf32-target.h +elf32-nios2.lo: elf32-nios2.c $(INCDIR)/filenames.h $(INCDIR)/bfdlink.h \ + genlink.h elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \ + $(INCDIR)/elf/external.h $(INCDIR)/elf/nios2.h \ + $(INCDIR)/elf/reloc-macros.h elf32-target.h elf32-openrisc.lo: elf32-openrisc.c $(INCDIR)/filenames.h \ elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \ $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(INCDIR)/elf/openrisc.h \ diff --git a/bfd/Makefile.in b/bfd/Makefile.in index a6685f2..1ea97fe 100644 --- a/bfd/Makefile.in +++ b/bfd/Makefile.in @@ -210,6 +210,7 @@ ALL_MACHINES = \ cpu-mips.lo \ cpu-mmix.lo \ cpu-msp430.lo \ + cpu-nios2.lo \ cpu-or32.lo \ cpu-ns32k.lo \ cpu-openrisc.lo \ @@ -267,6 +268,7 @@ ALL_MACHINES_CFILES = \ cpu-mmix.c \ cpu-msp430.c \ cpu-or32.c \ + cpu-nios2.c \ cpu-ns32k.c \ cpu-openrisc.c \ cpu-pdp11.c \ @@ -371,6 +373,7 @@ BFD32_BACKENDS = \ elfxx-mips.lo \ elf32-mips.lo \ elf32-msp430.lo \ + elf32-nios2.lo \ elf32-openrisc.lo \ elf32-or32.lo \ elf32-pj.lo \ @@ -536,6 +539,7 @@ BFD32_BACKENDS_CFILES = \ elfxx-mips.c \ elf32-mips.c \ elf32-msp430.c \ + elf32-nios2.c \ elf32-openrisc.c \ elf32-or32.c \ elf32-pj.c \ @@ -1482,6 +1486,7 @@ cpu-mmix.lo: cpu-mmix.c $(INCDIR)/filenames.h cpu-msp430.lo: cpu-msp430.c $(INCDIR)/filenames.h cpu-or32.lo: cpu-or32.c $(INCDIR)/filenames.h cpu-ns32k.lo: cpu-ns32k.c $(INCDIR)/filenames.h ns32k.h +cpu-nios2.lo: cpu-nios2.c $(INCDIR)/filenames.h cpu-openrisc.lo: cpu-openrisc.c $(INCDIR)/filenames.h cpu-pdp11.lo: cpu-pdp11.c $(INCDIR)/filenames.h cpu-pj.lo: cpu-pj.c $(INCDIR)/filenames.h @@ -1787,6 +1792,10 @@ elf32-msp430.lo: elf32-msp430.c $(INCDIR)/filenames.h \ $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h \ $(INCDIR)/elf/msp430.h $(INCDIR)/elf/reloc-macros.h \ elf32-target.h +elf32-nios2.lo: elf32-nios2.c $(INCDIR)/filenames.h $(INCDIR)/bfdlink.h \ + genlink.h elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \ + $(INCDIR)/elf/external.h $(INCDIR)/elf/nios2.h \ + $(INCDIR)/elf/reloc-macros.h elf32-target.h elf32-openrisc.lo: elf32-openrisc.c $(INCDIR)/filenames.h \ elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \ $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(INCDIR)/elf/openrisc.h \ diff --git a/bfd/archures.c b/bfd/archures.c index f8aeeef..82fe291 100644 --- a/bfd/archures.c +++ b/bfd/archures.c @@ -330,6 +330,8 @@ DESCRIPTION .#define bfd_mach_msp44 44 . bfd_arch_xtensa, {* Tensilica's Xtensa cores. *} .#define bfd_mach_xtensa 1 +. bfd_arch_nios2, +.#define bfd_mach_nios2 1 . bfd_arch_last . }; */ @@ -422,6 +424,7 @@ extern const bfd_arch_info_type bfd_w65_arch; extern const bfd_arch_info_type bfd_xstormy16_arch; extern const bfd_arch_info_type bfd_xtensa_arch; extern const bfd_arch_info_type bfd_z8k_arch; +extern const bfd_arch_info_type bfd_nios2_arch; static const bfd_arch_info_type * const bfd_archures_list[] = { @@ -460,6 +463,7 @@ static const bfd_arch_info_type * const bfd_archures_list[] = &bfd_mn10200_arch, &bfd_mn10300_arch, &bfd_msp430_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 2464d27..1938170 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -1728,6 +1728,8 @@ enum bfd_architecture #define bfd_mach_msp44 44 bfd_arch_xtensa, /* Tensilica's Xtensa cores. */ #define bfd_mach_xtensa 1 + bfd_arch_nios2, +#define bfd_mach_nios2 1 bfd_arch_last }; @@ -3439,6 +3441,23 @@ This is the 5 bits of a value. */ BFD_RELOC_MSP430_16_PCREL_BYTE, BFD_RELOC_MSP430_16_BYTE, +/* Relocations used by the Altera New Jersey 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, + /* IQ2000 Relocations. */ BFD_RELOC_IQ2000_OFFSET_16, BFD_RELOC_IQ2000_OFFSET_21, diff --git a/bfd/config.bfd b/bfd/config.bfd index 1428831..caf64b5 100755 --- a/bfd/config.bfd +++ b/bfd/config.bfd @@ -59,6 +59,7 @@ m6812*|m68hc12*) targ_archs="bfd_m68hc12_arch bfd_m68hc11_arch" ;; m68*) targ_archs=bfd_m68k_arch ;; m88*) targ_archs=bfd_m88k_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";; @@ -874,6 +875,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 46c8170..8c35166 100755 --- a/bfd/configure +++ b/bfd/configure @@ -6322,6 +6322,8 @@ do bfd_elf32_mcore_little_vec) tb="$tb elf32-mcore.lo elf32.lo $elf" ;; bfd_elf32_mn10200_vec) tb="$tb elf-m10200.lo elf32.lo $elf" ;; bfd_elf32_mn10300_vec) tb="$tb elf-m10300.lo elf32.lo $elf" ;; + bfd_elf32_littlenios2_vec) tb="$tb elf32-nios2.lo elf32.lo $elf" ;; + bfd_elf32_bignios2_vec) tb="$tb elf32-nios2.lo elf32.lo $elf" ;; bfd_elf32_msp430_vec) tb="$tb elf32-msp430.lo elf32.lo $elf" ;; bfd_elf32_nbigmips_vec) tb="$tb elfn32-mips.lo elfxx-mips.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;; bfd_elf32_nlittlemips_vec) tb="$tb elfn32-mips.lo elfxx-mips.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;; diff --git a/bfd/configure.in b/bfd/configure.in index 71f41f4..eed3fa0 100644 --- a/bfd/configure.in +++ b/bfd/configure.in @@ -631,6 +631,8 @@ do bfd_elf32_mcore_little_vec) tb="$tb elf32-mcore.lo elf32.lo $elf" ;; bfd_elf32_mn10200_vec) tb="$tb elf-m10200.lo elf32.lo $elf" ;; bfd_elf32_mn10300_vec) tb="$tb elf-m10300.lo elf32.lo $elf" ;; + bfd_elf32_littlenios2_vec) tb="$tb elf32-nios2.lo elf32.lo $elf" ;; + bfd_elf32_bignios2_vec) tb="$tb elf32-nios2.lo elf32.lo $elf" ;; bfd_elf32_msp430_vec) tb="$tb elf32-msp430.lo elf32.lo $elf" ;; bfd_elf32_nbigmips_vec) tb="$tb elfn32-mips.lo elfxx-mips.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;; bfd_elf32_nlittlemips_vec) tb="$tb elfn32-mips.lo elfxx-mips.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;; diff --git a/bfd/cpu-nios2.c b/bfd/cpu-nios2.c new file mode 100644 index 0000000..c8f39c9 --- /dev/null +++ b/bfd/cpu-nios2.c @@ -0,0 +1,70 @@ +/* bfd back-end for Altera Nios II support + + Copyright (C) 2003 + by Nigel Gray (ngray@altera.com). + +This file is part of BFD, the Binary File Descriptor library. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "bfd.h" +#include "sysdep.h" +#include "libbfd.h" + +static const bfd_arch_info_type *nios2_compatible + (const bfd_arch_info_type *, const bfd_arch_info_type *); + +/* The default routine tests bits_per_word, which is wrong on mips as + mips word size doesn't correlate with reloc size. */ + +static const bfd_arch_info_type * +nios2_compatible (const bfd_arch_info_type *a, const bfd_arch_info_type *b) +{ + if (a->arch != b->arch) + return NULL; + + /* Machine compatibility is checked in + _bfd_mips_elf_merge_private_bfd_data. */ + + return a; +} + +#define N(BITS_WORD, BITS_ADDR, NUMBER, PRINT, DEFAULT, NEXT) \ + { \ + BITS_WORD, /* bits in a word */ \ + BITS_ADDR, /* bits in an address */ \ + 8, /* 8 bits in a byte */ \ + bfd_arch_nios2, \ + NUMBER, \ + "nios2", \ + PRINT, \ + 3, \ + DEFAULT, \ + nios2_compatible, \ + bfd_default_scan, \ + NEXT, \ + } + +#define NN(index) (&arch_info_struct[(index) + 1]) + +static const bfd_arch_info_type arch_info_struct[] = +{ + N (32, 32, bfd_mach_nios2, "nios2", FALSE, 0), +}; + +/* There is only one architecture - but we give the default a machine number of 0 + so the linker can distinguish it */ +const bfd_arch_info_type bfd_nios2_arch = +N (32, 32, 0, "nios2", TRUE, &arch_info_struct[0]); diff --git a/bfd/elf.c b/bfd/elf.c index a14fd35..3a061ec 100644 --- a/bfd/elf.c +++ b/bfd/elf.c @@ -3286,9 +3286,14 @@ map_sections_to_segments (bfd *abfd) phdr_size = elf_tdata (abfd)->program_header_size; if (phdr_size == 0) phdr_size = get_elf_backend_data (abfd)->s->sizeof_phdr; + + /* NG - for standalone embedded applications we don't want the program + headers or ELF header in the output memory map (cf CSP) */ if ((abfd->flags & D_PAGED) == 0 || sections[0]->lma < phdr_size - || sections[0]->lma % maxpagesize < phdr_size % maxpagesize) + || sections[0]->lma % maxpagesize < phdr_size % maxpagesize + || (elf_tdata (abfd)->elf_header[0].e_ident[EI_OSABI] + == ELFOSABI_STANDALONE)) phdr_in_segment = FALSE; } diff --git a/bfd/elf32-nios2.c b/bfd/elf32-nios2.c new file mode 100644 index 0000000..045b3a9 --- /dev/null +++ b/bfd/elf32-nios2.c @@ -0,0 +1,2191 @@ +/* New Jersey-specific support for 32-bit ELF + + Copyright (C) 2005 + by Nigel Gray (ngray@altera.com). + + +This file is part of BFD, the Binary File Descriptor library. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* This file handles Altera New Jersey ELF targets */ + +#include "bfd.h" +#include "sysdep.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 + +/* Function prototypes */ + +static reloc_howto_type *nios2_elf32_bfd_reloc_type_lookup + (bfd *, bfd_reloc_code_real_type); + +static bfd_boolean nios2_elf32_relax_section + (bfd *, asection *, struct bfd_link_info *, bfd_boolean *); + +static bfd_boolean nios2_elf32_relax_delete_bytes + (bfd *, asection *, bfd_vma, int); + +static reloc_howto_type *nios2_elf32_rtype_to_howto + (unsigned int r_type, bfd_boolean rela_p); + +static void nios2_elf32_info_to_howto + (bfd * abfd, arelent * cache_ptr, Elf_Internal_Rela * dst); + +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); + +static reloc_howto_type *lookup_howto (unsigned int rtype); + +static bfd_reloc_status_type nios2_elf_final_gp + (bfd *, asymbol *, bfd_boolean, char **, bfd_vma *, + struct bfd_link_info *); + +static bfd_boolean nios2_elf_assign_gp + (bfd *, bfd_vma *, struct bfd_link_info *); + +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_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 **); + +static bfd_reloc_status_type nios2_elf32_do_hi16_relocate + (bfd *, reloc_howto_type *, asection *, + bfd_byte *, bfd_vma, bfd_vma, bfd_vma); + +static bfd_reloc_status_type nios2_elf32_do_lo16_relocate + (bfd *, reloc_howto_type *, asection *, + bfd_byte *, bfd_vma, bfd_vma, bfd_vma); + +static bfd_reloc_status_type nios2_elf32_do_hiadj16_relocate + (bfd *, reloc_howto_type *, asection *, + bfd_byte *, bfd_vma, bfd_vma, bfd_vma); + +static bfd_reloc_status_type nios2_elf32_do_pcrel16_relocate + (bfd *, reloc_howto_type *, asection *, + bfd_byte *, bfd_vma, bfd_vma, bfd_vma); + +static bfd_reloc_status_type nios2_elf32_do_call26_relocate + (bfd *, reloc_howto_type *, asection *, + bfd_byte *, bfd_vma, bfd_vma, bfd_vma); + +static bfd_reloc_status_type nios2_elf32_do_gprel_relocate + (bfd *, reloc_howto_type *, asection *, + bfd_byte *, bfd_vma, bfd_vma, bfd_vma); + +static bfd_reloc_status_type nios2_elf32_do_ujmp_relocate + (bfd *, reloc_howto_type *, asection *, + bfd_byte *, bfd_vma, bfd_vma, bfd_vma); + +static bfd_reloc_status_type nios2_elf32_do_cjmp_relocate + (bfd *, reloc_howto_type *, asection *, + bfd_byte *, bfd_vma, bfd_vma, bfd_vma); + +static bfd_reloc_status_type nios2_elf32_do_callr_relocate + (bfd *, reloc_howto_type *, asection *, + bfd_byte *, bfd_vma, bfd_vma, bfd_vma); + + +static void nios2_elf32_post_process_headers + (bfd *, struct bfd_link_info *); + +static bfd_boolean nios2_elf32_section_from_shdr + (bfd *, Elf_Internal_Shdr *, const char *name); + +static bfd_boolean nios2_elf32_section_flags + (flagword *, Elf_Internal_Shdr *); + +static bfd_boolean nios2_elf32_fake_sections + (bfd *, Elf_Internal_Shdr *, asection *); + + + +static bfd_boolean nios2_elf32_check_relocs + (bfd *, struct bfd_link_info *, asection *, + const Elf_Internal_Rela *); + +static asection *nios2_elf32_gc_mark_hook (asection * sec, + struct bfd_link_info * + info, + Elf_Internal_Rela * rel, + struct elf_link_hash_entry + * h, + Elf_Internal_Sym * sym); + + +/* target vector */ +extern const bfd_target bfd_elf32_littlenios2_vec; +extern const bfd_target bfd_elf32_bignios2_vec; + +/* 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), + +/* add other relocations here */ +}; + +static unsigned char elf_code_to_howto_index[R_NIOS2_ILLEGAL + 1]; + +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 New Jersey + 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}, +}; + +/* 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; +} + +/* Helper function for nios2_elf32_info_to_howto */ + +static reloc_howto_type * +nios2_elf32_rtype_to_howto (unsigned int r_type, + bfd_boolean rela_p ATTRIBUTE_UNUSED) +{ + BFD_ASSERT (r_type < R_NIOS2_ILLEGAL); + return &elf_nios2_howto_table_rel[r_type]; +} + +/* 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); + cache_ptr->howto = nios2_elf32_rtype_to_howto (r_type, FALSE); + + // FIXME - do we need to do anything else here??? +} + +/* The assembler has output long jmp/call sequences for all calls + * and pc-relative branches that it cannot guarantee are within + * range, so the linker must attempt to "relax" these sequences to + * short branches and calls if it can. We avoid having to re-relax by + * replacing redundant instructions with nops instead of deleting them. + * + * + **/ +static bfd_boolean +nios2_elf32_relax_section (bfd * abfd, + asection * sec, + struct bfd_link_info *link_info, bfd_boolean * again) +{ + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Rela *internal_relocs; + Elf_Internal_Rela *irel, *irelend; + bfd_byte *contents = NULL; + Elf_Internal_Sym *isymbuf = NULL; + +#define OP_MATCH_NOP 0x0001883a + + /* Assume nothing changes. */ + *again = FALSE; + + /* We don't have to do anything for a relocatable link, if + this section does not have relocs, or if this is not a + code section. */ + if (link_info->relocatable + || (sec->flags & SEC_RELOC) == 0 + || sec->reloc_count == 0 || (sec->flags & SEC_CODE) == 0) + return TRUE; + + /* If this is the first time we have been called for this section, + initialize the cooked size. */ + if (sec->_cooked_size == 0) + sec->_cooked_size = sec->_raw_size; + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + + /* Get a copy of the native relocations. */ + internal_relocs = (_bfd_elf_link_read_relocs + (abfd, sec, (void *) NULL, (Elf_Internal_Rela *) NULL, + link_info->keep_memory)); + if (internal_relocs == NULL) + goto error_return; + + /* Walk through them looking for relaxing opportunities. */ + irelend = internal_relocs + sec->reloc_count; + for (irel = internal_relocs; irel < irelend; irel++) + { + bfd_vma symval; + + /* If this isn't something that can be relaxed, then ignore + this reloc. */ + if (ELF32_R_TYPE (irel->r_info) != (int) R_NIOS2_UJMP + && ELF32_R_TYPE (irel->r_info) != (int) R_NIOS2_CJMP + && ELF32_R_TYPE (irel->r_info) != (int) R_NIOS2_CALLR) + { + continue; + } + + /* Get the section contents if we haven't done so already. */ + if (contents == NULL) + { + /* Get cached copy if it exists. */ + if (elf_section_data (sec)->this_hdr.contents != NULL) + contents = elf_section_data (sec)->this_hdr.contents; + else + { + /* Go get them off disk. */ + contents = (bfd_byte *) bfd_malloc (sec->_raw_size); + if (contents == NULL) + goto error_return; + + if (!bfd_get_section_contents (abfd, sec, contents, + (file_ptr) 0, sec->_raw_size)) + goto error_return; + } + } + + /* Read this BFD's local symbols if we haven't done so already. */ + if (isymbuf == NULL && symtab_hdr->sh_info != 0) + { + isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; + if (isymbuf == NULL) + isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr, + symtab_hdr->sh_info, 0, + NULL, NULL, NULL); + if (isymbuf == NULL) + goto error_return; + } + + /* Get the value of the symbol referred to by the reloc. */ + if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info) + { + /* A local symbol. */ + Elf_Internal_Sym *isym; + asection *sym_sec; + + isym = isymbuf + ELF32_R_SYM (irel->r_info); + if (isym->st_shndx == SHN_UNDEF) + sym_sec = bfd_und_section_ptr; + else if (isym->st_shndx == SHN_ABS) + sym_sec = bfd_abs_section_ptr; + else if (isym->st_shndx == SHN_COMMON) + sym_sec = bfd_com_section_ptr; + else + sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx); + symval = (isym->st_value + + sym_sec->output_section->vma + sym_sec->output_offset); + } + else + { + unsigned long indx; + struct elf_link_hash_entry *h; + + /* An external symbol. */ + indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info; + h = elf_sym_hashes (abfd)[indx]; + BFD_ASSERT (h != NULL); + if (h->root.type != bfd_link_hash_defined + && h->root.type != bfd_link_hash_defweak) + { + /* This appears to be a reference to an undefined + symbol. Just ignore it--it will be caught by the + regular reloc processing. */ + continue; + } + + symval = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + } + + /* For simplicity of coding, we are going to modify the section + contents, the section relocs, and the BFD symbol table. We + must tell the rest of the code not to free up this + information. It would be possible to instead create a table + of changes which have to be made, as is done in coff-mips.c; + that would be more work, but would require less memory when + the linker is run. */ + + /* try to turn : + * movhi at, %hi(symbol) + * movui at, %lo(symbol) + * callr at + * into: + * call symbol + */ + if (ELF32_R_TYPE (irel->r_info) == (int) R_NIOS2_CALLR) + { + bfd_vma targ_addr = symval + irel->r_addend; + bfd_vma curr_addr = (sec->output_section->vma + sec->output_offset); + bfd_vma targ_page, curr_page; + targ_page = targ_addr & 0xf0000000; + curr_page = curr_addr & 0xf0000000; + + if (targ_page == curr_page) + { + /* change the opcode to a call */ + bfd_put_32 (abfd, OP_MATCH_CALL, contents + irel->r_offset); + /* Note that we've changed the relocs, section contents, etc. */ + elf_section_data (sec)->relocs = internal_relocs; + elf_section_data (sec)->this_hdr.contents = contents; + symtab_hdr->contents = (unsigned char *) isymbuf; + + /* Fix the relocation's type. */ + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + R_NIOS2_CALL26); + + /* replace next two instructions with nops */ + bfd_put_32 (abfd, OP_MATCH_NOP, contents + irel->r_offset + 4); + bfd_put_32 (abfd, OP_MATCH_NOP, contents + irel->r_offset + 8); + } + } + + /* try to turn : + * movhi at, %hi(symbol) + * movui at, %lo(symbol) + * jmp at + * into: + * br symbol + */ + if (ELF32_R_TYPE (irel->r_info) == (int) R_NIOS2_UJMP) + { + bfd_vma pcrel_offset; + Elf_Internal_Rela *irelalign = NULL; + Elf_Internal_Rela *irela = elf_section_data (sec)->relocs; + Elf_Internal_Rela *irelend = irel + sec->reloc_count; + + for (; irela < irelend; irela++) + { + if (ELF32_R_TYPE (irela->r_info) == (int) R_NIOS2_ALIGN + && irela->r_offset > irel->r_offset + 4 + && 8 < (1 << irela->r_addend)) + { + irelalign = irela; + break; + } + } + + /* calculate the pcrelative offset from current location */ + pcrel_offset = symval; + pcrel_offset -= (sec->output_section->vma + sec->output_offset); + pcrel_offset += irel->r_addend; + + /* we need to compute the pcrel_offset from the next instruction */ + pcrel_offset -= (irel->r_offset + 4); + + /* does this value fit in 16 bits */ + if ((irelalign == NULL && (long) pcrel_offset <= 0x8004 + && (long) pcrel_offset >= -0x8000) || (irelalign != NULL + && (long) pcrel_offset + <= 0x7ffc + && (long) pcrel_offset + >= -0x8000)) + { + /* change the opcode to an unconditional branch */ + bfd_put_32 (abfd, OP_MATCH_BR, contents + irel->r_offset); + /* Note that we've changed the relocs, section contents, etc. */ + elf_section_data (sec)->relocs = internal_relocs; + elf_section_data (sec)->this_hdr.contents = contents; + symtab_hdr->contents = (unsigned char *) isymbuf; + + /* Fix the relocation's type. */ + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + R_NIOS2_PCREL16); + + /* replace next two instructions with nops */ + bfd_put_32 (abfd, OP_MATCH_NOP, contents + irel->r_offset + 4); + bfd_put_32 (abfd, OP_MATCH_NOP, contents + irel->r_offset + 8); + } + } + + /* try to turn : + * b{cond} a, b skip + * movhi at, %hi(symbol) + * movui at, %lo(symbol) + * jmp at + * skip: + * ... + * into: + * br{opp_cond} a, b, symbol + */ + if (ELF32_R_TYPE (irel->r_info) == (int) R_NIOS2_CJMP) + { + bfd_vma pcrel_offset; + Elf_Internal_Rela *irelalign = NULL; + Elf_Internal_Rela *irela = elf_section_data (sec)->relocs; + Elf_Internal_Rela *irelend = irel + sec->reloc_count; + + for (; irela < irelend; irela++) + { + if (ELF32_R_TYPE (irela->r_info) == (int) R_NIOS2_ALIGN + && irela->r_offset > irel->r_offset + 4 + && 8 < (1 << irela->r_addend)) + { + irelalign = irela; + break; + } + } + + /* calculate the pcrelative offset from current location */ + pcrel_offset = symval; + pcrel_offset -= (sec->output_section->vma + sec->output_offset); + pcrel_offset += irel->r_addend; + + /* we need to compute the pcrel_offset from this instruction + * ie the movhi */ + pcrel_offset -= (irel->r_offset); + + /* does this value fit in 16 bits */ + if ((irelalign == NULL && (long) pcrel_offset <= 0x8008 + && (long) pcrel_offset >= -0x8000) || (irelalign != NULL + && (long) pcrel_offset + <= 0x7ffc + && (long) pcrel_offset + >= -0x8000)) + { + unsigned long opcode, op_a, op_b; + /* get the conditional branch opcode */ + opcode = bfd_get_32 (abfd, contents + irel->r_offset - 4); + /* reverse the condition */ + switch (opcode & OP_MASK_OP) + { + case OP_MATCH_BEQ: + opcode = (opcode & ~OP_MASK_OP) | OP_MATCH_BNE; + break; + case OP_MATCH_BNE: + opcode = (opcode & ~OP_MASK_OP) | OP_MATCH_BEQ; + break; + case OP_MATCH_BGE: + case OP_MATCH_BGEU: + case OP_MATCH_BLT: + case OP_MATCH_BLTU: + /* swap the operands */ + op_a = (opcode & OP_MASK_RRT) << 5; + op_b = (opcode & OP_MASK_RRS) >> 5; + opcode = + (opcode & ~(OP_MASK_RRS | OP_MASK_RRT)) | op_a | op_b; + break; + default: + fprintf (stderr, + "relaxation error - expecting conditional branch, aborting\n"); + abort (); + break; + } + + /* we must set the branch target to zero so that the skip over the jmp doesn't get + * added to the jmp */ + opcode = opcode & (~OP_MASK_IMM16); + + /* change the opcode to the reversed conditional branch */ + bfd_put_32 (abfd, opcode, contents + irel->r_offset - 4); + /* Note that we've changed the relocs, section contents, etc. */ + elf_section_data (sec)->relocs = internal_relocs; + elf_section_data (sec)->this_hdr.contents = contents; + symtab_hdr->contents = (unsigned char *) isymbuf; + + /* Fix the relocation's type. */ + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + R_NIOS2_PCREL16); + + /* this relocation's offset has also been reduced by 4 bytes */ + irel->r_offset -= 4; + + /* replace next two instructions with nops */ + bfd_put_32 (abfd, OP_MATCH_NOP, contents + irel->r_offset + 4); + bfd_put_32 (abfd, OP_MATCH_NOP, contents + irel->r_offset + 8); + bfd_put_32 (abfd, OP_MATCH_NOP, contents + irel->r_offset + 12); + } + } + + /* otherwise, leave alone */ + } + + if (isymbuf != NULL && symtab_hdr->contents != (unsigned char *) isymbuf) + { + if (!link_info->keep_memory) + free (isymbuf); + else + { + /* Cache the symbols for elf_link_input_bfd. */ + symtab_hdr->contents = (unsigned char *) isymbuf; + } + } + + if (contents != NULL + && elf_section_data (sec)->this_hdr.contents != contents) + { + if (!link_info->keep_memory) + free (contents); + else + { + /* Cache the section contents for elf_link_input_bfd. */ + elf_section_data (sec)->this_hdr.contents = contents; + } + } + + if (internal_relocs != NULL + && elf_section_data (sec)->relocs != internal_relocs) + free (internal_relocs); + + + return TRUE; + +error_return: + if (isymbuf != NULL && symtab_hdr->contents != (unsigned char *) isymbuf) + free (isymbuf); + if (contents != NULL + && elf_section_data (sec)->this_hdr.contents != contents) + free (contents); + if (internal_relocs != NULL + && elf_section_data (sec)->relocs != internal_relocs) + free (internal_relocs); + + return FALSE; +} + +/* Delete some bytes from a section while relaxing. + * Copied from mn10200 port */ + +static bfd_boolean +nios2_elf32_relax_delete_bytes (bfd * abfd, + asection * sec, bfd_vma addr, int count) +{ + Elf_Internal_Shdr *symtab_hdr; + unsigned int sec_shndx; + bfd_byte *contents; + Elf_Internal_Rela *irel, *irelend; + Elf_Internal_Rela *irelalign; + bfd_vma toaddr; + Elf_Internal_Sym *isym; + Elf_Internal_Sym *isymend; + struct elf_link_hash_entry **sym_hashes; + struct elf_link_hash_entry **end_hashes; + unsigned int symcount; + asection *asec; + + sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec); + + contents = elf_section_data (sec)->this_hdr.contents; + + /* The deletion must stop at the next ALIGN reloc for an aligment + power larger than the number of bytes we are deleting. */ + + irelalign = NULL; + /* +1 because we need to readjust symbols at end of section */ + toaddr = sec->_cooked_size + 1; + + irel = elf_section_data (sec)->relocs; + irelend = irel + sec->reloc_count; + + for (; irel < irelend; irel++) + { + if (ELF32_R_TYPE (irel->r_info) == (int) R_NIOS2_ALIGN + && irel->r_offset > addr && count < (1 << irel->r_addend)) + { + irelalign = irel; + /* +1 because we need to readjust symbols at end of section */ + toaddr = irel->r_offset + 1; + break; + } + } + + + /* Actually delete the bytes. */ + memmove (contents + addr, contents + addr + count, + (size_t) ((toaddr - 1) - addr - count)); + + if (irelalign == NULL) + sec->_cooked_size -= count; + else + { + int i; + +#define NOP_OPCODE (0x0001883a) + + BFD_ASSERT ((count & 3) == 0); + for (i = 0; i < count; i += 4) + bfd_put_32 (abfd, (bfd_vma) NOP_OPCODE, + contents + (toaddr - 1) - count + i); + } + + /* get the symbol table */ + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + isym = (Elf_Internal_Sym *) symtab_hdr->contents; + + /* Adjust all the reloc offsets in this section. */ + for (irel = elf_section_data (sec)->relocs; irel < irelend; irel++) + { + /* Get the new reloc address. */ + if ((irel->r_offset > addr && irel->r_offset < toaddr)) + irel->r_offset -= count; + } + + /* Adjust relocations against targets in this section whose positions + * have moved as a result of the relaxation */ + + for (asec = abfd->sections; asec; asec = asec->next) + { + irelend = elf_section_data (asec)->relocs + asec->reloc_count; + for (irel = elf_section_data (asec)->relocs; irel < irelend; irel++) + { + Elf_Internal_Sym *sym; + /* if the symbol which this reloc is against doesn't change + * we need to change the reloc addend */ + + sym = isym + ELF32_R_SYM (irel->r_info); + if (sym->st_shndx == sec_shndx + && !(sym->st_value > addr && sym->st_value < toaddr) + && sym->st_value + irel->r_addend > addr + && sym->st_value + irel->r_addend < toaddr) + { + irel->r_addend -= count; + } + + } + } + + /* Adjust the local symbols defined in this section. */ + for (isymend = isym + symtab_hdr->sh_info; isym < isymend; isym++) + { + if (isym->st_shndx == sec_shndx + && isym->st_value > addr && isym->st_value < toaddr) + isym->st_value -= count; + + + } + + /* Now adjust the global symbols defined in this section. */ + symcount = (symtab_hdr->sh_size / sizeof (Elf32_External_Sym) + - symtab_hdr->sh_info); + sym_hashes = elf_sym_hashes (abfd); + end_hashes = sym_hashes + symcount; + for (; sym_hashes < end_hashes; sym_hashes++) + { + struct elf_link_hash_entry *sym_hash = *sym_hashes; + if ((sym_hash->root.type == bfd_link_hash_defined + || sym_hash->root.type == bfd_link_hash_defweak) + && sym_hash->root.u.def.section == sec + && sym_hash->root.u.def.value > addr + && sym_hash->root.u.def.value < toaddr) + { + sym_hash->root.u.def.value -= count; + } + } + + return TRUE; +} + +struct bfd_link_info *nios2_link_info = NULL; + +void +_bfd_set_link_info (info) + struct bfd_link_info *info; +{ + nios2_link_info = info; +} + +bfd_boolean linker_force_make_executable = FALSE; + +void +_bfd_set_force_make_executable (force) + bfd_boolean force; +{ + linker_force_make_executable = force; +} + +/* 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; +} + +/* We have to figure out the gp value, so that we can adjust the + symbol value correctly. We look up the symbol _gp in the output + BFD. If we can't find it, we're stuck. We cache it in the ELF + target data. We don't need to adjust the symbol value for an + external symbol if we are producing relocatable output. */ + +static bfd_reloc_status_type +nios2_elf_final_gp (bfd *output_bfd, asymbol *symbol, bfd_boolean relocatable, + char **error_message, bfd_vma *pgp, struct bfd_link_info *info) +{ + 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 this is called without link_info, then + we cannot be doing a final link */ + if (info == NULL) + relocatable = TRUE; + + if (relocatable) + { + /* Make up a value. */ + *pgp = symbol->section->output_section->vma + 0x4000; + _bfd_set_gp_value (output_bfd, *pgp); + } + else if (!nios2_elf_assign_gp (output_bfd, pgp, info)) + { + *error_message = + (char *) + _("global pointer relative relocation when _gp not defined"); + return bfd_reloc_dangerous; + } + } + + return bfd_reloc_ok; +} + + +/* 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 != (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_ok; + + 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 != (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_ok; + + 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 != (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_ok; + + 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_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 != (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_ok; + + 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 != (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_ok; + + 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 != (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_ok; + + relocation = symbol->value + + symbol->section->output_section->vma + symbol->section->output_offset; + + if ((r = + nios2_elf_final_gp (abfd, symbol, FALSE, msg, &gp, + nios2_link_info)) == 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 != (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_ok; + + 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 != (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_ok; + + 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 != (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_ok; + + + 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); +} + +/* Do the relocations which 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 = + ((symbol_value >> 16) & 0xffff) + ((symbol_value >> 15) & 0x01); + 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; +} + +/* + The function nios2_elf32_relocate_section is used by the linker + to perform relocations +*/ +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; + + symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (input_bfd); + relend = relocs + input_section->reloc_count; + +// size_t psymalloc = 0; +// _bfd_generic_link_output_symbols(output_bfd, input_bfd, info, &psymalloc); + 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; + 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; + + + + r_type = ELF32_R_TYPE (rel->r_info); + + r_symndx = ELF32_R_SYM (rel->r_info); + + if (info->relocatable) + { + /* This is a relocatable link. We don't have to change + anything, unless the reloc is against a section symbol, + in which case we have to adjust according to where the + section symbol winds up in the output section. */ + if (r_symndx < symtab_hdr->sh_info) + { + sym = local_syms + r_symndx; + + if (ELF_ST_TYPE (sym->st_info) == STT_SECTION) + { + sec = local_sections[r_symndx]; + rel->r_addend += sec->output_offset + sym->st_value; + } + } + continue; + } + + /* This is a final link. */ + 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 = (sec->output_section->vma + + sec->output_offset + sym->st_value); + + // this ensures that relocations against duplicated symbols + // in merged sections that have been removed are fixed up against + // the remaining symbol and not the one that has been removed + if ((sec->flags & SEC_MERGE) + && ELF_ST_TYPE (sym->st_info) == STT_SECTION) + { + rel->r_addend = + _bfd_elf_rel_local_sym (output_bfd, sym, &sec, rel->r_addend); + rel->r_addend -= relocation; + rel->r_addend += sec->output_section->vma + sec->output_offset; + } + + name = bfd_elf_string_from_elf_section + (input_bfd, symtab_hdr->sh_link, sym->st_name); + + name = (name == NULL) ? bfd_section_name (input_bfd, sec) : name; + } + 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; + + name = h->root.root.string; + + if (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + { + sec = h->root.u.def.section; + + relocation = (h->root.u.def.value + + sec->output_section->vma + sec->output_offset); + } + else if (h->root.type == bfd_link_hash_undefweak) + { + relocation = 0; + } + else + { + if (!((*info->callbacks->undefined_symbol) + (info, h->root.root.string, input_bfd, + input_section, rel->r_offset, TRUE))) + return FALSE; + relocation = 0; + } + } + + if (sec) + reloc_address = sec->output_section->vma + sec->output_offset + rel->r_offset; + else + reloc_address = 0; + + if (howto != NULL) + { + 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_HIADJ16: + r = + nios2_elf32_do_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) + { + 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: + 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; + /* comment - 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; + 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, 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 linker_force_make_executable; + } + } + } + return TRUE; +} + + + +/* Handle an NIOS2 specific section when reading an object file. This + is called when elfcode.h finds a section with an unknown type. + FIXME: We need to handle the SHF_NIOS2_GPREL flag */ + +static bfd_boolean +nios2_elf32_section_from_shdr (bfd *abfd, + Elf_Internal_Shdr *hdr, const char *name) +{ + asection *newsect; + + /* NG - I'm keeping this code commented out at the moment + in case we add a .mdebug section */ + + /* + switch (hdr->sh_type) + { + case SHT_NIOS2_DEBUG: + if (strcmp (name, ".mdebug") != 0) + return FALSE; + break; + default: + return FALSE; + } + */ + + if (!_bfd_elf_make_section_from_shdr (abfd, hdr, name)) + return FALSE; + + newsect = hdr->bfd_section; + + /* ditto */ + /* + if (hdr->sh_type == SHT_NIOS2_DEBUG) + { + if (! bfd_set_section_flags (abfd, newsect, + (bfd_get_section_flags (abfd, newsect) + | SEC_DEBUGGING))) + return FALSE; + } + */ + return TRUE; +} + +/* Convert NIOS2 specific section flags to bfd internal section flags. */ + +static bfd_boolean +nios2_elf32_section_flags (flagword *flags, Elf_Internal_Shdr *hdr) +{ + if (hdr->sh_flags & SHF_NIOS2_GPREL) + *flags |= SEC_SMALL_DATA; + + return TRUE; +} + +/* 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; + + name = bfd_get_section_name (abfd, sec); + + if (strcmp (name, ".mdebug") == 0) + { + /* we don't yet have an .mdebug section, but I'm leaving this here + in case we ever do + hdr->sh_type = SHT_NIOS2_DEBUG; + + if ((abfd->flags & DYNAMIC) != 0 ) + hdr->sh_entsize = 0; + else + hdr->sh_entsize = 1; + */ + } + else 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; +} + +/* Look through the relocs for a section during the first phase. + Since we don't do .gots or .plts, we just need to consider the + virtual table relocs for gc. */ + +static bfd_boolean +nios2_elf32_check_relocs (bfd *abfd, struct bfd_link_info *info, + asection *sec, const Elf_Internal_Rela *relocs) +{ + Elf_Internal_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes, **sym_hashes_end; + const Elf_Internal_Rela *rel; + const Elf_Internal_Rela *rel_end; + + if (info->relocatable) + return TRUE; + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (abfd); + sym_hashes_end = + sym_hashes + symtab_hdr->sh_size / sizeof (Elf32_External_Sym); + if (!elf_bad_symtab (abfd)) + sym_hashes_end -= symtab_hdr->sh_info; + + rel_end = relocs + sec->reloc_count; + for (rel = relocs; rel < rel_end; rel++) + { + 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]; + + switch (ELF32_R_TYPE (rel->r_info)) + { + /* 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; + } + } + + return TRUE; +} + + +/* Return the section that should be marked against GC for a given + relocation. */ + +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: + break; + + default: + switch (h->root.type) + { + case bfd_link_hash_defined: + case bfd_link_hash_defweak: + return h->root.u.def.section; + + case bfd_link_hash_common: + return h->root.u.c.p->section; + + default: + break; + } + } + } + else + return bfd_section_from_elf_index (sec->owner, sym->st_shndx); + + return NULL; +} + +/* + NG ??? I'm marking the sections as standalone ie. I'm linking for + standalone embedded applications, not for UNIX System V or any other + OS/ABI - this may need to change when we deal with embedded PIC or + dynamic linking +*/ + +static void +nios2_elf32_post_process_headers (bfd *abfd, + struct bfd_link_info *link_info ATTRIBUTE_UNUSED) +{ + Elf_Internal_Ehdr *i_ehdrp; /* Elf file header, internal form */ + + i_ehdrp = elf_elfheader (abfd); + i_ehdrp->e_ident[EI_OSABI] = ELFOSABI_STANDALONE; +} + +#define ELF_ARCH bfd_arch_nios2 +#define ELF_MACHINE_CODE EM_ALTERA_NIOS2 + +/* for now we just make this 1, as we have no MMU in New Jersey */ + +#define ELF_MAXPAGESIZE 1 + +/* relocation table lookup macros */ + +#define bfd_elf32_bfd_reloc_type_lookup nios2_elf32_bfd_reloc_type_lookup + +/* JUMP_TABLE_LINK macros */ + +#define bfd_elf32_bfd_relax_section nios2_elf32_relax_section + +/* 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_relocate_section nios2_elf32_relocate_section +#define elf_backend_section_from_shdr nios2_elf32_section_from_shdr +#define elf_backend_section_flags nios2_elf32_section_flags +#define elf_backend_fake_sections nios2_elf32_fake_sections +#define elf_backend_post_process_headers nios2_elf32_post_process_headers +#define elf_backend_check_relocs nios2_elf32_check_relocs + +#define elf_backend_gc_mark_hook nios2_elf32_gc_mark_hook + + + +/* Support for SGI-ish mips targets. */ +#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" + +#include "elf32-target.h" diff --git a/bfd/libbfd.h b/bfd/libbfd.h index 62043f3..3335b04 100644 --- a/bfd/libbfd.h +++ b/bfd/libbfd.h @@ -1504,6 +1504,21 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@", "BFD_RELOC_MSP430_16", "BFD_RELOC_MSP430_16_PCREL_BYTE", "BFD_RELOC_MSP430_16_BYTE", + "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_IQ2000_OFFSET_16", "BFD_RELOC_IQ2000_OFFSET_21", "BFD_RELOC_IQ2000_UHI16", diff --git a/bfd/reloc.c b/bfd/reloc.c index 9bffaa3..8807b30 100644 --- a/bfd/reloc.c +++ b/bfd/reloc.c @@ -3943,6 +3943,39 @@ 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 +ENUMDOC + Relocations used by the Altera New Jersey core + +ENUM BFD_RELOC_IQ2000_OFFSET_16 ENUMX BFD_RELOC_IQ2000_OFFSET_21 diff --git a/bfd/srec.c b/bfd/srec.c index c0a3d58..1a1d0ce 100644 --- a/bfd/srec.c +++ b/bfd/srec.c @@ -1009,9 +1009,14 @@ srec_write_header (abfd) { unsigned int len = strlen (abfd->filename); - /* I'll put an arbitrary 40 char limit on header size. */ - if (len > 40) - len = 40; + /* validate Chunk for header */ + if (Chunk == 0) + Chunk = 1; + else if (Chunk > MAXCHUNK - 2) /* S0 has 2 address bytes */ + Chunk = MAXCHUNK - 2; + + if (len > Chunk) + len = Chunk; return srec_write_record (abfd, 0, (bfd_vma) 0, abfd->filename, abfd->filename + len); diff --git a/bfd/targets.c b/bfd/targets.c index cec339c..1833c94 100644 --- a/bfd/targets.c +++ b/bfd/targets.c @@ -559,6 +559,8 @@ extern const bfd_target bfd_elf32_nbigmips_vec; extern const bfd_target bfd_elf32_nlittlemips_vec; extern const bfd_target bfd_elf32_ntradbigmips_vec; extern const bfd_target bfd_elf32_ntradlittlemips_vec; +extern const bfd_target bfd_elf32_littlenios2_vec; +extern const bfd_target bfd_elf32_bignios2_vec; extern const bfd_target bfd_elf32_openrisc_vec; extern const bfd_target bfd_elf32_or32_big_vec; extern const bfd_target bfd_elf32_pj_vec; @@ -746,6 +748,8 @@ extern const bfd_target sco5_core_vec; extern const bfd_target trad_core_vec; extern const bfd_target bfd_elf32_am33lin_vec; +extern const bfd_target bfd_elf32_littlenios2_vec; +extern const bfd_target bfd_elf32_bignios2_vec; static const bfd_target * const _bfd_target_vector[] = { #ifdef SELECT_VECS @@ -854,6 +858,8 @@ static const bfd_target * const _bfd_target_vector[] = { &bfd_elf32_ntradbigmips_vec, &bfd_elf32_ntradlittlemips_vec, #endif + &bfd_elf32_littlenios2_vec, + &bfd_elf32_bignios2_vec, &bfd_elf32_openrisc_vec, &bfd_elf32_or32_big_vec, &bfd_elf32_pj_vec, diff --git a/binutils/nios2_binutils_xfail.lst b/binutils/nios2_binutils_xfail.lst new file mode 100644 index 0000000..3219223 --- /dev/null +++ b/binutils/nios2_binutils_xfail.lst @@ -0,0 +1 @@ +No xfails diff --git a/binutils/objcopy.c b/binutils/objcopy.c index 9623398..a1b7a05 100644 --- a/binutils/objcopy.c +++ b/binutils/objcopy.c @@ -1705,8 +1705,53 @@ copy_file (const char *input_filename, const char *output_filename, delete = ! copy_object (ibfd, obfd); + if (!strcmp (obfd->xvec->name, "srec") + && (!strcmp (ibfd->xvec->name, "elf32-bignios2") + || !strcmp (ibfd->xvec->name, "elf32-littlenios2"))) + { + /* + * We're copying a big-endian or little-endian elf to an srec. + * Tag the elf with a hardcoded S0 record so other tools know + * which endianness was in the original elf file. + * + * This is a hack, as srec files don't really have an + * endianness. We just need a way to tell tools like + * flash2dat.pl which endianness was originally present. + * + * At some point in the future, we should stop using srec files + * entirely: flash2dat and friends should just read the elf file + * directly. + */ + char *new_filename = NULL; + const char *orig_filename = obfd->filename; + int result; + + if (!strcmp (ibfd->xvec->name, "elf32-bignios2")) + { + new_filename = strdup("-EB"); + } + else + { + new_filename = strdup("-EL"); + } + obfd->filename = new_filename; + + result = bfd_close (obfd); + + if ( obfd != NULL && new_filename == obfd->filename ) + { + free(new_filename); + obfd->filename = orig_filename; + } + + if (!result) + RETURN_NONFATAL (output_filename); + } + else + { if (!bfd_close (obfd)) RETURN_NONFATAL (output_filename); + } if (!bfd_close (ibfd)) RETURN_NONFATAL (input_filename); diff --git a/binutils/readelf.c b/binutils/readelf.c index e95a501..a436778 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -110,6 +110,8 @@ #include "aout/ar.h" +#include "elf/nios2.h" + #include "bucomm.h" #include "getopt.h" #include "libiberty.h" @@ -668,6 +670,7 @@ guess_is_rela (unsigned long e_machine) case EM_XTENSA: case EM_XTENSA_OLD: case EM_M32R: + case EM_ALTERA_NIOS2: return TRUE; case EM_MMA: @@ -1166,6 +1169,10 @@ dump_relocations (FILE *file, case EM_XTENSA: rtype = elf_xtensa_reloc_type (type); break; + + case EM_ALTERA_NIOS2: + rtype = elf_nios2_reloc_type (type); + break; } if (rtype == NULL) @@ -1649,6 +1656,7 @@ get_machine_name (unsigned e_machine) case EM_IQ2000: return "Vitesse IQ2000"; case EM_XTENSA_OLD: case EM_XTENSA: return "Tensilica Xtensa Processor"; + case EM_ALTERA_NIOS2: return "Altera Nios II"; default: sprintf (buff, _(": %x"), e_machine); return buff; diff --git a/binutils/testsuite/binutils-all/bintest.s b/binutils/testsuite/binutils-all/bintest.s index 9e00650..177b08c 100644 --- a/binutils/testsuite/binutils-all/bintest.s +++ b/binutils/testsuite/binutils-all/bintest.s @@ -1,5 +1,8 @@ .globl text_symbol .text + # this is needed to get the readelf -s, -S and -r tests to work + # with nios2 as it has relaxation on by default + .set norelax text_symbol: static_text_symbol: .long 1 diff --git a/configure b/configure index d11c49b..41c0c64 100755 --- a/configure +++ b/configure @@ -1434,6 +1434,10 @@ case "${target}" in mips*-*-*) noconfigdirs="$noconfigdirs gprof ${libgcj}" ;; + nios2*-*-*) + skipdirs=`echo " ${skipdirs} " | sed -e 's/ gprof / /'` + noconfigdirs="$noconfigdirs" + ;; romp-*-*) noconfigdirs="$noconfigdirs bfd binutils ld gas opcodes target-libgloss ${libgcj}" ;; diff --git a/configure.in b/configure.in index 6c0c465..c770956 100644 --- a/configure.in +++ b/configure.in @@ -667,6 +667,10 @@ case "${target}" in mips*-*-*) noconfigdirs="$noconfigdirs gprof ${libgcj}" ;; + nios2*-*-*) + skipdirs=`echo " ${skipdirs} " | sed -e 's/ gprof / /'` + noconfigdirs="$noconfigdirs" + ;; romp-*-*) noconfigdirs="$noconfigdirs bfd binutils ld gas opcodes target-libgloss ${libgcj}" ;; diff --git a/gas/Makefile.in b/gas/Makefile.in index 67ed432..7870a9e 100644 --- a/gas/Makefile.in +++ b/gas/Makefile.in @@ -299,6 +299,7 @@ CPU_TYPES = \ mn10200 \ mn10300 \ msp430 \ + nios2 \ ns32k \ openrisc \ or32 \ @@ -493,6 +494,7 @@ TARGET_CPU_CFILES = \ config/tc-mn10200.c \ config/tc-mn10300.c \ config/tc-msp430.c \ + config/tc-nios2.c \ config/tc-ns32k.c \ config/tc-openrisc.c \ config/tc-or32.c \ @@ -545,6 +547,7 @@ TARGET_CPU_HFILES = \ config/tc-mn10200.h \ config/tc-mn10300.h \ config/tc-msp430.h \ + config/tc-nios2.h \ config/tc-ns32k.h \ config/tc-openrisc.h \ config/tc-or32.h \ @@ -1171,6 +1174,13 @@ DEPTC_msp430_elf = $(INCDIR)/symcat.h $(srcdir)/config/obj-elf.h \ subsegs.h $(INCDIR)/obstack.h $(INCDIR)/opcode/msp430.h \ $(INCDIR)/safe-ctype.h +DEPTC_nios2_elf = $(INCDIR)/symcat.h $(srcdir)/config/obj-elf.h \ + $(BFDDIR)/elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \ + $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(srcdir)/config/tc-nios2.h \ + subsegs.h $(INCDIR)/obstack.h $(INCDIR)/safe-ctype.h \ + $(INCDIR)/opcode/nios2.h itbl-ops.h dwarf2dbg.h $(INCDIR)/elf/nios2.h \ + $(INCDIR)/elf/reloc-macros.h + DEPTC_ns32k_aout = $(INCDIR)/symcat.h $(srcdir)/config/obj-aout.h \ $(srcdir)/config/tc-ns32k.h $(BFDDIR)/libaout.h $(INCDIR)/bfdlink.h \ $(INCDIR)/opcode/ns32k.h $(INCDIR)/obstack.h @@ -1824,6 +1834,12 @@ DEPOBJ_msp430_elf = $(INCDIR)/symcat.h $(srcdir)/config/obj-elf.h \ $(INCDIR)/safe-ctype.h subsegs.h $(INCDIR)/obstack.h \ struc-symbol.h dwarf2dbg.h $(INCDIR)/aout/aout64.h +DEPOBJ_nios2_elf = $(INCDIR)/symcat.h $(srcdir)/config/obj-elf.h \ + $(BFDDIR)/elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \ + $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(srcdir)/config/tc-nios2.h \ + $(INCDIR)/safe-ctype.h subsegs.h $(INCDIR)/obstack.h \ + $(INCDIR)/elf/nios2.h $(INCDIR)/elf/reloc-macros.h + DEPOBJ_ns32k_aout = $(INCDIR)/symcat.h $(srcdir)/config/obj-aout.h \ $(srcdir)/config/tc-ns32k.h $(BFDDIR)/libaout.h $(INCDIR)/bfdlink.h \ $(INCDIR)/aout/aout64.h $(INCDIR)/obstack.h @@ -2349,6 +2365,10 @@ DEP_msp430_elf = $(srcdir)/config/obj-elf.h $(INCDIR)/symcat.h \ $(BFDDIR)/elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \ $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(srcdir)/config/tc-msp430.h +DEP_nios2_elf = $(srcdir)/config/obj-elf.h $(INCDIR)/symcat.h \ + $(BFDDIR)/elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \ + $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(srcdir)/config/tc-nios2.h + DEP_ns32k_aout = $(srcdir)/config/obj-aout.h $(srcdir)/config/tc-ns32k.h \ $(BFDDIR)/libaout.h $(INCDIR)/bfdlink.h diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c index 14d48f2..6d9639f 100644 --- a/gas/config/obj-elf.c +++ b/gas/config/obj-elf.c @@ -53,6 +53,10 @@ #include "elf/i370.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 0000000..21f994e --- /dev/null +++ b/gas/config/tc-nios2.c @@ -0,0 +1,3197 @@ +/* tc-nios2.c -- assemble code for a New Jersey processor. + + Copyright (C) 2003 + by Nigel Gray (ngray@altera.com). + + + This file is part of GAS. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, 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, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + + +#include +#include +#include +#include +#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" + +/* We can choose our endianness at run-time, regardless of configuration */ +extern int target_big_endian; + +#ifndef OBJ_ELF + /* we are not supporting any other target + so we throw a compile time error */ +OBJ_ELF not defined +#endif + 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 will generate a warning. + .set at -> noat = 0, assembly code using at will warn + macro expansions will 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 will warn + */ + 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 + 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 by the functions in tc-nios2.c to assemble an + instruction + ------------------------------------------------------------------*/ + +typedef struct nios2_insn_info +{ + /* assembled instruction */ + unsigned long insn_code; + /* ptr 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 New Jersey pseudo-ops into the + corresponding real op + */ +typedef struct nios2_ps_insn_info +{ + const char *pseudo_insn; + const char *insn; + const char *arg_modifier; + void (*arg_modifer_func) (const char *arg, char **parsedArgs, int numArg, + int startIndex); + int num; + int index; +} +nios2_ps_insn_infoS; + + + +/* function prototypes */ +static void NIOS2_CHECK_ASSEMBLY (unsigned int opcode, + const char *exp_opcode); +static void s_nios2_sdata (int); +void nios2_assemble_args_dst (nios2_insn_infoS * insn_info); +void nios2_assemble_args_tsi (nios2_insn_infoS * insn_info); +void nios2_assemble_args_tsu (nios2_insn_infoS * insn_info); +void nios2_assemble_args_o (nios2_insn_infoS * insn_info); +void nios2_assemble_args_m (nios2_insn_infoS * insn_info); +void nios2_assemble_args_s (nios2_insn_infoS * insn_info); +void nios2_assemble_args_tis (nios2_insn_infoS * insn_info); +void nios2_assemble_args_dc (nios2_insn_infoS * insn_info); +void nios2_assemble_args_cs (nios2_insn_infoS * insn_info); +void nios2_assemble_args_ldst (nios2_insn_infoS * insn_info); +void nios2_assemble_args_none (nios2_insn_infoS * insn_info); +void nios2_assemble_args_dsj (nios2_insn_infoS * insn_info); +void nios2_assemble_args_is (nios2_insn_infoS * insn_info); +void nios2_assemble_args_sto (nios2_insn_infoS * insn_info); +void nios2_assemble_args_d (nios2_insn_infoS * insn_info); +void nios2_assemble_args_b (nios2_insn_infoS * insn_info); + +nios2_insn_relocS *nios2_insn_reloc_new (bfd_reloc_code_real_type reloc_type, + unsigned int pcrel); +void nios2_insn_reloc_destroy (nios2_insn_relocS * reloc); +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); +char *nios2_consume_separator (char *argStr, const char *separator); +char *nios2_consume_arg (char *argStr, const char *argType); +void nios2_parse_args (char *argStr, const char *parseStr, char **parsedArgs); + +void nios2_modify_arg (const char *modifier, char **parsedArgs, int unused, + int index); +void nios2_append_arg (const char *append, char **parsedArgs, int numAppend, + int startIndex); +void nios2_insert_arg (const char *insert, char **parsedArgs, int numInsert, + int startIndex); +void nios2_swap_args (const char *unused, char **parsedArgs, int index_1, + int index_2); +void nios2_negate_arg (const char *modifier ATTRIBUTE_UNUSED, + char **parsedArgs, int unused ATTRIBUTE_UNUSED, + int index); +void nios2_translate_pseudo_insn (nios2_insn_infoS * insn); +valueT md_chars_to_number (char *buf, int n); +void md_number_to_imm (char *buf, valueT val, int n); +void md_number_to_disp (char *buf, valueT val, int n); +void md_number_to_field (char *buf, valueT val, int n); +static void nios2_align (int log_size, const char *pfill, symbolS * sym); +static void s_nios2_ucons (int nbytes); +static void s_nios2_set (int equiv); +static void s_nios2_align (int ignore); +static void s_nios2_text (int); +static void s_nios2_data (int); +static void s_nios2_section (int); +static bfd_boolean nios2_coproc_reg (const char *reg_name); +static void output_insn (void); +static void output_ubranch (void); +static void output_cbranch (void); +static void output_call (void); +static void output_movia (void); +static void output_andi (void); +static void output_addi (void); +static void output_ori (void); +static void output_xori (void); +static int can_evaluate_expr (void); +static int get_expr_value (void); + + +bfd_boolean nios2_check_overflow (valueT fixup, reloc_howto_type * howto); + +/* The known current alignment of the current section. */ +static int nios2_current_align; +static segT nios2_current_align_seg; + +/* The last seen label in the current section. This is used to auto-align + labels preceeding instructions. */ +static symbolS *nios2_last_label; + + +static int nios2_auto_align_on = 1; + +/* 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. + */ + +/* handle of the OPCODE hash table */ +static struct hash_control *nios2_opcode_hash = NULL; + +/* handle of the Register hash table */ +static struct hash_control *nios2_reg_hash = NULL; + +/* handle of the parse args hash table */ +static struct hash_control *nios2_arg_hash = NULL; + +/* pseudo-op hash table */ +static struct hash_control *nios2_ps_hash = NULL; + +/* 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 */ +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)); + } + } +} + +/* 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); + +/* Machine dependent pseudo-ops + These are actually 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} +}; + +#define BYTE_F 32764 +#define BYTE_B -32768 +#define ABS (long)0xffffffff /* special value to indicate non-pc relative jmp */ + +#define UBRANCH 1 +#define UJMP 2 +#define CBRANCH 3 +#define CJMP 4 + + +#define RELAX_MAX_SIZE(type) nios2_relax_table[nios2_relax_table[type].rlx_more].rlx_length +#define RELAX_SIZE(type) nios2_relax_table[type].rlx_length +#define RELAX_SUBSTATE(type) type + +/* machine dependent relaxations */ +struct relax_type nios2_relax_table[] = { + /* first entry unused (ends relaxation sequence) */ + {1, 1, 0, 0}, + /* unconditional branch */ + {BYTE_F, BYTE_B, 4, 2}, /* br label (label is in range) */ + /* unconditional jmp */ + {ABS, ABS, 12, 0}, /* movhi at, %hi(label) ; ori at, %lo(label) ; jmp at */ + /* conditional branch */ + {BYTE_F, BYTE_B, 4, 4}, /* br{cond} label (label is in range) */ + /* conditional jmp */ + {ABS, ABS, 16, 0}, /* br{opp_cond} skip ; movhi at, %hi(label) ; ori at, %lo(label) ; jmp at ; skip: } */ +}; + + +/* this is just the generic relax_frag function but + amended to include absolute jmps in the relax table + */ + +long +nios2_relax_frag (segT segment, fragS * fragP, long stretch) +{ + const relax_typeS *this_type; + const relax_typeS *start_type; + relax_substateT next_state; + relax_substateT this_state; + long growth; + offsetT aim; + addressT target; + addressT address; + symbolS *symbolP; + const relax_typeS *table; + + target = fragP->fr_offset; + address = fragP->fr_address; + table = nios2_relax_table; + this_state = fragP->fr_subtype; + start_type = this_type = table + this_state; + symbolP = fragP->fr_symbol; + + if (symbolP) + { + fragS *sym_frag; + + sym_frag = symbol_get_frag (symbolP); + +#ifndef DIFF_EXPR_OK +#if !defined (MANY_SEGMENTS) && !defined (BFD_ASSEMBLER) + know ((S_GET_SEGMENT (symbolP) == SEG_ABSOLUTE) + || (S_GET_SEGMENT (symbolP) == SEG_DATA) + || (S_GET_SEGMENT (symbolP) == SEG_BSS) + || (S_GET_SEGMENT (symbolP) == SEG_TEXT)); +#endif + know (sym_frag != NULL); +#endif + know (!(S_GET_SEGMENT (symbolP) == absolute_section) + || sym_frag == &zero_address_frag); + target += S_GET_VALUE (symbolP); + + /* If frag has yet to be reached on this pass, + assume it will move by STRETCH just as we did. + If this is not so, it will be because some frag + between grows, and that will force another pass. */ + + if (stretch != 0 + && sym_frag->relax_marker != fragP->relax_marker + && S_GET_SEGMENT (symbolP) == segment) + { + target += stretch; + } + } + + + /* NG we subtract 4 because all pc relative branches are + from the next instruction */ + aim = target - address - fragP->fr_fix - 4; + + if (aim < 0) + { + /* Look backwards. */ + for (next_state = this_type->rlx_more; next_state;) + { + if (aim >= this_type->rlx_backward + || this_type->rlx_backward == ABS) + next_state = 0; + else + { + /* Grow to next state. */ + this_state = next_state; + this_type = table + this_state; + next_state = this_type->rlx_more; + } + } + } + else + { + /* Look forwards. */ + + for (next_state = this_type->rlx_more; next_state;) + { + if (aim <= this_type->rlx_forward || this_type->rlx_forward == ABS) + next_state = 0; + else + { + /* Grow to next state. */ + this_state = next_state; + this_type = table + this_state; + next_state = this_type->rlx_more; + } + } + } + + + growth = this_type->rlx_length - start_type->rlx_length; + + if (growth != 0) + fragP->fr_subtype = this_state; + + return growth; +} + +/*-------------------------------------------------------------------------------- + The next table associates pointers to functions which 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; + + +const nios2_ps_insn_infoS nios2_ps_insn_info_structs[] = { + /* pseudo-op real-op arg arg_modifier_func num index */ + {"mov", "add", "zero", nios2_append_arg, 1, 3}, + {"movi", "addi", "zero", nios2_insert_arg, 1, 2}, + {"movhi", "orhi", "zero", nios2_insert_arg, 1, 2}, + {"movui", "ori", "zero", nios2_insert_arg, 1, 2}, + {"movia", "orhi", "zero", nios2_insert_arg, 1, 2}, + {"nop", "add", "zero", nios2_append_arg, 3, 1}, + {"bgt", "blt", "", nios2_swap_args, 1, 2}, + {"bgtu", "bltu", "", nios2_swap_args, 1, 2}, + {"ble", "bge", "", nios2_swap_args, 1, 2}, + {"bleu", "bgeu", "", nios2_swap_args, 1, 2}, + {"cmpgt", "cmplt", "", nios2_swap_args, 2, 3}, + {"cmpgtu", "cmpltu", "", nios2_swap_args, 2, 3}, + {"cmple", "cmpge", "", nios2_swap_args, 2, 3}, + {"cmpleu", "cmpgeu", "", nios2_swap_args, 2, 3}, + {"cmpgti", "cmpgei", "+1", nios2_modify_arg, 0, 3}, + {"cmpgtui", "cmpgeui", "+1", nios2_modify_arg, 0, 3}, + {"cmplei", "cmplti", "+1", nios2_modify_arg, 0, 3}, + {"cmpleui", "cmpltui", "+1", nios2_modify_arg, 0, 3}, + {"subi", "addi", "", nios2_negate_arg, 0, 3} + /* 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; + +/* 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} +}; + +#define NIOS2_NUM_SPECIAL_RELOCS \ + (sizeof(nios2_special_reloc)/sizeof(nios2_special_reloc[0])) +const int nios2_num_special_relocs = NIOS2_NUM_SPECIAL_RELOCS; + +/* + The function nios2_modify_arg appends the string modifier to the string contained + in the argument at index in the array parsedArgs[] +*/ +void +nios2_modify_arg (const char *modifier, + char **parsedArgs, int unused ATTRIBUTE_UNUSED, int index) +{ + assert (index < NIOS2_MAX_INSN_TOKENS); + +/* + we can't just strcat here because strcat will free the memory pointed to by the first + argument and allocate new memory - but at this stage, parsedArgs[index] may point into + the middle of a block of allocated memory, so trying to free it will cause a seg fault. + + */ + char *tmp = parsedArgs[index]; + parsedArgs[index] = + (char *) malloc (strlen (parsedArgs[index]) + strlen (modifier) + 1); + strcpy (parsedArgs[index], tmp); + strcat (parsedArgs[index], modifier); +} + + +void +nios2_negate_arg (const char *modifier ATTRIBUTE_UNUSED, + char **parsedArgs, int unused ATTRIBUTE_UNUSED, int index) +{ + char *tmp = parsedArgs[index]; + parsedArgs[index] = + (char *) malloc (strlen ("~(") + strlen (parsedArgs[index]) + + strlen (")+1") + 1); + + strcpy (parsedArgs[index], "~("); + strcat (parsedArgs[index], tmp); + strcat (parsedArgs[index], ")+1"); +} + +/* + The function nios2_swap_args swaps the pointers at indices index_1 and + index_2 in the array parsedArgs[] - this is used for operand swapping + for comparison operations + */ +void +nios2_swap_args (const char *unused ATTRIBUTE_UNUSED, + char **parsedArgs, int index_1, int index_2) +{ + char *tmp; + assert (index_1 < NIOS2_MAX_INSN_TOKENS && index_2 < NIOS2_MAX_INSN_TOKENS); + tmp = parsedArgs[index_1]; + parsedArgs[index_1] = parsedArgs[index_2]; + parsedArgs[index_2] = tmp; +} + +/* + This function appends the string append to the array of strings in + parsedArgs numAppend times starting at index startIndex in the array +*/ +void +nios2_append_arg (const char *append, char **parsedArgs, int numAppend, + int startIndex) +{ + int i, count; + char *tmp; + + assert ((startIndex + numAppend) < NIOS2_MAX_INSN_TOKENS); + i = startIndex; + count = numAppend; + + if (nios2_mode == NIOS2_MODE_TEST) + tmp = parsedArgs[startIndex]; + else + tmp = NULL; + + while (count > 0) + { + parsedArgs[i] = (char *) append; + ++i; + --count; + } + + assert (i == (startIndex + numAppend)); + parsedArgs[i] = tmp; + parsedArgs[i + 1] = NULL; +} + +/* This function inserts the string insert numInsert times in the array parsedArgs, + starting at the index startIndex + */ +void +nios2_insert_arg (const char *insert, char **parsedArgs, int numInsert, + int startIndex) +{ + int i, count, from, to; + + assert ((startIndex + numInsert) < NIOS2_MAX_INSN_TOKENS); + + to = startIndex + numInsert; + from = startIndex; + + /* move the existing arguments up to create space */ + i = NIOS2_MAX_INSN_TOKENS; + while ((i - numInsert) >= startIndex) + { + parsedArgs[i] = parsedArgs[i - numInsert]; + --i; + } + + i = startIndex; + count = numInsert; + while (count > 0) + { + parsedArgs[i] = (char *) insert; + ++i; + --count; + } +} + +/* + This function swaps the pseudo-op for a real op + FIXME - only works for 1-to-1 correspondence + */ +void +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_insn_infoS *) hash_find (nios2_ps_hash, + insn->insn_nios2_opcode->name); + + if (ps_insn != NULL) + { + insn->insn_nios2_opcode = + (struct nios2_opcode *) hash_find (nios2_opcode_hash, 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 (ps_insn->arg_modifier, + (char **) insn->insn_tokens, ps_insn->num, + ps_insn->index); + } + else + { + // we cannot recover from this + as_fatal (_("unrecognized pseudo-instruction %s"), + ps_insn->pseudo_insn); + } +} + +/******************************************************************** + The following functions are called by machine-independent parts of + the assembler + ********************************************************************/ + +/* + Function : void md_parse_option + (char** option_ptr, int* argc_ptr, char*** argv_ptr) + + Description : + + */ +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; +} + +/* + * 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"; +} + +/* + Function : md_show_usage(FILE* stream) + + Description : 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"); +} + +/* + Function : void md_begin() + + Description : + 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 () +{ + int i; + const char *inserted; + + /* create and fill a hashtable for the New Jersey 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; +} + + + + +/* made this global to avoid changing one function prototype */ +nios2_insn_infoS insn; + +/* + Function: void md_assemble(char* op_str) + + Description: assembles a single line of Nios II assembly + language + */ +void +md_assemble (char *op_str) +{ + char *argstr; + char *op_strdup; + nios2_arg_infoS *arg_info; + unsigned long saved_pinfo = 0; + + /* 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 = + (struct nios2_opcode *) hash_find (nios2_opcode_hash, + insn.insn_tokens[0]); + insn.insn_reloc = NULL; + + if (insn.insn_nios2_opcode != 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 (argstr, insn.insn_nios2_opcode->args, + (char **) &insn.insn_tokens[1]); + } + else + { + nios2_parse_args (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) + { + nios2_translate_pseudo_insn (&insn); + } + + /* find the assemble function, and call it */ + arg_info = + (nios2_arg_infoS *) hash_find (nios2_arg_hash, + insn.insn_nios2_opcode->args); + if (arg_info != NULL) + { + arg_info->assemble_args_func (&insn); + + if (nios2_as_options.relax != relax_none + && insn.insn_nios2_opcode->pinfo & NIOS2_INSN_UBRANCH) + output_ubranch (); + else if (nios2_as_options.relax != relax_none + && insn.insn_nios2_opcode->pinfo & NIOS2_INSN_CBRANCH) + output_cbranch (); + else if (nios2_as_options.relax == relax_all + && insn.insn_nios2_opcode->pinfo & NIOS2_INSN_CALL) + output_call (); + else if (insn.insn_nios2_opcode->pinfo & NIOS2_INSN_ANDI) + output_andi (); + else if (insn.insn_nios2_opcode->pinfo & NIOS2_INSN_ORI) + output_ori (); + else if (insn.insn_nios2_opcode->pinfo & NIOS2_INSN_XORI) + output_xori (); + else if (insn.insn_nios2_opcode->pinfo & NIOS2_INSN_ADDI) + output_addi (); + else if (saved_pinfo == NIOS2_INSN_MACRO_MOVIA) + output_movia (); + else + output_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]); + } +} + +/* output a normal instruction */ +static void +output_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 */ + reloc = insn.insn_reloc; + while (reloc != NULL) + { + /* this creates any fixups to be acted on later */ + fix_new_exp (frag_now, f - frag_now->fr_literal, 4, + &reloc->reloc_expression, reloc->reloc_pcrel, + reloc->reloc_type); + reloc = reloc->reloc_next; + } +} + +/* output an unconditional branch */ +static void +output_ubranch () +{ + char *f; + nios2_insn_relocS *reloc; + symbolS *symp; + offsetT offset; + + reloc = insn.insn_reloc; + + /* if the reloc is NULL, there was an error assembling the branch */ + if (reloc != NULL) + { + + symp = reloc->reloc_expression.X_add_symbol; + offset = reloc->reloc_expression.X_add_number; + + /* we must tag debug info here since we can't do it after + calling frag_var */ + dwarf2_emit_insn (4); + + /* 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, + RELAX_MAX_SIZE (UBRANCH), + RELAX_SIZE (UBRANCH), + RELAX_SUBSTATE (UBRANCH), 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 () +{ + char *f; + nios2_insn_relocS *reloc; + symbolS *symp; + offsetT offset; + + reloc = insn.insn_reloc; + + /* if the reloc is NULL, there was an error assembling the branch */ + if (reloc != NULL) + { + + symp = reloc->reloc_expression.X_add_symbol; + offset = reloc->reloc_expression.X_add_number; + + /* we must tag debug info here since we can't do it after + calling frag_var */ + dwarf2_emit_insn (4); + + /* 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, + RELAX_MAX_SIZE (CBRANCH), + RELAX_SIZE (CBRANCH), + RELAX_SUBSTATE (CBRANCH), 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 () +{ + char *f; + nios2_insn_relocS *reloc; + f = frag_more (12); + /* this allocates enough space for the instruction + and puts it in the current frag */ + reloc = insn.insn_reloc; + + /* if the reloc is NULL, there was an error assembling the branch */ + if (reloc != NULL) + { + md_number_to_chars (f, OP_MATCH_ORHI | 0x00400000, 4); + dwarf2_emit_insn (4); + md_number_to_chars (f + 4, OP_MATCH_ORI | 0x08400000, 4); + dwarf2_emit_insn (4); + md_number_to_chars (f + 8, OP_MATCH_CALLR | 0x08000000, 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_CALLR); + + + } +} + + +static int +can_evaluate_expr () +{ + /* 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 () +{ + 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 an addi - will silently convert to + * orhi if rA = r0 and (expr & 0xffff0000) == 0 */ +static void +output_addi () +{ + int expr_val = 0; + if (can_evaluate_expr ()) + { + expr_val = get_expr_value (); + 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 (); +} + +static void +output_andi () +{ + int expr_val = 0; + if (can_evaluate_expr ()) + { + expr_val = get_expr_value (); + 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 (); +} + +static void +output_ori () +{ + int expr_val = 0; + if (can_evaluate_expr ()) + { + expr_val = get_expr_value (); + 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 (); +} + +static void +output_xori () +{ + int expr_val = 0; + if (can_evaluate_expr ()) + { + expr_val = get_expr_value (); + 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 (); +} + + +/* output a movhi/addi pair for the movia pseudo-op */ +static void +output_movia () +{ + char *f; + nios2_insn_relocS *reloc; + f = frag_more (8); + unsigned long reg_index = GET_INSN_FIELD (IRT, insn.insn_code); + + /* this allocates enough space for the instruction + and puts it in the current frag */ + reloc = insn.insn_reloc; + + /* 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); + + } +} + +/* + 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. + */ +valueT +md_chars_to_number (char *buf, int n) +{ + int i; + valueT val; + + 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; +} + + +/* + Function : void md_number_to_chars(char *buf, valueT val, int n) + + Description : 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) +{ + assert (n == 1 || n == 2 || n == 4); + if (target_big_endian) + { + number_to_chars_bigendian (buf, val, n); + } + else + { + number_to_chars_littleendian (buf, val, n); + } +} + +/* + Function : void md_number_to_imm(char *buf, valueT val, int n) + + Description : this function is identical to md_number_to_chars + */ +void +md_number_to_imm (char *buf, valueT val, int n) +{ + md_number_to_chars (buf, val, n); +} + +/* + Function : void md_number_to_disp(char *buf, valueT val, int n) + + Description : this function is identical to md_number_to_chars + */ +void +md_number_to_disp (char *buf, valueT val, int n) +{ + md_number_to_chars (buf, val, n); +} + +/* + Function : void md_number_to_field(char *buf, valueT val, int n) + + Description : this function is identical to md_number_to_chars + */ +void +md_number_to_field (char *buf, valueT val, int n) +{ + md_number_to_chars (buf, val, n); +} + +/* + + Function : char * md_atof(int type, char *litP,int *sizeP) + + Description : + 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--) + { + md_number_to_chars (litP, (valueT) words[i], 2); + litP += 2; + } + } + else + { + for (i = 0; i < prec; i++) + { + md_number_to_chars (litP, (valueT) words[i], 2); + litP += 2; + } + } + + return NULL; +} + + + +int md_short_jump_size; +int md_long_jump_size; + +void +md_create_short_jump (char *result_ptr ATTRIBUTE_UNUSED, + addressT from_addr ATTRIBUTE_UNUSED, + addressT to_addr ATTRIBUTE_UNUSED, + fragS * frag ATTRIBUTE_UNUSED, + symbolS * to_symbol ATTRIBUTE_UNUSED) +{ + abort (); +} + +void +md_create_long_jump (char *ptr ATTRIBUTE_UNUSED, + addressT from_addr ATTRIBUTE_UNUSED, + addressT to_addr ATTRIBUTE_UNUSED, + fragS * frag ATTRIBUTE_UNUSED, + symbolS * to_symbol ATTRIBUTE_UNUSED) +{ + abort (); +} + +int +md_estimate_size_before_relax (fragS * fragp, segT segment ATTRIBUTE_UNUSED) +{ + /* we only support ELF targets */ + + switch (nios2_as_options.relax) + { + case relax_none: + case relax_section: + break; + case relax_all: + /* The NIOS2 linker performs relaxation so the assembler + always assumes the worst case, so that the linker can + replace with a better case if possible - this way, linker + relaxation can never cause a short branch to be out of range + */ + while (nios2_relax_table[fragp->fr_subtype].rlx_more != 0) + fragp->fr_subtype = nios2_relax_table[fragp->fr_subtype].rlx_more; + break; + default: + abort (); + break; + } + + /* return the estimated size of the frag */ + return nios2_relax_table[fragp->fr_subtype].rlx_length; +} + + +void +md_convert_frag (bfd * headers ATTRIBUTE_UNUSED, segT seg ATTRIBUTE_UNUSED, + fragS * fragp) +{ + unsigned char *buffer = fragp->fr_literal + fragp->fr_fix; + relax_substateT subtype = fragp->fr_subtype; + unsigned int growth = RELAX_SIZE (subtype); + unsigned int br_opcode, br_op_a, br_op_b; + + switch (subtype) + { + case UBRANCH: + /* we just need to generate the fixup for the symbol and offset */ + fix_new (fragp, fragp->fr_fix, 4, fragp->fr_symbol, fragp->fr_offset, 1, + BFD_RELOC_16_PCREL); + break; + case UJMP: + /* replace ubranch at fr_fix with : + movhi at, %hi(symbol+offset) + ori at, %lo(symbol+offset) + jmp at + */ + md_number_to_chars (buffer, OP_MATCH_ORHI | 0x00400000, 4); + md_number_to_chars (buffer + 4, OP_MATCH_ORI | 0x08400000, 4); + md_number_to_chars (buffer + 8, OP_MATCH_JMP | 0x08000000, 4); + fix_new (fragp, fragp->fr_fix, 4, fragp->fr_symbol, fragp->fr_offset, 0, + BFD_RELOC_NIOS2_UJMP); + break; + case CBRANCH: + /* we just need to generate the fixup for the symbol and offset */ + fix_new (fragp, fragp->fr_fix, 4, fragp->fr_symbol, fragp->fr_offset, 1, + BFD_RELOC_16_PCREL); + break; + case CJMP: + /* replace cbranch at fr_fix with : + b(opposite condition) r, s, skip + movhi at, %hi(symbol+offset) + ori at, %lo(symbol+offset) + jmp at + skip: + ... + */ + 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 | (12 << OP_SH_IMM16); + break; + case OP_MATCH_BNE: + br_opcode = + (br_opcode & ~OP_MASK_OP) | OP_MATCH_BEQ | (12 << OP_SH_IMM16); + break; + case OP_MATCH_BGE: + case OP_MATCH_BGEU: + case OP_MATCH_BLT: + case OP_MATCH_BLTU: + /* swap the operands */ + br_op_a = (br_opcode & OP_MASK_RRT) << 5; + br_op_b = (br_opcode & OP_MASK_RRS) >> 5; + br_opcode = + (br_opcode & ~(OP_MASK_RRS | OP_MASK_RRT)) | br_op_a | br_op_b | + (12 << OP_SH_IMM16); + break; + default: + as_bad_where (fragp->fr_file, fragp->fr_line, + _("expecting conditional branch for relaxation\n")); + abort (); + } + + md_number_to_chars (buffer, br_opcode, 4); + md_number_to_chars (buffer + 4, OP_MATCH_ORHI | 0x00400000, 4); + md_number_to_chars (buffer + 8, OP_MATCH_ORI | 0x08400000, 4); + md_number_to_chars (buffer + 12, OP_MATCH_JMP | 0x08000000, 4); + fix_new (fragp, fragp->fr_fix + 4, 4, fragp->fr_symbol, + fragp->fr_offset, 0, BFD_RELOC_NIOS2_CJMP); + break; + default: + as_bad_where (fragp->fr_file, fragp->fr_line, + _("can't relax instruction\n")); + abort (); + break; + } + + fragp->fr_fix += growth; +} + + +/* round up section size */ +valueT +md_section_align (asection * seg ATTRIBUTE_UNUSED, valueT size) +{ + /* I think byte alignment is fine here */ + return size; +} + + +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 0; +} + +/* nios2_fix_adjustable is called to see whether a reloc against a defined symbol + should be converted into a reloc against a section. */ + +int +nios2_fix_adjustable (fixS * fixp) +{ +#ifdef OBJ_ELF + /* Prevent all adjustments to global symbols. */ + if (OUTPUT_FLAVOR == bfd_target_elf_flavour + && (S_IS_EXTERN (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; + + return 1; +} + +/* + nios2_frob_symbol is called in adjust_reloc_syms through the macro + tc_frob_symbol - 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; + 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_addnumber; + 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); + assert (reloc->howto != NULL); + } + return reloc; +} + +long +md_pcrel_from (fixS * fixP ATTRIBUTE_UNUSED) +{ + return 0; +} + + +/* Apply a fixup to the object file. */ +void +md_apply_fix3 (fixS * fixP, valueT * valP, segT seg ATTRIBUTE_UNUSED) +{ + const struct nios2_opcode *opcode; + enum overflow_type overflow_msg_type; + bfd_boolean overflowed = FALSE; + valueT fixup = 0; + + /* assert that the fixup is one we can handle */ + 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_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 + // add other relocs here as we generate them + )); + + + /* 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) + { + fixP->fx_addnumber = fixP->fx_offset; + fixP->fx_done = 0; + } + else + { + valueT value; + + char *buf; + reloc_howto_type *howto; + 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 + { + fixup += *valP; + + /* 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 to be fixed up */ + buf = fixP->fx_frag->fr_literal + fixP->fx_where; + value = md_chars_to_number (buf, 4); + + /* What opcode is the instruction? This will determine + whether we check for overflow in immediate values + and what error message we get */ + opcode = nios2_find_opcode_hash (value); + overflow_msg_type = opcode->overflow_msg; + + overflowed = nios2_check_overflow (fixup, howto); + + + if (overflowed) + { + unsigned int range_min; + unsigned int range_max; + unsigned int address; + 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, + _(overflow_msgs[call_target_overflow]), + address, range_min, range_max); + break; + case branch_target_overflow: + as_bad_where (fixP->fx_file, fixP->fx_line, + _(overflow_msgs[branch_target_overflow]), + fixup, BYTE_B, BYTE_F); + break; + case address_offset_overflow: + as_bad_where (fixP->fx_file, fixP->fx_line, + _(overflow_msgs[address_offset_overflow]), + opcode->name, fixup, -32768, 32767); + break; + case signed_immed16_overflow: + as_bad_where (fixP->fx_file, fixP->fx_line, + _(overflow_msgs[signed_immed16_overflow]), + fixup, -32768, 32767); + break; + case unsigned_immed16_overflow: + as_bad_where (fixP->fx_file, fixP->fx_line, + _(overflow_msgs[unsigned_immed16_overflow]), + fixup, 0, 65535); + break; + case unsigned_immed5_overflow: + as_bad_where (fixP->fx_file, fixP->fx_line, + _(overflow_msgs[unsigned_immed5_overflow]), + fixup, 0, 31); + break; + case custom_opcode_overflow: + as_bad_where (fixP->fx_file, fixP->fx_line, + _(overflow_msgs[custom_opcode_overflow]), + fixup, 0, 255); + break; + default: + as_bad_where (fixP->fx_file, fixP->fx_line, + _ + ("unspecified overflow in immediate argument")); + break; + } + } + + + /* apply the rightshift */ + 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: + fixup = + (fixup << (32 - howto->bitsize)) >> (32 - howto->bitsize); + break; + } + + /* fixup the instruction */ + value = (value & ~howto->dst_mask) | (fixup << howto->bitpos); + md_number_to_chars (buf, value, 4); + } + + 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; + } +} + +bfd_boolean +nios2_check_overflow (valueT fixup, reloc_howto_type * howto) +{ + /* apply the rightshift before checking for overflow */ + 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) + 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; +} + +/* + Function : void md_end() + Description : Called just before the assembler exits + */ +void +md_end () +{ + /* FIXME - not yet implemented */ +} + + +/* + Creates a new nios2_insn_relocS and returns a pointer to it +*/ +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() + */ +void +nios2_insn_reloc_destroy (nios2_insn_relocS * reloc) +{ + assert (reloc != NULL); + free (reloc); +} + +/* + Function : nios2_assemble_expression(char* exprstr) + + Description : 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 + */ +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; + + assert (exprstr != NULL); + assert (insn != NULL); + + /* check for %gprel, %hi, %lo or %hiadj + 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; + 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) + { + if (reloc->reloc_expression.X_op == O_constant) + value = reloc->reloc_expression.X_add_number; + } + + return (unsigned long) value; +} + +/* + The function consume_separate 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. + */ +char * +nios2_consume_separator (char *argStr, const char *separator) +{ + char *argPtr; + + /* 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 brackets */ + + if (*separator == '(') + argPtr = strrchr (argStr, *separator); + else + argPtr = strchr (argStr, *separator); + + if (argPtr != NULL) + *argPtr++ = 0; + else + as_bad (_("expecting %c near %s"), *separator, argStr); + return argPtr; +} + +/* + 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. + */ +char * +nios2_consume_arg (char *argStr, const char *argType) +{ + char *temp; + int regno = -1; + + switch (*argType) + { + case 'c': + if (strncmp (argStr, "ctl", strlen ("ctl")) != 0 + && strncmp (argStr, "cpuid", strlen ("cpuid")) != 0 + && strncmp (argStr, "status", strlen ("status")) != 0 + && strncmp (argStr, "estatus", strlen ("estatus")) != 0 + && strncmp (argStr, "bstatus", strlen ("bstatus")) != 0 + && strncmp (argStr, "ienable", strlen ("ienable")) != 0 + && strncmp (argStr, "ipending", strlen ("ipending")) != 0 + && strncmp (argStr, "pteaddr", strlen ("pteaddr")) != 0 + && strncmp (argStr, "tlbacc", strlen ("tlbacc")) != 0 + && strncmp (argStr, "tlbmisc", strlen ("tlbmisc")) != 0 + && strncmp (argStr, "fstatus", strlen ("fstatus")) != 0) + { + 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 (strncmp (argStr, "ctl", strlen ("ctl")) == 0 + || strncmp (argStr, "cpuid", strlen ("cpuid")) == 0 + || strncmp (argStr, "status", strlen ("status")) == 0 + || strncmp (argStr, "estatus", strlen ("estatus")) == 0 + || strncmp (argStr, "bstatus", strlen ("bstatus")) == 0 + || strncmp (argStr, "ienable", strlen ("ienable")) == 0 + || strncmp (argStr, "ipending", strlen ("ipending")) == 0 + || strncmp (argStr, "pteaddr", strlen ("pteaddr")) == 0 + || strncmp (argStr, "tlbacc", strlen ("tlbacc")) == 0 + || strncmp (argStr, "tlbmisc", strlen ("tlbmisc")) == 0 + || strncmp (argStr, "fstatus", strlen ("fstatus")) == 0) + { + 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 + || strncmp (argStr, "at", strlen ("at")) == 0)) + { + 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 + || strncmp (argStr, "bt", strlen ("bt")) == 0)) + { + 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 + || strncmp (argStr, "ba", strlen ("ba")) == 0)) + { + 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 (strstr (argStr, "%hi(") || strstr (argStr, "%lo(") + || strstr (argStr, "%hiadj(") || strstr (argStr, "%gprel(")) + { + // we zap the brackets 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; + } + +#if 0 +/* ??? SPR:173865 This is actually supported by the HW but the documentation is a bit funny. + The compiler really want the extra register, so let it have it! */ + /* checks for jmp 31 */ + /* TODO: move test that insn is jmp to somewhere better.*/ + if ((strncmp (insn.insn_nios2_opcode->name, "jmp", strlen ("jmp")) == 0) + && (regno == 31 + || strncmp (argStr, "ra", strlen ("ra")) == 0)) + { + as_bad (_("It is illegal to jump to the address contained in register ra (r31). " + "To return from subroutines called by call or callr, use ret instead of jmp.")); + } +#endif + + return argStr; +} + +/* + The principal argument parsing function which takes a string + representing the instruction arguments, and extracts the argument + tokens + */ +void +nios2_parse_args (char *argStr, const char *parseStr, char **parsedArgs) +{ + 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")); + parsedArgs[0] = NULL; + return; + } + + + while (p != NULL && !terminate && i < NIOS2_MAX_INSN_TOKENS) + { + parsedArgs[i] = nios2_consume_arg (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 brackets with spaces */ + if (strstr (p, "%lo") == p || strstr (p, "%hi") == p + || strstr (p, "%hiadj") == p || strstr (p, "%gprel") == 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; + } + + parsedArgs[i] = NULL; + + if (*parseStr != '\0' && insn.insn_nios2_opcode->match != OP_MATCH_BREAK) + as_bad (_("missing argument")); + +} + + +/* 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) +{ + 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 (strncmp (reg_name, "c", 1) == 0 && + strncmp (reg_name, "ctl", strlen ("ctl")) != 0 && + hash_find (nios2_reg_hash, reg_name) != NULL) + return TRUE; + else + return FALSE; +} + + +/********************************************************************* + Argument assemble functions + + Description : 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 + *********************************************************************/ + +/* assembles register arguments "dst, src1, src2" */ +void +nios2_assemble_args_dst (nios2_insn_infoS * insn_info) +{ + struct nios2_reg *dst, *src1, *src2; + + if (insn_info->insn_tokens[1] != NULL && + insn_info->insn_tokens[2] != NULL && insn_info->insn_tokens[3] != NULL) + { + dst = + (struct nios2_reg *) hash_find (nios2_reg_hash, + insn_info->insn_tokens[1]); + src1 = + (struct nios2_reg *) hash_find (nios2_reg_hash, + insn_info->insn_tokens[2]); + src2 = + (struct nios2_reg *) hash_find (nios2_reg_hash, + 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]); + } +} + + +/* assembles arguments successfully parsed by nios2_parse_args_tsi */ +void +nios2_assemble_args_tsi (nios2_insn_infoS * insn_info) +{ + struct nios2_reg *dst, *src1; + unsigned int src2; + + if (insn_info->insn_tokens[1] != NULL && + insn_info->insn_tokens[2] != NULL && insn_info->insn_tokens[3] != NULL) + { + dst = + (struct nios2_reg *) hash_find (nios2_reg_hash, + insn_info->insn_tokens[1]); + src1 = + (struct nios2_reg *) hash_find (nios2_reg_hash, + insn_info->insn_tokens[2]); + 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); + } +} + + +/* assembles args successfully parsed by nios2_parse_args_tsu */ +void +nios2_assemble_args_tsu (nios2_insn_infoS * insn_info) +{ + struct nios2_reg *dst, *src1; + unsigned int src2; + + if (insn_info->insn_tokens[1] != NULL && + insn_info->insn_tokens[2] != NULL && insn_info->insn_tokens[3] != NULL) + { + dst = + (struct nios2_reg *) hash_find (nios2_reg_hash, + insn_info->insn_tokens[1]); + src1 = + (struct nios2_reg *) hash_find (nios2_reg_hash, + insn_info->insn_tokens[2]); + 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); + } +} + + +/* assembles args successfully parsed by nios2_parse_args_sti */ +void +nios2_assemble_args_sto (nios2_insn_infoS * insn_info) +{ + struct nios2_reg *dst, *src1; + unsigned int src2; + + if (insn_info->insn_tokens[1] != NULL && + insn_info->insn_tokens[2] != NULL && insn_info->insn_tokens[3] != NULL) + { + dst = + (struct nios2_reg *) hash_find (nios2_reg_hash, + insn_info->insn_tokens[1]); + src1 = + (struct nios2_reg *) hash_find (nios2_reg_hash, + insn_info->insn_tokens[2]); + 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); + } +} + + +void +nios2_assemble_args_o (nios2_insn_infoS * insn_info) +{ + unsigned long immed; + + if (insn_info->insn_tokens[1] != NULL) + { + 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); + } +} + + +void +nios2_assemble_args_is (nios2_insn_infoS * insn_info) +{ + struct nios2_reg *addr_src; + unsigned long immed; + + if (insn_info->insn_tokens[1] != NULL && insn_info->insn_tokens[2] != NULL) + { + addr_src = + (struct nios2_reg *) hash_find (nios2_reg_hash, + insn_info->insn_tokens[2]); + + 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); + } +} + + +void +nios2_assemble_args_m (nios2_insn_infoS * insn_info) +{ + unsigned long immed; + if (insn_info->insn_tokens[1] != NULL) + { + 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); + } +} + + +void +nios2_assemble_args_s (nios2_insn_infoS * insn_info) +{ + struct nios2_reg *src; + + if (insn_info->insn_tokens[1] != NULL) + { + src = + (struct nios2_reg *) hash_find (nios2_reg_hash, + 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]); + } +} + + +void +nios2_assemble_args_tis (nios2_insn_infoS * insn_info) +{ + struct nios2_reg *addr_src, *dst; + unsigned long immed; + + if (insn_info->insn_tokens[1] != NULL && + insn_info->insn_tokens[2] != NULL && insn_info->insn_tokens[3] != NULL) + { + + dst = + (struct nios2_reg *) hash_find (nios2_reg_hash, + insn_info->insn_tokens[1]); + addr_src = + (struct nios2_reg *) hash_find (nios2_reg_hash, + insn_info->insn_tokens[3]); + 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); + } +} + + +/* assemble rdctl dst, ctl */ +void +nios2_assemble_args_dc (nios2_insn_infoS * insn_info) +{ + struct nios2_reg *dst, *ctl; + + if (insn_info->insn_tokens[1] != NULL && insn_info->insn_tokens[2] != NULL) + { + ctl = + (struct nios2_reg *) hash_find (nios2_reg_hash, + insn_info->insn_tokens[2]); + dst = + (struct nios2_reg *) hash_find (nios2_reg_hash, + 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]); + } +} + + +/* assemble wrctl ctl, src */ +void +nios2_assemble_args_cs (nios2_insn_infoS * insn_info) +{ + struct nios2_reg *src, *ctl; + + if (insn_info->insn_tokens[1] != NULL && insn_info->insn_tokens[2] != NULL) + { + ctl = + (struct nios2_reg *) hash_find (nios2_reg_hash, + insn_info->insn_tokens[1]); + src = + (struct nios2_reg *) hash_find (nios2_reg_hash, + 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]); + } +} + + + +void +nios2_assemble_args_ldst (nios2_insn_infoS * insn_info) +{ + struct nios2_reg *dst, *src1, *src2; + unsigned long custom_n; + + 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) + { +#if 0 /* ??? Unused/half commented out code */ + char *end_p; + /* custom_n = nios2_strtoul(insn_info->insn_tokens[1], &end_p); */ +#endif + custom_n = + nios2_assemble_expression (insn_info->insn_tokens[1], insn_info, + insn_info->insn_reloc, + BFD_RELOC_NIOS2_IMM8, 0); + + dst = + (struct nios2_reg *) hash_find (nios2_reg_hash, + insn_info->insn_tokens[2]); + src1 = + (struct nios2_reg *) hash_find (nios2_reg_hash, + insn_info->insn_tokens[3]); + src2 = + (struct nios2_reg *) hash_find (nios2_reg_hash, + 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]); + } +} + + +void +nios2_assemble_args_none (nios2_insn_infoS * insn_info ATTRIBUTE_UNUSED) +{ + // nothing to do +} + + +void +nios2_assemble_args_dsj (nios2_insn_infoS * insn_info) +{ + struct nios2_reg *dst, *src1; + unsigned int src2; + + if (insn_info->insn_tokens[1] != NULL && + insn_info->insn_tokens[2] != NULL && insn_info->insn_tokens[3] != NULL) + { + dst = + (struct nios2_reg *) hash_find (nios2_reg_hash, + insn_info->insn_tokens[1]); + src1 = + (struct nios2_reg *) hash_find (nios2_reg_hash, + insn_info->insn_tokens[2]); + + // a 5-bit constant expression + 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); + } +} + + +/* assembles register arguments "dst" */ +void +nios2_assemble_args_d (nios2_insn_infoS * insn_info) +{ + struct nios2_reg *dst; + + if (insn_info->insn_tokens[1] != NULL) + { + dst = + (struct nios2_reg *) hash_find (nios2_reg_hash, + 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]); + } +} + +/* assemble break op */ +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]); +} + +/* Machine-dependent assembler directive handling follows */ + +/* + .set sets assembler options eg noat/at and is also used + to set symbol values (.equ, .equiv ) +*/ +void +s_nios2_set (int equiv) +{ + char *directive = input_line_pointer; + char delim = get_symbol_end (); + char *endline; + 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 = FALSE; + *endline = 0; + + if (!strcmp (directive, "noat")) + { + done = TRUE; + nios2_as_options.noat = TRUE; + } + + if (!strcmp (directive, "at")) + { + done = TRUE; + nios2_as_options.noat = FALSE; + } + + if (!strcmp (directive, "nobreak")) + { + done = TRUE; + nios2_as_options.nobreak = TRUE; + } + + if (!strcmp (directive, "break")) + { + done = TRUE; + nios2_as_options.nobreak = FALSE; + } + + if (!strcmp (directive, "norelax")) + { + done = TRUE; + nios2_as_options.relax = relax_none; + } + else if (!strcmp (directive, "relaxsection")) + { + done = TRUE; + nios2_as_options.relax = relax_section; + } + else if (!strcmp (directive, "relaxall")) + { + done = TRUE; + nios2_as_options.relax = relax_all; + } + + + 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); +} + +/* nop fill pattern for text section */ +static char const nop[4] = { 0x3a, 0x88, 0x01, 0x00 }; + +/* nios2_frob_label() is called when after a label is recognized. */ + +void +nios2_frob_label (symbolS * 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; +} + + + +/* Hook into cons for auto-alignment. */ + +void +nios2_cons_align (int size) +{ + int log_size; + const char *pfill = NULL; + + log_size = 0; + 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; +} + +static void +s_nios2_sdata (int ignore ATTRIBUTE_UNUSED) +{ + int temp; + + temp = get_absolute_expression (); + subseg_new (".sdata", 0); + demand_empty_rest_of_line (); +} + +/* 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 0; +} + +/* 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; +} + +/* 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; +} + +/* 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; + + 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); + } +} + +/* This is called from HANDLE_ALIGN in tc-nios2.h. */ + +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, 4, &abs_symbol, fragp->fr_offset, 0, + BFD_RELOC_NIOS2_ALIGN); + +} + +/* 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; +} diff --git a/gas/config/tc-nios2.h b/gas/config/tc-nios2.h new file mode 100644 index 0000000..660aaa3 --- /dev/null +++ b/gas/config/tc-nios2.h @@ -0,0 +1,105 @@ +/* tc-nios2.h -- header file for tc-nios2.c. + + Copyright (C) 2003 + by Nigel Gray (ngray@altera.com). + + This file is part of GAS. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, 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, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, 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 + +#ifdef OBJ_ELF +extern const char *nios2_target_format (void); +#define TARGET_FORMAT nios2_target_format () +#define TARGET_ARCH bfd_arch_nios2 +#endif + +/* An 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) + +/* NG this may need to change when we look at implementing symbols */ +#define md_undefined_symbol(name) (0) + +/* 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 + + +#endif // TC_NIOS2 diff --git a/gas/configure b/gas/configure index e66abca..cbb3889 100755 --- a/gas/configure +++ b/gas/configure @@ -4173,6 +4173,8 @@ for this_target in $target $canon_targets ; do m8*) cpu_type=m88k ;; mips*el) cpu_type=mips endian=little ;; mips*) cpu_type=mips endian=big ;; + nios2eb) cpu_type=nios2 endian=big ;; + nios2el|nios2) cpu_type=nios2 endian=little ;; or32*) cpu_type=or32 endian=big ;; pjl*) cpu_type=pj endian=little ;; pj*) cpu_type=pj endian=big ;; @@ -4439,6 +4441,7 @@ echo "$as_me: error: Unknown vendor for mips-bsd configuration." >&2;} mn10200-*-*) fmt=elf ;; mn10300-*-*) fmt=elf ;; msp430-*-*) fmt=elf ;; + nios2*-*-*) fmt=elf ;; openrisc-*-*) fmt=elf ;; or32-*-rtems*) fmt=elf ;; or32-*-coff) fmt=coff ;; @@ -4589,7 +4592,7 @@ _ACEOF fi case ${cpu_type}-${fmt} in - alpha*-* | arm-* | i386-* | ia64*-* | mips-* | ns32k-* \ + alpha*-* | arm-* | i386-* | ia64*-* | mips-* | nios2*-* | ns32k-* \ | pdp11-* | ppc-* | sparc-* | strongarm-* | xscale-* \ | *-elf | *-ecoff | *-som) bfd_gas=yes ;; diff --git a/gas/configure.in b/gas/configure.in index f7d0acb..e20754e 100644 --- a/gas/configure.in +++ b/gas/configure.in @@ -143,6 +143,8 @@ changequote([,])dnl m8*) cpu_type=m88k ;; mips*el) cpu_type=mips endian=little ;; mips*) cpu_type=mips endian=big ;; + nios2eb) cpu_type=nios2 endian=big ;; + nios2el|nios2) cpu_type=nios2 endian=little ;; or32*) cpu_type=or32 endian=big ;; pjl*) cpu_type=pj endian=little ;; pj*) cpu_type=pj endian=big ;; @@ -400,6 +402,7 @@ changequote([,])dnl mn10200-*-*) fmt=elf ;; mn10300-*-*) fmt=elf ;; msp430-*-*) fmt=elf ;; + nios2*-*-*) fmt=elf ;; openrisc-*-*) fmt=elf ;; or32-*-rtems*) fmt=elf ;; or32-*-coff) fmt=coff ;; @@ -539,7 +542,7 @@ changequote([,])dnl fi case ${cpu_type}-${fmt} in - alpha*-* | arm-* | i386-* | ia64*-* | mips-* | ns32k-* \ + alpha*-* | arm-* | i386-* | ia64*-* | mips-* | nios2*-* | ns32k-* \ | pdp11-* | ppc-* | sparc-* | strongarm-* | xscale-* \ | *-elf | *-ecoff | *-som) bfd_gas=yes ;; diff --git a/gas/doc/Makefile.am b/gas/doc/Makefile.am index 7f0f805..3e6af57 100644 --- a/gas/doc/Makefile.am +++ b/gas/doc/Makefile.am @@ -24,6 +24,7 @@ asconfig.texi: $(CONFIG).texi || cp $(srcdir)/$(CONFIG).texi ./asconfig.texi CPU_DOCS = \ + c-nios2.texi \ c-a29k.texi \ c-alpha.texi \ c-arc.texi \ diff --git a/gas/doc/Makefile.in b/gas/doc/Makefile.in index 0c6a1b0..c7472d0 100644 --- a/gas/doc/Makefile.in +++ b/gas/doc/Makefile.in @@ -213,6 +213,7 @@ POD2MAN = pod2man --center="GNU Development Tools" \ man_MANS = as.1 info_TEXINFOS = as.texinfo CPU_DOCS = \ + c-nios2.texi \ c-a29k.texi \ c-alpha.texi \ c-arc.texi \ diff --git a/gas/doc/all.texi b/gas/doc/all.texi index 4e302ce..3094553 100644 --- a/gas/doc/all.texi +++ b/gas/doc/all.texi @@ -26,6 +26,7 @@ @c CPUs of interest @c ================ +@set NIOSII @set A29K @set ALPHA @set ARC diff --git a/gas/doc/as.texinfo b/gas/doc/as.texinfo index d9d23df..ab2affa 100644 --- a/gas/doc/as.texinfo +++ b/gas/doc/as.texinfo @@ -238,6 +238,15 @@ gcc(1), ld(1), and the Info entries for @file{binutils} and @file{ld}. @c @c Target dependent options are listed below. Keep the list sorted. @c Add an empty line for separation. + + +@ifset NIOSII +@emph{Target Altera Nios II options:} + [@b{-relax-all}] + [@b{-relax-section}] + [@b{-no-relax}] +@end ifset + @ifset A29K @c am29k has no machine-dependent assembler options @end ifset @@ -577,6 +586,21 @@ Standard input, or source files to assemble. @end table +@ifset NIOSII +The following options are available when @value{AS} is configured for +an Altera Nios II processor. + +@table @gcctabopt +@item -relax-all +Replace all branch and call instructions with @code{jmp} and @code{callr} sequences +@item -relax-section +Replace identified out of range branches with @code{jmp} sequences (default) +@item -no-relax +Do not replace any branches or calls +@end table +@end ifset + + @ifset ARC The following options are available when @value{AS} is configured for an ARC processor. @@ -2035,6 +2059,9 @@ This means you may not nest these comments. @cindex line comment character Anything from the @dfn{line comment} character to the next newline is considered a comment and is ignored. The line comment character is +@ifset NIOSII +@samp{#} for the Altera Nios II family; +@end ifset @ifset A29K @samp{;} for the AMD 29K family; @end ifset @@ -3876,7 +3903,7 @@ is already a multiple of 8, no change is needed. For the tic54x, the first expression is the alignment request in words. For other systems, including the i386 using a.out format, and the arm and -strongarm, it is the +strongarm, and the Altera Nios II, it is the number of low-order zero bits the location counter must have after advancement. For example @samp{.align 3} advances the location counter until it a multiple of 8. If the location counter is already a @@ -5864,6 +5891,9 @@ include details on any machine's instruction set. For details on that subject, see the hardware manufacturer's manual. @menu +@ifset NIOSII +* NiosII-Dependent:: Altera Nios II Dependent Features +@end ifset @ifset A29K * AMD29K-Dependent:: AMD 29K Dependent Features @end ifset @@ -5974,6 +6004,11 @@ subject, see the hardware manufacturer's manual. @c node and sectioning commands; hence the repetition of @chapter BLAH @c in both conditional blocks. + +@ifset NIOSII +@include c-nios2.texi +@end ifset + @ifset A29K @include c-a29k.texi @end ifset diff --git a/gas/doc/c-nios2.texi b/gas/doc/c-nios2.texi new file mode 100644 index 0000000..b2d5d61 --- /dev/null +++ b/gas/doc/c-nios2.texi @@ -0,0 +1,222 @@ +@c Copyright 2004 +@c This is part of the GAS manual. +@c For copying conditions, see the file as.texinfo. +@ifset GENERIC +@page +@node NiosII-Dependent +@chapter Altera Nios II Dependent Features +@end ifset +@ifclear GENERIC +@node Machine Dependencies +@chapter ltera 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 + +@table @code + +@cindex @code{relax-all} command line option, Nios II +@item -relax-all +Replace all branch and call instructions with @code{jmp} and @code{callr} sequences + +@cindex @code{relax-section} command line option, Nios II +@item -relax-section +Replace identified out of range branches with @code{jmp} sequences (default) + +@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 + +@end table + + +@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 +@samp{#} is the line comment character. + +@cindex line separator character, Nios II +@cindex Nios II line separator 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 %hiadj is: +((@var{expression} >> 16) & 0xffff) + ((@var{expression} >> 15) & 0x01). + +The intention of the @code{%hiadj} relocation is to be used with +an @code{addi}, @code{ld} or @code{st} instructions +along with a @code{%lo}. + +@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 + + +@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 @var{.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 un-aligned constant 2-bytes in size + +@cindex @code{4byte} directive, Nios II +@item .4byte @var{expression} +Create an un-aligned constant 4-bytes in size + +@cindex @code{8byte} directive, Nios II +@item .8byte @var{expression} +Create an un-aligned constant 8-bytes in size + +@cindex @code{16byte} directive, Nios II +@item .16byte @var{expression} +Create an un-aligned 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 and macro or relaxation expansions will +generate a warning. + +@cindex @code{set at} directive, Nios II +@item .set at +Assembly code using @code{at} register will generate +warnings, and macro expansion and relaxation will be +enabled. + +@cindex @code{set nobreak} directive, Nios II +@item .set nobreak +Allows assembly code to use @code{ba}, @code{bt}, +registers without warning. + +@cindex @code{set break} directive, Nios II +@item .set break +Turns warnings back on for using @code{ba}, @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. No +additional pseudo-instructions are needed on this family. + +For information on the Nios II machine instruction set, see the @cite{Nios II +User's Manual} + diff --git a/gas/testsuite/gas/macros/irp.s b/gas/testsuite/gas/macros/irp.s index f37dd54..95a91e3 100644 --- a/gas/testsuite/gas/macros/irp.s +++ b/gas/testsuite/gas/macros/irp.s @@ -1,3 +1,4 @@ + .set norelax .irp param,1,2,3 .long foo\param .endr diff --git a/gas/testsuite/gas/macros/rept.s b/gas/testsuite/gas/macros/rept.s index 571b6f8..d6f86c1 100644 --- a/gas/testsuite/gas/macros/rept.s +++ b/gas/testsuite/gas/macros/rept.s @@ -1,3 +1,4 @@ + .set norelax .rept 3 .long foo1 .endr diff --git a/gas/testsuite/gas/macros/test2.s b/gas/testsuite/gas/macros/test2.s index 0cfca29..ad62adc 100644 --- a/gas/testsuite/gas/macros/test2.s +++ b/gas/testsuite/gas/macros/test2.s @@ -1,3 +1,4 @@ + .set norelax .macro m arg1 arg2 arg3 .long \arg1 .ifc ,\arg2\arg3 diff --git a/gas/testsuite/gas/macros/test3.s b/gas/testsuite/gas/macros/test3.s index e80aa20..0070527 100644 --- a/gas/testsuite/gas/macros/test3.s +++ b/gas/testsuite/gas/macros/test3.s @@ -1,3 +1,4 @@ + .set norelax .macro m arg1 arg2 \arg1 .exitm diff --git a/gas/testsuite/gas/nios2/add.d b/gas/testsuite/gas/nios2/add.d new file mode 100644 index 0000000..ba3d27f --- /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 0000000..5b72a82 --- /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 0000000..90a9e5f --- /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 0000000..5683839 --- /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 0000000..d11e7fc --- /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