From efaed573357cafd2cd54c700ae2eab18014882e7 Mon Sep 17 00:00:00 2001 From: Frans Meulenbroeks Date: Tue, 6 Jul 2010 21:17:14 +0200 Subject: binutils 2.17.50.0.12: added nios2 support added nios2 support this is based upon the windriver toolchain (see http://www.nioswiki.com/Build_the_gcc4_toolchain for details). The nios2 changes are lifted from the wrs toolchain and ported to 2.17.50.0.12. Also solved a few bugs As there is no functional change for other platforms no PR bump is needed (the patches are only applied for nios2 machines) Signed-off-by: Frans Meulenbroeks --- .../binutils-nios2-files.patch | 41015 +++++++++++++++++++ .../binutils-2.17.50.0.12/binutils-nios2.patch | 576 + recipes/binutils/binutils_2.17.50.0.12.bb | 5 + 3 files changed, 41596 insertions(+) create mode 100644 recipes/binutils/binutils-2.17.50.0.12/binutils-nios2-files.patch create mode 100644 recipes/binutils/binutils-2.17.50.0.12/binutils-nios2.patch (limited to 'recipes/binutils') diff --git a/recipes/binutils/binutils-2.17.50.0.12/binutils-nios2-files.patch b/recipes/binutils/binutils-2.17.50.0.12/binutils-nios2-files.patch new file mode 100644 index 0000000000..fc77ca2fea --- /dev/null +++ b/recipes/binutils/binutils-2.17.50.0.12/binutils-nios2-files.patch @@ -0,0 +1,41015 @@ +Index: binutils-2.17.50.0.12/bfd/cpu-nios2.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ binutils-2.17.50.0.12/bfd/cpu-nios2.c 2010-06-30 15:06:08.000000000 +0200 +@@ -0,0 +1,71 @@ ++/* NOT ASSIGNED TO FSF. COPYRIGHT ALTERA. */ ++/* 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]); +Index: binutils-2.17.50.0.12/bfd/elf32-nios2.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ binutils-2.17.50.0.12/bfd/elf32-nios2.c 2010-06-30 15:06:08.000000000 +0200 +@@ -0,0 +1,4992 @@ ++/* NOT ASSIGNED TO FSF. COPYRIGHT ALTERA. */ ++/* New Jersey-specific support for 32-bit ELF ++ ++ Copyright (C) 2005 ++ by Nigel Gray (ngray@altera.com). ++ ++ Copyright (C) 2008, CodeSourcery Inc. ++ ++ ++This file is part of BFD, the Binary File Descriptor library. ++ ++This program is free software; you can redistribute it and/or modify ++it under the terms of the GNU General Public License as published by ++the Free Software Foundation; either version 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 *); ++#if 0 ++static bfd_boolean nios2_elf32_relax_delete_bytes ++ (bfd *, asection *, bfd_vma, int); ++#endif ++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_pcrel_lo16_relocate ++ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); ++ ++static bfd_reloc_status_type nios2_elf32_pcrel_hiadj16_relocate ++ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); ++ ++static bfd_reloc_status_type nios2_elf32_pcrel16_relocate ++ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); ++ ++static bfd_reloc_status_type nios2_elf32_call26_relocate ++ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); ++ ++static bfd_reloc_status_type nios2_elf32_gprel_relocate ++ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); ++ ++static bfd_reloc_status_type nios2_elf32_ujmp_relocate ++ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); ++ ++static bfd_reloc_status_type nios2_elf32_cjmp_relocate ++ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); ++ ++static bfd_reloc_status_type nios2_elf32_callr_relocate ++ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); ++ ++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_pcrel_lo16_relocate ++ (bfd *, reloc_howto_type *, asection *, ++ bfd_byte *, bfd_vma, bfd_vma, bfd_vma); ++ ++static bfd_reloc_status_type nios2_elf32_do_pcrel_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 bfd_boolean nios2_elf32_section_from_shdr ++ (bfd *, Elf_Internal_Shdr *, const char *name, int shindex); ++ ++static bfd_boolean nios2_elf32_section_flags ++ (flagword *, const 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); ++ ++void ++_bfd_set_link_info (struct bfd_link_info *info); ++ ++void ++_bfd_set_force_make_executable (bfd_boolean force); ++ ++ ++/* Target vector. */ ++extern const bfd_target bfd_elf32_littlenios2_vec; ++extern const bfd_target bfd_elf32_bignios2_vec; ++ ++/* Offset of tp and dtp pointers from start of TLS block. */ ++#define TP_OFFSET 0x7000 ++#define DTP_OFFSET 0x8000 ++ ++/* The relocation table used for SHT_REL sections. */ ++ ++static reloc_howto_type elf_nios2_howto_table_rel[] = { ++ /* No relocation. */ ++ HOWTO (R_NIOS2_NONE, /* type */ ++ 0, /* rightshift */ ++ 0, /* size (0 = byte, 1 = short, 2 = long) */ ++ 0, /* bitsize */ ++ FALSE, /* pc_relative */ ++ 0, /* bitpos */ ++ complain_overflow_dont, /* complain_on_overflow */ ++ bfd_elf_generic_reloc, /* special_function */ ++ "R_NIOS2_NONE", /* name */ ++ FALSE, /* partial_inplace */ ++ 0, /* src_mask */ ++ 0, /* dst_mask */ ++ FALSE), /* pcrel_offset */ ++ ++ /* 16-bit signed immediate relocation */ ++ HOWTO (R_NIOS2_S16, /* type */ ++ 0, /* rightshift */ ++ 2, /* size (0 = byte, 1 = short, 2 = long) */ ++ 16, /* bitsize */ ++ FALSE, /* pc_relative */ ++ 6, /* bitpos */ ++ complain_overflow_signed, /* complain on overflow */ ++ bfd_elf_generic_reloc, /* special function */ ++ "R_NIOS2_S16", /* name */ ++ FALSE, /* partial_inplace */ ++ 0x003fffc0, /* src_mask */ ++ 0x003fffc0, /* dest_mask */ ++ FALSE), /* pcrel_offset */ ++ ++ /* 16-bit unsigned immediate relocation */ ++ HOWTO (R_NIOS2_U16, /* type */ ++ 0, /* rightshift */ ++ 2, /* size (0 = byte, 1 = short, 2 = long) */ ++ 16, /* bitsize */ ++ FALSE, /* pc_relative */ ++ 6, /* bitpos */ ++ complain_overflow_unsigned, /* complain on overflow */ ++ bfd_elf_generic_reloc, /* special function */ ++ "R_NIOS2_U16", /* name */ ++ FALSE, /* partial_inplace */ ++ 0x003fffc0, /* src_mask */ ++ 0x003fffc0, /* dest_mask */ ++ FALSE), /* pcrel_offset */ ++ ++ HOWTO (R_NIOS2_PCREL16, /* type */ ++ 0, /* rightshift */ ++ 2, /* size (0 = byte, 1 = short, 2 = long) */ ++ 16, /* bitsize */ ++ TRUE, /* pc_relative */ ++ 6, /* bitpos */ ++ complain_overflow_signed, /* complain on overflow */ ++ nios2_elf32_pcrel16_relocate, /* special function */ ++ "R_NIOS2_PCREL16", /* name */ ++ FALSE, /* partial_inplace */ ++ 0x003fffc0, /* src_mask */ ++ 0x003fffc0, /* dest_mask */ ++ TRUE), /* pcrel_offset */ ++ ++ HOWTO (R_NIOS2_CALL26, /* type */ ++ 2, /* rightshift */ ++ 2, /* size (0 = byte, 1 = short, 2 = long) */ ++ 26, /* bitsize */ ++ FALSE, /* pc_relative */ ++ 6, /* bitpos */ ++ complain_overflow_dont, /* complain on overflow */ ++ nios2_elf32_call26_relocate, /* special function */ ++ "R_NIOS2_CALL26", /* name */ ++ FALSE, /* partial_inplace */ ++ 0xffffffc0, /* src_mask */ ++ 0xffffffc0, /* dst_mask */ ++ FALSE), /* pcrel_offset */ ++ ++ HOWTO (R_NIOS2_IMM5, ++ 0, ++ 2, ++ 5, ++ FALSE, ++ 6, ++ complain_overflow_bitfield, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_IMM5", ++ FALSE, ++ 0x000007c0, ++ 0x000007c0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_CACHE_OPX, ++ 0, ++ 2, ++ 5, ++ FALSE, ++ 22, ++ complain_overflow_bitfield, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_CACHE_OPX", ++ FALSE, ++ 0x07c00000, ++ 0x07c00000, ++ FALSE), ++ ++ HOWTO (R_NIOS2_IMM6, ++ 0, ++ 2, ++ 6, ++ FALSE, ++ 6, ++ complain_overflow_bitfield, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_IMM6", ++ FALSE, ++ 0x00000fc0, ++ 0x00000fc0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_IMM8, ++ 0, ++ 2, ++ 8, ++ FALSE, ++ 6, ++ complain_overflow_bitfield, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_IMM8", ++ FALSE, ++ 0x00003fc0, ++ 0x00003fc0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_HI16, ++ 0, ++ 2, ++ 32, ++ FALSE, ++ 6, ++ complain_overflow_dont, ++ nios2_elf32_hi16_relocate, ++ "R_NIOS2_HI16", ++ FALSE, ++ 0x003fffc0, ++ 0x003fffc0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_LO16, ++ 0, ++ 2, ++ 32, ++ FALSE, ++ 6, ++ complain_overflow_dont, ++ nios2_elf32_lo16_relocate, ++ "R_NIOS2_LO16", ++ FALSE, ++ 0x003fffc0, ++ 0x003fffc0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_HIADJ16, ++ 0, ++ 2, ++ 32, ++ FALSE, ++ 6, ++ complain_overflow_dont, ++ nios2_elf32_hiadj16_relocate, ++ "R_NIOS2_HIADJ16", ++ FALSE, ++ 0x003fffc0, ++ 0x003fffc0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_BFD_RELOC_32, ++ 0, ++ 2, /* long */ ++ 32, ++ FALSE, ++ 0, ++ complain_overflow_dont, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_BFD_RELOC32", ++ FALSE, ++ 0xffffffff, ++ 0xffffffff, ++ FALSE), ++ ++ HOWTO (R_NIOS2_BFD_RELOC_16, ++ 0, ++ 1, /* short */ ++ 16, ++ FALSE, ++ 0, ++ complain_overflow_bitfield, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_BFD_RELOC16", ++ FALSE, ++ 0x0000ffff, ++ 0x0000ffff, ++ FALSE), ++ ++ HOWTO (R_NIOS2_BFD_RELOC_8, ++ 0, ++ 0, /* byte */ ++ 8, ++ FALSE, ++ 0, ++ complain_overflow_bitfield, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_BFD_RELOC8", ++ FALSE, ++ 0x000000ff, ++ 0x000000ff, ++ FALSE), ++ ++ HOWTO (R_NIOS2_GPREL, ++ 0, ++ 2, ++ 32, ++ FALSE, ++ 6, ++ complain_overflow_dont, ++ nios2_elf32_gprel_relocate, ++ "R_NIOS2_GPREL", ++ FALSE, ++ 0x003fffc0, ++ 0x003fffc0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_GNU_VTINHERIT, ++ 0, ++ 2, /* short */ ++ 0, ++ FALSE, ++ 0, ++ complain_overflow_dont, ++ NULL, ++ "R_NIOS2_GNU_VTINHERIT", ++ FALSE, ++ 0, ++ 0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_GNU_VTENTRY, ++ 0, ++ 2, /* byte */ ++ 0, ++ FALSE, ++ 0, ++ complain_overflow_dont, ++ _bfd_elf_rel_vtable_reloc_fn, ++ "R_NIOS2_GNU_VTENTRY", ++ FALSE, ++ 0, ++ 0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_UJMP, ++ 0, ++ 2, ++ 32, ++ FALSE, ++ 6, ++ complain_overflow_dont, ++ nios2_elf32_ujmp_relocate, ++ "R_NIOS2_UJMP", ++ FALSE, ++ 0x003fffc0, ++ 0x003fffc0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_CJMP, ++ 0, ++ 2, ++ 32, ++ FALSE, ++ 6, ++ complain_overflow_dont, ++ nios2_elf32_cjmp_relocate, ++ "R_NIOS2_CJMP", ++ FALSE, ++ 0x003fffc0, ++ 0x003fffc0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_CALLR, ++ 0, ++ 2, ++ 32, ++ FALSE, ++ 6, ++ complain_overflow_dont, ++ nios2_elf32_callr_relocate, ++ "R_NIOS2_CALLR", ++ FALSE, ++ 0x003fffc0, ++ 0x003fffc0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_ALIGN, ++ 0, ++ 2, ++ 0, ++ FALSE, ++ 0, ++ complain_overflow_dont, ++ nios2_elf32_ignore_reloc, ++ "R_NIOS2_ALIGN", ++ FALSE, ++ 0, ++ 0, ++ TRUE), ++ ++ ++ HOWTO (R_NIOS2_GOT16, ++ 0, ++ 2, ++ 16, ++ FALSE, ++ 6, ++ complain_overflow_bitfield, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_GOT16", ++ FALSE, ++ 0x003fffc0, ++ 0x003fffc0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_CALL16, ++ 0, ++ 2, ++ 16, ++ FALSE, ++ 6, ++ complain_overflow_bitfield, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_CALL16", ++ FALSE, ++ 0x003fffc0, ++ 0x003fffc0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_GOTOFF_LO, ++ 0, ++ 2, ++ 16, ++ FALSE, ++ 6, ++ complain_overflow_dont, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_GOTOFF_LO", ++ FALSE, ++ 0x003fffc0, ++ 0x003fffc0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_GOTOFF_HA, ++ 0, ++ 2, ++ 16, ++ FALSE, ++ 6, ++ complain_overflow_dont, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_GOTOFF_HA", ++ FALSE, ++ 0x003fffc0, ++ 0x003fffc0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_PCREL_LO, ++ 0, ++ 2, ++ 16, ++ TRUE, ++ 6, ++ complain_overflow_dont, ++ nios2_elf32_pcrel_lo16_relocate, ++ "R_NIOS2_PCREL_LO", ++ FALSE, ++ 0x003fffc0, ++ 0x003fffc0, ++ TRUE), ++ ++ HOWTO (R_NIOS2_PCREL_HA, ++ 0, ++ 2, ++ 16, ++ FALSE, /* This is a PC-relative relocation, but we need to subtract ++ PC ourselves before the HIADJ. */ ++ 6, ++ complain_overflow_dont, ++ nios2_elf32_pcrel_hiadj16_relocate, ++ "R_NIOS2_PCREL_HA", ++ FALSE, ++ 0x003fffc0, ++ 0x003fffc0, ++ TRUE), ++ ++ HOWTO (R_NIOS2_TLS_GD16, ++ 0, ++ 2, ++ 16, ++ FALSE, ++ 6, ++ complain_overflow_bitfield, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_TLS_GD16", ++ FALSE, ++ 0x003fffc0, ++ 0x003fffc0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_TLS_LDM16, ++ 0, ++ 2, ++ 16, ++ FALSE, ++ 6, ++ complain_overflow_bitfield, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_TLS_LDM16", ++ FALSE, ++ 0x003fffc0, ++ 0x003fffc0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_TLS_LDO16, ++ 0, ++ 2, ++ 16, ++ FALSE, ++ 6, ++ complain_overflow_bitfield, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_TLS_LDO16", ++ FALSE, ++ 0x003fffc0, ++ 0x003fffc0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_TLS_IE16, ++ 0, ++ 2, ++ 16, ++ FALSE, ++ 6, ++ complain_overflow_bitfield, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_TLS_IE16", ++ FALSE, ++ 0x003fffc0, ++ 0x003fffc0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_TLS_LE16, ++ 0, ++ 2, ++ 16, ++ FALSE, ++ 6, ++ complain_overflow_bitfield, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_TLS_LE16", ++ FALSE, ++ 0x003fffc0, ++ 0x003fffc0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_TLS_DTPMOD, ++ 0, ++ 2, ++ 32, ++ FALSE, ++ 0, ++ complain_overflow_dont, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_TLS_DTPMOD", ++ FALSE, ++ 0xffffffff, ++ 0xffffffff, ++ FALSE), ++ ++ HOWTO (R_NIOS2_TLS_DTPREL, ++ 0, ++ 2, ++ 32, ++ FALSE, ++ 0, ++ complain_overflow_dont, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_TLS_DTPREL", ++ FALSE, ++ 0xffffffff, ++ 0xffffffff, ++ FALSE), ++ ++ HOWTO (R_NIOS2_TLS_TPREL, ++ 0, ++ 2, ++ 32, ++ FALSE, ++ 0, ++ complain_overflow_dont, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_TLS_TPREL", ++ FALSE, ++ 0xffffffff, ++ 0xffffffff, ++ FALSE), ++ ++ HOWTO (R_NIOS2_COPY, ++ 0, ++ 2, ++ 32, ++ FALSE, ++ 0, ++ complain_overflow_dont, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_COPY", ++ FALSE, ++ 0, ++ 0, ++ FALSE), ++ ++ HOWTO (R_NIOS2_GLOB_DAT, ++ 0, ++ 2, ++ 32, ++ FALSE, ++ 0, ++ complain_overflow_dont, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_GLOB_DAT", ++ FALSE, ++ 0xffffffff, ++ 0xffffffff, ++ FALSE), ++ ++ HOWTO (R_NIOS2_JUMP_SLOT, ++ 0, ++ 2, ++ 32, ++ FALSE, ++ 0, ++ complain_overflow_dont, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_JUMP_SLOT", ++ FALSE, ++ 0xffffffff, ++ 0xffffffff, ++ FALSE), ++ ++ HOWTO (R_NIOS2_RELATIVE, ++ 0, ++ 2, ++ 32, ++ FALSE, ++ 0, ++ complain_overflow_dont, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_RELATIVE", ++ FALSE, ++ 0xffffffff, ++ 0xffffffff, ++ FALSE), ++ ++ HOWTO (R_NIOS2_GOTOFF, ++ 0, ++ 2, ++ 32, ++ FALSE, ++ 0, ++ complain_overflow_dont, ++ bfd_elf_generic_reloc, ++ "R_NIOS2_GOTOFF", ++ FALSE, ++ 0xffffffff, ++ 0xffffffff, ++ FALSE), ++ ++/* Add other relocations here. */ ++}; ++ ++static unsigned char elf_code_to_howto_index[R_NIOS2_ILLEGAL + 1]; ++ ++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}, ++ {BFD_RELOC_NIOS2_GOT16, R_NIOS2_GOT16}, ++ {BFD_RELOC_NIOS2_CALL16, R_NIOS2_CALL16}, ++ {BFD_RELOC_NIOS2_GOTOFF_LO, R_NIOS2_GOTOFF_LO}, ++ {BFD_RELOC_NIOS2_GOTOFF_HA, R_NIOS2_GOTOFF_HA}, ++ {BFD_RELOC_NIOS2_PCREL_LO, R_NIOS2_PCREL_LO}, ++ {BFD_RELOC_NIOS2_PCREL_HA, R_NIOS2_PCREL_HA}, ++ {BFD_RELOC_NIOS2_TLS_GD16, R_NIOS2_TLS_GD16}, ++ {BFD_RELOC_NIOS2_TLS_LDM16, R_NIOS2_TLS_LDM16}, ++ {BFD_RELOC_NIOS2_TLS_LDO16, R_NIOS2_TLS_LDO16}, ++ {BFD_RELOC_NIOS2_TLS_IE16, R_NIOS2_TLS_IE16}, ++ {BFD_RELOC_NIOS2_TLS_LE16, R_NIOS2_TLS_LE16}, ++ {BFD_RELOC_NIOS2_TLS_DTPMOD, R_NIOS2_TLS_DTPMOD}, ++ {BFD_RELOC_NIOS2_TLS_DTPREL, R_NIOS2_TLS_DTPREL}, ++ {BFD_RELOC_NIOS2_TLS_TPREL, R_NIOS2_TLS_TPREL}, ++ {BFD_RELOC_NIOS2_COPY, R_NIOS2_COPY}, ++ {BFD_RELOC_NIOS2_GLOB_DAT, R_NIOS2_GLOB_DAT}, ++ {BFD_RELOC_NIOS2_JUMP_SLOT, R_NIOS2_JUMP_SLOT}, ++ {BFD_RELOC_NIOS2_RELATIVE, R_NIOS2_RELATIVE}, ++ {BFD_RELOC_NIOS2_GOTOFF, R_NIOS2_GOTOFF} ++}; ++ ++/* The Nios II linker needs to keep track of the number of relocs that it ++ decides to copy as dynamic relocs in check_relocs for each symbol. ++ This is so that it can later discard them if they are found to be ++ unnecessary. We store the information in a field extending the ++ regular ELF linker hash table. */ ++ ++struct elf32_nios2_dyn_relocs ++{ ++ struct elf32_nios2_dyn_relocs *next; ++ ++ /* The input section of the reloc. */ ++ asection *sec; ++ ++ /* Total number of relocs copied for the input section. */ ++ bfd_size_type count; ++ ++ /* Number of pc-relative relocs copied for the input section. */ ++ bfd_size_type pc_count; ++}; ++ ++/* Nios II ELF linker hash entry. */ ++ ++struct elf32_nios2_link_hash_entry ++{ ++ struct elf_link_hash_entry root; ++ ++ /* Track dynamic relocs copied for this symbol. */ ++ struct elf32_nios2_dyn_relocs *dyn_relocs; ++ ++#define GOT_UNKNOWN 0 ++#define GOT_NORMAL 1 ++#define GOT_TLS_GD 2 ++#define GOT_TLS_IE 4 ++ unsigned char tls_type; ++ ++ /* We need to detect and take special action for symbols which are only ++ referenced with %call() and not with %got(). Such symbols do not need ++ a dynamic GOT reloc in shared objects, only a dynamic PLT reloc. Lazy ++ linking will not work if the dynamic GOT reloc exists. ++ To check for this condition efficiently, we compare got_types_used against ++ CALL16_USED, meaning ++ (got_types_used & (GOT16_USED | CALL16_USED)) == CALL16_USED. */ ++#define GOT16_USED 1 ++#define CALL16_USED 2 ++ unsigned char got_types_used; ++}; ++ ++#define elf32_nios2_hash_entry(ent) \ ++ ((struct elf32_nios2_link_hash_entry *) (ent)) ++ ++/* Get the Nios II elf linker hash table from a link_info structure. */ ++#define elf32_nios2_hash_table(info) \ ++ ((struct elf32_nios2_link_hash_table *) ((info)->hash)) ++ ++/* Nios II ELF linker hash table. */ ++struct elf32_nios2_link_hash_table ++ { ++ /* The main hash table. */ ++ struct elf_link_hash_table root; ++ ++ /* Short-cuts to get to dynamic linker sections. */ ++ asection *sgot; ++ asection *sgotplt; ++ asection *srelgot; ++ asection *splt; ++ asection *srelplt; ++ asection *sdynbss; ++ asection *srelbss; ++ asection *sbss; ++ ++ union { ++ bfd_signed_vma refcount; ++ bfd_vma offset; ++ } tls_ldm_got; ++ ++ /* Small local sym to section mapping cache. */ ++ struct sym_sec_cache sym_sec; ++ ++ bfd_vma res_n_size; ++ }; ++ ++struct nios2_elf32_obj_tdata ++{ ++ struct elf_obj_tdata root; ++ ++ /* tls_type for each local got entry. */ ++ char *local_got_tls_type; ++ ++ /* TRUE if TLS GD relocs have been seen for this object. */ ++ bfd_boolean has_tlsgd; ++}; ++ ++#define elf32_nios2_tdata(abfd) \ ++ ((struct nios2_elf32_obj_tdata *) (abfd)->tdata.any) ++ ++#define elf32_nios2_local_got_tls_type(abfd) \ ++ (elf32_nios2_tdata (abfd)->local_got_tls_type) ++ ++/* The name of the dynamic interpreter. This is put in the .interp ++ section. */ ++ ++#define ELF_DYNAMIC_INTERPRETER "/lib/ld.so.1" ++ ++/* PLT implementation for position-dependent code. */ ++ ++static const bfd_vma nios2_plt_entry[] = { /* .PLTn: */ ++ 0x03c00034, /* movhi r15, %hiadj(plt_got_slot_address) */ ++ 0x7bc00017, /* ldw r15, %lo(plt_got_slot_address)(r15) */ ++ 0x7800683a /* jmp r15 */ ++}; ++ ++static const bfd_vma nios2_plt0_entry[] = { /* .PLTresolve */ ++ 0x03800034, /* movhi r14, %hiadj(res_0) */ ++ 0x73800004, /* addi r14, r14, %lo(res_0) */ ++ 0x7b9fc83a, /* sub r15, r15, r14 */ ++ 0x03400034, /* movhi r13, %hiadj(_GLOBAL_OFFSET_TABLE_) */ ++ 0x6b800017, /* ldw r14, %lo(_GLOBAL_OFFSET_TABLE_+4)(r13) */ ++ 0x6b400017, /* ldw r13, %lo(_GLOBAL_OFFSET_TABLE_+8)(r13) */ ++ 0x6800683a /* jmp r13 */ ++}; ++ ++/* PLT implementation for position-independent code. */ ++ ++static const bfd_vma nios2_so_plt_entry[] = { /* .PLTn */ ++ 0x03c00034, /* movhi r15, %hiadj(index * 4) */ ++ 0x7bc00004, /* addi r15, r15, %lo(index * 4) */ ++ 0x00000006 /* br .PLTresolve */ ++}; ++ ++static const bfd_vma nios2_so_plt0_entry[] = { /* .PLTresolve */ ++ 0x001ce03a, /* nextpc r14 */ ++ 0x03400034, /* movhi r13, %hiadj(_GLOBAL_OFFSET_TABLE_) */ ++ 0x6b9b883a, /* add r13, r13, r14 */ ++ 0x6b800017, /* ldw r14, %lo(_GLOBAL_OFFSET_TABLE_+4)(r13) */ ++ 0x6b400017, /* ldw r13, %lo(_GLOBAL_OFFSET_TABLE_+8)(r13) */ ++ 0x6800683a /* jmp r13 */ ++}; ++ ++/* Support for core dump NOTE sections */ ++static bfd_boolean ++nios2_grok_prstatus (bfd *abfd, Elf_Internal_Note *note) ++{ ++ int offset; ++ size_t size; ++ ++ switch (note->descsz) ++ { ++ default: ++ return FALSE; ++ ++ case 212: /* Linux/Nios II */ ++ /* pr_cursig */ ++ elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12); ++ ++ /* pr_pid */ ++ elf_tdata (abfd)->core_pid = bfd_get_32 (abfd, note->descdata + 24); ++ ++ /* pr_reg */ ++ offset = 72; ++ size = 136; ++ ++ break; ++ } ++ ++ /* Make a ".reg/999" section. */ ++ return _bfd_elfcore_make_pseudosection (abfd, ".reg", ++ size, note->descpos + offset); ++} ++ ++static bfd_boolean ++nios2_grok_psinfo (bfd *abfd, Elf_Internal_Note *note) ++{ ++ switch (note->descsz) ++ { ++ default: ++ return FALSE; ++ ++ case 124: /* Linux/Nios II elf_prpsinfo */ ++ elf_tdata (abfd)->core_program ++ = _bfd_elfcore_strndup (abfd, note->descdata + 28, 16); ++ elf_tdata (abfd)->core_command ++ = _bfd_elfcore_strndup (abfd, note->descdata + 44, 80); ++ } ++ ++ /* Note that for some reason, a spurious space is tacked ++ onto the end of the args in some (at least one anyway) ++ implementations, so strip it off if it exists. */ ++ ++ { ++ char *command = elf_tdata (abfd)->core_command; ++ int n = strlen (command); ++ ++ if (0 < n && command[n - 1] == ' ') ++ command[n - 1] = '\0'; ++ } ++ ++ return TRUE; ++} ++ ++/* Create an entry in a Nios II ELF linker hash table. */ ++ ++static struct bfd_hash_entry * ++link_hash_newfunc (struct bfd_hash_entry *entry, ++ struct bfd_hash_table *table, const char *string) ++{ ++ /* Allocate the structure if it has not already been allocated by a ++ subclass. */ ++ if (entry == NULL) ++ { ++ entry = bfd_hash_allocate (table, ++ sizeof (struct elf32_nios2_link_hash_entry)); ++ if (entry == NULL) ++ return entry; ++ } ++ ++ /* Call the allocation method of the superclass. */ ++ entry = _bfd_elf_link_hash_newfunc (entry, table, string); ++ if (entry != NULL) ++ { ++ struct elf32_nios2_link_hash_entry *eh; ++ ++ eh = (struct elf32_nios2_link_hash_entry *) entry; ++ eh->dyn_relocs = NULL; ++ eh->tls_type = GOT_UNKNOWN; ++ eh->got_types_used = 0; ++ } ++ ++ return entry; ++} ++ ++/* 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; ++} ++ ++/* Given a reloc name, return a howto structure. */ ++ ++static reloc_howto_type * ++nios2_elf32_bfd_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, ++ const char *r_name) ++{ ++ unsigned int i; ++ for (i = 0; ++ i < (sizeof (elf_nios2_howto_table_rel) ++ / sizeof (elf_nios2_howto_table_rel[0])); ++ i++) ++ if (elf_nios2_howto_table_rel[i].name != NULL ++ && strcasecmp (elf_nios2_howto_table_rel[i].name, r_name) == 0) ++ return &elf_nios2_howto_table_rel[i]; ++ ++ 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??? */ ++} ++ ++/* Return the base VMA address which should be subtracted from real addresses ++ when resolving @dtpoff relocation. ++ This is PT_TLS segment p_vaddr. */ ++ ++static bfd_vma ++dtpoff_base (struct bfd_link_info *info) ++{ ++ /* If tls_sec is NULL, we should have signalled an error already. */ ++ if (elf_hash_table (info)->tls_sec == NULL) ++ return 0; ++ return elf_hash_table (info)->tls_sec->vma; ++} ++ ++/* Return the relocation value for @tpoff relocation ++ if STT_TLS virtual address is ADDRESS. */ ++ ++static bfd_vma ++tpoff (struct bfd_link_info *info, bfd_vma address) ++{ ++ struct elf_link_hash_table *htab = elf_hash_table (info); ++ ++ /* If tls_sec is NULL, we should have signalled an error already. */ ++ if (htab->tls_sec == NULL) ++ return 0; ++ return address - htab->tls_sec->vma; ++} ++ ++/* 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->size == 0) ++ sec->size = sec->rawsize; ++ ++ 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->rawsize); ++ if (contents == NULL) ++ goto error_return; ++ ++ if (!bfd_get_section_contents (abfd, sec, contents, ++ (file_ptr) 0, sec->rawsize)) ++ 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; ++} ++#if 0 ++/* 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->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->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; ++} ++#endif ++struct bfd_link_info *nios2_link_info = NULL; ++ ++void ++_bfd_set_link_info (struct bfd_link_info *info) ++{ ++ nios2_link_info = info; ++} ++ ++bfd_boolean linker_force_make_executable = FALSE; ++ ++void ++_bfd_set_force_make_executable (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_pcrel_lo16_relocate (bfd *abfd, arelent *reloc_entry, ++ asymbol *symbol, void *data, ++ asection *input_section, bfd *output_bfd, ++ char **error_message ATTRIBUTE_UNUSED) ++{ ++ /* This part is from bfd_elf_generic_reloc. */ ++ if (output_bfd != (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_pcrel_lo16_relocate ( ++ abfd, reloc_entry->howto, input_section, data, reloc_entry->address, ++ (symbol->value + symbol->section->output_section->vma + ++ symbol->section->output_offset), ++ reloc_entry->addend); ++} ++ ++static bfd_reloc_status_type ++nios2_elf32_pcrel_hiadj16_relocate (bfd *abfd, arelent *reloc_entry, ++ asymbol *symbol, void *data, ++ asection *input_section, bfd *output_bfd, ++ char **error_message ATTRIBUTE_UNUSED) ++{ ++ /* This part is from bfd_elf_generic_reloc. */ ++ if (output_bfd != (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_pcrel_hiadj16_relocate ( ++ abfd, reloc_entry->howto, input_section, data, reloc_entry->address, ++ (symbol->value + symbol->section->output_section->vma + ++ symbol->section->output_offset), ++ reloc_entry->addend); ++} ++ ++static bfd_reloc_status_type ++nios2_elf32_pcrel16_relocate (bfd *abfd, arelent *reloc_entry, asymbol *symbol, ++ void *data, asection *input_section, ++ bfd *output_bfd, ++ char **error_message ATTRIBUTE_UNUSED) ++{ ++ /* This part is from bfd_elf_generic_reloc. */ ++ if (output_bfd != (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); ++} ++ ++/* The usual way of loading a 32-bit constant into a Nios II register is to ++ load the high 16 bits in one instruction and then add the low 16 bits with ++ a signed add. This means that the high halfword needs to be adjusted to ++ compensate for the sign bit of the low halfword. This function returns the ++ adjusted high halfword for a given 32-bit constant. */ ++ ++static bfd_vma hiadj(bfd_vma symbol_value) ++{ ++ return ++ (((symbol_value >> 16) & 0xffff) + ((symbol_value >> 15) & 0x01)) & 0xffff; ++} ++ ++static bfd_reloc_status_type ++nios2_elf32_do_hiadj16_relocate (bfd *abfd, reloc_howto_type *howto, ++ asection *input_section ATTRIBUTE_UNUSED, ++ bfd_byte *data, bfd_vma offset, ++ bfd_vma symbol_value, bfd_vma addend) ++{ ++ symbol_value = symbol_value + addend; ++ addend = 0; ++ symbol_value = hiadj(symbol_value); ++ return _bfd_final_link_relocate (howto, abfd, input_section, data, offset, ++ symbol_value, addend); ++} ++ ++static bfd_reloc_status_type ++nios2_elf32_do_pcrel_lo16_relocate (bfd *abfd, reloc_howto_type *howto, ++ asection *input_section ATTRIBUTE_UNUSED, ++ bfd_byte *data, bfd_vma offset, ++ bfd_vma symbol_value, bfd_vma addend) ++{ ++ symbol_value = symbol_value + addend; ++ addend = 0; ++ symbol_value = symbol_value & 0xffff; ++ return _bfd_final_link_relocate (howto, abfd, input_section, ++ data, offset, symbol_value, addend); ++} ++ ++static bfd_reloc_status_type ++nios2_elf32_do_pcrel_hiadj16_relocate (bfd *abfd, reloc_howto_type *howto, ++ asection *input_section ++ ATTRIBUTE_UNUSED, ++ bfd_byte *data, bfd_vma offset, ++ bfd_vma symbol_value, bfd_vma addend) ++{ ++ symbol_value = symbol_value + addend; ++ symbol_value -= (input_section->output_section->vma + ++ input_section->output_offset); ++ symbol_value -= offset; ++ addend = 0; ++ symbol_value = hiadj(symbol_value); ++ return _bfd_final_link_relocate (howto, abfd, input_section, data, offset, ++ symbol_value, addend); ++} ++ ++static bfd_reloc_status_type ++nios2_elf32_do_pcrel16_relocate (bfd *abfd, reloc_howto_type *howto, ++ asection *input_section ATTRIBUTE_UNUSED, ++ bfd_byte *data, bfd_vma offset, ++ bfd_vma symbol_value, bfd_vma addend) ++{ ++ /* NIOS2 pc relative relocations are relative to the next 32-bit instruction ++ so we need to subtract 4 before doing a final_link_relocate. */ ++ symbol_value = symbol_value + addend - 4; ++ addend = 0; ++ return _bfd_final_link_relocate (howto, abfd, input_section, ++ data, offset, symbol_value, addend); ++} ++ ++static bfd_reloc_status_type ++nios2_elf32_do_call26_relocate (bfd *abfd, reloc_howto_type *howto, ++ asection *input_section ATTRIBUTE_UNUSED, ++ bfd_byte *data, bfd_vma offset, ++ bfd_vma symbol_value, bfd_vma addend) ++{ ++ /* Check that the relocation is in the same page as the current address. */ ++ if (((symbol_value + addend) & 0xf0000000) ++ != ((input_section->output_section->vma + offset) & 0xf0000000)) ++ return bfd_reloc_overflow; ++ ++ return _bfd_final_link_relocate (howto, abfd, input_section, ++ data, offset, symbol_value, addend); ++} ++ ++ ++static bfd_reloc_status_type ++nios2_elf32_do_gprel_relocate (bfd *abfd, reloc_howto_type *howto, ++ asection *input_section ATTRIBUTE_UNUSED, ++ bfd_byte *data, bfd_vma offset, ++ bfd_vma symbol_value, bfd_vma addend) ++{ ++ /* Because we need the output_bfd, the special handling is done ++ in nios2_elf32_relocate_section or in nios2_elf32_gprel_relocate. */ ++ return _bfd_final_link_relocate (howto, abfd, input_section, ++ data, offset, symbol_value, addend); ++} ++ ++static bfd_reloc_status_type ++nios2_elf32_do_ujmp_relocate (bfd *abfd, reloc_howto_type *howto, ++ asection *input_section ATTRIBUTE_UNUSED, ++ bfd_byte *data, bfd_vma offset, ++ bfd_vma symbol_value, bfd_vma addend) ++{ ++ bfd_vma symbol_lo16, symbol_hi16; ++ bfd_reloc_status_type r; ++ symbol_value = symbol_value + addend; ++ addend = 0; ++ symbol_hi16 = (symbol_value >> 16) & 0xffff; ++ symbol_lo16 = symbol_value & 0xffff; ++ ++ r = _bfd_final_link_relocate (howto, abfd, input_section, ++ data, offset, symbol_hi16, addend); ++ ++ if (r == bfd_reloc_ok) ++ return _bfd_final_link_relocate (howto, abfd, input_section, ++ data, offset + 4, symbol_lo16, addend); ++ ++ return r; ++} ++ ++static bfd_reloc_status_type ++nios2_elf32_do_cjmp_relocate (bfd *abfd, reloc_howto_type *howto, ++ asection *input_section ATTRIBUTE_UNUSED, ++ bfd_byte *data, bfd_vma offset, ++ bfd_vma symbol_value, bfd_vma addend) ++{ ++ bfd_vma symbol_lo16, symbol_hi16; ++ bfd_reloc_status_type r; ++ symbol_value = symbol_value + addend; ++ addend = 0; ++ symbol_hi16 = (symbol_value >> 16) & 0xffff; ++ symbol_lo16 = symbol_value & 0xffff; ++ ++ r = _bfd_final_link_relocate (howto, abfd, input_section, ++ data, offset, symbol_hi16, addend); ++ ++ if (r == bfd_reloc_ok) ++ return _bfd_final_link_relocate (howto, abfd, input_section, ++ data, offset + 4, symbol_lo16, addend); ++ ++ return r; ++} ++ ++static bfd_reloc_status_type ++nios2_elf32_do_callr_relocate (bfd *abfd, reloc_howto_type *howto, ++ asection *input_section ATTRIBUTE_UNUSED, ++ bfd_byte *data, bfd_vma offset, ++ bfd_vma symbol_value, bfd_vma addend) ++{ ++ bfd_vma symbol_lo16, symbol_hi16; ++ bfd_reloc_status_type r; ++ symbol_value = symbol_value + addend; ++ addend = 0; ++ symbol_hi16 = (symbol_value >> 16) & 0xffff; ++ symbol_lo16 = symbol_value & 0xffff; ++ ++ r = _bfd_final_link_relocate (howto, abfd, input_section, ++ data, offset, symbol_hi16, addend); ++ ++ if (r == bfd_reloc_ok) ++ return _bfd_final_link_relocate (howto, abfd, input_section, ++ data, offset + 4, symbol_lo16, addend); ++ ++ return r; ++} ++ ++/* 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; ++ struct elf32_nios2_link_hash_table *htab; ++ asection *sgot; ++ asection *splt; ++ asection *sreloc = NULL; ++ bfd_vma *local_got_offsets; ++ bfd *dynobj = NULL; ++ ++ symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; ++ sym_hashes = elf_sym_hashes (input_bfd); ++ relend = relocs + input_section->reloc_count; ++ ++ htab = elf32_nios2_hash_table (info); ++ sgot = htab->sgot; ++ splt = htab->splt; ++ local_got_offsets = elf_local_got_offsets (input_bfd); ++ ++ dynobj = elf_hash_table (info)->dynobj; ++ ++#if 0 ++ size_t psymalloc = 0; ++ _bfd_generic_link_output_symbols(output_bfd, input_bfd, info, &psymalloc); ++#endif ++ for (rel = relocs; rel < relend; rel++) ++ { ++ reloc_howto_type *howto; ++ unsigned long r_symndx; ++ Elf_Internal_Sym *sym; ++ asection *sec; ++ struct elf_link_hash_entry *h; ++ struct elf32_nios2_link_hash_entry *eh; ++ bfd_vma relocation; ++ bfd_vma gp; ++ bfd_vma reloc_address; ++ bfd_reloc_status_type r = bfd_reloc_ok; ++ const char *name = NULL; ++ int r_type; ++ const char *format; ++ char msgbuf[256]; ++ const char* msg = (const char*) NULL; ++ bfd_boolean unresolved_reloc; ++ bfd_vma off; ++ int use_plt; ++ ++ ++ ++ r_type = ELF32_R_TYPE (rel->r_info); ++ ++ r_symndx = ELF32_R_SYM (rel->r_info); ++ ++ 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 = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel); ++ } ++ else ++ { ++ bfd_boolean warned; ++ ++ RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel, ++ r_symndx, symtab_hdr, sym_hashes, ++ h, sec, relocation, ++ unresolved_reloc, warned); ++ } ++ ++ if (sec != NULL && elf_discarded_section (sec)) ++ { ++ /* For relocs against symbols from removed linkonce sections, ++ or sections discarded by a linker script, we just want the ++ section contents zeroed. Avoid any special processing. */ ++ _bfd_clear_contents (howto, input_bfd, contents + rel->r_offset); ++ rel->r_info = 0; ++ rel->r_addend = 0; ++ continue; ++ } ++ ++ if (sec && sec->output_section != NULL) ++ 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_PCREL_LO: ++ r = ++ nios2_elf32_do_pcrel_lo16_relocate (input_bfd, howto, ++ input_section, contents, ++ rel->r_offset, relocation, ++ rel->r_addend); ++ break; ++ case R_NIOS2_HIADJ16: ++ r = ++ nios2_elf32_do_hiadj16_relocate (input_bfd, howto, ++ input_section, contents, ++ rel->r_offset, relocation, ++ rel->r_addend); ++ break; ++ case R_NIOS2_PCREL_HA: ++ r = ++ nios2_elf32_do_pcrel_hiadj16_relocate (input_bfd, howto, ++ input_section, contents, ++ rel->r_offset, ++ relocation, ++ rel->r_addend); ++ break; ++ case R_NIOS2_PCREL16: ++ r = ++ nios2_elf32_do_pcrel16_relocate (input_bfd, howto, ++ input_section, contents, ++ rel->r_offset, relocation, ++ rel->r_addend); ++ break; ++ case R_NIOS2_GPREL: ++ /* Turns an absolute address into a gp-relative address. */ ++ if (!nios2_elf_assign_gp (output_bfd, &gp, info)) ++ { ++ format = _("global pointer relative relocation at address " ++ "0x%08x when _gp not defined\n"); ++ sprintf(msgbuf, format, reloc_address); ++ msg = msgbuf; ++ r = bfd_reloc_dangerous; ++ } ++ else ++ { ++ bfd_vma symbol_address = rel->r_addend + relocation; ++ relocation = relocation + rel->r_addend - gp; ++ rel->r_addend = 0; ++ if ((signed) relocation < -32768 ++ || (signed) relocation > 32767) ++ { ++ 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: ++ /* Handle relocations which should use the PLT entry. ++ NIOS2_BFD_RELOC_32 relocations will use the symbol's value, ++ which may point to a PLT entry, but we don't need to handle ++ that here. If we created a PLT entry, all branches in this ++ object should go to it. */ ++ if (h != NULL && splt != NULL && h->plt.offset != (bfd_vma) -1) ++ { ++ /* If we've created a .plt section, and assigned a PLT entry ++ to this function, it should not be known to bind locally. ++ If it were, we would have cleared the PLT entry. */ ++ BFD_ASSERT (!SYMBOL_CALLS_LOCAL (info, h)); ++ ++ relocation = (splt->output_section->vma ++ + splt->output_offset ++ + h->plt.offset); ++ ++ unresolved_reloc = FALSE; ++ } ++ r = ++ nios2_elf32_do_call26_relocate (input_bfd, howto, ++ input_section, contents, ++ rel->r_offset, relocation, ++ rel->r_addend); ++ break; ++ case R_NIOS2_ALIGN: ++ r = bfd_reloc_ok; ++ /* For symmetry this would be ++ r = nios2_elf32_do_ignore_reloc (input_bfd, howto, ++ input_section, contents, ++ rel->r_offset, relocation, ++ rel->r_addend); ++ but do_ignore_reloc would do no more than return bfd_reloc_ok. ++ */ ++ break; ++ ++ case R_NIOS2_GOT16: ++ case R_NIOS2_CALL16: ++ /* Relocation is to the entry for this symbol in the ++ global offset table. */ ++ if (sgot == NULL) ++ { ++ r = bfd_reloc_notsupported; ++ break; ++ } ++ ++ use_plt = 0; ++ ++ if (h != NULL) ++ { ++ bfd_boolean dyn; ++ ++ eh = (struct elf32_nios2_link_hash_entry *)h; ++ use_plt = (eh->got_types_used == CALL16_USED && ++ h->plt.offset != (bfd_vma) -1); ++ ++ off = h->got.offset; ++ BFD_ASSERT (off != (bfd_vma) -1); ++ dyn = elf_hash_table (info)->dynamic_sections_created; ++ if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) ++ || (info->shared ++ && SYMBOL_REFERENCES_LOCAL (info, h)) ++ || (ELF_ST_VISIBILITY (h->other) ++ && h->root.type == bfd_link_hash_undefweak)) ++ { ++ /* This is actually a static link, or it is a -Bsymbolic ++ link and the symbol is defined locally. We must ++ initialize this entry in the global offset table. ++ Since the offset must always be a multiple of 4, we ++ use the least significant bit to record whether we ++ have initialized it already. ++ ++ When doing a dynamic link, we create a .rela.got ++ relocation entry to initialize the value. This is ++ done in the finish_dynamic_symbol routine. */ ++ if ((off & 1) != 0) ++ off &= ~1; ++ else ++ { ++ bfd_put_32 (output_bfd, relocation, ++ sgot->contents + off); ++ h->got.offset |= 1; ++ } ++ } ++ else ++ unresolved_reloc = FALSE; ++ } ++ else ++ { ++ BFD_ASSERT (local_got_offsets != NULL && ++ local_got_offsets[r_symndx] != (bfd_vma) -1); ++ ++ off = local_got_offsets[r_symndx]; ++ ++ /* The offset must always be a multiple of 4. We use the ++ least significant bit to record whether we have already ++ generated the necessary reloc. */ ++ if ((off & 1) != 0) ++ off &= ~1; ++ else ++ { ++ bfd_put_32 (output_bfd, relocation, ++ sgot->contents + off); ++ ++ if (info->shared) ++ { ++ asection * srelgot; ++ Elf_Internal_Rela outrel; ++ bfd_byte *loc; ++ ++ srelgot = htab->srelgot; ++ BFD_ASSERT (srelgot != NULL); ++ ++ outrel.r_addend = relocation; ++ outrel.r_offset = (sgot->output_section->vma ++ + sgot->output_offset ++ + off); ++ outrel.r_info = ELF32_R_INFO (0, R_NIOS2_RELATIVE); ++ loc = srelgot->contents; ++ loc += (srelgot->reloc_count++ * ++ sizeof (Elf32_External_Rela)); ++ bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); ++ } ++ ++ local_got_offsets[r_symndx] |= 1; ++ } ++ } ++ ++ if (use_plt && info->shared) ++ { ++ off = ((h->plt.offset - 24) / 12 + 3) * 4; ++ relocation = htab->sgotplt->output_offset + off; ++ } ++ else ++ relocation = sgot->output_offset + off; ++ ++ /* This relocation does not use the addend. */ ++ rel->r_addend = 0; ++ ++ r = _bfd_final_link_relocate (howto, input_bfd, input_section, ++ contents, rel->r_offset, ++ relocation, rel->r_addend); ++ break; ++ ++ case R_NIOS2_GOTOFF_LO: ++ case R_NIOS2_GOTOFF_HA: ++ case R_NIOS2_GOTOFF: ++ /* Relocation is relative to the start of the ++ global offset table. */ ++ ++ BFD_ASSERT (sgot != NULL); ++ if (sgot == NULL) ++ { ++ r = bfd_reloc_notsupported; ++ break; ++ } ++ ++ /* Note that sgot->output_offset is not involved in this ++ calculation. We always want the start of .got. If we ++ define _GLOBAL_OFFSET_TABLE in a different way, as is ++ permitted by the ABI, we might have to change this ++ calculation. */ ++ relocation -= sgot->output_section->vma; ++ switch (howto->type) ++ { ++ case R_NIOS2_GOTOFF_LO: ++ r = nios2_elf32_do_lo16_relocate (input_bfd, howto, ++ input_section, contents, ++ rel->r_offset, relocation, ++ rel->r_addend); ++ break; ++ case R_NIOS2_GOTOFF_HA: ++ r = nios2_elf32_do_hiadj16_relocate (input_bfd, howto, ++ input_section, contents, ++ rel->r_offset, ++ relocation, ++ rel->r_addend); ++ break; ++ default: ++ r = _bfd_final_link_relocate (howto, input_bfd, ++ input_section, contents, ++ rel->r_offset, relocation, ++ rel->r_addend); ++ break; ++ } ++ break; ++ ++ case R_NIOS2_TLS_LDO16: ++ relocation -= dtpoff_base (info) + DTP_OFFSET; ++ ++ r = _bfd_final_link_relocate (howto, input_bfd, input_section, ++ contents, rel->r_offset, ++ relocation, rel->r_addend); ++ break; ++ case R_NIOS2_TLS_LDM16: ++ { ++ bfd_vma off; ++ ++ if (htab->sgot == NULL) ++ abort (); ++ ++ off = htab->tls_ldm_got.offset; ++ ++ if ((off & 1) != 0) ++ off &= ~1; ++ else ++ { ++ /* If we don't know the module number, create a relocation ++ for it. */ ++ if (info->shared) ++ { ++ Elf_Internal_Rela outrel; ++ bfd_byte *loc; ++ ++ if (htab->srelgot == NULL) ++ abort (); ++ ++ outrel.r_addend = 0; ++ outrel.r_offset = (htab->sgot->output_section->vma ++ + htab->sgot->output_offset ++ + off); ++ outrel.r_info = ELF32_R_INFO (0, R_NIOS2_TLS_DTPMOD); ++ ++ loc = htab->srelgot->contents; ++ loc += (htab->srelgot->reloc_count++ ++ * sizeof (Elf32_External_Rela)); ++ bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); ++ } ++ else ++ bfd_put_32 (output_bfd, 1, ++ htab->sgot->contents + off); ++ ++ htab->tls_ldm_got.offset |= 1; ++ } ++ ++ relocation = (htab->sgot->output_offset + off); ++ ++ r = _bfd_final_link_relocate (howto, input_bfd, ++ input_section, contents, ++ rel->r_offset, relocation, ++ rel->r_addend); ++ } ++ ++ break; ++ case R_NIOS2_TLS_GD16: ++ case R_NIOS2_TLS_IE16: ++ { ++ bfd_vma off; ++ int indx; ++ char tls_type; ++ ++ if (htab->sgot == NULL) ++ abort (); ++ ++ indx = 0; ++ if (h != NULL) ++ { ++ bfd_boolean dyn; ++ dyn = htab->root.dynamic_sections_created; ++ if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) ++ && (!info->shared ++ || !SYMBOL_REFERENCES_LOCAL (info, h))) ++ { ++ unresolved_reloc = FALSE; ++ indx = h->dynindx; ++ } ++ off = h->got.offset; ++ tls_type = (((struct elf32_nios2_link_hash_entry *) h) ++ ->tls_type); ++ } ++ else ++ { ++ if (local_got_offsets == NULL) ++ abort (); ++ off = local_got_offsets[r_symndx]; ++ tls_type = (elf32_nios2_local_got_tls_type (input_bfd) ++ [r_symndx]); ++ } ++ ++ if (tls_type == GOT_UNKNOWN) ++ abort (); ++ ++ if ((off & 1) != 0) ++ off &= ~1; ++ else ++ { ++ bfd_boolean need_relocs = FALSE; ++ Elf_Internal_Rela outrel; ++ bfd_byte *loc = NULL; ++ int cur_off = off; ++ ++ /* The GOT entries have not been initialized yet. Do it ++ now, and emit any relocations. If both an IE GOT and a ++ GD GOT are necessary, we emit the GD first. */ ++ ++ if ((info->shared || indx != 0) ++ && (h == NULL ++ || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT ++ || h->root.type != bfd_link_hash_undefweak)) ++ { ++ need_relocs = TRUE; ++ if (htab->srelgot == NULL) ++ abort (); ++ loc = htab->srelgot->contents; ++ loc += (htab->srelgot->reloc_count * ++ sizeof (Elf32_External_Rela)); ++ } ++ ++ if (tls_type & GOT_TLS_GD) ++ { ++ if (need_relocs) ++ { ++ outrel.r_addend = 0; ++ outrel.r_offset = (htab->sgot->output_section->vma ++ + htab->sgot->output_offset ++ + cur_off); ++ outrel.r_info = ELF32_R_INFO (indx, ++ R_NIOS2_TLS_DTPMOD); ++ ++ bfd_elf32_swap_reloca_out (output_bfd, &outrel, ++ loc); ++ htab->srelgot->reloc_count++; ++ loc += sizeof (Elf32_External_Rela); ++ ++ if (indx == 0) ++ bfd_put_32 (output_bfd, ++ (relocation - dtpoff_base (info) - ++ DTP_OFFSET), ++ htab->sgot->contents + cur_off + 4); ++ else ++ { ++ outrel.r_addend = 0; ++ outrel.r_info = ELF32_R_INFO (indx, ++ R_NIOS2_TLS_DTPREL); ++ outrel.r_offset += 4; ++ ++ bfd_elf32_swap_reloca_out (output_bfd, &outrel, ++ loc); ++ htab->srelgot->reloc_count++; ++ loc += sizeof (Elf32_External_Rela); ++ } ++ } ++ else ++ { ++ /* If we are not emitting relocations for a ++ general dynamic reference, then we must be in a ++ static link or an executable link with the ++ symbol binding locally. Mark it as belonging ++ to module 1, the executable. */ ++ bfd_put_32 (output_bfd, 1, ++ htab->sgot->contents + cur_off); ++ bfd_put_32 (output_bfd, (relocation - ++ dtpoff_base (info) - ++ DTP_OFFSET), ++ htab->sgot->contents + cur_off + 4); ++ } ++ ++ cur_off += 8; ++ } ++ ++ if (tls_type & GOT_TLS_IE) ++ { ++ if (need_relocs) ++ { ++ if (indx == 0) ++ outrel.r_addend = (relocation - ++ dtpoff_base (info)); ++ else ++ outrel.r_addend = 0; ++ outrel.r_offset = (htab->sgot->output_section->vma ++ + htab->sgot->output_offset ++ + cur_off); ++ outrel.r_info = ELF32_R_INFO (indx, ++ R_NIOS2_TLS_TPREL); ++ ++ bfd_elf32_swap_reloca_out (output_bfd, &outrel, ++ loc); ++ htab->srelgot->reloc_count++; ++ loc += sizeof (Elf32_External_Rela); ++ } ++ else ++ bfd_put_32 (output_bfd, (tpoff (info, relocation) ++ - TP_OFFSET), ++ htab->sgot->contents + cur_off); ++ cur_off += 4; ++ } ++ ++ if (h != NULL) ++ h->got.offset |= 1; ++ else ++ local_got_offsets[r_symndx] |= 1; ++ } ++ ++ if ((tls_type & GOT_TLS_GD) && r_type != R_NIOS2_TLS_GD16) ++ off += 8; ++ relocation = (htab->sgot->output_offset + off); ++ ++ r = _bfd_final_link_relocate (howto, input_bfd, input_section, ++ contents, rel->r_offset, ++ relocation, rel->r_addend); ++ } ++ ++ break; ++ case R_NIOS2_TLS_LE16: ++ if (info->shared) ++ { ++ (*_bfd_error_handler) ++ (_("%B(%A+0x%lx): R_NIOS2_TLS_LE16 relocation not " ++ "permitted in shared object"), ++ input_bfd, input_section, ++ (long) rel->r_offset, howto->name); ++ return FALSE; ++ } ++ else ++ relocation = tpoff (info, relocation) - TP_OFFSET; ++ ++ r = _bfd_final_link_relocate (howto, input_bfd, input_section, ++ contents, rel->r_offset, ++ relocation, rel->r_addend); ++ break; ++ ++ case R_NIOS2_BFD_RELOC_32: ++ if (info->shared ++ && (input_section->flags & SEC_ALLOC) != 0 ++ && (h == NULL ++ || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT ++ || h->root.type != bfd_link_hash_undefweak)) ++ { ++ Elf_Internal_Rela outrel; ++ bfd_byte *loc; ++ bfd_boolean skip, relocate; ++ ++ /* When generating a shared object, these relocations ++ are copied into the output file to be resolved at run ++ time. */ ++ ++ skip = FALSE; ++ relocate = FALSE; ++ ++ outrel.r_offset = ++ _bfd_elf_section_offset (output_bfd, info, input_section, ++ rel->r_offset); ++ if (outrel.r_offset == (bfd_vma) -1) ++ skip = TRUE; ++ else if (outrel.r_offset == (bfd_vma) -2) ++ skip = TRUE, relocate = TRUE; ++ outrel.r_offset += (input_section->output_section->vma ++ + input_section->output_offset); ++ ++ if (skip) ++ memset (&outrel, 0, sizeof outrel); ++ else if (h != NULL ++ && h->dynindx != -1 ++ && (!info->shared ++ || !info->symbolic ++ || !h->def_regular)) ++ { ++ outrel.r_info = ELF32_R_INFO (h->dynindx, r_type); ++ outrel.r_addend = rel->r_addend; ++ } ++ else ++ { ++ /* This symbol is local, or marked to become local. */ ++ outrel.r_addend = relocation + rel->r_addend; ++ relocate = TRUE; ++ outrel.r_info = ELF32_R_INFO (0, R_NIOS2_RELATIVE); ++ } ++ ++ sreloc = elf_section_data (input_section)->sreloc; ++ if (sreloc == NULL) ++ abort (); ++ ++ loc = sreloc->contents; ++ loc += sreloc->reloc_count++ * sizeof (Elf32_External_Rela); ++ bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); ++ ++ /* This reloc will be computed at runtime, so there's no ++ need to do anything now, except for R_NIOS2_BFD_RELOC_32 ++ relocations that have been turned into ++ R_NIOS2_RELATIVE. */ ++ if (!relocate) ++ break; ++ } ++ ++ r = _bfd_final_link_relocate (howto, input_bfd, ++ input_section, contents, ++ rel->r_offset, relocation, ++ rel->r_addend); ++ break; ++ ++ case R_NIOS2_TLS_DTPREL: ++ relocation -= dtpoff_base (info); ++ /* Fall through. */ ++ ++ default: ++ r = _bfd_final_link_relocate (howto, input_bfd, ++ input_section, contents, ++ rel->r_offset, relocation, ++ rel->r_addend); ++ break; ++ } ++ } ++ else ++ { ++ r = bfd_reloc_notsupported; ++ } ++ ++ if (r != bfd_reloc_ok) ++ { ++ if (h != NULL) ++ name = h->root.root.string; ++ else ++ { ++ name = (bfd_elf_string_from_elf_section ++ (input_bfd, symtab_hdr->sh_link, sym->st_name)); ++ if (name == NULL || *name == '\0') ++ name = bfd_section_name (input_bfd, sec); ++ } ++ ++ switch (r) ++ { ++ case bfd_reloc_overflow: ++ r = info->callbacks->reloc_overflow ++ (info, NULL, name, howto->name, (bfd_vma) 0, ++ input_bfd, input_section, rel->r_offset); ++ break; ++ ++ case bfd_reloc_undefined: ++ r = info->callbacks->undefined_symbol ++ (info, name, input_bfd, input_section, rel->r_offset, TRUE); ++ break; ++ ++ case bfd_reloc_outofrange: ++ if (msg == NULL) ++ msg = _("relocation out of range"); ++ break; ++ ++ case bfd_reloc_notsupported: ++ if (msg == NULL) ++ msg = _("unsupported relocation"); ++ break; ++ ++ case bfd_reloc_dangerous: ++ if (msg == NULL) ++ msg = _("dangerous relocation"); ++ break; ++ ++ default: ++ if (msg == NULL) ++ msg = _("unknown error"); ++ break; ++ } ++ ++ if (msg) ++ { ++ r = info->callbacks->warning ++ (info, msg, name, input_bfd, input_section, rel->r_offset); ++ return 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, int shindex) ++{ ++ 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, shindex)) ++ 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, const 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; ++} ++ ++/* Create .got, .gotplt, and .rela.got sections in DYNOBJ, and set up ++ shortcuts to them in our hash table. */ ++ ++static bfd_boolean ++create_got_section (bfd *dynobj, struct bfd_link_info *info) ++{ ++ struct elf32_nios2_link_hash_table *htab; ++ ++ htab = elf32_nios2_hash_table (info); ++ ++ if (! _bfd_elf_create_got_section (dynobj, info)) ++ return FALSE; ++ ++ htab->sgot = bfd_get_section_by_name (dynobj, ".got"); ++ htab->sgotplt = bfd_get_section_by_name (dynobj, ".got.plt"); ++ if (!htab->sgot || !htab->sgotplt) ++ abort (); ++ ++ /* In order for the two loads in .PLTresolve to share the same %hiadj, ++ _GLOBAL_OFFSET_TABLE_ must be aligned to a 16-byte boundary. */ ++ if (!bfd_set_section_alignment (dynobj, htab->sgotplt, 4)) ++ return FALSE; ++ ++ htab->srelgot = bfd_make_section_with_flags (dynobj, ++ ".rela.got", ++ (SEC_ALLOC | SEC_LOAD ++ | SEC_HAS_CONTENTS ++ | SEC_IN_MEMORY ++ | SEC_LINKER_CREATED ++ | SEC_READONLY)); ++ if (htab->srelgot == NULL ++ || ! bfd_set_section_alignment (dynobj, htab->srelgot, 2)) ++ return FALSE; ++ return TRUE; ++} ++ ++/* Create .plt, .rela.plt, .got, .got.plt, .rela.got, .dynbss, and ++ .rela.bss sections in DYNOBJ, and set up shortcuts to them in our ++ hash table. */ ++ ++static bfd_boolean ++nios2_elf32_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info) ++{ ++ struct elf32_nios2_link_hash_table *htab; ++ ++ htab = elf32_nios2_hash_table (info); ++ if (!htab->sgot && !create_got_section (dynobj, info)) ++ return FALSE; ++ ++ _bfd_elf_create_dynamic_sections (dynobj, info); ++ ++ htab->splt = bfd_get_section_by_name (dynobj, ".plt"); ++ ++ /* In order for the two loads in a shared object .PLTresolve to share the ++ same %hiadj, the start of the PLT (as well as the GOT) must be aligned ++ to a 16-byte boundary. This is because the addresses for these loads ++ include the -(.plt+4) PIC correction. */ ++ if (!bfd_set_section_alignment (dynobj, htab->splt, 4)) ++ return FALSE; ++ ++ htab->srelplt = bfd_get_section_by_name (dynobj, ".rela.plt"); ++ htab->sdynbss = bfd_get_section_by_name (dynobj, ".dynbss"); ++ if (!htab->splt || !htab->srelplt || !htab->sdynbss) ++ return FALSE; ++ if (!info->shared) ++ { ++ htab->srelbss = bfd_get_section_by_name (dynobj, ".rela.bss"); ++ if (!htab->srelbss) ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++/* Copy the extra info we tack onto an elf_link_hash_entry. */ ++ ++static void ++nios2_elf32_copy_indirect_symbol (struct bfd_link_info *info, ++ struct elf_link_hash_entry *dir, ++ struct elf_link_hash_entry *ind) ++{ ++ struct elf32_nios2_link_hash_entry *edir, *eind; ++ ++ edir = (struct elf32_nios2_link_hash_entry *) dir; ++ eind = (struct elf32_nios2_link_hash_entry *) ind; ++ ++ if (eind->dyn_relocs != NULL) ++ { ++ if (edir->dyn_relocs != NULL) ++ { ++ struct elf32_nios2_dyn_relocs **pp; ++ struct elf32_nios2_dyn_relocs *p; ++ ++ /* Add reloc counts against the indirect sym to the direct sym ++ list. Merge any entries against the same section. */ ++ for (pp = &eind->dyn_relocs; (p = *pp) != NULL; ) ++ { ++ struct elf32_nios2_dyn_relocs *q; ++ ++ for (q = edir->dyn_relocs; q != NULL; q = q->next) ++ if (q->sec == p->sec) ++ { ++ q->pc_count += p->pc_count; ++ q->count += p->count; ++ *pp = p->next; ++ break; ++ } ++ if (q == NULL) ++ pp = &p->next; ++ } ++ *pp = edir->dyn_relocs; ++ } ++ ++ edir->dyn_relocs = eind->dyn_relocs; ++ eind->dyn_relocs = NULL; ++ } ++ ++ if (ind->root.type == bfd_link_hash_indirect ++ && dir->got.refcount <= 0) ++ { ++ edir->tls_type = eind->tls_type; ++ eind->tls_type = GOT_UNKNOWN; ++ } ++ ++ edir->got_types_used |= eind->got_types_used; ++ ++ _bfd_elf_link_hash_copy_indirect (info, dir, ind); ++} ++ ++/* Look through the relocs for a section during the first phase. */ ++ ++static bfd_boolean ++nios2_elf32_check_relocs (bfd *abfd, struct bfd_link_info *info, ++ asection *sec, const Elf_Internal_Rela *relocs) ++{ ++ bfd *dynobj; ++ Elf_Internal_Shdr *symtab_hdr; ++ struct elf_link_hash_entry **sym_hashes, **sym_hashes_end; ++ const Elf_Internal_Rela *rel; ++ const Elf_Internal_Rela *rel_end; ++ struct elf32_nios2_link_hash_table *htab; ++ asection *sgot; ++ asection *srelgot; ++ asection *sreloc = NULL; ++ bfd_signed_vma *local_got_refcounts; ++ ++ if (info->relocatable) ++ return TRUE; ++ ++ dynobj = elf_hash_table (info)->dynobj; ++ symtab_hdr = &elf_tdata (abfd)->symtab_hdr; ++ sym_hashes = elf_sym_hashes (abfd); ++ sym_hashes_end = ++ sym_hashes + symtab_hdr->sh_size / sizeof (Elf32_External_Sym); ++ if (!elf_bad_symtab (abfd)) ++ sym_hashes_end -= symtab_hdr->sh_info; ++ local_got_refcounts = elf_local_got_refcounts (abfd); ++ ++ htab = elf32_nios2_hash_table (info); ++ sgot = htab->sgot; ++ srelgot = htab->srelgot; ++ ++ rel_end = relocs + sec->reloc_count; ++ for (rel = relocs; rel < rel_end; rel++) ++ { ++ unsigned int r_type; ++ struct elf_link_hash_entry *h; ++ unsigned long r_symndx; ++ ++ r_symndx = ELF32_R_SYM (rel->r_info); ++ if (r_symndx < symtab_hdr->sh_info) ++ h = NULL; ++ else ++ { ++ h = sym_hashes[r_symndx - symtab_hdr->sh_info]; ++ while (h->root.type == bfd_link_hash_indirect ++ || h->root.type == bfd_link_hash_warning) ++ h = (struct elf_link_hash_entry *) h->root.u.i.link; ++ } ++ ++ r_type = ELF32_R_TYPE (rel->r_info); ++ ++ switch (r_type) ++ { ++ case R_NIOS2_GOT16: ++ case R_NIOS2_CALL16: ++ case R_NIOS2_TLS_GD16: ++ case R_NIOS2_TLS_IE16: ++ /* This symbol requires a global offset table entry. */ ++ { ++ int tls_type, old_tls_type; ++ ++ switch (r_type) ++ { ++ default: ++ case R_NIOS2_GOT16: ++ case R_NIOS2_CALL16: ++ tls_type = GOT_NORMAL; ++ break; ++ case R_NIOS2_TLS_GD16: ++ tls_type = GOT_TLS_GD; ++ break; ++ case R_NIOS2_TLS_IE16: ++ tls_type = GOT_TLS_IE; ++ break; ++ } ++ ++ if (dynobj == NULL) ++ { ++ /* Create the .got section. */ ++ elf_hash_table (info)->dynobj = dynobj = abfd; ++ nios2_elf32_create_dynamic_sections (dynobj, info); ++ } ++ ++ if (sgot == NULL) ++ { ++ sgot = htab->sgot; ++ BFD_ASSERT (sgot != NULL); ++ } ++ ++ if (srelgot == NULL ++ && (h != NULL || info->shared)) ++ { ++ srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); ++ BFD_ASSERT (srelgot != NULL); ++ } ++ ++ if (h != NULL) ++ { ++ struct elf32_nios2_link_hash_entry *eh = ++ (struct elf32_nios2_link_hash_entry *)h; ++ h->got.refcount++; ++ old_tls_type = elf32_nios2_hash_entry(h)->tls_type; ++ if (r_type == R_NIOS2_CALL16) ++ { ++ /* Make sure a plt entry is created for this symbol if it ++ turns out to be a function defined by a dynamic object. ++ */ ++ h->plt.refcount++; ++ ++ h->needs_plt = 1; ++ ++ h->type = STT_FUNC; ++ ++ eh->got_types_used |= CALL16_USED; ++ } ++ else ++ eh->got_types_used |= GOT16_USED; ++ } ++ else ++ { ++ /* This is a global offset table entry for a local symbol. */ ++ if (local_got_refcounts == NULL) ++ { ++ bfd_size_type size; ++ ++ size = symtab_hdr->sh_info; ++ size *= (sizeof (bfd_signed_vma) + sizeof(char)); ++ local_got_refcounts = ((bfd_signed_vma *) ++ bfd_zalloc (abfd, size)); ++ if (local_got_refcounts == NULL) ++ return FALSE; ++ elf_local_got_refcounts (abfd) = local_got_refcounts; ++ elf32_nios2_local_got_tls_type (abfd) = ++ (char *) (local_got_refcounts + symtab_hdr->sh_info); ++ } ++ local_got_refcounts[r_symndx]++; ++ old_tls_type = ++ elf32_nios2_local_got_tls_type (abfd) [r_symndx]; ++ } ++ ++ /* We will already have issued an error message if there is a ++ TLS / non-TLS mismatch, based on the symbol type. We don't ++ support any linker relaxations. So just combine any TLS ++ types needed. */ ++ if (old_tls_type != GOT_UNKNOWN && old_tls_type != GOT_NORMAL ++ && tls_type != GOT_NORMAL) ++ tls_type |= old_tls_type; ++ ++ if (old_tls_type != tls_type) ++ { ++ if (h != NULL) ++ elf32_nios2_hash_entry (h)->tls_type = tls_type; ++ else ++ elf32_nios2_local_got_tls_type (abfd) [r_symndx] = tls_type; ++ } ++ } ++ /* Fall through */ ++ case R_NIOS2_TLS_LDM16: ++ if (r_type == R_NIOS2_TLS_LDM16) ++ htab->tls_ldm_got.refcount++; ++ ++ if (htab->sgot == NULL) ++ { ++ if (htab->root.dynobj == NULL) ++ htab->root.dynobj = abfd; ++ if (!create_got_section (htab->root.dynobj, info)) ++ return FALSE; ++ } ++ break; ++ ++ /* This relocation describes the C++ object vtable hierarchy. ++ Reconstruct it for later use during GC. */ ++ case R_NIOS2_GNU_VTINHERIT: ++ if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset)) ++ return FALSE; ++ break; ++ ++ /* This relocation describes which C++ vtable entries are actually ++ used. Record for later use during GC. */ ++ case R_NIOS2_GNU_VTENTRY: ++ if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend)) ++ return FALSE; ++ break; ++ ++ case R_NIOS2_BFD_RELOC_32: ++ case R_NIOS2_CALL26: ++ case R_NIOS2_HIADJ16: ++ case R_NIOS2_LO16: ++ ++ if (h != NULL) ++ { ++ /* If this reloc is in a read-only section, we might ++ need a copy reloc. We can't check reliably at this ++ stage whether the section is read-only, as input ++ sections have not yet been mapped to output sections. ++ Tentatively set the flag for now, and correct in ++ adjust_dynamic_symbol. */ ++ if (!info->shared) ++ h->non_got_ref = 1; ++ ++ /* Make sure a plt entry is created for this symbol if it ++ turns out to be a function defined by a dynamic object. */ ++ h->plt.refcount++; ++ ++ if (r_type == R_NIOS2_CALL26) ++ h->needs_plt = 1; ++ } ++ ++ /* If we are creating a shared library, we need to copy the ++ reloc into the shared library. */ ++ if (info->shared ++ && (sec->flags & SEC_ALLOC) != 0 ++ && (r_type == R_NIOS2_BFD_RELOC_32 ++ || (h != NULL && ! h->needs_plt ++ && (! info->symbolic || ! h->def_regular)))) ++ { ++ struct elf32_nios2_dyn_relocs *p; ++ struct elf32_nios2_dyn_relocs **head; ++ ++ /* When creating a shared object, we must copy these ++ reloc types into the output file. We create a reloc ++ section in dynobj and make room for this reloc. */ ++ if (sreloc == NULL) ++ { ++ const char *name; ++ ++ name = (bfd_elf_string_from_elf_section ++ (abfd, ++ elf_elfheader (abfd)->e_shstrndx, ++ elf_section_data (sec)->rel_hdr.sh_name)); ++ if (name == NULL) ++ return FALSE; ++ ++ BFD_ASSERT (CONST_STRNEQ (name, ".rela") ++ && strcmp (bfd_get_section_name (abfd, sec), ++ name + 5) == 0); ++ ++ sreloc = bfd_get_section_by_name (dynobj, name); ++ if (sreloc == NULL) ++ { ++ sreloc = bfd_make_section_with_flags ( ++ dynobj, ++ name, ++ (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | ++ SEC_IN_MEMORY | SEC_LINKER_CREATED | SEC_READONLY)); ++ if (sreloc == NULL || ++ !bfd_set_section_alignment (dynobj, sreloc, 2)) ++ return FALSE; ++ } ++ elf_section_data (sec)->sreloc = sreloc; ++ } ++ ++ /* If this is a global symbol, we count the number of ++ relocations we need for this symbol. */ ++ if (h != NULL) ++ head = &((struct elf32_nios2_link_hash_entry *) h)->dyn_relocs; ++ else ++ { ++ /* Track dynamic relocs needed for local syms too. ++ We really need local syms available to do this ++ easily. Oh well. */ ++ ++ asection *s; ++ void *vpp; ++ ++ s = bfd_section_from_r_symndx (abfd, &htab->sym_sec, ++ sec, r_symndx); ++ if (s == NULL) ++ return FALSE; ++ ++ vpp = &elf_section_data (s)->local_dynrel; ++ head = (struct elf32_nios2_dyn_relocs **) vpp; ++ } ++ ++ p = *head; ++ if (p == NULL || p->sec != sec) ++ { ++ bfd_size_type amt = sizeof *p; ++ p = ((struct elf32_nios2_dyn_relocs *) ++ bfd_alloc (htab->root.dynobj, amt)); ++ if (p == NULL) ++ return FALSE; ++ p->next = *head; ++ *head = p; ++ p->sec = sec; ++ p->count = 0; ++ p->pc_count = 0; ++ } ++ ++ p->count += 1; ++ ++ } ++ break; ++ } ++ } ++ ++ return TRUE; ++} ++ ++ ++/* 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: ++ return NULL; ++ } ++ return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym); ++} ++ ++/* Update the got entry reference counts for the section being removed. */ ++ ++static bfd_boolean ++nios2_elf32_gc_sweep_hook (bfd *abfd, ++ struct bfd_link_info *info, ++ asection *sec, ++ const Elf_Internal_Rela *relocs) ++{ ++ Elf_Internal_Shdr *symtab_hdr; ++ struct elf_link_hash_entry **sym_hashes; ++ bfd_signed_vma *local_got_refcounts; ++ const Elf_Internal_Rela *rel, *relend; ++ bfd *dynobj; ++ asection *sgot; ++ asection *srelgot; ++ struct elf32_nios2_link_hash_table *htab; ++ ++ htab = elf32_nios2_hash_table (info); ++ sgot = htab->sgot; ++ srelgot = htab->srelgot; ++ ++ elf_section_data (sec)->local_dynrel = NULL; ++ ++ dynobj = elf_hash_table (info)->dynobj; ++ if (dynobj == NULL) ++ return TRUE; ++ ++ symtab_hdr = &elf_tdata (abfd)->symtab_hdr; ++ sym_hashes = elf_sym_hashes (abfd); ++ local_got_refcounts = elf_local_got_refcounts (abfd); ++ ++ relend = relocs + sec->reloc_count; ++ for (rel = relocs; rel < relend; rel++) ++ { ++ unsigned long r_symndx; ++ struct elf_link_hash_entry *h = NULL; ++ int r_type; ++ ++ r_symndx = ELF32_R_SYM (rel->r_info); ++ if (r_symndx >= symtab_hdr->sh_info) ++ { ++ h = sym_hashes[r_symndx - symtab_hdr->sh_info]; ++ while (h->root.type == bfd_link_hash_indirect ++ || h->root.type == bfd_link_hash_warning) ++ h = (struct elf_link_hash_entry *) h->root.u.i.link; ++ } ++ ++ r_type = ELF32_R_TYPE (rel->r_info); ++ switch (r_type) ++ { ++ case R_NIOS2_GOT16: ++ case R_NIOS2_CALL16: ++ if (h != NULL) ++ { ++ if (h->got.refcount > 0) ++ --h->got.refcount; ++ } ++ else if (local_got_refcounts != NULL) ++ { ++ if (local_got_refcounts[r_symndx] > 0) ++ --local_got_refcounts[r_symndx]; ++ } ++ break; ++ ++ case R_NIOS2_PCREL_LO: ++ case R_NIOS2_PCREL_HA: ++ case R_NIOS2_BFD_RELOC_32: ++ case R_NIOS2_CALL26: ++ if (h != NULL) ++ { ++ struct elf32_nios2_link_hash_entry *eh; ++ struct elf32_nios2_dyn_relocs **pp; ++ struct elf32_nios2_dyn_relocs *p; ++ ++ eh = (struct elf32_nios2_link_hash_entry *) h; ++ ++ if (h->plt.refcount > 0) ++ --h->plt.refcount; ++ ++ if (r_type == R_NIOS2_PCREL_LO || r_type == R_NIOS2_PCREL_HA || ++ r_type == R_NIOS2_BFD_RELOC_32) ++ { ++ for (pp = &eh->dyn_relocs; (p = *pp) != NULL; ++ pp = &p->next) ++ if (p->sec == sec) ++ { ++ p->count -= 1; ++ if (p->count == 0) ++ *pp = p->next; ++ break; ++ } ++ } ++ } ++ break; ++ ++ default: ++ break; ++ } ++ } ++ ++ return TRUE; ++} ++ ++/* Install 16-bit immediate value VALUE at offset OFFSET into section SEC. */ ++ ++static void ++nios2_elf32_install_imm16 (asection *sec, bfd_vma offset, bfd_vma value) ++{ ++ bfd_vma word = bfd_get_32 (sec->owner, sec->contents + offset); ++ ++ BFD_ASSERT(value <= 0xffff); ++ ++ bfd_put_32 (sec->owner, word | ((value & 0xffff) << 6), ++ sec->contents + offset); ++} ++ ++/* Install COUNT 32-bit values DATA starting at offset OFFSET into section SEC. ++ */ ++ ++static void ++nios2_elf32_install_data (asection *sec, const bfd_vma *data, bfd_vma offset, ++ int count) ++{ ++ while (count--) ++ { ++ bfd_put_32 (sec->owner, *data, sec->contents + offset); ++ offset += 4; ++ ++data; ++ } ++} ++ ++ ++/* Finish up dynamic symbol handling. We set the contents of various ++ dynamic sections here. */ ++ ++static bfd_boolean ++nios2_elf32_finish_dynamic_symbol (bfd * output_bfd, ++ struct bfd_link_info * info, ++ struct elf_link_hash_entry * h, ++ Elf_Internal_Sym * sym) ++{ ++ struct elf32_nios2_link_hash_table *htab; ++ struct elf32_nios2_link_hash_entry *eh = ++ (struct elf32_nios2_link_hash_entry *)h; ++ bfd *dynobj; ++ int use_plt; ++ ++ htab = elf32_nios2_hash_table (info); ++ ++ dynobj = elf_hash_table (info)->dynobj; ++ ++ if (h->plt.offset != (bfd_vma) -1) ++ { ++ asection *splt; ++ asection *sgotplt; ++ asection *srela; ++ bfd_vma plt_index; ++ bfd_vma got_offset; ++ Elf_Internal_Rela rela; ++ bfd_byte *loc; ++ bfd_vma got_address; ++ ++ /* This symbol has an entry in the procedure linkage table. Set ++ it up. */ ++ ++ BFD_ASSERT (h->dynindx != -1); ++ ++ splt = htab->splt; ++ sgotplt = htab->sgotplt; ++ srela = htab->srelplt; ++ BFD_ASSERT (splt != NULL && sgotplt != NULL && srela != NULL); ++ ++ /* Emit the PLT entry. */ ++ if (info->shared) ++ { ++ nios2_elf32_install_data (splt, nios2_so_plt_entry, h->plt.offset, ++ 3); ++ plt_index = (h->plt.offset - 24) / 12; ++ got_offset = (plt_index + 3) * 4; ++ nios2_elf32_install_imm16 (splt, h->plt.offset, ++ hiadj(plt_index * 4)); ++ nios2_elf32_install_imm16 (splt, h->plt.offset + 4, ++ (plt_index * 4) & 0xffff); ++ nios2_elf32_install_imm16 (splt, h->plt.offset + 8, ++ 0xfff4 - h->plt.offset); ++ got_address = (sgotplt->output_section->vma + sgotplt->output_offset ++ + got_offset); ++ ++ /* Fill in the entry in the global offset table. There are no ++ res_n slots for a shared object PLT, instead the .got.plt entries ++ point to the PLT entries. */ ++ bfd_put_32 (output_bfd, ++ splt->output_section->vma + splt->output_offset ++ + h->plt.offset, sgotplt->contents + got_offset); ++ } ++ else ++ { ++ plt_index = (h->plt.offset - 28 - htab->res_n_size) / 12; ++ got_offset = (plt_index + 3) * 4; ++ ++ nios2_elf32_install_data (splt, nios2_plt_entry, h->plt.offset, 3); ++ got_address = (sgotplt->output_section->vma + sgotplt->output_offset ++ + got_offset); ++ nios2_elf32_install_imm16 (splt, h->plt.offset, hiadj(got_address)); ++ nios2_elf32_install_imm16 (splt, h->plt.offset + 4, ++ got_address & 0xffff); ++ ++ /* Fill in the entry in the global offset table. */ ++ bfd_put_32 (output_bfd, ++ splt->output_section->vma + splt->output_offset ++ + plt_index * 4, sgotplt->contents + got_offset); ++ } ++ ++ /* Fill in the entry in the .rela.plt section. */ ++ rela.r_offset = got_address; ++ rela.r_info = ELF32_R_INFO (h->dynindx, R_NIOS2_JUMP_SLOT); ++ rela.r_addend = 0; ++ loc = srela->contents + plt_index * sizeof (Elf32_External_Rela); ++ bfd_elf32_swap_reloca_out (output_bfd, &rela, loc); ++ ++ if (!h->def_regular) ++ { ++ /* Mark the symbol as undefined, rather than as defined in ++ the .plt section. Leave the value alone. */ ++ sym->st_shndx = SHN_UNDEF; ++ /* If the symbol is weak, we do need to clear the value. ++ Otherwise, the PLT entry would provide a definition for ++ the symbol even if the symbol wasn't defined anywhere, ++ and so the symbol would never be NULL. */ ++ if (!h->ref_regular_nonweak) ++ sym->st_value = 0; ++ } ++ } ++ ++ use_plt = (eh->got_types_used == CALL16_USED && ++ h->plt.offset != (bfd_vma) -1); ++ ++ if (!use_plt && h->got.offset != (bfd_vma) -1 ++ && (elf32_nios2_hash_entry (h)->tls_type & GOT_TLS_GD) == 0 ++ && (elf32_nios2_hash_entry (h)->tls_type & GOT_TLS_IE) == 0) ++ { ++ asection *sgot; ++ asection *srela; ++ Elf_Internal_Rela rela; ++ bfd_byte *loc; ++ bfd_vma offset; ++ ++ /* This symbol has an entry in the global offset table. Set it ++ up. */ ++ ++ sgot = htab->sgot; ++ srela = htab->srelgot; ++ BFD_ASSERT (sgot != NULL && srela != NULL); ++ ++ offset = (h->got.offset & ~(bfd_vma) 1); ++ rela.r_offset = (sgot->output_section->vma ++ + sgot->output_offset + offset); ++ ++ /* If this is a -Bsymbolic link, and the symbol is defined ++ locally, we just want to emit a RELATIVE reloc. Likewise if ++ the symbol was forced to be local because of a version file. ++ The entry in the global offset table will already have been ++ initialized in the relocate_section function. */ ++ ++ if (info->shared && SYMBOL_REFERENCES_LOCAL (info, h)) ++ { ++ rela.r_info = ELF32_R_INFO (0, R_NIOS2_RELATIVE); ++ rela.r_addend = bfd_get_signed_32 (output_bfd, ++ (sgot->contents + offset)); ++ bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + offset); ++ } ++ else ++ { ++ bfd_put_32 (output_bfd, (bfd_vma) 0, ++ sgot->contents + offset); ++ rela.r_info = ELF32_R_INFO (h->dynindx, R_NIOS2_GLOB_DAT); ++ rela.r_addend = 0; ++ } ++ ++ loc = srela->contents; ++ loc += srela->reloc_count++ * sizeof (Elf32_External_Rela); ++ bfd_elf32_swap_reloca_out (output_bfd, &rela, loc); ++ } ++ ++ if (use_plt && h->got.offset != (bfd_vma) -1) ++ { ++ bfd_vma offset = (h->got.offset & ~(bfd_vma) 1); ++ asection *sgot = htab->sgot; ++ asection *splt = htab->splt; ++ bfd_put_32 (output_bfd, (splt->output_section->vma + splt->output_offset ++ + h->plt.offset), sgot->contents + offset); ++ } ++ ++ if (h->needs_copy) ++ { ++ asection *s; ++ Elf_Internal_Rela rela; ++ bfd_byte *loc; ++ ++ /* This symbol needs a copy reloc. Set it up. */ ++ ++ BFD_ASSERT (h->dynindx != -1 ++ && (h->root.type == bfd_link_hash_defined ++ || h->root.type == bfd_link_hash_defweak)); ++ ++ s = bfd_get_section_by_name (h->root.u.def.section->owner, ++ ".rela.bss"); ++ BFD_ASSERT (s != NULL); ++ ++ rela.r_offset = (h->root.u.def.value ++ + h->root.u.def.section->output_section->vma ++ + h->root.u.def.section->output_offset); ++ rela.r_info = ELF32_R_INFO (h->dynindx, R_NIOS2_COPY); ++ rela.r_addend = 0; ++ loc = s->contents + s->reloc_count++ * sizeof (Elf32_External_Rela); ++ bfd_elf32_swap_reloca_out (output_bfd, &rela, loc); ++ } ++ ++ /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. */ ++ if (strcmp (h->root.root.string, "_DYNAMIC") == 0 ++ || h == elf_hash_table (info)->hgot) ++ sym->st_shndx = SHN_ABS; ++ ++ return TRUE; ++} ++ ++/* Finish up the dynamic sections. */ ++ ++static bfd_boolean ++nios2_elf32_finish_dynamic_sections (bfd * output_bfd, ++ struct bfd_link_info * info) ++{ ++ bfd *dynobj; ++ asection *sgotplt; ++ asection *sdyn; ++ struct elf32_nios2_link_hash_table *htab; ++ ++ htab = elf32_nios2_hash_table (info); ++ ++ dynobj = elf_hash_table (info)->dynobj; ++ ++ sgotplt = htab->sgotplt; ++ BFD_ASSERT (sgotplt != NULL); ++ sdyn = bfd_get_section_by_name (dynobj, ".dynamic"); ++ ++ if (elf_hash_table (info)->dynamic_sections_created) ++ { ++ asection *splt; ++ Elf32_External_Dyn *dyncon, *dynconend; ++ ++ splt = htab->splt; ++ BFD_ASSERT (splt != NULL && sdyn != NULL); ++ ++ dyncon = (Elf32_External_Dyn *) sdyn->contents; ++ dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->size); ++ for (; dyncon < dynconend; dyncon++) ++ { ++ Elf_Internal_Dyn dyn; ++ const char *name; ++ asection *s; ++ ++ bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn); ++ ++ switch (dyn.d_tag) ++ { ++ default: ++ break; ++ ++ case DT_PLTGOT: ++ name = ".got"; ++ goto get_vma; ++ case DT_JMPREL: ++ name = ".rela.plt"; ++ get_vma: ++ s = bfd_get_section_by_name (output_bfd, name); ++ BFD_ASSERT (s != NULL); ++ dyn.d_un.d_ptr = s->vma; ++ bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); ++ break; ++ ++ case DT_PLTRELSZ: ++ s = bfd_get_section_by_name (output_bfd, ".rela.plt"); ++ BFD_ASSERT (s != NULL); ++ dyn.d_un.d_val = s->size; ++ bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); ++ break; ++ ++ case DT_RELASZ: ++ /* The procedure linkage table relocs (DT_JMPREL) should ++ not be included in the overall relocs (DT_RELA). ++ Therefore, we override the DT_RELASZ entry here to ++ make it not include the JMPREL relocs. Since the ++ linker script arranges for .rela.plt to follow all ++ other relocation sections, we don't have to worry ++ about changing the DT_RELA entry. */ ++ s = bfd_get_section_by_name (output_bfd, ".rela.plt"); ++ if (s != NULL) ++ dyn.d_un.d_val -= s->size; ++ bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); ++ break; ++ ++ case DT_NIOS2_GP: ++ s = bfd_get_section_by_name (output_bfd, ".got"); ++ BFD_ASSERT (s != NULL); ++ dyn.d_un.d_ptr = s->vma + 0x7ff0; ++ bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); ++ break; ++ } ++ } ++ ++ /* Fill in the first entry in the procedure linkage table. */ ++ if (splt->size > 0) ++ { ++ bfd_vma got_address = (sgotplt->output_section->vma + ++ sgotplt->output_offset); ++ if (info->shared) ++ { ++ bfd_vma corrected = got_address - (splt->output_section->vma + ++ splt->output_offset + 4); ++ nios2_elf32_install_data (splt, nios2_so_plt0_entry, 0, 6); ++ nios2_elf32_install_imm16 (splt, 4, hiadj (corrected)); ++ nios2_elf32_install_imm16 (splt, 12, (corrected & 0xffff) + 4); ++ nios2_elf32_install_imm16 (splt, 16, (corrected & 0xffff) + 8); ++ ++ elf_section_data (splt->output_section)->this_hdr.sh_entsize ++ = 24; ++ } ++ else ++ { ++ /* Divide by 4 here, not 3 because we already corrected for the ++ res_N branches. */ ++ bfd_vma res_size = (splt->size - 28) / 4; ++ bfd_vma res_start = (splt->output_section->vma + ++ splt->output_offset); ++ bfd_vma res_offset; ++ ++ for (res_offset = 0; res_offset < res_size; res_offset += 4) ++ bfd_put_32 (output_bfd, ++ 6 | ((res_size - (res_offset + 4)) << 6), ++ splt->contents + res_offset); ++ ++ nios2_elf32_install_data (splt, nios2_plt0_entry, res_size, 7); ++ nios2_elf32_install_imm16 (splt, res_size, hiadj (res_start)); ++ nios2_elf32_install_imm16 (splt, res_size + 4, ++ res_start & 0xffff); ++ nios2_elf32_install_imm16 (splt, res_size + 12, ++ hiadj (got_address)); ++ nios2_elf32_install_imm16 (splt, res_size + 16, ++ (got_address & 0xffff) + 4); ++ nios2_elf32_install_imm16 (splt, res_size + 20, ++ (got_address & 0xffff) + 8); ++ ++ elf_section_data (splt->output_section)->this_hdr.sh_entsize ++ = 28 + res_size; ++ } ++ } ++ } ++ /* Fill in the first three entries in the global offset table. */ ++ if (sgotplt->size > 0) ++ { ++ if (sdyn == NULL) ++ bfd_put_32 (output_bfd, (bfd_vma) 0, sgotplt->contents); ++ else ++ bfd_put_32 (output_bfd, ++ sdyn->output_section->vma + sdyn->output_offset, ++ sgotplt->contents); ++ bfd_put_32 (output_bfd, (bfd_vma) 0, sgotplt->contents + 4); ++ bfd_put_32 (output_bfd, (bfd_vma) 0, sgotplt->contents + 8); ++ } ++ ++ elf_section_data (sgotplt->output_section)->this_hdr.sh_entsize = 4; ++ ++ return TRUE; ++} ++ ++/* Adjust a symbol defined by a dynamic object and referenced by a ++ regular object. The current definition is in some section of the ++ dynamic object, but we're not including those sections. We have to ++ change the definition to something the rest of the link can ++ understand. */ ++ ++static bfd_boolean ++nios2_elf32_adjust_dynamic_symbol (struct bfd_link_info * info, ++ struct elf_link_hash_entry * h) ++{ ++ struct elf32_nios2_link_hash_table *htab; ++ bfd *dynobj; ++ asection *s; ++ unsigned align2; ++ ++ htab = elf32_nios2_hash_table (info); ++ dynobj = elf_hash_table (info)->dynobj; ++ ++ /* Make sure we know what is going on here. */ ++ BFD_ASSERT (dynobj != NULL ++ && (h->needs_plt ++ || h->u.weakdef != NULL ++ || (h->def_dynamic ++ && h->ref_regular ++ && !h->def_regular))); ++ ++ /* If this is a function, put it in the procedure linkage table. We ++ will fill in the contents of the procedure linkage table later, ++ when we know the address of the .got section. */ ++ if (h->type == STT_FUNC ++ || h->needs_plt) ++ { ++ if (h->plt.refcount <= 0 ++ || SYMBOL_CALLS_LOCAL (info, h) ++ || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT ++ && h->root.type == bfd_link_hash_undefweak)) ++ { ++ /* This case can occur if we saw a PLT reloc in an input ++ file, but the symbol was never referred to by a dynamic ++ object, or if all references were garbage collected. In ++ such a case, we don't actually need to build a procedure ++ linkage table, and we can just do a PCREL reloc instead. */ ++ h->plt.offset = (bfd_vma) -1; ++ h->needs_plt = 0; ++ } ++ ++ return TRUE; ++ } ++ ++ /* Reinitialize the plt offset now that it is not used as a reference ++ count any more. */ ++ h->plt.offset = (bfd_vma) -1; ++ ++ /* If this is a weak symbol, and there is a real definition, the ++ processor independent code will have arranged for us to see the ++ real definition first, and we can just use the same value. */ ++ if (h->u.weakdef != NULL) ++ { ++ BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined ++ || h->u.weakdef->root.type == bfd_link_hash_defweak); ++ h->root.u.def.section = h->u.weakdef->root.u.def.section; ++ h->root.u.def.value = h->u.weakdef->root.u.def.value; ++ return TRUE; ++ } ++ ++ /* If there are no non-GOT references, we do not need a copy ++ relocation. */ ++ if (!h->non_got_ref) ++ return TRUE; ++ ++ /* This is a reference to a symbol defined by a dynamic object which ++ is not a function. */ ++ ++ /* If we are creating a shared library, we must presume that the ++ only references to the symbol are via the global offset table. ++ For such cases we need not do anything here; the relocations will ++ be handled correctly by relocate_section. */ ++ if (info->shared) ++ return TRUE; ++ ++ if (h->size == 0) ++ { ++ (*_bfd_error_handler) (_("dynamic variable `%s' is zero size"), ++ h->root.root.string); ++ return TRUE; ++ } ++ ++ /* We must allocate the symbol in our .dynbss section, which will ++ become part of the .bss section of the executable. There will be ++ an entry for this symbol in the .dynsym section. The dynamic ++ object will contain position independent code, so all references ++ from the dynamic object to this symbol will go through the global ++ offset table. The dynamic linker will use the .dynsym entry to ++ determine the address it must put in the global offset table, so ++ both the dynamic object and the regular object will refer to the ++ same memory location for the variable. */ ++ ++ s = htab->sdynbss; ++ BFD_ASSERT (s != NULL); ++ ++ /* We must generate a R_NIOS2_COPY reloc to tell the dynamic linker to ++ copy the initial value out of the dynamic object and into the ++ runtime process image. We need to remember the offset into the ++ .rela.bss section we are going to use. */ ++ if ((h->root.u.def.section->flags & SEC_ALLOC) != 0) ++ { ++ asection *srel; ++ ++ srel = htab->srelbss; ++ BFD_ASSERT (srel != NULL); ++ srel->size += sizeof (Elf32_External_Rela); ++ h->needs_copy = 1; ++ } ++ ++ align2 = bfd_log2 (h->size); ++ if (align2 > h->root.u.def.section->alignment_power) ++ align2 = h->root.u.def.section->alignment_power; ++ ++ /* align dynbss */ ++ s->size = BFD_ALIGN (s->size, (bfd_size_type)1 << align2); ++ if (align2 > bfd_get_section_alignment (dynobj, s) ++ && !bfd_set_section_alignment (dynobj, s, align2)) ++ return FALSE; ++ ++ /* Define the symbol as being at this point in the section. */ ++ h->root.u.def.section = s; ++ h->root.u.def.value = s->size; ++ ++ /* Increment the section size to make room for the symbol. */ ++ s->size += h->size; ++ ++ return TRUE; ++} ++ ++static bfd_boolean ++adjust_dynrelocs (struct elf_link_hash_entry *h, PTR inf) ++{ ++ struct bfd_link_info *info; ++ struct elf32_nios2_link_hash_table *htab; ++ ++ if (h->root.type == bfd_link_hash_indirect) ++ return TRUE; ++ ++ if (h->root.type == bfd_link_hash_warning) ++ /* When warning symbols are created, they **replace** the "real" ++ entry in the hash table, thus we never get to see the real ++ symbol in a hash traversal. So look at it now. */ ++ h = (struct elf_link_hash_entry *) h->root.u.i.link; ++ ++ info = (struct bfd_link_info *) inf; ++ htab = elf32_nios2_hash_table (info); ++ ++ if (h->plt.offset != (bfd_vma)-1) ++ h->plt.offset += htab->res_n_size; ++ if (htab->splt == h->root.u.def.section) ++ h->root.u.def.value += htab->res_n_size; ++ ++ return TRUE; ++} ++ ++/* Allocate space in .plt, .got and associated reloc sections for ++ dynamic relocs. */ ++ ++static bfd_boolean ++allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf) ++{ ++ struct bfd_link_info *info; ++ struct elf32_nios2_link_hash_table *htab; ++ struct elf32_nios2_link_hash_entry *eh; ++ struct elf32_nios2_dyn_relocs *p; ++ int use_plt; ++ ++ if (h->root.type == bfd_link_hash_indirect) ++ return TRUE; ++ ++ if (h->root.type == bfd_link_hash_warning) ++ /* When warning symbols are created, they **replace** the "real" ++ entry in the hash table, thus we never get to see the real ++ symbol in a hash traversal. So look at it now. */ ++ h = (struct elf_link_hash_entry *) h->root.u.i.link; ++ ++ info = (struct bfd_link_info *) inf; ++ htab = elf32_nios2_hash_table (info); ++ ++ if (htab->root.dynamic_sections_created ++ && h->plt.refcount > 0) ++ { ++ /* Make sure this symbol is output as a dynamic symbol. ++ Undefined weak syms won't yet be marked as dynamic. */ ++ if (h->dynindx == -1 ++ && !h->forced_local) ++ { ++ if (! bfd_elf_link_record_dynamic_symbol (info, h)) ++ return FALSE; ++ } ++ ++ if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, info->shared, h)) ++ { ++ asection *s = htab->splt; ++ ++ /* Allocate room for the header. */ ++ if (s->size == 0) ++ { ++ if (info->shared) ++ s->size = 24; ++ else ++ s->size = 28; ++ } ++ ++ h->plt.offset = s->size; ++ ++ /* If this symbol is not defined in a regular file, and we are ++ not generating a shared library, then set the symbol to this ++ location in the .plt. This is required to make function ++ pointers compare as equal between the normal executable and ++ the shared library. */ ++ if (! info->shared ++ && !h->def_regular) ++ { ++ h->root.u.def.section = s; ++ h->root.u.def.value = h->plt.offset; ++ } ++ ++ /* Make room for this entry. */ ++ s->size += 12; ++ ++ /* We also need to make an entry in the .rela.plt section. */ ++ htab->srelplt->size += sizeof (Elf32_External_Rela); ++ ++ /* And the .got.plt section. */ ++ htab->sgotplt->size += 4; ++ } ++ else ++ { ++ h->plt.offset = (bfd_vma) -1; ++ h->needs_plt = 0; ++ } ++ } ++ else ++ { ++ h->plt.offset = (bfd_vma) -1; ++ h->needs_plt = 0; ++ } ++ ++ eh = (struct elf32_nios2_link_hash_entry *) h; ++ use_plt = (eh->got_types_used == CALL16_USED && ++ h->plt.offset != (bfd_vma) -1); ++ ++ if (h->got.refcount > 0) ++ { ++ asection *s; ++ bfd_boolean dyn; ++ int tls_type = eh->tls_type; ++ int indx; ++ ++ /* Make sure this symbol is output as a dynamic symbol. ++ Undefined weak syms won't yet be marked as dynamic. */ ++ if (h->dynindx == -1 ++ && !h->forced_local) ++ { ++ if (! bfd_elf_link_record_dynamic_symbol (info, h)) ++ return FALSE; ++ } ++ ++ s = htab->sgot; ++ h->got.offset = s->size; ++ ++ if (tls_type == GOT_UNKNOWN) ++ abort (); ++ ++ if (tls_type == GOT_NORMAL) ++ /* Non-TLS symbols need one GOT slot. */ ++ s->size += 4; ++ else ++ { ++ if (tls_type & GOT_TLS_GD) ++ /* R_NIOS2_TLS_GD16 needs 2 consecutive GOT slots. */ ++ s->size += 8; ++ if (tls_type & GOT_TLS_IE) ++ /* R_NIOS2_TLS_IE16 needs one GOT slot. */ ++ s->size += 4; ++ } ++ ++ dyn = htab->root.dynamic_sections_created; ++ ++ indx = 0; ++ if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) ++ && (!info->shared ++ || !SYMBOL_REFERENCES_LOCAL (info, h))) ++ indx = h->dynindx; ++ ++ if (tls_type != GOT_NORMAL ++ && (info->shared || indx != 0) ++ && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT ++ || h->root.type != bfd_link_hash_undefweak)) ++ { ++ if (tls_type & GOT_TLS_IE) ++ htab->srelgot->size += sizeof (Elf32_External_Rela); ++ ++ if (tls_type & GOT_TLS_GD) ++ htab->srelgot->size += sizeof (Elf32_External_Rela); ++ ++ if ((tls_type & GOT_TLS_GD) && indx != 0) ++ htab->srelgot->size += sizeof (Elf32_External_Rela); ++ } ++ else if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT ++ || h->root.type != bfd_link_hash_undefweak) ++ && !use_plt && (info->shared ++ || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h))) ++ htab->srelgot->size += sizeof (Elf32_External_Rela); ++ } ++ else ++ h->got.offset = (bfd_vma) -1; ++ ++ if (eh->dyn_relocs == NULL) ++ return TRUE; ++ ++ /* In the shared -Bsymbolic case, discard space allocated for ++ dynamic pc-relative relocs against symbols which turn out to be ++ defined in regular objects. For the normal shared case, discard ++ space for pc-relative relocs that have become local due to symbol ++ visibility changes. */ ++ ++ if (info->shared) ++ { ++ if (h->def_regular ++ && (h->forced_local ++ || info->symbolic)) ++ { ++ struct elf32_nios2_dyn_relocs **pp; ++ ++ for (pp = &eh->dyn_relocs; (p = *pp) != NULL; ) ++ { ++ p->count -= p->pc_count; ++ p->pc_count = 0; ++ if (p->count == 0) ++ *pp = p->next; ++ else ++ pp = &p->next; ++ } ++ } ++ ++ /* Also discard relocs on undefined weak syms with non-default ++ visibility. */ ++ if (eh->dyn_relocs != NULL ++ && h->root.type == bfd_link_hash_undefweak) ++ { ++ if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT) ++ eh->dyn_relocs = NULL; ++ ++ /* Make sure undefined weak symbols are output as a dynamic ++ symbol in PIEs. */ ++ else if (h->dynindx == -1 ++ && !h->forced_local) ++ { ++ if (! bfd_elf_link_record_dynamic_symbol (info, h)) ++ return FALSE; ++ } ++ } ++ } ++ else ++ { ++ /* For the non-shared case, discard space for relocs against ++ symbols which turn out to need copy relocs or are not ++ dynamic. */ ++ ++ if (!h->non_got_ref ++ && ((h->def_dynamic ++ && !h->def_regular) ++ || (htab->root.dynamic_sections_created ++ && (h->root.type == bfd_link_hash_undefweak ++ || h->root.type == bfd_link_hash_undefined)))) ++ { ++ /* Make sure this symbol is output as a dynamic symbol. ++ Undefined weak syms won't yet be marked as dynamic. */ ++ if (h->dynindx == -1 ++ && !h->forced_local) ++ { ++ if (! bfd_elf_link_record_dynamic_symbol (info, h)) ++ return FALSE; ++ } ++ ++ /* If that succeeded, we know we'll be keeping all the ++ relocs. */ ++ if (h->dynindx != -1) ++ goto keep; ++ } ++ ++ eh->dyn_relocs = NULL; ++ ++ keep: ; ++ } ++ ++ /* Finally, allocate space. */ ++ for (p = eh->dyn_relocs; p != NULL; p = p->next) ++ { ++ asection *sreloc = elf_section_data (p->sec)->sreloc; ++ sreloc->size += p->count * sizeof (Elf32_External_Rela); ++ } ++ ++ return TRUE; ++} ++ ++/* Set the sizes of the dynamic sections. */ ++ ++static bfd_boolean ++nios2_elf32_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, ++ struct bfd_link_info *info) ++{ ++ bfd *dynobj; ++ asection *s; ++ bfd_boolean plt; ++ bfd_boolean got; ++ bfd_boolean relocs; ++ bfd *ibfd; ++ struct elf32_nios2_link_hash_table *htab; ++ ++ htab = elf32_nios2_hash_table (info); ++ dynobj = elf_hash_table (info)->dynobj; ++ BFD_ASSERT (dynobj != NULL); ++ ++ htab->res_n_size = 0; ++ if (elf_hash_table (info)->dynamic_sections_created) ++ { ++ /* Set the contents of the .interp section to the interpreter. */ ++ if (info->executable) ++ { ++ s = bfd_get_section_by_name (dynobj, ".interp"); ++ BFD_ASSERT (s != NULL); ++ s->size = sizeof ELF_DYNAMIC_INTERPRETER; ++ s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER; ++ } ++ } ++ else ++ { ++ /* We may have created entries in the .rela.got section. ++ However, if we are not creating the dynamic sections, we will ++ not actually use these entries. Reset the size of .rela.got, ++ which will cause it to get stripped from the output file ++ below. */ ++ s = htab->srelgot; ++ if (s != NULL) ++ s->size = 0; ++ } ++ ++ /* Set up .got offsets for local syms, and space for local dynamic ++ relocs. */ ++ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) ++ { ++ bfd_signed_vma *local_got; ++ bfd_signed_vma *end_local_got; ++ char *local_tls_type; ++ bfd_size_type locsymcount; ++ Elf_Internal_Shdr *symtab_hdr; ++ asection *srel; ++ ++ if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour) ++ continue; ++ ++ for (s = ibfd->sections; s != NULL; s = s->next) ++ { ++ struct elf32_nios2_dyn_relocs *p; ++ ++ for (p = elf_section_data (s)->local_dynrel; p != NULL; p = p->next) ++ { ++ if (!bfd_is_abs_section (p->sec) ++ && bfd_is_abs_section (p->sec->output_section)) ++ { ++ /* Input section has been discarded, either because ++ it is a copy of a linkonce section or due to ++ linker script /DISCARD/, so we'll be discarding ++ the relocs too. */ ++ } ++ else if (p->count != 0) ++ { ++ srel = elf_section_data (p->sec)->sreloc; ++ srel->size += p->count * sizeof (Elf32_External_Rela); ++ if ((p->sec->output_section->flags & SEC_READONLY) != 0) ++ info->flags |= DF_TEXTREL; ++ } ++ } ++ } ++ ++ local_got = elf_local_got_refcounts (ibfd); ++ if (!local_got) ++ continue; ++ ++ symtab_hdr = &elf_tdata (ibfd)->symtab_hdr; ++ locsymcount = symtab_hdr->sh_info; ++ end_local_got = local_got + locsymcount; ++ local_tls_type = elf32_nios2_local_got_tls_type (ibfd); ++ s = htab->sgot; ++ srel = htab->srelgot; ++ for (; local_got < end_local_got; ++local_got, ++local_tls_type) ++ { ++ if (*local_got > 0) ++ { ++ *local_got = s->size; ++ if (*local_tls_type & GOT_TLS_GD) ++ /* TLS_GD relocs need an 8-byte structure in the GOT. */ ++ s->size += 8; ++ if (*local_tls_type & GOT_TLS_IE) ++ s->size += 4; ++ if (*local_tls_type == GOT_NORMAL) ++ s->size += 4; ++ ++ if (info->shared || *local_tls_type == GOT_TLS_GD) ++ srel->size += sizeof (Elf32_External_Rela); ++ } ++ else ++ *local_got = (bfd_vma) -1; ++ } ++ } ++ ++ if (htab->tls_ldm_got.refcount > 0) ++ { ++ /* Allocate two GOT entries and one dynamic relocation (if necessary) ++ for R_NIOS2_TLS_LDM16 relocations. */ ++ htab->tls_ldm_got.offset = htab->sgot->size; ++ htab->sgot->size += 8; ++ if (info->shared) ++ htab->srelgot->size += sizeof (Elf32_External_Rela); ++ } ++ else ++ htab->tls_ldm_got.offset = -1; ++ ++ /* Allocate global sym .plt and .got entries, and space for global ++ sym dynamic relocs. */ ++ elf_link_hash_traverse (& htab->root, allocate_dynrelocs, info); ++ ++ /* The check_relocs and adjust_dynamic_symbol entry points have ++ determined the sizes of the various dynamic sections. Allocate ++ memory for them. */ ++ plt = FALSE; ++ got = FALSE; ++ relocs = FALSE; ++ for (s = dynobj->sections; s != NULL; s = s->next) ++ { ++ const char *name; ++ ++ if ((s->flags & SEC_LINKER_CREATED) == 0) ++ continue; ++ ++ /* It's OK to base decisions on the section name, because none ++ of the dynobj section names depend upon the input files. */ ++ name = bfd_get_section_name (dynobj, s); ++ ++ if (strcmp (name, ".plt") == 0) ++ { ++ /* Remember whether there is a PLT. */ ++ plt = s->size != 0; ++ ++ /* Correct for the number of res_N branches. */ ++ if (plt && !info->shared) ++ { ++ htab->res_n_size = (s->size-28) / 3; ++ s->size += htab->res_n_size; ++ } ++ } ++ else if (CONST_STRNEQ (name, ".rela")) ++ { ++ if (s->size != 0) ++ { ++ relocs = TRUE; ++ ++ /* We use the reloc_count field as a counter if we need ++ to copy relocs into the output file. */ ++ s->reloc_count = 0; ++ } ++ } ++ else if (CONST_STRNEQ (name, ".got")) ++ got = s->size != 0; ++ else if (strcmp (name, ".dynbss") != 0) ++ { ++ /* It's not one of our sections, so don't allocate space. */ ++ continue; ++ } ++ ++ if (s->size == 0) ++ { ++ /* If we don't need this section, strip it from the ++ output file. This is mostly to handle .rela.bss and ++ .rela.plt. We must create both sections in ++ create_dynamic_sections, because they must be created ++ before the linker maps input sections to output ++ sections. The linker does that before ++ adjust_dynamic_symbol is called, and it is that ++ function which decides whether anything needs to go ++ into these sections. */ ++ s->flags |= SEC_EXCLUDE; ++ continue; ++ } ++ ++ if ((s->flags & SEC_HAS_CONTENTS) == 0) ++ continue; ++ ++ /* Allocate memory for the section contents. */ ++ /* FIXME: This should be a call to bfd_alloc not bfd_zalloc. ++ Unused entries should be reclaimed before the section's contents ++ are written out, but at the moment this does not happen. Thus in ++ order to prevent writing out garbage, we initialise the section's ++ contents to zero. */ ++ s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size); ++ if (s->contents == NULL) ++ return FALSE; ++ } ++ ++ /* Adjust dynamic symbols that point to the plt to account for the ++ now-known number of resN slots. */ ++ if (htab->res_n_size) ++ elf_link_hash_traverse (& htab->root, adjust_dynrelocs, info); ++ ++ if (elf_hash_table (info)->dynamic_sections_created) ++ { ++ /* Add some entries to the .dynamic section. We fill in the ++ values later, in elf_nios2_finish_dynamic_sections, but we ++ must add the entries now so that we get the correct size for ++ the .dynamic section. The DT_DEBUG entry is filled in by the ++ dynamic linker and used by the debugger. */ ++#define add_dynamic_entry(TAG, VAL) \ ++ _bfd_elf_add_dynamic_entry (info, TAG, VAL) ++ ++ if (!info->shared) ++ { ++ if (!add_dynamic_entry (DT_DEBUG, 0)) ++ return FALSE; ++ } ++ ++ if (got) ++ if (!add_dynamic_entry (DT_PLTGOT, 0)) ++ return FALSE; ++ ++ if (plt) ++ if (!add_dynamic_entry (DT_PLTRELSZ, 0) ++ || !add_dynamic_entry (DT_PLTREL, DT_RELA) ++ || !add_dynamic_entry (DT_JMPREL, 0)) ++ return FALSE; ++ ++ if (relocs) ++ { ++ if (!add_dynamic_entry (DT_RELA, 0) ++ || !add_dynamic_entry (DT_RELASZ, 0) ++ || !add_dynamic_entry (DT_RELAENT, sizeof (Elf32_External_Rela))) ++ return FALSE; ++ } ++ ++ if (!info->shared) ++ { ++ if (!add_dynamic_entry (DT_NIOS2_GP, 0)) ++ return FALSE; ++ } ++ ++ if ((info->flags & DF_TEXTREL) != 0) ++ { ++ if (!add_dynamic_entry (DT_TEXTREL, 0)) ++ return FALSE; ++ } ++ } ++#undef add_dynamic_entry ++ ++ return TRUE; ++} ++ ++/* Create a Nios II elf linker hash table. */ ++ ++static struct bfd_link_hash_table * ++nios2_elf32_link_hash_table_create (bfd *abfd) ++{ ++ struct elf32_nios2_link_hash_table *ret; ++ bfd_size_type amt = sizeof (struct elf32_nios2_link_hash_table); ++ ++ ret = bfd_malloc (amt); ++ if (ret == NULL) ++ return NULL; ++ ++ if (!_bfd_elf_link_hash_table_init (& ret->root, abfd, ++ link_hash_newfunc, ++ sizeof (struct ++ elf32_nios2_link_hash_entry))) ++ { ++ free (ret); ++ return NULL; ++ } ++ ++ ret->sgot = NULL; ++ ret->sgotplt = NULL; ++ ret->srelgot = NULL; ++ ret->splt = NULL; ++ ret->srelplt = NULL; ++ ret->sdynbss = NULL; ++ ret->srelbss = NULL; ++ ret->sbss = NULL; ++ ret->tls_ldm_got.refcount = 0; ++ ret->sym_sec.abfd = NULL; ++ return &ret->root.root; ++} ++ ++static enum elf_reloc_type_class ++nios2_elf32_reloc_type_class (const Elf_Internal_Rela *rela) ++{ ++ switch ((int) ELF32_R_TYPE (rela->r_info)) ++ { ++ case R_NIOS2_RELATIVE: ++ return reloc_class_relative; ++ case R_NIOS2_JUMP_SLOT: ++ return reloc_class_plt; ++ case R_NIOS2_COPY: ++ return reloc_class_copy; ++ default: ++ return reloc_class_normal; ++ } ++} ++ ++/* Return 1 if target is one of ours. */ ++ ++static bfd_boolean ++is_nios2_elf_target (const struct bfd_target *targ) ++{ ++ return (targ == &bfd_elf32_littlenios2_vec ++ || targ == &bfd_elf32_bignios2_vec); ++} ++ ++/* Hook called by the linker routine which adds symbols from an object ++ file. We use it to put .comm items in .sbss, and not .bss. */ ++ ++static bfd_boolean ++nios2_elf_add_symbol_hook (bfd *abfd, ++ struct bfd_link_info *info, ++ Elf_Internal_Sym *sym, ++ const char **namep ATTRIBUTE_UNUSED, ++ flagword *flagsp ATTRIBUTE_UNUSED, ++ asection **secp, ++ bfd_vma *valp) ++{ ++ bfd *dynobj; ++ ++ if (sym->st_shndx == SHN_COMMON ++ && !info->relocatable ++ && sym->st_size <= elf_gp_size (abfd) ++ && is_nios2_elf_target (info->hash->creator)) ++ { ++ /* Common symbols less than or equal to -G nn bytes are automatically ++ put into .sbss. */ ++ struct elf32_nios2_link_hash_table *htab; ++ ++ htab = elf32_nios2_hash_table (info); ++ if (htab->sbss == NULL) ++ { ++ flagword flags = SEC_IS_COMMON | SEC_LINKER_CREATED; ++ ++ dynobj = elf_hash_table (info)->dynobj; ++ if (!dynobj) ++ dynobj = abfd; ++ ++ htab->sbss = bfd_make_section_anyway_with_flags (dynobj, ".sbss", ++ flags); ++ if (htab->sbss == NULL) ++ return FALSE; ++ } ++ ++ *secp = htab->sbss; ++ *valp = sym->st_size; ++ } ++ ++ return TRUE; ++} ++ ++/* Decide whether to attempt to turn absptr or lsda encodings in ++ shared libraries into pcrel within the given input section. */ ++ ++static bfd_boolean ++nios2_elf32_can_make_relative_eh_frame(bfd *input_bfd ATTRIBUTE_UNUSED, ++ struct bfd_link_info *info ++ ATTRIBUTE_UNUSED, ++ asection *eh_frame_section ++ ATTRIBUTE_UNUSED) ++{ ++ /* We can't use PC-relative encodings in the .eh_frame section. */ ++ return FALSE; ++} ++ ++#define ELF_ARCH bfd_arch_nios2 ++#define ELF_MACHINE_CODE EM_ALTERA_NIOS2 ++ ++/* The Nios II MMU uses a 4K page size. */ ++ ++#define ELF_MAXPAGESIZE 0x1000 ++ ++#define bfd_elf32_bfd_link_hash_table_create \ ++ nios2_elf32_link_hash_table_create ++ ++/* Relocation table lookup macros. */ ++ ++#define bfd_elf32_bfd_reloc_type_lookup nios2_elf32_bfd_reloc_type_lookup ++#define bfd_elf32_bfd_reloc_name_lookup nios2_elf32_bfd_reloc_name_lookup ++ ++/* JUMP_TABLE_LINK macros. */ ++ ++#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_can_refcount 1 ++#define elf_backend_plt_readonly 1 ++#define elf_backend_want_got_plt 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_check_relocs nios2_elf32_check_relocs ++ ++#define elf_backend_gc_mark_hook nios2_elf32_gc_mark_hook ++#define elf_backend_gc_sweep_hook nios2_elf32_gc_sweep_hook ++#define elf_backend_create_dynamic_sections \ ++ nios2_elf32_create_dynamic_sections ++#define elf_backend_finish_dynamic_symbol nios2_elf32_finish_dynamic_symbol ++#define elf_backend_finish_dynamic_sections \ ++ nios2_elf32_finish_dynamic_sections ++#define elf_backend_adjust_dynamic_symbol nios2_elf32_adjust_dynamic_symbol ++#define elf_backend_reloc_type_class nios2_elf32_reloc_type_class ++#define elf_backend_size_dynamic_sections nios2_elf32_size_dynamic_sections ++#define elf_backend_add_symbol_hook nios2_elf_add_symbol_hook ++#define elf_backend_copy_indirect_symbol nios2_elf32_copy_indirect_symbol ++ ++#define elf_backend_grok_prstatus nios2_grok_prstatus ++#define elf_backend_grok_psinfo nios2_grok_psinfo ++ ++#undef elf_backend_can_make_relative_eh_frame ++#define elf_backend_can_make_relative_eh_frame \ ++ nios2_elf32_can_make_relative_eh_frame ++ ++/* 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" ++ ++#define elf_backend_got_header_size 12 ++ ++#include "elf32-target.h" +Index: binutils-2.17.50.0.12/gas/config/tc-nios2.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ binutils-2.17.50.0.12/gas/config/tc-nios2.c 2010-07-05 14:50:39.000000000 +0200 +@@ -0,0 +1,3361 @@ ++/* NOT ASSIGNED TO FSF. COPYRIGHT ALTERA. */ ++/* 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" ++#include "dw2gencfi.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; ++ /* Pointer to the relevant bit of the opcode table. */ ++ const struct nios2_opcode *insn_nios2_opcode; ++ /* After parsing ptrs to the tokens in the instruction fill this array ++ it is terminated with a null pointer (hence the first +1). ++ The second +1 is because in some parts of the code the opcode ++ is not counted as a token, but still placed in this array. */ ++ const char *insn_tokens[NIOS2_MAX_INSN_TOKENS + 1 + 1]; ++ ++ /* This holds information used to generate fixups ++ and eventually relocations if it is not null. */ ++ nios2_insn_relocS *insn_reloc; ++} ++nios2_insn_infoS; ++ ++ ++/* This struct associates an argument assemble function with ++ an argument syntax string. Used by the assembler to find out ++ how to parse and assemble a set of instruction operands and ++ return the instruction field values. */ ++ ++typedef struct nios2_arg_info ++{ ++ const char *args; ++ void (*assemble_args_func) (nios2_insn_infoS * insn_info); ++} ++nios2_arg_infoS; ++ ++/* This struct is used to convert 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); ++void md_apply_fix (fixS * fixP, valueT * valP, segT seg ATTRIBUTE_UNUSED); ++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; ++ ++#ifdef OBJ_ELF ++/* Pre-defined "_GLOBAL_OFFSET_TABLE_" */ ++symbolS * GOT_symbol; ++#endif ++ ++#define streq(a, b) (strcmp (a, b) == 0) ++ ++/* 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 0 ++#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 ++#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}, ++ {"%call", BFD_RELOC_NIOS2_CALL16}, ++ {"%gotoff_lo", BFD_RELOC_NIOS2_GOTOFF_LO}, ++ {"%gotoff_hiadj", BFD_RELOC_NIOS2_GOTOFF_HA}, ++ {"%tls_gd", BFD_RELOC_NIOS2_TLS_GD16}, ++ {"%tls_ldm", BFD_RELOC_NIOS2_TLS_LDM16}, ++ {"%tls_ldo", BFD_RELOC_NIOS2_TLS_LDO16}, ++ {"%tls_ie", BFD_RELOC_NIOS2_TLS_IE16}, ++ {"%tls_le", BFD_RELOC_NIOS2_TLS_LE16}, ++ {"%gotoff", BFD_RELOC_NIOS2_GOTOFF}, ++ {"%got", BFD_RELOC_NIOS2_GOT16} ++}; ++ ++#define NIOS2_NUM_SPECIAL_RELOCS \ ++ (sizeof(nios2_special_reloc)/sizeof(nios2_special_reloc[0])) ++const int nios2_num_special_relocs = NIOS2_NUM_SPECIAL_RELOCS; ++ ++/* 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 ++ ********************************************************************/ ++ ++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"; ++} ++ ++/* Machine-dependent usage message. */ ++void ++md_show_usage (FILE * stream) ++{ ++ fprintf (stream, " NIOS2 options:\n" ++ " -relax-all replace all branch and call " ++ "instructions with jmp and callr sequences\n" ++ " -relax-section replace identified out of range " ++ "branches with jmp sequences (default)\n" ++ " -no-relax do not replace any branches or calls\n" ++ " -EB force big-endian byte ordering\n" ++ " -EL force little-endian byte ordering\n"); ++} ++ ++/* This function is called once, at assembler startup time. ++ It should set up all the tables, etc. that the MD part of the ++ assembler will need. */ ++void ++md_begin (void) ++{ ++ int i; ++ const char *inserted; ++ ++ /* Create and fill a hashtable for the 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; ++ ++/* 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 (void) ++{ ++ 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 (void) ++{ ++ 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 (void) ++{ ++ 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 (void) ++{ ++ /* 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 (void) ++{ ++ 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 (void) ++{ ++ 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 (void) ++{ ++ 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 (void) ++{ ++ 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 (void) ++{ ++ 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 (void) ++{ ++ 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; ++} ++ ++ ++/* This function turns a C long int, short int or char ++ into the series of bytes that represent the number ++ on the target machine. ++ The function is also used to convert .byte8 and .byte16 directives ++*/ ++void ++md_number_to_chars (char *buf, valueT val, int n) ++{ ++ if (target_big_endian) ++ { ++ number_to_chars_bigendian (buf, val, n); ++ } ++ else ++ { ++ number_to_chars_littleendian (buf, val, n); ++ } ++} ++ ++/* 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); ++} ++ ++/* 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); ++} ++ ++/* 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); ++} ++ ++/* 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 generic_force_reloc (fixp); ++} ++ ++/* 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) ++{ ++ if (fixp->fx_addsy == NULL) ++ return 1; ++ ++#ifdef OBJ_ELF ++ /* Prevent all adjustments to global symbols. */ ++ if (OUTPUT_FLAVOR == bfd_target_elf_flavour ++ && (S_IS_EXTERNAL (fixp->fx_addsy) || S_IS_WEAK (fixp->fx_addsy))) ++ return 0; ++#endif ++ if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT ++ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) ++ return 0; ++ ++ /* Preserve relocations against symbols with function type. */ ++ if (symbol_get_bfdsym (fixp->fx_addsy)->flags & BSF_FUNCTION) ++ return 0; ++ ++ /* Don't allow symbols to be discarded on GOT related relocs. */ ++ if (fixp->fx_r_type == BFD_RELOC_NIOS2_GOT16 ++ || fixp->fx_r_type == BFD_RELOC_NIOS2_CALL16 ++ || fixp->fx_r_type == BFD_RELOC_NIOS2_GOTOFF_LO ++ || fixp->fx_r_type == BFD_RELOC_NIOS2_GOTOFF_HA ++ || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_GD16 ++ || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_LDM16 ++ || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_LDO16 ++ || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_IE16 ++ || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_LE16 ++ || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_DTPMOD ++ || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_DTPREL ++ || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_TPREL ++ || fixp->fx_r_type == BFD_RELOC_NIOS2_GOTOFF) ++ return 0; ++ ++ return 1; ++} ++ ++/* 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_offset; // fixp->fx_addnumber; ++ ++ if (fixp->fx_pcrel) ++ { ++ switch (fixp->fx_r_type) ++ { ++ case BFD_RELOC_16: ++ fixp->fx_r_type = BFD_RELOC_16_PCREL; ++ break; ++ case BFD_RELOC_NIOS2_LO16: ++ fixp->fx_r_type = BFD_RELOC_NIOS2_PCREL_LO; ++ break; ++ case BFD_RELOC_NIOS2_HIADJ16: ++ fixp->fx_r_type = BFD_RELOC_NIOS2_PCREL_HA; ++ break; ++ default: ++ break; ++ } ++ } ++ ++ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type); ++ if (reloc->howto == NULL) ++ { ++ as_bad_where (fixp->fx_file, fixp->fx_line, ++ _("can't represent relocation type %s"), ++ bfd_get_reloc_code_name (fixp->fx_r_type)); ++ ++ /* Set howto to a garbage value so that we can keep going. */ ++ reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32); ++ 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_fix (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 || ++ fixP->fx_r_type == BFD_RELOC_NIOS2_GOT16 || ++ fixP->fx_r_type == BFD_RELOC_NIOS2_CALL16 || ++ fixP->fx_r_type == BFD_RELOC_NIOS2_GOTOFF_LO || ++ fixP->fx_r_type == BFD_RELOC_NIOS2_GOTOFF_HA || ++ fixP->fx_r_type == BFD_RELOC_NIOS2_TLS_GD16 || ++ fixP->fx_r_type == BFD_RELOC_NIOS2_TLS_LDM16 || ++ fixP->fx_r_type == BFD_RELOC_NIOS2_TLS_LDO16 || ++ fixP->fx_r_type == BFD_RELOC_NIOS2_TLS_IE16 || ++ fixP->fx_r_type == BFD_RELOC_NIOS2_TLS_LE16 || ++ fixP->fx_r_type == BFD_RELOC_NIOS2_GOTOFF || ++ fixP->fx_r_type == BFD_RELOC_NIOS2_TLS_DTPREL ++ /* Add other relocs here as we generate them. */ ++ )); ++ ++ ++ /* 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; ++ ++ switch (fixP->fx_r_type) ++ { ++ case BFD_RELOC_NIOS2_TLS_GD16: ++ case BFD_RELOC_NIOS2_TLS_LDM16: ++ case BFD_RELOC_NIOS2_TLS_LDO16: ++ case BFD_RELOC_NIOS2_TLS_IE16: ++ case BFD_RELOC_NIOS2_TLS_LE16: ++ case BFD_RELOC_NIOS2_TLS_DTPMOD: ++ case BFD_RELOC_NIOS2_TLS_DTPREL: ++ case BFD_RELOC_NIOS2_TLS_TPREL: ++ S_SET_THREAD_LOCAL (fixP->fx_addsy); ++ break; ++ default: ++ break; ++ } ++ } ++ else ++ { ++ 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 = ((signed)fixup) >> howto->rightshift; ++ ++ /* Truncate the fixup to right size. */ ++ switch (fixP->fx_r_type) ++ { ++ case BFD_RELOC_NIOS2_HI16: ++ fixup = (fixup >> 16) & 0xFFFF; ++ break; ++ case BFD_RELOC_NIOS2_LO16: ++ fixup = fixup & 0xFFFF; ++ break; ++ case BFD_RELOC_NIOS2_HIADJ16: ++ fixup = ((fixup >> 16) & 0xFFFF) + ((fixup >> 15) & 0x01); ++ break; ++ default: ++ fixup = fixup & (howto->dst_mask >> howto->bitpos); ++ 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 = ((signed)fixup) >> howto->rightshift; ++ ++ /* Check for overflow - return TRUE if overflow, FALSE if not. */ ++ switch (howto->complain_on_overflow) ++ { ++ case complain_overflow_dont: ++ break; ++ case complain_overflow_bitfield: ++ if ((fixup >> howto->bitsize) != 0) ++ 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; ++} ++ ++/* Called just before the assembler exits. */ ++void ++md_end () ++{ ++ /* FIXME - not yet implemented */ ++} ++ ++/* Under ELF we need to default _GLOBAL_OFFSET_TABLE. ++ Otherwise we have no need to default values of symbols. */ ++ ++symbolS * ++md_undefined_symbol (char * name ATTRIBUTE_UNUSED) ++{ ++#ifdef OBJ_ELF ++ if (name[0] == '_' && name[1] == 'G' ++ && streq (name, GLOBAL_OFFSET_TABLE_NAME)) ++ { ++ if (!GOT_symbol) ++ { ++ if (symbol_find (name)) ++ as_bad ("GOT already in the symbol table"); ++ ++ GOT_symbol = symbol_new (name, undefined_section, ++ (valueT) 0, & zero_address_frag); ++ } ++ ++ return GOT_symbol; ++ } ++#endif ++ ++ return 0; ++} ++ ++ ++/* 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); ++} ++ ++/* 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 relocation operators. ++ Change the relocation type and advance the ptr to the start of ++ the expression proper. */ ++ for (i = 0; i < nios2_num_special_relocs; i++) ++ { ++ if (strstr (exprstr, nios2_special_reloc[i].string) != NULL) ++ { ++ reloc_type = nios2_special_reloc[i].reloc_type; ++ exprstr += strlen (nios2_special_reloc[i].string) + 1; ++ ++ /* %lo and %hiadj have different meanings for PC-relative ++ expressions. */ ++ if (pcrel) ++ { ++ if (reloc_type == BFD_RELOC_NIOS2_LO16) ++ reloc_type = BFD_RELOC_NIOS2_PCREL_LO; ++ if (reloc_type == BFD_RELOC_NIOS2_HIADJ16) ++ reloc_type = BFD_RELOC_NIOS2_PCREL_HA; ++ } ++ ++ break; ++ } ++ } ++ ++ /* We potentially have a relocation. */ ++ reloc = nios2_insn_reloc_new (reloc_type, pcrel); ++ if (prev_reloc != NULL) ++ prev_reloc->reloc_next = reloc; ++ else ++ insn->insn_reloc = reloc; ++ ++ /* Parse the expression string. */ ++ saved_line_ptr = input_line_pointer; ++ input_line_pointer = (char *) exprstr; ++ expression (&reloc->reloc_expression); ++ input_line_pointer = saved_line_ptr; ++ ++ /* This is redundant as the fixup will put this into ++ the instruction, but it is included here so that ++ self-test mode (-r) works. */ ++ value = 0; ++ if (nios2_mode == NIOS2_MODE_TEST) ++ { ++ 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, "exception", strlen ("exception")) != 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 ++ && strncmp (argStr, "config", strlen ("config")) != 0 ++ && strncmp (argStr, "mpubase", strlen ("mpubase")) != 0 ++ && strncmp (argStr, "mpuacc", strlen ("mpuacc")) != 0 ++ && strncmp (argStr, "badaddr", strlen ("badaddr")) != 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, "exception", strlen ("exception")) == 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 ++ || strncmp (argStr, "config", strlen ("config")) == 0 ++ || strncmp (argStr, "mpubase", strlen ("mpubase")) == 0 ++ || strncmp (argStr, "mpuacc", strlen ("mpuacc")) == 0 ++ || strncmp (argStr, "badaddr", strlen ("badaddr")) == 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(") ++ || strstr (argStr, "%got(") || strstr (argStr, "%call(") ++ || strstr (argStr, "%gotoff_lo(") ++ || strstr (argStr, "%gotoff_hiadj(") ++ || strstr (argStr, "%tls_gd(") || strstr (argStr, "%tls_ldm(") ++ || strstr (argStr, "%tls_ldo(") || strstr (argStr, "%tls_ie(") ++ || strstr (argStr, "%tls_le(") || strstr (argStr, "%gotoff(")) ++ { ++ /* 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 ++ || strstr (p, "%got") == p || strstr (argStr, "%call") ++ || strstr (p, "%gotoff_lo") == p ++ || strstr (p, "%gotoff_hiadj") == p ++ || strstr (p, "%tls_gd") == p || strstr (p, "%tls_ldm") == p ++ || strstr (p, "%tls_ldo") == p || strstr (p, "%tls_ie") == p ++ || strstr (p, "%tls_le") == p || strstr (p, "%gotoff") == 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; ++} ++ ++static int nios2_tls_ldo_reloc; ++ ++void ++nios2_cons (expressionS *exp, int size) ++{ ++ /* Handle %tls_ldo(...) */ ++ ++ nios2_tls_ldo_reloc = 0; ++ ++ SKIP_WHITESPACE (); ++ if (input_line_pointer[0] == '%') ++ { ++ if (strncmp (input_line_pointer + 1, "tls_ldo", 7) == 0) ++ { ++ if (size != 4) ++ as_bad (_("Illegal operands: %%tls_ldo in %d-byte data field"), ++ size); ++ else ++ { ++ input_line_pointer += 8; ++ nios2_tls_ldo_reloc = 1; ++ } ++ } ++ if (nios2_tls_ldo_reloc) ++ { ++ SKIP_WHITESPACE (); ++ if (input_line_pointer[0] != '(') ++ as_bad (_("Illegal operands: %%tls_ldo requires arguments in ()")); ++ else ++ { ++ int c; ++ char *end = ++input_line_pointer; ++ int npar = 0; ++ ++ while (! is_end_of_line[(c = *end)]) ++ { ++ if (c == '(') ++ npar++; ++ else if (c == ')') ++ { ++ if (!npar) ++ break; ++ npar--; ++ } ++ end++; ++ } ++ ++ if (c != ')') ++ as_bad (_("Illegal operands: %%tls_ldo requires arguments in ()")); ++ else ++ { ++ *end = '\0'; ++ expression (exp); ++ *end = c; ++ if (input_line_pointer != end) ++ as_bad (_("Illegal operands: %%tls_ldo requires arguments in ()")); ++ else ++ { ++ input_line_pointer++; ++ SKIP_WHITESPACE (); ++ c = *input_line_pointer; ++ if (! is_end_of_line[c] && c != ',') ++ as_bad (_("Illegal operands: garbage after %%tls_ldo()")); ++ } ++ } ++ } ++ } ++ } ++ if (!nios2_tls_ldo_reloc) ++ expression (exp); ++} ++ ++void ++nios2_cons_fix_new (fragS *frag, int where, unsigned int nbytes, ++ expressionS *exp) ++{ ++ bfd_reloc_code_real_type r; ++ ++ r = (nbytes == 1 ? BFD_RELOC_8 : ++ (nbytes == 2 ? BFD_RELOC_16 : ++ (nbytes == 4 ? BFD_RELOC_32 : BFD_RELOC_64))); ++ ++ if (nios2_tls_ldo_reloc) ++ r = BFD_RELOC_NIOS2_TLS_DTPREL; ++ ++ fix_new_exp (frag, where, (int) nbytes, exp, 0, r); ++ nios2_tls_ldo_reloc = 0; ++} ++ ++/* 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; ++} ++ ++/* Convert REGNAME to a DWARF-2 register number. */ ++ ++int ++tc_nios2_regname_to_dw2regnum (char *regname) ++{ ++ struct nios2_reg *r = (struct nios2_reg *) hash_find (nios2_reg_hash, ++ regname); ++ if (r == NULL) ++ return -1; ++ return r->index; ++} ++ ++/* Initialize the DWARF-2 unwind information for this procedure. */ ++ ++void ++tc_nios2_frame_initial_instructions (void) ++{ ++ cfi_add_CFA_def_cfa (27, 0); ++} +Index: binutils-2.17.50.0.12/gas/config/tc-nios2.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ binutils-2.17.50.0.12/gas/config/tc-nios2.h 2010-06-30 15:06:08.000000000 +0200 +@@ -0,0 +1,124 @@ ++/* NOT ASSIGNED TO FSF. COPYRIGHT ALTERA. */ ++/* 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) ++ ++/* 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 *); ++ ++extern int tc_nios2_regname_to_dw2regnum (char *regname); ++ ++extern void tc_nios2_frame_initial_instructions (void); ++ ++#define md_relax_frag nios2_relax_frag ++extern long nios2_relax_frag ++ (segT segment, fragS * fragP, long stretch); ++ ++#ifdef OBJ_ELF ++#define ELF_TC_SPECIAL_SECTIONS \ ++ { ".sdata", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_NIOS2_GPREL }, \ ++ { ".sbss", SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_NIOS2_GPREL }, \ ++ { ".lit4", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_NIOS2_GPREL }, \ ++ { ".lit8", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_NIOS2_GPREL }, ++ ++/* Processor specific section directives */ ++#define md_elf_section_letter nios2_elf_section_letter ++extern int nios2_elf_section_letter (int, char **); ++#define md_elf_section_flags nios2_elf_section_flags ++extern flagword nios2_elf_section_flags (flagword, int, int); ++#endif ++ ++#define GLOBAL_OFFSET_TABLE_NAME "_GLOBAL_OFFSET_TABLE_" ++ ++#define DIFF_EXPR_OK ++ ++#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) nios2_cons (EXP, NBYTES) ++extern void nios2_cons (expressionS *exp, int size); ++ ++#define TC_CONS_FIX_NEW nios2_cons_fix_new ++extern void nios2_cons_fix_new(struct frag *frag, int where, ++ unsigned int nbytes, struct expressionS *exp); ++ ++/* We want .cfi_* pseudo-ops for generating unwind info. */ ++#define TARGET_USE_CFIPOP 1 ++#define DWARF2_DEFAULT_RETURN_COLUMN 31 ++#define DWARF2_CIE_DATA_ALIGNMENT (-4) ++#define tc_regname_to_dw2regnum tc_nios2_regname_to_dw2regnum ++#define tc_cfi_frame_initial_instructions tc_nios2_frame_initial_instructions ++#define tc_cfi_no_pcrel_expr 1 ++ ++#endif // TC_NIOS2 +Index: binutils-2.17.50.0.12/gas/doc/c-nios2.texi +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ binutils-2.17.50.0.12/gas/doc/c-nios2.texi 2010-06-30 15:06:08.000000000 +0200 +@@ -0,0 +1,223 @@ ++@c NOT ASSIGNED TO FSF. COPYRIGHT ALTERA. ++@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} ++ +Index: binutils-2.17.50.0.12/gas/testsuite/gas/nios2/add.d +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ binutils-2.17.50.0.12/gas/testsuite/gas/nios2/add.d 2010-06-30 15:06:08.000000000 +0200 +@@ -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 +Index: binutils-2.17.50.0.12/gas/testsuite/gas/nios2/add.s +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ binutils-2.17.50.0.12/gas/testsuite/gas/nios2/add.s 2010-06-30 15:06:08.000000000 +0200 +@@ -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 +Index: binutils-2.17.50.0.12/gas/testsuite/gas/nios2/align_fill.d +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ binutils-2.17.50.0.12/gas/testsuite/gas/nios2/align_fill.d 2010-06-30 15:06:08.000000000 +0200 +@@ -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 ++ ... +Index: binutils-2.17.50.0.12/gas/testsuite/gas/nios2/align_fill.s +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ binutils-2.17.50.0.12/gas/testsuite/gas/nios2/align_fill.s 2010-06-30 15:06:08.000000000 +0200 +@@ -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)" +Index: binutils-2.17.50.0.12/gas/testsuite/gas/nios2/align_text.d +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ binutils-2.17.50.0.12/gas/testsuite/gas/nios2/align_text.d 2010-06-30 15:06:08.000000000 +0200 +@@ -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