summaryrefslogtreecommitdiffstats
path: root/contrib
diff options
context:
space:
mode:
authorobrien <obrien@FreeBSD.org>2008-05-29 02:29:59 +0000
committerobrien <obrien@FreeBSD.org>2008-05-29 02:29:59 +0000
commitcd5f96a9efbe194cb6e0506e727cb6d287247d69 (patch)
tree8d527b1462ba161a1cbe2165e1f019efb0c2e036 /contrib
parent2e6f1b26dbf52b25a354bb58ba058fb297b526ea (diff)
downloadFreeBSD-src-cd5f96a9efbe194cb6e0506e727cb6d287247d69.zip
FreeBSD-src-cd5f96a9efbe194cb6e0506e727cb6d287247d69.tar.gz
Import of Binutils from the FSF 2.15 branch (just post-.0 release).
These bits are taken from the FSF anoncvs repo on 23-May-2004 04:41:00 UTC.
Diffstat (limited to 'contrib')
-rw-r--r--contrib/binutils/bfd/coff-mips.c2701
-rw-r--r--contrib/binutils/bfd/cpu-mips.c127
-rw-r--r--contrib/binutils/bfd/elf32-mips.c1618
-rw-r--r--contrib/binutils/bfd/elf64-mips.c2815
-rw-r--r--contrib/binutils/bfd/elfn32-mips.c1983
-rw-r--r--contrib/binutils/bfd/elfxx-mips.c9234
-rw-r--r--contrib/binutils/bfd/elfxx-mips.h126
-rw-r--r--contrib/binutils/bfd/hosts/mipsbsd.h12
-rw-r--r--contrib/binutils/bfd/hosts/mipsmach3.h10
-rw-r--r--contrib/binutils/bfd/hosts/news-mips.h12
-rw-r--r--contrib/binutils/bfd/mipsbsd.c486
-rw-r--r--contrib/binutils/bfd/pe-mips.c1000
-rw-r--r--contrib/binutils/bfd/pei-mips.c30
-rw-r--r--contrib/binutils/gas/config/e-mipsecoff.c37
-rw-r--r--contrib/binutils/gas/config/e-mipself.c37
-rw-r--r--contrib/binutils/gas/config/itbl-mips.h47
-rw-r--r--contrib/binutils/gas/config/tc-mips.c14431
-rw-r--r--contrib/binutils/gas/config/tc-mips.h188
-rw-r--r--contrib/binutils/gas/config/te-tmips.h40
-rw-r--r--contrib/binutils/gas/doc/c-mips.texi377
-rw-r--r--contrib/binutils/gprof/mips.c114
-rw-r--r--contrib/binutils/include/coff/mips.h367
-rw-r--r--contrib/binutils/include/coff/mipspe.h66
-rw-r--r--contrib/binutils/include/opcode/mips.h914
-rw-r--r--contrib/binutils/ld/emulparams/elf32bmipn32-defs.sh58
-rw-r--r--contrib/binutils/ld/emulparams/elf32btsmip.sh9
-rw-r--r--contrib/binutils/ld/emulparams/elf32btsmipn32.sh15
-rw-r--r--contrib/binutils/ld/emulparams/elf32ltsmip.sh2
-rw-r--r--contrib/binutils/ld/emulparams/elf32ltsmipn32.sh4
-rw-r--r--contrib/binutils/ld/emulparams/elf32mipswindiss.sh27
-rw-r--r--contrib/binutils/ld/emulparams/elf64btsmip.sh16
-rw-r--r--contrib/binutils/ld/emulparams/elf64ltsmip.sh4
-rw-r--r--contrib/binutils/ld/emulparams/mipsbig.sh6
-rw-r--r--contrib/binutils/ld/emulparams/mipsbsd.sh7
-rw-r--r--contrib/binutils/ld/emulparams/mipsidt.sh11
-rw-r--r--contrib/binutils/ld/emulparams/mipsidtl.sh11
-rw-r--r--contrib/binutils/ld/emulparams/mipslit.sh6
-rw-r--r--contrib/binutils/ld/emulparams/mipslnews.sh9
-rw-r--r--contrib/binutils/ld/emulparams/mipspe.sh9
-rw-r--r--contrib/binutils/ld/emultempl/mipsecoff.em248
-rw-r--r--contrib/binutils/ld/emultempl/mipself.em177
-rw-r--r--contrib/binutils/ld/scripttempl/mips.sc72
-rw-r--r--contrib/binutils/ld/scripttempl/mipsbsd.sc30
-rw-r--r--contrib/binutils/opcodes/mips-dis.c1835
-rw-r--r--contrib/binutils/opcodes/mips-opc.c1218
-rw-r--r--contrib/binutils/opcodes/mips16-opc.c227
46 files changed, 40773 insertions, 0 deletions
diff --git a/contrib/binutils/bfd/coff-mips.c b/contrib/binutils/bfd/coff-mips.c
new file mode 100644
index 0000000..d6d8218
--- /dev/null
+++ b/contrib/binutils/bfd/coff-mips.c
@@ -0,0 +1,2701 @@
+/* BFD back-end for MIPS Extended-Coff files.
+ Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ 2000, 2001, 2002, 2003
+ Free Software Foundation, Inc.
+ Original version by Per Bothner.
+ Full support added by Ian Lance Taylor, ian@cygnus.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 "bfdlink.h"
+#include "libbfd.h"
+#include "coff/internal.h"
+#include "coff/sym.h"
+#include "coff/symconst.h"
+#include "coff/ecoff.h"
+#include "coff/mips.h"
+#include "libcoff.h"
+#include "libecoff.h"
+
+/* Prototypes for static functions. */
+
+static bfd_boolean mips_ecoff_bad_format_hook
+ PARAMS ((bfd *abfd, PTR filehdr));
+static void mips_ecoff_swap_reloc_in
+ PARAMS ((bfd *, PTR, struct internal_reloc *));
+static void mips_ecoff_swap_reloc_out
+ PARAMS ((bfd *, const struct internal_reloc *, PTR));
+static void mips_adjust_reloc_in
+ PARAMS ((bfd *, const struct internal_reloc *, arelent *));
+static void mips_adjust_reloc_out
+ PARAMS ((bfd *, const arelent *, struct internal_reloc *));
+static bfd_reloc_status_type mips_generic_reloc
+ PARAMS ((bfd *abfd, arelent *reloc, asymbol *symbol, PTR data,
+ asection *section, bfd *output_bfd, char **error));
+static bfd_reloc_status_type mips_refhi_reloc
+ PARAMS ((bfd *abfd, arelent *reloc, asymbol *symbol, PTR data,
+ asection *section, bfd *output_bfd, char **error));
+static bfd_reloc_status_type mips_reflo_reloc
+ PARAMS ((bfd *abfd, arelent *reloc, asymbol *symbol, PTR data,
+ asection *section, bfd *output_bfd, char **error));
+static bfd_reloc_status_type mips_gprel_reloc
+ PARAMS ((bfd *abfd, arelent *reloc, asymbol *symbol, PTR data,
+ asection *section, bfd *output_bfd, char **error));
+static bfd_reloc_status_type mips_relhi_reloc
+ PARAMS ((bfd *abfd, arelent *reloc, asymbol *symbol, PTR data,
+ asection *section, bfd *output_bfd, char **error));
+static bfd_reloc_status_type mips_rello_reloc
+ PARAMS ((bfd *abfd, arelent *reloc, asymbol *symbol, PTR data,
+ asection *section, bfd *output_bfd, char **error));
+static bfd_reloc_status_type mips_switch_reloc
+ PARAMS ((bfd *abfd, arelent *reloc, asymbol *symbol, PTR data,
+ asection *section, bfd *output_bfd, char **error));
+static void mips_relocate_hi
+ PARAMS ((struct internal_reloc *refhi, struct internal_reloc *reflo,
+ bfd *input_bfd, asection *input_section, bfd_byte *contents,
+ size_t adjust, bfd_vma relocation, bfd_boolean pcrel));
+static bfd_boolean mips_relocate_section
+ PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *, PTR));
+static bfd_boolean mips_read_relocs
+ PARAMS ((bfd *, asection *));
+static bfd_boolean mips_relax_section
+ PARAMS ((bfd *, asection *, struct bfd_link_info *, bfd_boolean *));
+static bfd_boolean mips_relax_pcrel16
+ PARAMS ((struct bfd_link_info *, bfd *, asection *,
+ struct ecoff_link_hash_entry *, bfd_byte *, bfd_vma));
+static reloc_howto_type *mips_bfd_reloc_type_lookup
+ PARAMS ((bfd *, bfd_reloc_code_real_type));
+
+/* ECOFF has COFF sections, but the debugging information is stored in
+ a completely different format. ECOFF targets use some of the
+ swapping routines from coffswap.h, and some of the generic COFF
+ routines in coffgen.c, but, unlike the real COFF targets, do not
+ use coffcode.h itself.
+
+ Get the generic COFF swapping routines, except for the reloc,
+ symbol, and lineno ones. Give them ECOFF names. */
+#define MIPSECOFF
+#define NO_COFF_RELOCS
+#define NO_COFF_SYMBOLS
+#define NO_COFF_LINENOS
+#define coff_swap_filehdr_in mips_ecoff_swap_filehdr_in
+#define coff_swap_filehdr_out mips_ecoff_swap_filehdr_out
+#define coff_swap_aouthdr_in mips_ecoff_swap_aouthdr_in
+#define coff_swap_aouthdr_out mips_ecoff_swap_aouthdr_out
+#define coff_swap_scnhdr_in mips_ecoff_swap_scnhdr_in
+#define coff_swap_scnhdr_out mips_ecoff_swap_scnhdr_out
+#include "coffswap.h"
+
+/* Get the ECOFF swapping routines. */
+#define ECOFF_32
+#include "ecoffswap.h"
+
+/* How to process the various relocs types. */
+
+static reloc_howto_type mips_howto_table[] =
+{
+ /* Reloc type 0 is ignored. The reloc reading code ensures that
+ this is a reference to the .abs section, which will cause
+ bfd_perform_relocation to do nothing. */
+ HOWTO (MIPS_R_IGNORE, /* type */
+ 0, /* rightshift */
+ 0, /* size (0 = byte, 1 = short, 2 = long) */
+ 8, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ 0, /* special_function */
+ "IGNORE", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A 16 bit reference to a symbol, normally from a data section. */
+ HOWTO (MIPS_R_REFHALF, /* type */
+ 0, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ mips_generic_reloc, /* special_function */
+ "REFHALF", /* name */
+ TRUE, /* partial_inplace */
+ 0xffff, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A 32 bit reference to a symbol, normally from a data section. */
+ HOWTO (MIPS_R_REFWORD, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ mips_generic_reloc, /* special_function */
+ "REFWORD", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A 26 bit absolute jump address. */
+ HOWTO (MIPS_R_JMPADDR, /* type */
+ 2, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 26, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ /* This needs complex overflow
+ detection, because the upper four
+ bits must match the PC. */
+ mips_generic_reloc, /* special_function */
+ "JMPADDR", /* name */
+ TRUE, /* partial_inplace */
+ 0x3ffffff, /* src_mask */
+ 0x3ffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* The high 16 bits of a symbol value. Handled by the function
+ mips_refhi_reloc. */
+ HOWTO (MIPS_R_REFHI, /* type */
+ 16, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ mips_refhi_reloc, /* special_function */
+ "REFHI", /* name */
+ TRUE, /* partial_inplace */
+ 0xffff, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* The low 16 bits of a symbol value. */
+ HOWTO (MIPS_R_REFLO, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ mips_reflo_reloc, /* special_function */
+ "REFLO", /* name */
+ TRUE, /* partial_inplace */
+ 0xffff, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A reference to an offset from the gp register. Handled by the
+ function mips_gprel_reloc. */
+ HOWTO (MIPS_R_GPREL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips_gprel_reloc, /* special_function */
+ "GPREL", /* name */
+ TRUE, /* partial_inplace */
+ 0xffff, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A reference to a literal using an offset from the gp register.
+ Handled by the function mips_gprel_reloc. */
+ HOWTO (MIPS_R_LITERAL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips_gprel_reloc, /* special_function */
+ "LITERAL", /* name */
+ TRUE, /* partial_inplace */
+ 0xffff, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (8),
+ EMPTY_HOWTO (9),
+ EMPTY_HOWTO (10),
+ EMPTY_HOWTO (11),
+
+ /* This reloc is a Cygnus extension used when generating position
+ independent code for embedded systems. It represents a 16 bit PC
+ relative reloc rightshifted twice as used in the MIPS branch
+ instructions. */
+ HOWTO (MIPS_R_PCREL16, /* type */
+ 2, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips_generic_reloc, /* special_function */
+ "PCREL16", /* name */
+ TRUE, /* partial_inplace */
+ 0xffff, /* src_mask */
+ 0xffff, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* This reloc is a Cygnus extension used when generating position
+ independent code for embedded systems. It represents the high 16
+ bits of a PC relative reloc. The next reloc must be
+ MIPS_R_RELLO, and the addend is formed from the addends of the
+ two instructions, just as in MIPS_R_REFHI and MIPS_R_REFLO. The
+ final value is actually PC relative to the location of the
+ MIPS_R_RELLO reloc, not the MIPS_R_RELHI reloc. */
+ HOWTO (MIPS_R_RELHI, /* type */
+ 16, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ mips_relhi_reloc, /* special_function */
+ "RELHI", /* name */
+ TRUE, /* partial_inplace */
+ 0xffff, /* src_mask */
+ 0xffff, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* This reloc is a Cygnus extension used when generating position
+ independent code for embedded systems. It represents the low 16
+ bits of a PC relative reloc. */
+ HOWTO (MIPS_R_RELLO, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ mips_rello_reloc, /* special_function */
+ "RELLO", /* name */
+ TRUE, /* partial_inplace */
+ 0xffff, /* src_mask */
+ 0xffff, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ EMPTY_HOWTO (15),
+ EMPTY_HOWTO (16),
+ EMPTY_HOWTO (17),
+ EMPTY_HOWTO (18),
+ EMPTY_HOWTO (19),
+ EMPTY_HOWTO (20),
+ EMPTY_HOWTO (21),
+
+ /* This reloc is a Cygnus extension used when generating position
+ independent code for embedded systems. It represents an entry in
+ a switch table, which is the difference between two symbols in
+ the .text section. The symndx is actually the offset from the
+ reloc address to the subtrahend. See include/coff/mips.h for
+ more details. */
+ HOWTO (MIPS_R_SWITCH, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ mips_switch_reloc, /* special_function */
+ "SWITCH", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ TRUE) /* pcrel_offset */
+};
+
+#define MIPS_HOWTO_COUNT \
+ (sizeof mips_howto_table / sizeof mips_howto_table[0])
+
+/* When the linker is doing relaxing, it may change an external PCREL16
+ reloc. This typically represents an instruction like
+ bal foo
+ We change it to
+ .set noreorder
+ bal $L1
+ lui $at,%hi(foo - $L1)
+ $L1:
+ addiu $at,%lo(foo - $L1)
+ addu $at,$at,$31
+ jalr $at
+ PCREL16_EXPANSION_ADJUSTMENT is the number of bytes this changes the
+ instruction by. */
+
+#define PCREL16_EXPANSION_ADJUSTMENT (4 * 4)
+
+/* See whether the magic number matches. */
+
+static bfd_boolean
+mips_ecoff_bad_format_hook (abfd, filehdr)
+ bfd *abfd;
+ PTR filehdr;
+{
+ struct internal_filehdr *internal_f = (struct internal_filehdr *) filehdr;
+
+ switch (internal_f->f_magic)
+ {
+ case MIPS_MAGIC_1:
+ /* I don't know what endianness this implies. */
+ return TRUE;
+
+ case MIPS_MAGIC_BIG:
+ case MIPS_MAGIC_BIG2:
+ case MIPS_MAGIC_BIG3:
+ return bfd_big_endian (abfd);
+
+ case MIPS_MAGIC_LITTLE:
+ case MIPS_MAGIC_LITTLE2:
+ case MIPS_MAGIC_LITTLE3:
+ return bfd_little_endian (abfd);
+
+ default:
+ return FALSE;
+ }
+}
+
+/* Reloc handling. MIPS ECOFF relocs are packed into 8 bytes in
+ external form. They use a bit which indicates whether the symbol
+ is external. */
+
+/* Swap a reloc in. */
+
+static void
+mips_ecoff_swap_reloc_in (abfd, ext_ptr, intern)
+ bfd *abfd;
+ PTR ext_ptr;
+ struct internal_reloc *intern;
+{
+ const RELOC *ext = (RELOC *) ext_ptr;
+
+ intern->r_vaddr = H_GET_32 (abfd, ext->r_vaddr);
+ if (bfd_header_big_endian (abfd))
+ {
+ intern->r_symndx = (((int) ext->r_bits[0]
+ << RELOC_BITS0_SYMNDX_SH_LEFT_BIG)
+ | ((int) ext->r_bits[1]
+ << RELOC_BITS1_SYMNDX_SH_LEFT_BIG)
+ | ((int) ext->r_bits[2]
+ << RELOC_BITS2_SYMNDX_SH_LEFT_BIG));
+ intern->r_type = ((ext->r_bits[3] & RELOC_BITS3_TYPE_BIG)
+ >> RELOC_BITS3_TYPE_SH_BIG);
+ intern->r_extern = (ext->r_bits[3] & RELOC_BITS3_EXTERN_BIG) != 0;
+ }
+ else
+ {
+ intern->r_symndx = (((int) ext->r_bits[0]
+ << RELOC_BITS0_SYMNDX_SH_LEFT_LITTLE)
+ | ((int) ext->r_bits[1]
+ << RELOC_BITS1_SYMNDX_SH_LEFT_LITTLE)
+ | ((int) ext->r_bits[2]
+ << RELOC_BITS2_SYMNDX_SH_LEFT_LITTLE));
+ intern->r_type = (((ext->r_bits[3] & RELOC_BITS3_TYPE_LITTLE)
+ >> RELOC_BITS3_TYPE_SH_LITTLE)
+ | ((ext->r_bits[3] & RELOC_BITS3_TYPEHI_LITTLE)
+ << RELOC_BITS3_TYPEHI_SH_LITTLE));
+ intern->r_extern = (ext->r_bits[3] & RELOC_BITS3_EXTERN_LITTLE) != 0;
+ }
+
+ /* If this is a MIPS_R_SWITCH reloc, or an internal MIPS_R_RELHI or
+ MIPS_R_RELLO reloc, r_symndx is actually the offset from the
+ reloc address to the base of the difference (see
+ include/coff/mips.h for more details). We copy symndx into the
+ r_offset field so as not to confuse ecoff_slurp_reloc_table in
+ ecoff.c. In adjust_reloc_in we then copy r_offset into the reloc
+ addend. */
+ if (intern->r_type == MIPS_R_SWITCH
+ || (! intern->r_extern
+ && (intern->r_type == MIPS_R_RELLO
+ || intern->r_type == MIPS_R_RELHI)))
+ {
+ BFD_ASSERT (! intern->r_extern);
+ intern->r_offset = intern->r_symndx;
+ if (intern->r_offset & 0x800000)
+ intern->r_offset -= 0x1000000;
+ intern->r_symndx = RELOC_SECTION_TEXT;
+ }
+}
+
+/* Swap a reloc out. */
+
+static void
+mips_ecoff_swap_reloc_out (abfd, intern, dst)
+ bfd *abfd;
+ const struct internal_reloc *intern;
+ PTR dst;
+{
+ RELOC *ext = (RELOC *) dst;
+ long r_symndx;
+
+ BFD_ASSERT (intern->r_extern
+ || (intern->r_symndx >= 0 && intern->r_symndx <= 12));
+
+ /* If this is a MIPS_R_SWITCH reloc, or an internal MIPS_R_RELLO or
+ MIPS_R_RELHI reloc, we actually want to write the contents of
+ r_offset out as the symbol index. This undoes the change made by
+ mips_ecoff_swap_reloc_in. */
+ if (intern->r_type != MIPS_R_SWITCH
+ && (intern->r_extern
+ || (intern->r_type != MIPS_R_RELHI
+ && intern->r_type != MIPS_R_RELLO)))
+ r_symndx = intern->r_symndx;
+ else
+ {
+ BFD_ASSERT (intern->r_symndx == RELOC_SECTION_TEXT);
+ r_symndx = intern->r_offset & 0xffffff;
+ }
+
+ H_PUT_32 (abfd, intern->r_vaddr, ext->r_vaddr);
+ if (bfd_header_big_endian (abfd))
+ {
+ ext->r_bits[0] = r_symndx >> RELOC_BITS0_SYMNDX_SH_LEFT_BIG;
+ ext->r_bits[1] = r_symndx >> RELOC_BITS1_SYMNDX_SH_LEFT_BIG;
+ ext->r_bits[2] = r_symndx >> RELOC_BITS2_SYMNDX_SH_LEFT_BIG;
+ ext->r_bits[3] = (((intern->r_type << RELOC_BITS3_TYPE_SH_BIG)
+ & RELOC_BITS3_TYPE_BIG)
+ | (intern->r_extern ? RELOC_BITS3_EXTERN_BIG : 0));
+ }
+ else
+ {
+ ext->r_bits[0] = r_symndx >> RELOC_BITS0_SYMNDX_SH_LEFT_LITTLE;
+ ext->r_bits[1] = r_symndx >> RELOC_BITS1_SYMNDX_SH_LEFT_LITTLE;
+ ext->r_bits[2] = r_symndx >> RELOC_BITS2_SYMNDX_SH_LEFT_LITTLE;
+ ext->r_bits[3] = (((intern->r_type << RELOC_BITS3_TYPE_SH_LITTLE)
+ & RELOC_BITS3_TYPE_LITTLE)
+ | ((intern->r_type >> RELOC_BITS3_TYPEHI_SH_LITTLE
+ & RELOC_BITS3_TYPEHI_LITTLE))
+ | (intern->r_extern ? RELOC_BITS3_EXTERN_LITTLE : 0));
+ }
+}
+
+/* Finish canonicalizing a reloc. Part of this is generic to all
+ ECOFF targets, and that part is in ecoff.c. The rest is done in
+ this backend routine. It must fill in the howto field. */
+
+static void
+mips_adjust_reloc_in (abfd, intern, rptr)
+ bfd *abfd;
+ const struct internal_reloc *intern;
+ arelent *rptr;
+{
+ if (intern->r_type > MIPS_R_SWITCH)
+ abort ();
+
+ if (! intern->r_extern
+ && (intern->r_type == MIPS_R_GPREL
+ || intern->r_type == MIPS_R_LITERAL))
+ rptr->addend += ecoff_data (abfd)->gp;
+
+ /* If the type is MIPS_R_IGNORE, make sure this is a reference to
+ the absolute section so that the reloc is ignored. */
+ if (intern->r_type == MIPS_R_IGNORE)
+ rptr->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
+
+ /* If this is a MIPS_R_SWITCH reloc, or an internal MIPS_R_RELHI or
+ MIPS_R_RELLO reloc, we want the addend field of the BFD relocto
+ hold the value which was originally in the symndx field of the
+ internal MIPS ECOFF reloc. This value was copied into
+ intern->r_offset by mips_swap_reloc_in, and here we copy it into
+ the addend field. */
+ if (intern->r_type == MIPS_R_SWITCH
+ || (! intern->r_extern
+ && (intern->r_type == MIPS_R_RELHI
+ || intern->r_type == MIPS_R_RELLO)))
+ rptr->addend = intern->r_offset;
+
+ rptr->howto = &mips_howto_table[intern->r_type];
+}
+
+/* Make any adjustments needed to a reloc before writing it out. None
+ are needed for MIPS. */
+
+static void
+mips_adjust_reloc_out (abfd, rel, intern)
+ bfd *abfd ATTRIBUTE_UNUSED;
+ const arelent *rel;
+ struct internal_reloc *intern;
+{
+ /* For a MIPS_R_SWITCH reloc, or an internal MIPS_R_RELHI or
+ MIPS_R_RELLO reloc, we must copy rel->addend into
+ intern->r_offset. This will then be written out as the symbol
+ index by mips_ecoff_swap_reloc_out. This operation parallels the
+ action of mips_adjust_reloc_in. */
+ if (intern->r_type == MIPS_R_SWITCH
+ || (! intern->r_extern
+ && (intern->r_type == MIPS_R_RELHI
+ || intern->r_type == MIPS_R_RELLO)))
+ intern->r_offset = rel->addend;
+}
+
+/* ECOFF relocs are either against external symbols, or against
+ sections. If we are producing relocatable output, and the reloc
+ is against an external symbol, and nothing has given us any
+ additional addend, the resulting reloc will also be against the
+ same symbol. In such a case, we don't want to change anything
+ about the way the reloc is handled, since it will all be done at
+ final link time. Rather than put special case code into
+ bfd_perform_relocation, all the reloc types use this howto
+ function. It just short circuits the reloc if producing
+ relocatable output against an external symbol. */
+
+static bfd_reloc_status_type
+mips_generic_reloc (abfd,
+ reloc_entry,
+ symbol,
+ data,
+ input_section,
+ output_bfd,
+ error_message)
+ bfd *abfd ATTRIBUTE_UNUSED;
+ arelent *reloc_entry;
+ asymbol *symbol;
+ PTR data ATTRIBUTE_UNUSED;
+ asection *input_section;
+ bfd *output_bfd;
+ char **error_message ATTRIBUTE_UNUSED;
+{
+ if (output_bfd != (bfd *) NULL
+ && (symbol->flags & BSF_SECTION_SYM) == 0
+ && reloc_entry->addend == 0)
+ {
+ reloc_entry->address += input_section->output_offset;
+ return bfd_reloc_ok;
+ }
+
+ return bfd_reloc_continue;
+}
+
+/* Do a REFHI relocation. This has to be done in combination with a
+ REFLO reloc, because there is a carry from the REFLO to the REFHI.
+ Here we just save the information we need; we do the actual
+ relocation when we see the REFLO. MIPS ECOFF requires that the
+ REFLO immediately follow the REFHI. As a GNU extension, we permit
+ an arbitrary number of HI relocs to be associated with a single LO
+ reloc. This extension permits gcc to output the HI and LO relocs
+ itself. */
+
+struct mips_hi
+{
+ struct mips_hi *next;
+ bfd_byte *addr;
+ bfd_vma addend;
+};
+
+/* FIXME: This should not be a static variable. */
+
+static struct mips_hi *mips_refhi_list;
+
+static bfd_reloc_status_type
+mips_refhi_reloc (abfd,
+ reloc_entry,
+ symbol,
+ data,
+ input_section,
+ output_bfd,
+ error_message)
+ bfd *abfd ATTRIBUTE_UNUSED;
+ arelent *reloc_entry;
+ asymbol *symbol;
+ PTR data;
+ asection *input_section;
+ bfd *output_bfd;
+ char **error_message ATTRIBUTE_UNUSED;
+{
+ bfd_reloc_status_type ret;
+ bfd_vma relocation;
+ struct mips_hi *n;
+
+ /* If we're relocating, and this an external symbol, we don't want
+ to change anything. */
+ if (output_bfd != (bfd *) NULL
+ && (symbol->flags & BSF_SECTION_SYM) == 0
+ && reloc_entry->addend == 0)
+ {
+ reloc_entry->address += input_section->output_offset;
+ return bfd_reloc_ok;
+ }
+
+ ret = bfd_reloc_ok;
+ if (bfd_is_und_section (symbol->section)
+ && output_bfd == (bfd *) NULL)
+ ret = bfd_reloc_undefined;
+
+ if (bfd_is_com_section (symbol->section))
+ relocation = 0;
+ else
+ relocation = symbol->value;
+
+ relocation += symbol->section->output_section->vma;
+ relocation += symbol->section->output_offset;
+ relocation += reloc_entry->addend;
+
+ if (reloc_entry->address > input_section->_cooked_size)
+ return bfd_reloc_outofrange;
+
+ /* Save the information, and let REFLO do the actual relocation. */
+ n = (struct mips_hi *) bfd_malloc ((bfd_size_type) sizeof *n);
+ if (n == NULL)
+ return bfd_reloc_outofrange;
+ n->addr = (bfd_byte *) data + reloc_entry->address;
+ n->addend = relocation;
+ n->next = mips_refhi_list;
+ mips_refhi_list = n;
+
+ if (output_bfd != (bfd *) NULL)
+ reloc_entry->address += input_section->output_offset;
+
+ return ret;
+}
+
+/* Do a REFLO relocation. This is a straightforward 16 bit inplace
+ relocation; this function exists in order to do the REFHI
+ relocation described above. */
+
+static bfd_reloc_status_type
+mips_reflo_reloc (abfd,
+ reloc_entry,
+ symbol,
+ data,
+ input_section,
+ output_bfd,
+ error_message)
+ bfd *abfd;
+ arelent *reloc_entry;
+ asymbol *symbol;
+ PTR data;
+ asection *input_section;
+ bfd *output_bfd;
+ char **error_message;
+{
+ if (mips_refhi_list != NULL)
+ {
+ struct mips_hi *l;
+
+ l = mips_refhi_list;
+ while (l != NULL)
+ {
+ unsigned long insn;
+ unsigned long val;
+ unsigned long vallo;
+ struct mips_hi *next;
+
+ /* Do the REFHI relocation. Note that we actually don't
+ need to know anything about the REFLO itself, except
+ where to find the low 16 bits of the addend needed by the
+ REFHI. */
+ insn = bfd_get_32 (abfd, l->addr);
+ vallo = (bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address)
+ & 0xffff);
+ val = ((insn & 0xffff) << 16) + vallo;
+ val += l->addend;
+
+ /* The low order 16 bits are always treated as a signed
+ value. Therefore, a negative value in the low order bits
+ requires an adjustment in the high order bits. We need
+ to make this adjustment in two ways: once for the bits we
+ took from the data, and once for the bits we are putting
+ back in to the data. */
+ if ((vallo & 0x8000) != 0)
+ val -= 0x10000;
+ if ((val & 0x8000) != 0)
+ val += 0x10000;
+
+ insn = (insn &~ (unsigned) 0xffff) | ((val >> 16) & 0xffff);
+ bfd_put_32 (abfd, (bfd_vma) insn, l->addr);
+
+ next = l->next;
+ free (l);
+ l = next;
+ }
+
+ mips_refhi_list = NULL;
+ }
+
+ /* Now do the REFLO reloc in the usual way. */
+ return mips_generic_reloc (abfd, reloc_entry, symbol, data,
+ input_section, output_bfd, error_message);
+}
+
+/* Do a GPREL relocation. This is a 16 bit value which must become
+ the offset from the gp register. */
+
+static bfd_reloc_status_type
+mips_gprel_reloc (abfd,
+ reloc_entry,
+ symbol,
+ data,
+ input_section,
+ output_bfd,
+ error_message)
+ bfd *abfd;
+ arelent *reloc_entry;
+ asymbol *symbol;
+ PTR data;
+ asection *input_section;
+ bfd *output_bfd;
+ char **error_message;
+{
+ bfd_boolean relocatable;
+ bfd_vma gp;
+ bfd_vma relocation;
+ unsigned long val;
+ unsigned long insn;
+
+ /* If we're relocating, and this is an external symbol with no
+ addend, we don't want to change anything. We will only have an
+ addend if this is a newly created reloc, not read from an ECOFF
+ file. */
+ if (output_bfd != (bfd *) NULL
+ && (symbol->flags & BSF_SECTION_SYM) == 0
+ && reloc_entry->addend == 0)
+ {
+ reloc_entry->address += input_section->output_offset;
+ return bfd_reloc_ok;
+ }
+
+ if (output_bfd != (bfd *) NULL)
+ relocatable = TRUE;
+ else
+ {
+ relocatable = FALSE;
+ output_bfd = symbol->section->output_section->owner;
+ }
+
+ if (bfd_is_und_section (symbol->section) && ! relocatable)
+ return bfd_reloc_undefined;
+
+ /* 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 ECOFF
+ target data. We don't need to adjust the symbol value for an
+ external symbol if we are producing relocatable output. */
+ gp = _bfd_get_gp_value (output_bfd);
+ if (gp == 0
+ && (! relocatable
+ || (symbol->flags & BSF_SECTION_SYM) != 0))
+ {
+ if (relocatable)
+ {
+ /* Make up a value. */
+ gp = symbol->section->output_section->vma + 0x4000;
+ _bfd_set_gp_value (output_bfd, gp);
+ }
+ else
+ {
+ unsigned int count;
+ asymbol **sym;
+ unsigned int i;
+
+ count = bfd_get_symcount (output_bfd);
+ sym = bfd_get_outsymbols (output_bfd);
+
+ if (sym == (asymbol **) NULL)
+ i = count;
+ else
+ {
+ for (i = 0; i < count; i++, sym++)
+ {
+ register const char *name;
+
+ name = bfd_asymbol_name (*sym);
+ if (*name == '_' && strcmp (name, "_gp") == 0)
+ {
+ gp = bfd_asymbol_value (*sym);
+ _bfd_set_gp_value (output_bfd, gp);
+ break;
+ }
+ }
+ }
+
+ if (i >= count)
+ {
+ /* Only get the error once. */
+ gp = 4;
+ _bfd_set_gp_value (output_bfd, gp);
+ *error_message =
+ (char *) _("GP relative relocation when _gp not defined");
+ return bfd_reloc_dangerous;
+ }
+ }
+ }
+
+ if (bfd_is_com_section (symbol->section))
+ relocation = 0;
+ else
+ relocation = symbol->value;
+
+ relocation += symbol->section->output_section->vma;
+ relocation += symbol->section->output_offset;
+
+ if (reloc_entry->address > input_section->_cooked_size)
+ return bfd_reloc_outofrange;
+
+ insn = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
+
+ /* Set val to the offset into the section or symbol. */
+ val = ((insn & 0xffff) + reloc_entry->addend) & 0xffff;
+ if (val & 0x8000)
+ val -= 0x10000;
+
+ /* Adjust val for the final section location and GP value. If we
+ are producing relocatable output, we don't want to do this for
+ an external symbol. */
+ if (! relocatable
+ || (symbol->flags & BSF_SECTION_SYM) != 0)
+ val += relocation - gp;
+
+ insn = (insn &~ (unsigned) 0xffff) | (val & 0xffff);
+ bfd_put_32 (abfd, (bfd_vma) insn, (bfd_byte *) data + reloc_entry->address);
+
+ if (relocatable)
+ reloc_entry->address += input_section->output_offset;
+
+ /* Make sure it fit in 16 bits. */
+ if ((long) val >= 0x8000 || (long) val < -0x8000)
+ return bfd_reloc_overflow;
+
+ return bfd_reloc_ok;
+}
+
+/* Do a RELHI relocation. We do this in conjunction with a RELLO
+ reloc, just as REFHI and REFLO are done together. RELHI and RELLO
+ are Cygnus extensions used when generating position independent
+ code for embedded systems. */
+
+/* FIXME: This should not be a static variable. */
+
+static struct mips_hi *mips_relhi_list;
+
+static bfd_reloc_status_type
+mips_relhi_reloc (abfd,
+ reloc_entry,
+ symbol,
+ data,
+ input_section,
+ output_bfd,
+ error_message)
+ bfd *abfd ATTRIBUTE_UNUSED;
+ arelent *reloc_entry;
+ asymbol *symbol;
+ PTR data;
+ asection *input_section;
+ bfd *output_bfd;
+ char **error_message ATTRIBUTE_UNUSED;
+{
+ bfd_reloc_status_type ret;
+ bfd_vma relocation;
+ struct mips_hi *n;
+
+ /* If this is a reloc against a section symbol, then it is correct
+ in the object file. The only time we want to change this case is
+ when we are relaxing, and that is handled entirely by
+ mips_relocate_section and never calls this function. */
+ if ((symbol->flags & BSF_SECTION_SYM) != 0)
+ {
+ if (output_bfd != (bfd *) NULL)
+ reloc_entry->address += input_section->output_offset;
+ return bfd_reloc_ok;
+ }
+
+ /* This is an external symbol. If we're relocating, we don't want
+ to change anything. */
+ if (output_bfd != (bfd *) NULL)
+ {
+ reloc_entry->address += input_section->output_offset;
+ return bfd_reloc_ok;
+ }
+
+ ret = bfd_reloc_ok;
+ if (bfd_is_und_section (symbol->section)
+ && output_bfd == (bfd *) NULL)
+ ret = bfd_reloc_undefined;
+
+ if (bfd_is_com_section (symbol->section))
+ relocation = 0;
+ else
+ relocation = symbol->value;
+
+ relocation += symbol->section->output_section->vma;
+ relocation += symbol->section->output_offset;
+ relocation += reloc_entry->addend;
+
+ if (reloc_entry->address > input_section->_cooked_size)
+ return bfd_reloc_outofrange;
+
+ /* Save the information, and let RELLO do the actual relocation. */
+ n = (struct mips_hi *) bfd_malloc ((bfd_size_type) sizeof *n);
+ if (n == NULL)
+ return bfd_reloc_outofrange;
+ n->addr = (bfd_byte *) data + reloc_entry->address;
+ n->addend = relocation;
+ n->next = mips_relhi_list;
+ mips_relhi_list = n;
+
+ if (output_bfd != (bfd *) NULL)
+ reloc_entry->address += input_section->output_offset;
+
+ return ret;
+}
+
+/* Do a RELLO relocation. This is a straightforward 16 bit PC
+ relative relocation; this function exists in order to do the RELHI
+ relocation described above. */
+
+static bfd_reloc_status_type
+mips_rello_reloc (abfd,
+ reloc_entry,
+ symbol,
+ data,
+ input_section,
+ output_bfd,
+ error_message)
+ bfd *abfd;
+ arelent *reloc_entry;
+ asymbol *symbol;
+ PTR data;
+ asection *input_section;
+ bfd *output_bfd;
+ char **error_message;
+{
+ if (mips_relhi_list != NULL)
+ {
+ struct mips_hi *l;
+
+ l = mips_relhi_list;
+ while (l != NULL)
+ {
+ unsigned long insn;
+ unsigned long val;
+ unsigned long vallo;
+ struct mips_hi *next;
+
+ /* Do the RELHI relocation. Note that we actually don't
+ need to know anything about the RELLO itself, except
+ where to find the low 16 bits of the addend needed by the
+ RELHI. */
+ insn = bfd_get_32 (abfd, l->addr);
+ vallo = (bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address)
+ & 0xffff);
+ val = ((insn & 0xffff) << 16) + vallo;
+ val += l->addend;
+
+ /* If the symbol is defined, make val PC relative. If the
+ symbol is not defined we don't want to do this, because
+ we don't want the value in the object file to incorporate
+ the address of the reloc. */
+ if (! bfd_is_und_section (bfd_get_section (symbol))
+ && ! bfd_is_com_section (bfd_get_section (symbol)))
+ val -= (input_section->output_section->vma
+ + input_section->output_offset
+ + reloc_entry->address);
+
+ /* The low order 16 bits are always treated as a signed
+ value. Therefore, a negative value in the low order bits
+ requires an adjustment in the high order bits. We need
+ to make this adjustment in two ways: once for the bits we
+ took from the data, and once for the bits we are putting
+ back in to the data. */
+ if ((vallo & 0x8000) != 0)
+ val -= 0x10000;
+ if ((val & 0x8000) != 0)
+ val += 0x10000;
+
+ insn = (insn &~ (unsigned) 0xffff) | ((val >> 16) & 0xffff);
+ bfd_put_32 (abfd, (bfd_vma) insn, l->addr);
+
+ next = l->next;
+ free (l);
+ l = next;
+ }
+
+ mips_relhi_list = NULL;
+ }
+
+ /* If this is a reloc against a section symbol, then it is correct
+ in the object file. The only time we want to change this case is
+ when we are relaxing, and that is handled entirely by
+ mips_relocate_section and never calls this function. */
+ if ((symbol->flags & BSF_SECTION_SYM) != 0)
+ {
+ if (output_bfd != (bfd *) NULL)
+ reloc_entry->address += input_section->output_offset;
+ return bfd_reloc_ok;
+ }
+
+ /* bfd_perform_relocation does not handle pcrel_offset relocations
+ correctly when generating a relocatable file, so handle them
+ directly here. */
+ if (output_bfd != (bfd *) NULL)
+ {
+ reloc_entry->address += input_section->output_offset;
+ return bfd_reloc_ok;
+ }
+
+ /* Now do the RELLO reloc in the usual way. */
+ return mips_generic_reloc (abfd, reloc_entry, symbol, data,
+ input_section, output_bfd, error_message);
+}
+
+/* This is the special function for the MIPS_R_SWITCH reloc. This
+ special reloc is normally correct in the object file, and only
+ requires special handling when relaxing. We don't want
+ bfd_perform_relocation to tamper with it at all. */
+
+static bfd_reloc_status_type
+mips_switch_reloc (abfd,
+ reloc_entry,
+ symbol,
+ data,
+ input_section,
+ output_bfd,
+ error_message)
+ bfd *abfd ATTRIBUTE_UNUSED;
+ arelent *reloc_entry ATTRIBUTE_UNUSED;
+ asymbol *symbol ATTRIBUTE_UNUSED;
+ PTR data ATTRIBUTE_UNUSED;
+ asection *input_section ATTRIBUTE_UNUSED;
+ bfd *output_bfd ATTRIBUTE_UNUSED;
+ char **error_message ATTRIBUTE_UNUSED;
+{
+ return bfd_reloc_ok;
+}
+
+/* Get the howto structure for a generic reloc type. */
+
+static reloc_howto_type *
+mips_bfd_reloc_type_lookup (abfd, code)
+ bfd *abfd ATTRIBUTE_UNUSED;
+ bfd_reloc_code_real_type code;
+{
+ int mips_type;
+
+ switch (code)
+ {
+ case BFD_RELOC_16:
+ mips_type = MIPS_R_REFHALF;
+ break;
+ case BFD_RELOC_32:
+ case BFD_RELOC_CTOR:
+ mips_type = MIPS_R_REFWORD;
+ break;
+ case BFD_RELOC_MIPS_JMP:
+ mips_type = MIPS_R_JMPADDR;
+ break;
+ case BFD_RELOC_HI16_S:
+ mips_type = MIPS_R_REFHI;
+ break;
+ case BFD_RELOC_LO16:
+ mips_type = MIPS_R_REFLO;
+ break;
+ case BFD_RELOC_GPREL16:
+ mips_type = MIPS_R_GPREL;
+ break;
+ case BFD_RELOC_MIPS_LITERAL:
+ mips_type = MIPS_R_LITERAL;
+ break;
+ case BFD_RELOC_16_PCREL_S2:
+ mips_type = MIPS_R_PCREL16;
+ break;
+ case BFD_RELOC_PCREL_HI16_S:
+ mips_type = MIPS_R_RELHI;
+ break;
+ case BFD_RELOC_PCREL_LO16:
+ mips_type = MIPS_R_RELLO;
+ break;
+ case BFD_RELOC_GPREL32:
+ mips_type = MIPS_R_SWITCH;
+ break;
+ default:
+ return (reloc_howto_type *) NULL;
+ }
+
+ return &mips_howto_table[mips_type];
+}
+
+/* A helper routine for mips_relocate_section which handles the REFHI
+ and RELHI relocations. The REFHI relocation must be followed by a
+ REFLO relocation (and RELHI by a RELLO), and the addend used is
+ formed from the addends of both instructions. */
+
+static void
+mips_relocate_hi (refhi, reflo, input_bfd, input_section, contents, adjust,
+ relocation, pcrel)
+ struct internal_reloc *refhi;
+ struct internal_reloc *reflo;
+ bfd *input_bfd;
+ asection *input_section;
+ bfd_byte *contents;
+ size_t adjust;
+ bfd_vma relocation;
+ bfd_boolean pcrel;
+{
+ unsigned long insn;
+ unsigned long val;
+ unsigned long vallo;
+
+ if (refhi == NULL)
+ return;
+
+ insn = bfd_get_32 (input_bfd,
+ contents + adjust + refhi->r_vaddr - input_section->vma);
+ if (reflo == NULL)
+ vallo = 0;
+ else
+ vallo = (bfd_get_32 (input_bfd,
+ contents + adjust + reflo->r_vaddr - input_section->vma)
+ & 0xffff);
+
+ val = ((insn & 0xffff) << 16) + vallo;
+ val += relocation;
+
+ /* The low order 16 bits are always treated as a signed value.
+ Therefore, a negative value in the low order bits requires an
+ adjustment in the high order bits. We need to make this
+ adjustment in two ways: once for the bits we took from the data,
+ and once for the bits we are putting back in to the data. */
+ if ((vallo & 0x8000) != 0)
+ val -= 0x10000;
+
+ if (pcrel)
+ val -= (input_section->output_section->vma
+ + input_section->output_offset
+ + (reflo->r_vaddr - input_section->vma + adjust));
+
+ if ((val & 0x8000) != 0)
+ val += 0x10000;
+
+ insn = (insn &~ (unsigned) 0xffff) | ((val >> 16) & 0xffff);
+ bfd_put_32 (input_bfd, (bfd_vma) insn,
+ contents + adjust + refhi->r_vaddr - input_section->vma);
+}
+
+/* Relocate a section while linking a MIPS ECOFF file. */
+
+static bfd_boolean
+mips_relocate_section (output_bfd, info, input_bfd, input_section,
+ contents, external_relocs)
+ bfd *output_bfd;
+ struct bfd_link_info *info;
+ bfd *input_bfd;
+ asection *input_section;
+ bfd_byte *contents;
+ PTR external_relocs;
+{
+ asection **symndx_to_section;
+ struct ecoff_link_hash_entry **sym_hashes;
+ bfd_vma gp;
+ bfd_boolean gp_undefined;
+ size_t adjust;
+ long *offsets;
+ struct external_reloc *ext_rel;
+ struct external_reloc *ext_rel_end;
+ unsigned int i;
+ bfd_boolean got_lo;
+ struct internal_reloc lo_int_rel;
+ bfd_size_type amt;
+
+ BFD_ASSERT (input_bfd->xvec->byteorder
+ == output_bfd->xvec->byteorder);
+
+ /* We keep a table mapping the symndx found in an internal reloc to
+ the appropriate section. This is faster than looking up the
+ section by name each time. */
+ symndx_to_section = ecoff_data (input_bfd)->symndx_to_section;
+ if (symndx_to_section == (asection **) NULL)
+ {
+ amt = NUM_RELOC_SECTIONS * sizeof (asection *);
+ symndx_to_section = (asection **) bfd_alloc (input_bfd, amt);
+ if (!symndx_to_section)
+ return FALSE;
+
+ symndx_to_section[RELOC_SECTION_NONE] = NULL;
+ symndx_to_section[RELOC_SECTION_TEXT] =
+ bfd_get_section_by_name (input_bfd, ".text");
+ symndx_to_section[RELOC_SECTION_RDATA] =
+ bfd_get_section_by_name (input_bfd, ".rdata");
+ symndx_to_section[RELOC_SECTION_DATA] =
+ bfd_get_section_by_name (input_bfd, ".data");
+ symndx_to_section[RELOC_SECTION_SDATA] =
+ bfd_get_section_by_name (input_bfd, ".sdata");
+ symndx_to_section[RELOC_SECTION_SBSS] =
+ bfd_get_section_by_name (input_bfd, ".sbss");
+ symndx_to_section[RELOC_SECTION_BSS] =
+ bfd_get_section_by_name (input_bfd, ".bss");
+ symndx_to_section[RELOC_SECTION_INIT] =
+ bfd_get_section_by_name (input_bfd, ".init");
+ symndx_to_section[RELOC_SECTION_LIT8] =
+ bfd_get_section_by_name (input_bfd, ".lit8");
+ symndx_to_section[RELOC_SECTION_LIT4] =
+ bfd_get_section_by_name (input_bfd, ".lit4");
+ symndx_to_section[RELOC_SECTION_XDATA] = NULL;
+ symndx_to_section[RELOC_SECTION_PDATA] = NULL;
+ symndx_to_section[RELOC_SECTION_FINI] =
+ bfd_get_section_by_name (input_bfd, ".fini");
+ symndx_to_section[RELOC_SECTION_LITA] = NULL;
+ symndx_to_section[RELOC_SECTION_ABS] = NULL;
+
+ ecoff_data (input_bfd)->symndx_to_section = symndx_to_section;
+ }
+
+ sym_hashes = ecoff_data (input_bfd)->sym_hashes;
+
+ gp = _bfd_get_gp_value (output_bfd);
+ if (gp == 0)
+ gp_undefined = TRUE;
+ else
+ gp_undefined = FALSE;
+
+ got_lo = FALSE;
+
+ adjust = 0;
+
+ if (ecoff_section_data (input_bfd, input_section) == NULL)
+ offsets = NULL;
+ else
+ offsets = ecoff_section_data (input_bfd, input_section)->offsets;
+
+ ext_rel = (struct external_reloc *) external_relocs;
+ ext_rel_end = ext_rel + input_section->reloc_count;
+ for (i = 0; ext_rel < ext_rel_end; ext_rel++, i++)
+ {
+ struct internal_reloc int_rel;
+ bfd_boolean use_lo = FALSE;
+ bfd_vma addend;
+ reloc_howto_type *howto;
+ struct ecoff_link_hash_entry *h = NULL;
+ asection *s = NULL;
+ bfd_vma relocation;
+ bfd_reloc_status_type r;
+
+ if (! got_lo)
+ mips_ecoff_swap_reloc_in (input_bfd, (PTR) ext_rel, &int_rel);
+ else
+ {
+ int_rel = lo_int_rel;
+ got_lo = FALSE;
+ }
+
+ BFD_ASSERT (int_rel.r_type
+ < sizeof mips_howto_table / sizeof mips_howto_table[0]);
+
+ /* The REFHI and RELHI relocs requires special handling. they
+ must be followed by a REFLO or RELLO reloc, respectively, and
+ the addend is formed from both relocs. */
+ if (int_rel.r_type == MIPS_R_REFHI
+ || int_rel.r_type == MIPS_R_RELHI)
+ {
+ struct external_reloc *lo_ext_rel;
+
+ /* As a GNU extension, permit an arbitrary number of REFHI
+ or RELHI relocs before the REFLO or RELLO reloc. This
+ permits gcc to emit the HI and LO relocs itself. */
+ for (lo_ext_rel = ext_rel + 1;
+ lo_ext_rel < ext_rel_end;
+ lo_ext_rel++)
+ {
+ mips_ecoff_swap_reloc_in (input_bfd, (PTR) lo_ext_rel,
+ &lo_int_rel);
+ if (lo_int_rel.r_type != int_rel.r_type)
+ break;
+ }
+
+ if (lo_ext_rel < ext_rel_end
+ && (lo_int_rel.r_type
+ == (int_rel.r_type == MIPS_R_REFHI
+ ? MIPS_R_REFLO
+ : MIPS_R_RELLO))
+ && int_rel.r_extern == lo_int_rel.r_extern
+ && int_rel.r_symndx == lo_int_rel.r_symndx)
+ {
+ use_lo = TRUE;
+ if (lo_ext_rel == ext_rel + 1)
+ got_lo = TRUE;
+ }
+ }
+
+ howto = &mips_howto_table[int_rel.r_type];
+
+ /* The SWITCH reloc must be handled specially. This reloc is
+ marks the location of a difference between two portions of an
+ object file. The symbol index does not reference a symbol,
+ but is actually the offset from the reloc to the subtrahend
+ of the difference. This reloc is correct in the object file,
+ and needs no further adjustment, unless we are relaxing. If
+ we are relaxing, we may have to add in an offset. Since no
+ symbols are involved in this reloc, we handle it completely
+ here. */
+ if (int_rel.r_type == MIPS_R_SWITCH)
+ {
+ if (offsets != NULL
+ && offsets[i] != 0)
+ {
+ r = _bfd_relocate_contents (howto, input_bfd,
+ (bfd_vma) offsets[i],
+ (contents
+ + adjust
+ + int_rel.r_vaddr
+ - input_section->vma));
+ BFD_ASSERT (r == bfd_reloc_ok);
+ }
+
+ continue;
+ }
+
+ if (int_rel.r_extern)
+ {
+ h = sym_hashes[int_rel.r_symndx];
+ /* If h is NULL, that means that there is a reloc against an
+ external symbol which we thought was just a debugging
+ symbol. This should not happen. */
+ if (h == (struct ecoff_link_hash_entry *) NULL)
+ abort ();
+ }
+ else
+ {
+ if (int_rel.r_symndx < 0 || int_rel.r_symndx >= NUM_RELOC_SECTIONS)
+ s = NULL;
+ else
+ s = symndx_to_section[int_rel.r_symndx];
+
+ if (s == (asection *) NULL)
+ abort ();
+ }
+
+ /* The GPREL reloc uses an addend: the difference in the GP
+ values. */
+ if (int_rel.r_type != MIPS_R_GPREL
+ && int_rel.r_type != MIPS_R_LITERAL)
+ addend = 0;
+ else
+ {
+ if (gp_undefined)
+ {
+ if (! ((*info->callbacks->reloc_dangerous)
+ (info, _("GP relative relocation used when GP not defined"),
+ input_bfd, input_section,
+ int_rel.r_vaddr - input_section->vma)))
+ return FALSE;
+ /* Only give the error once per link. */
+ gp = 4;
+ _bfd_set_gp_value (output_bfd, gp);
+ gp_undefined = FALSE;
+ }
+ if (! int_rel.r_extern)
+ {
+ /* This is a relocation against a section. The current
+ addend in the instruction is the difference between
+ INPUT_SECTION->vma and the GP value of INPUT_BFD. We
+ must change this to be the difference between the
+ final definition (which will end up in RELOCATION)
+ and the GP value of OUTPUT_BFD (which is in GP). */
+ addend = ecoff_data (input_bfd)->gp - gp;
+ }
+ else if (! info->relocatable
+ || h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ {
+ /* This is a relocation against a defined symbol. The
+ current addend in the instruction is simply the
+ desired offset into the symbol (normally zero). We
+ are going to change this into a relocation against a
+ defined symbol, so we want the instruction to hold
+ the difference between the final definition of the
+ symbol (which will end up in RELOCATION) and the GP
+ value of OUTPUT_BFD (which is in GP). */
+ addend = - gp;
+ }
+ else
+ {
+ /* This is a relocation against an undefined or common
+ symbol. The current addend in the instruction is
+ simply the desired offset into the symbol (normally
+ zero). We are generating relocatable output, and we
+ aren't going to define this symbol, so we just leave
+ the instruction alone. */
+ addend = 0;
+ }
+ }
+
+ /* If we are relaxing, mips_relax_section may have set
+ offsets[i] to some value. A value of 1 means we must expand
+ a PC relative branch into a multi-instruction of sequence,
+ and any other value is an addend. */
+ if (offsets != NULL
+ && offsets[i] != 0)
+ {
+ BFD_ASSERT (! info->relocatable);
+ BFD_ASSERT (int_rel.r_type == MIPS_R_PCREL16
+ || int_rel.r_type == MIPS_R_RELHI
+ || int_rel.r_type == MIPS_R_RELLO);
+ if (offsets[i] != 1)
+ addend += offsets[i];
+ else
+ {
+ bfd_byte *here;
+
+ BFD_ASSERT (int_rel.r_extern
+ && int_rel.r_type == MIPS_R_PCREL16);
+
+ /* Move the rest of the instructions up. */
+ here = (contents
+ + adjust
+ + int_rel.r_vaddr
+ - input_section->vma);
+ memmove (here + PCREL16_EXPANSION_ADJUSTMENT, here,
+ (size_t) (input_section->_raw_size
+ - (int_rel.r_vaddr - input_section->vma)));
+
+ /* Generate the new instructions. */
+ if (! mips_relax_pcrel16 (info, input_bfd, input_section,
+ h, here,
+ (input_section->output_section->vma
+ + input_section->output_offset
+ + (int_rel.r_vaddr
+ - input_section->vma)
+ + adjust)))
+ return FALSE;
+
+ /* We must adjust everything else up a notch. */
+ adjust += PCREL16_EXPANSION_ADJUSTMENT;
+
+ /* mips_relax_pcrel16 handles all the details of this
+ relocation. */
+ continue;
+ }
+ }
+
+ /* If we are relaxing, and this is a reloc against the .text
+ segment, we may need to adjust it if some branches have been
+ expanded. The reloc types which are likely to occur in the
+ .text section are handled efficiently by mips_relax_section,
+ and thus do not need to be handled here. */
+ if (ecoff_data (input_bfd)->debug_info.adjust != NULL
+ && ! int_rel.r_extern
+ && int_rel.r_symndx == RELOC_SECTION_TEXT
+ && (strcmp (bfd_get_section_name (input_bfd, input_section),
+ ".text") != 0
+ || (int_rel.r_type != MIPS_R_PCREL16
+ && int_rel.r_type != MIPS_R_SWITCH
+ && int_rel.r_type != MIPS_R_RELHI
+ && int_rel.r_type != MIPS_R_RELLO)))
+ {
+ bfd_vma adr;
+ struct ecoff_value_adjust *a;
+
+ /* We need to get the addend so that we know whether we need
+ to adjust the address. */
+ BFD_ASSERT (int_rel.r_type == MIPS_R_REFWORD);
+
+ adr = bfd_get_32 (input_bfd,
+ (contents
+ + adjust
+ + int_rel.r_vaddr
+ - input_section->vma));
+
+ for (a = ecoff_data (input_bfd)->debug_info.adjust;
+ a != (struct ecoff_value_adjust *) NULL;
+ a = a->next)
+ {
+ if (adr >= a->start && adr < a->end)
+ addend += a->adjust;
+ }
+ }
+
+ if (info->relocatable)
+ {
+ /* We are generating relocatable output, and must convert
+ the existing reloc. */
+ if (int_rel.r_extern)
+ {
+ if ((h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ && ! bfd_is_abs_section (h->root.u.def.section))
+ {
+ const char *name;
+
+ /* This symbol is defined in the output. Convert
+ the reloc from being against the symbol to being
+ against the section. */
+
+ /* Clear the r_extern bit. */
+ int_rel.r_extern = 0;
+
+ /* Compute a new r_symndx value. */
+ s = h->root.u.def.section;
+ name = bfd_get_section_name (output_bfd,
+ s->output_section);
+
+ int_rel.r_symndx = -1;
+ switch (name[1])
+ {
+ case 'b':
+ if (strcmp (name, ".bss") == 0)
+ int_rel.r_symndx = RELOC_SECTION_BSS;
+ break;
+ case 'd':
+ if (strcmp (name, ".data") == 0)
+ int_rel.r_symndx = RELOC_SECTION_DATA;
+ break;
+ case 'f':
+ if (strcmp (name, ".fini") == 0)
+ int_rel.r_symndx = RELOC_SECTION_FINI;
+ break;
+ case 'i':
+ if (strcmp (name, ".init") == 0)
+ int_rel.r_symndx = RELOC_SECTION_INIT;
+ break;
+ case 'l':
+ if (strcmp (name, ".lit8") == 0)
+ int_rel.r_symndx = RELOC_SECTION_LIT8;
+ else if (strcmp (name, ".lit4") == 0)
+ int_rel.r_symndx = RELOC_SECTION_LIT4;
+ break;
+ case 'r':
+ if (strcmp (name, ".rdata") == 0)
+ int_rel.r_symndx = RELOC_SECTION_RDATA;
+ break;
+ case 's':
+ if (strcmp (name, ".sdata") == 0)
+ int_rel.r_symndx = RELOC_SECTION_SDATA;
+ else if (strcmp (name, ".sbss") == 0)
+ int_rel.r_symndx = RELOC_SECTION_SBSS;
+ break;
+ case 't':
+ if (strcmp (name, ".text") == 0)
+ int_rel.r_symndx = RELOC_SECTION_TEXT;
+ break;
+ }
+
+ if (int_rel.r_symndx == -1)
+ abort ();
+
+ /* Add the section VMA and the symbol value. */
+ relocation = (h->root.u.def.value
+ + s->output_section->vma
+ + s->output_offset);
+
+ /* For a PC relative relocation, the object file
+ currently holds just the addend. We must adjust
+ by the address to get the right value. */
+ if (howto->pc_relative)
+ {
+ relocation -= int_rel.r_vaddr - input_section->vma;
+
+ /* If we are converting a RELHI or RELLO reloc
+ from being against an external symbol to
+ being against a section, we must put a
+ special value into the r_offset field. This
+ value is the old addend. The r_offset for
+ both the RELHI and RELLO relocs are the same,
+ and we set both when we see RELHI. */
+ if (int_rel.r_type == MIPS_R_RELHI)
+ {
+ long addhi, addlo;
+
+ addhi = bfd_get_32 (input_bfd,
+ (contents
+ + adjust
+ + int_rel.r_vaddr
+ - input_section->vma));
+ addhi &= 0xffff;
+ if (addhi & 0x8000)
+ addhi -= 0x10000;
+ addhi <<= 16;
+
+ if (! use_lo)
+ addlo = 0;
+ else
+ {
+ addlo = bfd_get_32 (input_bfd,
+ (contents
+ + adjust
+ + lo_int_rel.r_vaddr
+ - input_section->vma));
+ addlo &= 0xffff;
+ if (addlo & 0x8000)
+ addlo -= 0x10000;
+
+ lo_int_rel.r_offset = addhi + addlo;
+ }
+
+ int_rel.r_offset = addhi + addlo;
+ }
+ }
+
+ h = NULL;
+ }
+ else
+ {
+ /* Change the symndx value to the right one for the
+ output BFD. */
+ int_rel.r_symndx = h->indx;
+ if (int_rel.r_symndx == -1)
+ {
+ /* This symbol is not being written out. */
+ if (! ((*info->callbacks->unattached_reloc)
+ (info, h->root.root.string, input_bfd,
+ input_section,
+ int_rel.r_vaddr - input_section->vma)))
+ return FALSE;
+ int_rel.r_symndx = 0;
+ }
+ relocation = 0;
+ }
+ }
+ else
+ {
+ /* This is a relocation against a section. Adjust the
+ value by the amount the section moved. */
+ relocation = (s->output_section->vma
+ + s->output_offset
+ - s->vma);
+ }
+
+ relocation += addend;
+ addend = 0;
+
+ /* Adjust a PC relative relocation by removing the reference
+ to the original address in the section and including the
+ reference to the new address. However, external RELHI
+ and RELLO relocs are PC relative, but don't include any
+ reference to the address. The addend is merely an
+ addend. */
+ if (howto->pc_relative
+ && (! int_rel.r_extern
+ || (int_rel.r_type != MIPS_R_RELHI
+ && int_rel.r_type != MIPS_R_RELLO)))
+ relocation -= (input_section->output_section->vma
+ + input_section->output_offset
+ - input_section->vma);
+
+ /* Adjust the contents. */
+ if (relocation == 0)
+ r = bfd_reloc_ok;
+ else
+ {
+ if (int_rel.r_type != MIPS_R_REFHI
+ && int_rel.r_type != MIPS_R_RELHI)
+ r = _bfd_relocate_contents (howto, input_bfd, relocation,
+ (contents
+ + adjust
+ + int_rel.r_vaddr
+ - input_section->vma));
+ else
+ {
+ mips_relocate_hi (&int_rel,
+ use_lo ? &lo_int_rel : NULL,
+ input_bfd, input_section, contents,
+ adjust, relocation,
+ int_rel.r_type == MIPS_R_RELHI);
+ r = bfd_reloc_ok;
+ }
+ }
+
+ /* Adjust the reloc address. */
+ int_rel.r_vaddr += (input_section->output_section->vma
+ + input_section->output_offset
+ - input_section->vma);
+
+ /* Save the changed reloc information. */
+ mips_ecoff_swap_reloc_out (input_bfd, &int_rel, (PTR) ext_rel);
+ }
+ else
+ {
+ /* We are producing a final executable. */
+ if (int_rel.r_extern)
+ {
+ /* This is a reloc against a symbol. */
+ if (h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ {
+ asection *hsec;
+
+ hsec = h->root.u.def.section;
+ relocation = (h->root.u.def.value
+ + hsec->output_section->vma
+ + hsec->output_offset);
+ }
+ else
+ {
+ if (! ((*info->callbacks->undefined_symbol)
+ (info, h->root.root.string, input_bfd,
+ input_section,
+ int_rel.r_vaddr - input_section->vma, TRUE)))
+ return FALSE;
+ relocation = 0;
+ }
+ }
+ else
+ {
+ /* This is a reloc against a section. */
+ relocation = (s->output_section->vma
+ + s->output_offset
+ - s->vma);
+
+ /* A PC relative reloc is already correct in the object
+ file. Make it look like a pcrel_offset relocation by
+ adding in the start address. */
+ if (howto->pc_relative)
+ {
+ if (int_rel.r_type != MIPS_R_RELHI || ! use_lo)
+ relocation += int_rel.r_vaddr + adjust;
+ else
+ relocation += lo_int_rel.r_vaddr + adjust;
+ }
+ }
+
+ if (int_rel.r_type != MIPS_R_REFHI
+ && int_rel.r_type != MIPS_R_RELHI)
+ r = _bfd_final_link_relocate (howto,
+ input_bfd,
+ input_section,
+ contents,
+ (int_rel.r_vaddr
+ - input_section->vma
+ + adjust),
+ relocation,
+ addend);
+ else
+ {
+ mips_relocate_hi (&int_rel,
+ use_lo ? &lo_int_rel : NULL,
+ input_bfd, input_section, contents, adjust,
+ relocation,
+ int_rel.r_type == MIPS_R_RELHI);
+ r = bfd_reloc_ok;
+ }
+ }
+
+ /* MIPS_R_JMPADDR requires peculiar overflow detection. The
+ instruction provides a 28 bit address (the two lower bits are
+ implicit zeroes) which is combined with the upper four bits
+ of the instruction address. */
+ if (r == bfd_reloc_ok
+ && int_rel.r_type == MIPS_R_JMPADDR
+ && (((relocation
+ + addend
+ + (int_rel.r_extern ? 0 : s->vma))
+ & 0xf0000000)
+ != ((input_section->output_section->vma
+ + input_section->output_offset
+ + (int_rel.r_vaddr - input_section->vma)
+ + adjust)
+ & 0xf0000000)))
+ r = bfd_reloc_overflow;
+
+ if (r != bfd_reloc_ok)
+ {
+ switch (r)
+ {
+ default:
+ case bfd_reloc_outofrange:
+ abort ();
+ case bfd_reloc_overflow:
+ {
+ const char *name;
+
+ if (int_rel.r_extern)
+ name = h->root.root.string;
+ else
+ name = bfd_section_name (input_bfd, s);
+ if (! ((*info->callbacks->reloc_overflow)
+ (info, name, howto->name, (bfd_vma) 0,
+ input_bfd, input_section,
+ int_rel.r_vaddr - input_section->vma)))
+ return FALSE;
+ }
+ break;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+/* Read in the relocs for a section. */
+
+static bfd_boolean
+mips_read_relocs (abfd, sec)
+ bfd *abfd;
+ asection *sec;
+{
+ struct ecoff_section_tdata *section_tdata;
+ bfd_size_type amt;
+
+ section_tdata = ecoff_section_data (abfd, sec);
+ if (section_tdata == (struct ecoff_section_tdata *) NULL)
+ {
+ amt = sizeof (struct ecoff_section_tdata);
+ sec->used_by_bfd = (PTR) bfd_alloc (abfd, amt);
+ if (sec->used_by_bfd == NULL)
+ return FALSE;
+
+ section_tdata = ecoff_section_data (abfd, sec);
+ section_tdata->external_relocs = NULL;
+ section_tdata->contents = NULL;
+ section_tdata->offsets = NULL;
+ }
+
+ if (section_tdata->external_relocs == NULL)
+ {
+ amt = ecoff_backend (abfd)->external_reloc_size;
+ amt *= sec->reloc_count;
+ section_tdata->external_relocs = (PTR) bfd_alloc (abfd, amt);
+ if (section_tdata->external_relocs == NULL && amt != 0)
+ return FALSE;
+
+ if (bfd_seek (abfd, sec->rel_filepos, SEEK_SET) != 0
+ || bfd_bread (section_tdata->external_relocs, amt, abfd) != amt)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Relax a section when linking a MIPS ECOFF file. This is used for
+ embedded PIC code, which always uses PC relative branches which
+ only have an 18 bit range on MIPS. If a branch is not in range, we
+ generate a long instruction sequence to compensate. Each time we
+ find a branch to expand, we have to check all the others again to
+ make sure they are still in range. This is slow, but it only has
+ to be done when -relax is passed to the linker.
+
+ This routine figures out which branches need to expand; the actual
+ expansion is done in mips_relocate_section when the section
+ contents are relocated. The information is stored in the offsets
+ field of the ecoff_section_tdata structure. An offset of 1 means
+ that the branch must be expanded into a multi-instruction PC
+ relative branch (such an offset will only occur for a PC relative
+ branch to an external symbol). Any other offset must be a multiple
+ of four, and is the amount to change the branch by (such an offset
+ will only occur for a PC relative branch within the same section).
+
+ We do not modify the section relocs or contents themselves so that
+ if memory usage becomes an issue we can discard them and read them
+ again. The only information we must save in memory between this
+ routine and the mips_relocate_section routine is the table of
+ offsets. */
+
+static bfd_boolean
+mips_relax_section (abfd, sec, info, again)
+ bfd *abfd;
+ asection *sec;
+ struct bfd_link_info *info;
+ bfd_boolean *again;
+{
+ struct ecoff_section_tdata *section_tdata;
+ bfd_byte *contents = NULL;
+ long *offsets;
+ struct external_reloc *ext_rel;
+ struct external_reloc *ext_rel_end;
+ unsigned int i;
+
+ /* Assume we are not going to need another pass. */
+ *again = FALSE;
+
+ /* If we are not generating an ECOFF file, this is much too
+ confusing to deal with. */
+ if (info->hash->creator->flavour != bfd_get_flavour (abfd))
+ return TRUE;
+
+ /* If there are no relocs, there is nothing to do. */
+ if (sec->reloc_count == 0)
+ return TRUE;
+
+ /* We are only interested in PC relative relocs, and why would there
+ ever be one from anything but the .text section? */
+ if (strcmp (bfd_get_section_name (abfd, sec), ".text") != 0)
+ return TRUE;
+
+ /* Read in the relocs, if we haven't already got them. */
+ section_tdata = ecoff_section_data (abfd, sec);
+ if (section_tdata == (struct ecoff_section_tdata *) NULL
+ || section_tdata->external_relocs == NULL)
+ {
+ if (! mips_read_relocs (abfd, sec))
+ goto error_return;
+ section_tdata = ecoff_section_data (abfd, sec);
+ }
+
+ if (sec->_cooked_size == 0)
+ {
+ /* We must initialize _cooked_size only the first time we are
+ called. */
+ sec->_cooked_size = sec->_raw_size;
+ }
+
+ contents = section_tdata->contents;
+ offsets = section_tdata->offsets;
+
+ /* Look for any external PC relative relocs. Internal PC relative
+ relocs are already correct in the object file, so they certainly
+ can not overflow. */
+ ext_rel = (struct external_reloc *) section_tdata->external_relocs;
+ ext_rel_end = ext_rel + sec->reloc_count;
+ for (i = 0; ext_rel < ext_rel_end; ext_rel++, i++)
+ {
+ struct internal_reloc int_rel;
+ struct ecoff_link_hash_entry *h;
+ asection *hsec;
+ bfd_signed_vma relocation;
+ struct external_reloc *adj_ext_rel;
+ unsigned int adj_i;
+ unsigned long ext_count;
+ struct ecoff_link_hash_entry **adj_h_ptr;
+ struct ecoff_link_hash_entry **adj_h_ptr_end;
+ struct ecoff_value_adjust *adjust;
+ bfd_size_type amt;
+
+ /* If we have already expanded this reloc, we certainly don't
+ need to do it again. */
+ if (offsets != (long *) NULL && offsets[i] == 1)
+ continue;
+
+ /* Quickly check that this reloc is external PCREL16. */
+ if (bfd_header_big_endian (abfd))
+ {
+ if ((ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_BIG) == 0
+ || (((ext_rel->r_bits[3] & RELOC_BITS3_TYPE_BIG)
+ >> RELOC_BITS3_TYPE_SH_BIG)
+ != MIPS_R_PCREL16))
+ continue;
+ }
+ else
+ {
+ if ((ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_LITTLE) == 0
+ || (((ext_rel->r_bits[3] & RELOC_BITS3_TYPE_LITTLE)
+ >> RELOC_BITS3_TYPE_SH_LITTLE)
+ != MIPS_R_PCREL16))
+ continue;
+ }
+
+ mips_ecoff_swap_reloc_in (abfd, (PTR) ext_rel, &int_rel);
+
+ h = ecoff_data (abfd)->sym_hashes[int_rel.r_symndx];
+ if (h == (struct ecoff_link_hash_entry *) NULL)
+ abort ();
+
+ if (h->root.type != bfd_link_hash_defined
+ && h->root.type != bfd_link_hash_defweak)
+ {
+ /* Just ignore undefined symbols. These will presumably
+ generate an error later in the link. */
+ continue;
+ }
+
+ /* Get the value of the symbol. */
+ hsec = h->root.u.def.section;
+ relocation = (h->root.u.def.value
+ + hsec->output_section->vma
+ + hsec->output_offset);
+
+ /* Subtract out the current address. */
+ relocation -= (sec->output_section->vma
+ + sec->output_offset
+ + (int_rel.r_vaddr - sec->vma));
+
+ /* The addend is stored in the object file. In the normal case
+ of ``bal symbol'', the addend will be -4. It will only be
+ different in the case of ``bal symbol+constant''. To avoid
+ always reading in the section contents, we don't check the
+ addend in the object file (we could easily check the contents
+ if we happen to have already read them in, but I fear that
+ this could be confusing). This means we will screw up if
+ there is a branch to a symbol that is in range, but added to
+ a constant which puts it out of range; in such a case the
+ link will fail with a reloc overflow error. Since the
+ compiler will never generate such code, it should be easy
+ enough to work around it by changing the assembly code in the
+ source file. */
+ relocation -= 4;
+
+ /* Now RELOCATION is the number we want to put in the object
+ file. See whether it fits. */
+ if (relocation >= -0x20000 && relocation < 0x20000)
+ continue;
+
+ /* Now that we know this reloc needs work, which will rarely
+ happen, go ahead and grab the section contents. */
+ if (contents == (bfd_byte *) NULL)
+ {
+ if (info->keep_memory)
+ contents = (bfd_byte *) bfd_alloc (abfd, sec->_raw_size);
+ else
+ contents = (bfd_byte *) bfd_malloc (sec->_raw_size);
+ if (contents == (bfd_byte *) NULL)
+ goto error_return;
+ if (! bfd_get_section_contents (abfd, sec, (PTR) contents,
+ (file_ptr) 0, sec->_raw_size))
+ goto error_return;
+ if (info->keep_memory)
+ section_tdata->contents = contents;
+ }
+
+ /* We only support changing the bal instruction. It would be
+ possible to handle other PC relative branches, but some of
+ them (the conditional branches) would require a different
+ length instruction sequence which would complicate both this
+ routine and mips_relax_pcrel16. It could be written if
+ somebody felt it were important. Ignoring this reloc will
+ presumably cause a reloc overflow error later on. */
+ if (bfd_get_32 (abfd, contents + int_rel.r_vaddr - sec->vma)
+ != 0x0411ffff) /* bgezal $0,. == bal . */
+ continue;
+
+ /* Bother. We need to expand this reloc, and we will need to
+ make another relaxation pass since this change may put other
+ relocs out of range. We need to examine the local branches
+ and we need to allocate memory to hold the offsets we must
+ add to them. We also need to adjust the values of all
+ symbols in the object file following this location. */
+
+ sec->_cooked_size += PCREL16_EXPANSION_ADJUSTMENT;
+ *again = TRUE;
+
+ if (offsets == (long *) NULL)
+ {
+ bfd_size_type size;
+
+ size = (bfd_size_type) sec->reloc_count * sizeof (long);
+ offsets = (long *) bfd_zalloc (abfd, size);
+ if (offsets == (long *) NULL)
+ goto error_return;
+ section_tdata->offsets = offsets;
+ }
+
+ offsets[i] = 1;
+
+ /* Now look for all PC relative references that cross this reloc
+ and adjust their offsets. */
+ adj_ext_rel = (struct external_reloc *) section_tdata->external_relocs;
+ for (adj_i = 0; adj_ext_rel < ext_rel_end; adj_ext_rel++, adj_i++)
+ {
+ struct internal_reloc adj_int_rel;
+ bfd_vma start, stop;
+ int change;
+
+ mips_ecoff_swap_reloc_in (abfd, (PTR) adj_ext_rel, &adj_int_rel);
+
+ if (adj_int_rel.r_type == MIPS_R_PCREL16)
+ {
+ unsigned long insn;
+
+ /* We only care about local references. External ones
+ will be relocated correctly anyhow. */
+ if (adj_int_rel.r_extern)
+ continue;
+
+ /* We are only interested in a PC relative reloc within
+ this section. FIXME: Cross section PC relative
+ relocs may not be handled correctly; does anybody
+ care? */
+ if (adj_int_rel.r_symndx != RELOC_SECTION_TEXT)
+ continue;
+
+ start = adj_int_rel.r_vaddr;
+
+ insn = bfd_get_32 (abfd,
+ contents + adj_int_rel.r_vaddr - sec->vma);
+
+ stop = (insn & 0xffff) << 2;
+ if ((stop & 0x20000) != 0)
+ stop -= 0x40000;
+ stop += adj_int_rel.r_vaddr + 4;
+ }
+ else if (adj_int_rel.r_type == MIPS_R_RELHI)
+ {
+ struct internal_reloc rello;
+ long addhi, addlo;
+
+ /* The next reloc must be MIPS_R_RELLO, and we handle
+ them together. */
+ BFD_ASSERT (adj_ext_rel + 1 < ext_rel_end);
+
+ mips_ecoff_swap_reloc_in (abfd, (PTR) (adj_ext_rel + 1), &rello);
+
+ BFD_ASSERT (rello.r_type == MIPS_R_RELLO);
+
+ addhi = bfd_get_32 (abfd,
+ contents + adj_int_rel.r_vaddr - sec->vma);
+ addhi &= 0xffff;
+ if (addhi & 0x8000)
+ addhi -= 0x10000;
+ addhi <<= 16;
+
+ addlo = bfd_get_32 (abfd, contents + rello.r_vaddr - sec->vma);
+ addlo &= 0xffff;
+ if (addlo & 0x8000)
+ addlo -= 0x10000;
+
+ if (adj_int_rel.r_extern)
+ {
+ /* The value we want here is
+ sym - RELLOaddr + addend
+ which we can express as
+ sym - (RELLOaddr - addend)
+ Therefore if we are expanding the area between
+ RELLOaddr and RELLOaddr - addend we must adjust
+ the addend. This is admittedly ambiguous, since
+ we might mean (sym + addend) - RELLOaddr, but in
+ practice we don't, and there is no way to handle
+ that case correctly since at this point we have
+ no idea whether any reloc is being expanded
+ between sym and sym + addend. */
+ start = rello.r_vaddr - (addhi + addlo);
+ stop = rello.r_vaddr;
+ }
+ else
+ {
+ /* An internal RELHI/RELLO pair represents the
+ difference between two addresses, $LC0 - foo.
+ The symndx value is actually the difference
+ between the reloc address and $LC0. This lets us
+ compute $LC0, and, by considering the addend,
+ foo. If the reloc we are expanding falls between
+ those two relocs, we must adjust the addend. At
+ this point, the symndx value is actually in the
+ r_offset field, where it was put by
+ mips_ecoff_swap_reloc_in. */
+ start = rello.r_vaddr - adj_int_rel.r_offset;
+ stop = start + addhi + addlo;
+ }
+ }
+ else if (adj_int_rel.r_type == MIPS_R_SWITCH)
+ {
+ /* A MIPS_R_SWITCH reloc represents a word of the form
+ .word $L3-$LS12
+ The value in the object file is correct, assuming the
+ original value of $L3. The symndx value is actually
+ the difference between the reloc address and $LS12.
+ This lets us compute the original value of $LS12 as
+ vaddr - symndx
+ and the original value of $L3 as
+ vaddr - symndx + addend
+ where addend is the value from the object file. At
+ this point, the symndx value is actually found in the
+ r_offset field, since it was moved by
+ mips_ecoff_swap_reloc_in. */
+ start = adj_int_rel.r_vaddr - adj_int_rel.r_offset;
+ stop = start + bfd_get_32 (abfd,
+ (contents
+ + adj_int_rel.r_vaddr
+ - sec->vma));
+ }
+ else
+ continue;
+
+ /* If the range expressed by this reloc, which is the
+ distance between START and STOP crosses the reloc we are
+ expanding, we must adjust the offset. The sign of the
+ adjustment depends upon the direction in which the range
+ crosses the reloc being expanded. */
+ if (start <= int_rel.r_vaddr && stop > int_rel.r_vaddr)
+ change = PCREL16_EXPANSION_ADJUSTMENT;
+ else if (start > int_rel.r_vaddr && stop <= int_rel.r_vaddr)
+ change = - PCREL16_EXPANSION_ADJUSTMENT;
+ else
+ change = 0;
+
+ offsets[adj_i] += change;
+
+ if (adj_int_rel.r_type == MIPS_R_RELHI)
+ {
+ adj_ext_rel++;
+ adj_i++;
+ offsets[adj_i] += change;
+ }
+ }
+
+ /* Find all symbols in this section defined by this object file
+ and adjust their values. Note that we decide whether to
+ adjust the value based on the value stored in the ECOFF EXTR
+ structure, because the value stored in the hash table may
+ have been changed by an earlier expanded reloc and thus may
+ no longer correctly indicate whether the symbol is before or
+ after the expanded reloc. */
+ ext_count = ecoff_data (abfd)->debug_info.symbolic_header.iextMax;
+ adj_h_ptr = ecoff_data (abfd)->sym_hashes;
+ adj_h_ptr_end = adj_h_ptr + ext_count;
+ for (; adj_h_ptr < adj_h_ptr_end; adj_h_ptr++)
+ {
+ struct ecoff_link_hash_entry *adj_h;
+
+ adj_h = *adj_h_ptr;
+ if (adj_h != (struct ecoff_link_hash_entry *) NULL
+ && (adj_h->root.type == bfd_link_hash_defined
+ || adj_h->root.type == bfd_link_hash_defweak)
+ && adj_h->root.u.def.section == sec
+ && adj_h->esym.asym.value > int_rel.r_vaddr)
+ adj_h->root.u.def.value += PCREL16_EXPANSION_ADJUSTMENT;
+ }
+
+ /* Add an entry to the symbol value adjust list. This is used
+ by bfd_ecoff_debug_accumulate to adjust the values of
+ internal symbols and FDR's. */
+ amt = sizeof (struct ecoff_value_adjust);
+ adjust = (struct ecoff_value_adjust *) bfd_alloc (abfd, amt);
+ if (adjust == (struct ecoff_value_adjust *) NULL)
+ goto error_return;
+
+ adjust->start = int_rel.r_vaddr;
+ adjust->end = sec->vma + sec->_raw_size;
+ adjust->adjust = PCREL16_EXPANSION_ADJUSTMENT;
+
+ adjust->next = ecoff_data (abfd)->debug_info.adjust;
+ ecoff_data (abfd)->debug_info.adjust = adjust;
+ }
+
+ if (contents != (bfd_byte *) NULL && ! info->keep_memory)
+ free (contents);
+
+ return TRUE;
+
+ error_return:
+ if (contents != (bfd_byte *) NULL && ! info->keep_memory)
+ free (contents);
+ return FALSE;
+}
+
+/* This routine is called from mips_relocate_section when a PC
+ relative reloc must be expanded into the five instruction sequence.
+ It handles all the details of the expansion, including resolving
+ the reloc. */
+
+static bfd_boolean
+mips_relax_pcrel16 (info, input_bfd, input_section, h, location, address)
+ struct bfd_link_info *info ATTRIBUTE_UNUSED;
+ bfd *input_bfd;
+ asection *input_section ATTRIBUTE_UNUSED;
+ struct ecoff_link_hash_entry *h;
+ bfd_byte *location;
+ bfd_vma address;
+{
+ bfd_vma relocation;
+
+ /* 0x0411ffff is bgezal $0,. == bal . */
+ BFD_ASSERT (bfd_get_32 (input_bfd, location) == 0x0411ffff);
+
+ /* We need to compute the distance between the symbol and the
+ current address plus eight. */
+ relocation = (h->root.u.def.value
+ + h->root.u.def.section->output_section->vma
+ + h->root.u.def.section->output_offset);
+ relocation -= address + 8;
+
+ /* If the lower half is negative, increment the upper 16 half. */
+ if ((relocation & 0x8000) != 0)
+ relocation += 0x10000;
+
+ bfd_put_32 (input_bfd, (bfd_vma) 0x04110001, location); /* bal .+8 */
+ bfd_put_32 (input_bfd,
+ 0x3c010000 | ((relocation >> 16) & 0xffff), /* lui $at,XX */
+ location + 4);
+ bfd_put_32 (input_bfd,
+ 0x24210000 | (relocation & 0xffff), /* addiu $at,$at,XX */
+ location + 8);
+ bfd_put_32 (input_bfd,
+ (bfd_vma) 0x003f0821, location + 12); /* addu $at,$at,$ra */
+ bfd_put_32 (input_bfd,
+ (bfd_vma) 0x0020f809, location + 16); /* jalr $at */
+
+ return TRUE;
+}
+
+/* Given a .sdata section and a .rel.sdata in-memory section, store
+ relocation information into the .rel.sdata section which can be
+ used at runtime to relocate the section. This is called by the
+ linker when the --embedded-relocs switch is used. This is called
+ after the add_symbols entry point has been called for all the
+ objects, and before the final_link entry point is called. This
+ function presumes that the object was compiled using
+ -membedded-pic. */
+
+bfd_boolean
+bfd_mips_ecoff_create_embedded_relocs (abfd, info, datasec, relsec, errmsg)
+ bfd *abfd;
+ struct bfd_link_info *info;
+ asection *datasec;
+ asection *relsec;
+ char **errmsg;
+{
+ struct ecoff_link_hash_entry **sym_hashes;
+ struct ecoff_section_tdata *section_tdata;
+ struct external_reloc *ext_rel;
+ struct external_reloc *ext_rel_end;
+ bfd_byte *p;
+ bfd_size_type amt;
+
+ BFD_ASSERT (! info->relocatable);
+
+ *errmsg = NULL;
+
+ if (datasec->reloc_count == 0)
+ return TRUE;
+
+ sym_hashes = ecoff_data (abfd)->sym_hashes;
+
+ if (! mips_read_relocs (abfd, datasec))
+ return FALSE;
+
+ amt = (bfd_size_type) datasec->reloc_count * 4;
+ relsec->contents = (bfd_byte *) bfd_alloc (abfd, amt);
+ if (relsec->contents == NULL)
+ return FALSE;
+
+ p = relsec->contents;
+
+ section_tdata = ecoff_section_data (abfd, datasec);
+ ext_rel = (struct external_reloc *) section_tdata->external_relocs;
+ ext_rel_end = ext_rel + datasec->reloc_count;
+ for (; ext_rel < ext_rel_end; ext_rel++, p += 4)
+ {
+ struct internal_reloc int_rel;
+ bfd_boolean text_relative;
+
+ mips_ecoff_swap_reloc_in (abfd, (PTR) ext_rel, &int_rel);
+
+ /* We are going to write a four byte word into the runtime reloc
+ section. The word will be the address in the data section
+ which must be relocated. This must be on a word boundary,
+ which means the lower two bits must be zero. We use the
+ least significant bit to indicate how the value in the data
+ section must be relocated. A 0 means that the value is
+ relative to the text section, while a 1 indicates that the
+ value is relative to the data section. Given that we are
+ assuming the code was compiled using -membedded-pic, there
+ should not be any other possibilities. */
+
+ /* We can only relocate REFWORD relocs at run time. */
+ if (int_rel.r_type != MIPS_R_REFWORD)
+ {
+ *errmsg = _("unsupported reloc type");
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+
+ if (int_rel.r_extern)
+ {
+ struct ecoff_link_hash_entry *h;
+
+ h = sym_hashes[int_rel.r_symndx];
+ /* If h is NULL, that means that there is a reloc against an
+ external symbol which we thought was just a debugging
+ symbol. This should not happen. */
+ if (h == (struct ecoff_link_hash_entry *) NULL)
+ abort ();
+ if ((h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ && (h->root.u.def.section->flags & SEC_CODE) != 0)
+ text_relative = TRUE;
+ else
+ text_relative = FALSE;
+ }
+ else
+ {
+ switch (int_rel.r_symndx)
+ {
+ case RELOC_SECTION_TEXT:
+ text_relative = TRUE;
+ break;
+ case RELOC_SECTION_SDATA:
+ case RELOC_SECTION_SBSS:
+ case RELOC_SECTION_LIT8:
+ text_relative = FALSE;
+ break;
+ default:
+ /* No other sections should appear in -membedded-pic
+ code. */
+ *errmsg = _("reloc against unsupported section");
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ }
+
+ if ((int_rel.r_offset & 3) != 0)
+ {
+ *errmsg = _("reloc not properly aligned");
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+
+ bfd_put_32 (abfd,
+ (int_rel.r_vaddr - datasec->vma + datasec->output_offset
+ + (text_relative ? 0 : 1)),
+ p);
+ }
+
+ return TRUE;
+}
+
+/* This is the ECOFF backend structure. The backend field of the
+ target vector points to this. */
+
+static const struct ecoff_backend_data mips_ecoff_backend_data =
+{
+ /* COFF backend structure. */
+ {
+ (void (*) PARAMS ((bfd *,PTR,int,int,int,int,PTR))) bfd_void, /* aux_in */
+ (void (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* sym_in */
+ (void (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* lineno_in */
+ (unsigned (*) PARAMS ((bfd *,PTR,int,int,int,int,PTR)))bfd_void,/*aux_out*/
+ (unsigned (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* sym_out */
+ (unsigned (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* lineno_out */
+ (unsigned (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* reloc_out */
+ mips_ecoff_swap_filehdr_out, mips_ecoff_swap_aouthdr_out,
+ mips_ecoff_swap_scnhdr_out,
+ FILHSZ, AOUTSZ, SCNHSZ, 0, 0, 0, 0, FILNMLEN, TRUE, FALSE, 4, FALSE, 2,
+ mips_ecoff_swap_filehdr_in, mips_ecoff_swap_aouthdr_in,
+ mips_ecoff_swap_scnhdr_in, NULL,
+ mips_ecoff_bad_format_hook, _bfd_ecoff_set_arch_mach_hook,
+ _bfd_ecoff_mkobject_hook, _bfd_ecoff_styp_to_sec_flags,
+ _bfd_ecoff_set_alignment_hook, _bfd_ecoff_slurp_symbol_table,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL
+ },
+ /* Supported architecture. */
+ bfd_arch_mips,
+ /* Initial portion of armap string. */
+ "__________",
+ /* The page boundary used to align sections in a demand-paged
+ executable file. E.g., 0x1000. */
+ 0x1000,
+ /* TRUE if the .rdata section is part of the text segment, as on the
+ Alpha. FALSE if .rdata is part of the data segment, as on the
+ MIPS. */
+ FALSE,
+ /* Bitsize of constructor entries. */
+ 32,
+ /* Reloc to use for constructor entries. */
+ &mips_howto_table[MIPS_R_REFWORD],
+ {
+ /* Symbol table magic number. */
+ magicSym,
+ /* Alignment of debugging information. E.g., 4. */
+ 4,
+ /* Sizes of external symbolic information. */
+ sizeof (struct hdr_ext),
+ sizeof (struct dnr_ext),
+ sizeof (struct pdr_ext),
+ sizeof (struct sym_ext),
+ sizeof (struct opt_ext),
+ sizeof (struct fdr_ext),
+ sizeof (struct rfd_ext),
+ sizeof (struct ext_ext),
+ /* Functions to swap in external symbolic data. */
+ ecoff_swap_hdr_in,
+ ecoff_swap_dnr_in,
+ ecoff_swap_pdr_in,
+ ecoff_swap_sym_in,
+ ecoff_swap_opt_in,
+ ecoff_swap_fdr_in,
+ ecoff_swap_rfd_in,
+ ecoff_swap_ext_in,
+ _bfd_ecoff_swap_tir_in,
+ _bfd_ecoff_swap_rndx_in,
+ /* Functions to swap out external symbolic data. */
+ ecoff_swap_hdr_out,
+ ecoff_swap_dnr_out,
+ ecoff_swap_pdr_out,
+ ecoff_swap_sym_out,
+ ecoff_swap_opt_out,
+ ecoff_swap_fdr_out,
+ ecoff_swap_rfd_out,
+ ecoff_swap_ext_out,
+ _bfd_ecoff_swap_tir_out,
+ _bfd_ecoff_swap_rndx_out,
+ /* Function to read in symbolic data. */
+ _bfd_ecoff_slurp_symbolic_info
+ },
+ /* External reloc size. */
+ RELSZ,
+ /* Reloc swapping functions. */
+ mips_ecoff_swap_reloc_in,
+ mips_ecoff_swap_reloc_out,
+ /* Backend reloc tweaking. */
+ mips_adjust_reloc_in,
+ mips_adjust_reloc_out,
+ /* Relocate section contents while linking. */
+ mips_relocate_section,
+ /* Do final adjustments to filehdr and aouthdr. */
+ NULL,
+ /* Read an element from an archive at a given file position. */
+ _bfd_get_elt_at_filepos
+};
+
+/* Looking up a reloc type is MIPS specific. */
+#define _bfd_ecoff_bfd_reloc_type_lookup mips_bfd_reloc_type_lookup
+
+/* Getting relocated section contents is generic. */
+#define _bfd_ecoff_bfd_get_relocated_section_contents \
+ bfd_generic_get_relocated_section_contents
+
+/* Handling file windows is generic. */
+#define _bfd_ecoff_get_section_contents_in_window \
+ _bfd_generic_get_section_contents_in_window
+
+/* Relaxing sections is MIPS specific. */
+#define _bfd_ecoff_bfd_relax_section mips_relax_section
+
+/* GC of sections is not done. */
+#define _bfd_ecoff_bfd_gc_sections bfd_generic_gc_sections
+
+/* Merging of sections is not done. */
+#define _bfd_ecoff_bfd_merge_sections bfd_generic_merge_sections
+
+#define _bfd_ecoff_bfd_discard_group bfd_generic_discard_group
+
+extern const bfd_target ecoff_big_vec;
+
+const bfd_target ecoff_little_vec =
+{
+ "ecoff-littlemips", /* name */
+ bfd_target_ecoff_flavour,
+ BFD_ENDIAN_LITTLE, /* data byte order is little */
+ BFD_ENDIAN_LITTLE, /* header byte order is little */
+
+ (HAS_RELOC | EXEC_P | /* object flags */
+ HAS_LINENO | HAS_DEBUG |
+ HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED),
+
+ (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_CODE | SEC_DATA),
+ 0, /* leading underscore */
+ ' ', /* ar_pad_char */
+ 15, /* ar_max_namelen */
+ bfd_getl64, bfd_getl_signed_64, bfd_putl64,
+ bfd_getl32, bfd_getl_signed_32, bfd_putl32,
+ bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* data */
+ bfd_getl64, bfd_getl_signed_64, bfd_putl64,
+ bfd_getl32, bfd_getl_signed_32, bfd_putl32,
+ bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* hdrs */
+
+ {_bfd_dummy_target, coff_object_p, /* bfd_check_format */
+ _bfd_ecoff_archive_p, _bfd_dummy_target},
+ {bfd_false, _bfd_ecoff_mkobject, /* bfd_set_format */
+ _bfd_generic_mkarchive, bfd_false},
+ {bfd_false, _bfd_ecoff_write_object_contents, /* bfd_write_contents */
+ _bfd_write_archive_contents, bfd_false},
+
+ BFD_JUMP_TABLE_GENERIC (_bfd_ecoff),
+ BFD_JUMP_TABLE_COPY (_bfd_ecoff),
+ BFD_JUMP_TABLE_CORE (_bfd_nocore),
+ BFD_JUMP_TABLE_ARCHIVE (_bfd_ecoff),
+ BFD_JUMP_TABLE_SYMBOLS (_bfd_ecoff),
+ BFD_JUMP_TABLE_RELOCS (_bfd_ecoff),
+ BFD_JUMP_TABLE_WRITE (_bfd_ecoff),
+ BFD_JUMP_TABLE_LINK (_bfd_ecoff),
+ BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
+
+ & ecoff_big_vec,
+
+ (PTR) &mips_ecoff_backend_data
+};
+
+const bfd_target ecoff_big_vec =
+{
+ "ecoff-bigmips", /* name */
+ bfd_target_ecoff_flavour,
+ BFD_ENDIAN_BIG, /* data byte order is big */
+ BFD_ENDIAN_BIG, /* header byte order is big */
+
+ (HAS_RELOC | EXEC_P | /* object flags */
+ HAS_LINENO | HAS_DEBUG |
+ HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED),
+
+ (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_CODE | SEC_DATA),
+ 0, /* leading underscore */
+ ' ', /* ar_pad_char */
+ 15, /* ar_max_namelen */
+ bfd_getb64, bfd_getb_signed_64, bfd_putb64,
+ bfd_getb32, bfd_getb_signed_32, bfd_putb32,
+ bfd_getb16, bfd_getb_signed_16, bfd_putb16,
+ bfd_getb64, bfd_getb_signed_64, bfd_putb64,
+ bfd_getb32, bfd_getb_signed_32, bfd_putb32,
+ bfd_getb16, bfd_getb_signed_16, bfd_putb16,
+ {_bfd_dummy_target, coff_object_p, /* bfd_check_format */
+ _bfd_ecoff_archive_p, _bfd_dummy_target},
+ {bfd_false, _bfd_ecoff_mkobject, /* bfd_set_format */
+ _bfd_generic_mkarchive, bfd_false},
+ {bfd_false, _bfd_ecoff_write_object_contents, /* bfd_write_contents */
+ _bfd_write_archive_contents, bfd_false},
+
+ BFD_JUMP_TABLE_GENERIC (_bfd_ecoff),
+ BFD_JUMP_TABLE_COPY (_bfd_ecoff),
+ BFD_JUMP_TABLE_CORE (_bfd_nocore),
+ BFD_JUMP_TABLE_ARCHIVE (_bfd_ecoff),
+ BFD_JUMP_TABLE_SYMBOLS (_bfd_ecoff),
+ BFD_JUMP_TABLE_RELOCS (_bfd_ecoff),
+ BFD_JUMP_TABLE_WRITE (_bfd_ecoff),
+ BFD_JUMP_TABLE_LINK (_bfd_ecoff),
+ BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
+
+ & ecoff_little_vec,
+
+ (PTR) &mips_ecoff_backend_data
+};
+
+const bfd_target ecoff_biglittle_vec =
+{
+ "ecoff-biglittlemips", /* name */
+ bfd_target_ecoff_flavour,
+ BFD_ENDIAN_LITTLE, /* data byte order is little */
+ BFD_ENDIAN_BIG, /* header byte order is big */
+
+ (HAS_RELOC | EXEC_P | /* object flags */
+ HAS_LINENO | HAS_DEBUG |
+ HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED),
+
+ (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_CODE | SEC_DATA),
+ 0, /* leading underscore */
+ ' ', /* ar_pad_char */
+ 15, /* ar_max_namelen */
+ bfd_getl64, bfd_getl_signed_64, bfd_putl64,
+ bfd_getl32, bfd_getl_signed_32, bfd_putl32,
+ bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* data */
+ bfd_getb64, bfd_getb_signed_64, bfd_putb64,
+ bfd_getb32, bfd_getb_signed_32, bfd_putb32,
+ bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* hdrs */
+
+ {_bfd_dummy_target, coff_object_p, /* bfd_check_format */
+ _bfd_ecoff_archive_p, _bfd_dummy_target},
+ {bfd_false, _bfd_ecoff_mkobject, /* bfd_set_format */
+ _bfd_generic_mkarchive, bfd_false},
+ {bfd_false, _bfd_ecoff_write_object_contents, /* bfd_write_contents */
+ _bfd_write_archive_contents, bfd_false},
+
+ BFD_JUMP_TABLE_GENERIC (_bfd_ecoff),
+ BFD_JUMP_TABLE_COPY (_bfd_ecoff),
+ BFD_JUMP_TABLE_CORE (_bfd_nocore),
+ BFD_JUMP_TABLE_ARCHIVE (_bfd_ecoff),
+ BFD_JUMP_TABLE_SYMBOLS (_bfd_ecoff),
+ BFD_JUMP_TABLE_RELOCS (_bfd_ecoff),
+ BFD_JUMP_TABLE_WRITE (_bfd_ecoff),
+ BFD_JUMP_TABLE_LINK (_bfd_ecoff),
+ BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
+
+ NULL,
+
+ (PTR) &mips_ecoff_backend_data
+};
diff --git a/contrib/binutils/bfd/cpu-mips.c b/contrib/binutils/bfd/cpu-mips.c
new file mode 100644
index 0000000..01ecc4e
--- /dev/null
+++ b/contrib/binutils/bfd/cpu-mips.c
@@ -0,0 +1,127 @@
+/* bfd back-end for mips support
+ Copyright 1990, 1991, 1993, 1994, 1995, 1996, 1997, 1998, 2000, 2001,
+ 2002, 2003 Free Software Foundation, Inc.
+ Written by Steve Chamberlain of Cygnus Support.
+
+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 *mips_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 *
+mips_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_mips, \
+ NUMBER, \
+ "mips", \
+ PRINT, \
+ 3, \
+ DEFAULT, \
+ mips_compatible, \
+ bfd_default_scan, \
+ NEXT, \
+ }
+
+enum
+{
+ I_mips3000,
+ I_mips3900,
+ I_mips4000,
+ I_mips4010,
+ I_mips4100,
+ I_mips4111,
+ I_mips4120,
+ I_mips4300,
+ I_mips4400,
+ I_mips4600,
+ I_mips4650,
+ I_mips5000,
+ I_mips5400,
+ I_mips5500,
+ I_mips6000,
+ I_mips7000,
+ I_mips8000,
+ I_mips10000,
+ I_mips12000,
+ I_mips16,
+ I_mips5,
+ I_mipsisa32,
+ I_mipsisa32r2,
+ I_mipsisa64,
+ I_mipsisa64r2,
+ I_sb1,
+};
+
+#define NN(index) (&arch_info_struct[(index) + 1])
+
+static const bfd_arch_info_type arch_info_struct[] =
+{
+ N (32, 32, bfd_mach_mips3000, "mips:3000", FALSE, NN(I_mips3000)),
+ N (32, 32, bfd_mach_mips3900, "mips:3900", FALSE, NN(I_mips3900)),
+ N (64, 64, bfd_mach_mips4000, "mips:4000", FALSE, NN(I_mips4000)),
+ N (64, 64, bfd_mach_mips4010, "mips:4010", FALSE, NN(I_mips4010)),
+ N (64, 64, bfd_mach_mips4100, "mips:4100", FALSE, NN(I_mips4100)),
+ N (64, 64, bfd_mach_mips4111, "mips:4111", FALSE, NN(I_mips4111)),
+ N (64, 64, bfd_mach_mips4120, "mips:4120", FALSE, NN(I_mips4120)),
+ N (64, 64, bfd_mach_mips4300, "mips:4300", FALSE, NN(I_mips4300)),
+ N (64, 64, bfd_mach_mips4400, "mips:4400", FALSE, NN(I_mips4400)),
+ N (64, 64, bfd_mach_mips4600, "mips:4600", FALSE, NN(I_mips4600)),
+ N (64, 64, bfd_mach_mips4650, "mips:4650", FALSE, NN(I_mips4650)),
+ N (64, 64, bfd_mach_mips5000, "mips:5000", FALSE, NN(I_mips5000)),
+ N (64, 64, bfd_mach_mips5400, "mips:5400", FALSE, NN(I_mips5400)),
+ N (64, 64, bfd_mach_mips5500, "mips:5500", FALSE, NN(I_mips5500)),
+ N (32, 32, bfd_mach_mips6000, "mips:6000", FALSE, NN(I_mips6000)),
+ N (64, 64, bfd_mach_mips7000, "mips:7000", FALSE, NN(I_mips7000)),
+ N (64, 64, bfd_mach_mips8000, "mips:8000", FALSE, NN(I_mips8000)),
+ N (64, 64, bfd_mach_mips10000,"mips:10000", FALSE, NN(I_mips10000)),
+ N (64, 64, bfd_mach_mips12000,"mips:12000", FALSE, NN(I_mips12000)),
+ N (64, 64, bfd_mach_mips16, "mips:16", FALSE, NN(I_mips16)),
+ N (64, 64, bfd_mach_mips5, "mips:mips5", FALSE, NN(I_mips5)),
+ N (32, 32, bfd_mach_mipsisa32, "mips:isa32", FALSE, NN(I_mipsisa32)),
+ N (32, 32, bfd_mach_mipsisa32r2,"mips:isa32r2", FALSE, NN(I_mipsisa32r2)),
+ N (64, 64, bfd_mach_mipsisa64, "mips:isa64", FALSE, NN(I_mipsisa64)),
+ N (64, 64, bfd_mach_mipsisa64r2,"mips:isa64r2", FALSE, NN(I_mipsisa64r2)),
+ N (64, 64, bfd_mach_mips_sb1, "mips:sb1", FALSE, 0),
+};
+
+/* The default architecture is mips:3000, but with a machine number of
+ zero. This lets the linker distinguish between a default setting
+ of mips, and an explicit setting of mips:3000. */
+
+const bfd_arch_info_type bfd_mips_arch =
+N (32, 32, 0, "mips", TRUE, &arch_info_struct[0]);
diff --git a/contrib/binutils/bfd/elf32-mips.c b/contrib/binutils/bfd/elf32-mips.c
new file mode 100644
index 0000000..a0480f0
--- /dev/null
+++ b/contrib/binutils/bfd/elf32-mips.c
@@ -0,0 +1,1618 @@
+/* MIPS-specific support for 32-bit ELF
+ Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+ 2003 Free Software Foundation, Inc.
+
+ Most of the information added by Ian Lance Taylor, Cygnus Support,
+ <ian@cygnus.com>.
+ N32/64 ABI support added by Mark Mitchell, CodeSourcery, LLC.
+ <mark@codesourcery.com>
+ Traditional MIPS targets support added by Koundinya.K, Dansk Data
+ Elektronik & Operations Research Group. <kk@ddeorg.soft.net>
+
+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 MIPS ELF targets. SGI Irix 5 uses a slightly
+ different MIPS ELF from other targets. This matters when linking.
+ This file supports both, switching at runtime. */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+#include "bfdlink.h"
+#include "genlink.h"
+#include "elf-bfd.h"
+#include "elfxx-mips.h"
+#include "elf/mips.h"
+
+/* Get the ECOFF swapping routines. */
+#include "coff/sym.h"
+#include "coff/symconst.h"
+#include "coff/internal.h"
+#include "coff/ecoff.h"
+#include "coff/mips.h"
+#define ECOFF_SIGNED_32
+#include "ecoffswap.h"
+
+static bfd_reloc_status_type gprel32_with_gp
+ (bfd *, asymbol *, arelent *, asection *, bfd_boolean, void *, bfd_vma);
+static bfd_reloc_status_type mips_elf_gprel32_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type mips32_64bit_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static reloc_howto_type *bfd_elf32_bfd_reloc_type_lookup
+ (bfd *, bfd_reloc_code_real_type);
+static reloc_howto_type *mips_elf32_rtype_to_howto
+ (unsigned int, bfd_boolean);
+static void mips_info_to_howto_rel
+ (bfd *, arelent *, Elf_Internal_Rela *);
+static void mips_info_to_howto_rela
+ (bfd *, arelent *, Elf_Internal_Rela *);
+static bfd_boolean mips_elf_sym_is_global
+ (bfd *, asymbol *);
+static bfd_boolean mips_elf32_object_p
+ (bfd *);
+static bfd_boolean mips_elf_is_local_label_name
+ (bfd *, const char *);
+static bfd_reloc_status_type mips16_jump_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type mips16_gprel_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type mips_elf_final_gp
+ (bfd *, asymbol *, bfd_boolean, char **, bfd_vma *);
+static bfd_boolean mips_elf_assign_gp
+ (bfd *, bfd_vma *);
+static bfd_boolean elf32_mips_grok_prstatus
+ (bfd *, Elf_Internal_Note *);
+static bfd_boolean elf32_mips_grok_psinfo
+ (bfd *, Elf_Internal_Note *);
+static irix_compat_t elf32_mips_irix_compat
+ (bfd *);
+
+extern const bfd_target bfd_elf32_bigmips_vec;
+extern const bfd_target bfd_elf32_littlemips_vec;
+
+/* Nonzero if ABFD is using the N32 ABI. */
+#define ABI_N32_P(abfd) \
+ ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI2) != 0)
+
+/* Whether we are trying to be compatible with IRIX at all. */
+#define SGI_COMPAT(abfd) \
+ (elf32_mips_irix_compat (abfd) != ict_none)
+
+/* The number of local .got entries we reserve. */
+#define MIPS_RESERVED_GOTNO (2)
+
+/* In case we're on a 32-bit machine, construct a 64-bit "-1" value
+ from smaller values. Start with zero, widen, *then* decrement. */
+#define MINUS_ONE (((bfd_vma)0) - 1)
+
+/* The relocation table used for SHT_REL sections. */
+
+static reloc_howto_type elf_mips_howto_table_rel[] =
+{
+ /* No relocation. */
+ HOWTO (R_MIPS_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_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_NONE", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 16 bit relocation. */
+ HOWTO (R_MIPS_16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit relocation. */
+ HOWTO (R_MIPS_32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit symbol relative relocation. */
+ HOWTO (R_MIPS_REL32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_REL32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 26 bit jump address. */
+ HOWTO (R_MIPS_26, /* type */
+ 2, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 26, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ /* This needs complex overflow
+ detection, because the upper four
+ bits must match the PC + 4. */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_26", /* name */
+ TRUE, /* partial_inplace */
+ 0x03ffffff, /* src_mask */
+ 0x03ffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of symbol value. */
+ HOWTO (R_MIPS_HI16, /* type */
+ 16, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_hi16_reloc, /* special_function */
+ "R_MIPS_HI16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of symbol value. */
+ HOWTO (R_MIPS_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_lo16_reloc, /* special_function */
+ "R_MIPS_LO16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* GP relative reference. */
+ HOWTO (R_MIPS_GPREL16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf32_gprel16_reloc, /* special_function */
+ "R_MIPS_GPREL16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Reference to literal section. */
+ HOWTO (R_MIPS_LITERAL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf32_gprel16_reloc, /* special_function */
+ "R_MIPS_LITERAL", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Reference to global offset table. */
+ HOWTO (R_MIPS_GOT16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_got16_reloc, /* special_function */
+ "R_MIPS_GOT16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 16 bit PC relative reference. */
+ HOWTO (R_MIPS_PC16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_PC16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* 16 bit call through global offset table. */
+ HOWTO (R_MIPS_CALL16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_CALL16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit GP relative reference. */
+ HOWTO (R_MIPS_GPREL32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ mips_elf_gprel32_reloc, /* special_function */
+ "R_MIPS_GPREL32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* The remaining relocs are defined on Irix 5, although they are
+ not defined by the ABI. */
+ EMPTY_HOWTO (13),
+ EMPTY_HOWTO (14),
+ EMPTY_HOWTO (15),
+
+ /* A 5 bit shift field. */
+ HOWTO (R_MIPS_SHIFT5, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 5, /* bitsize */
+ FALSE, /* pc_relative */
+ 6, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_SHIFT5", /* name */
+ TRUE, /* partial_inplace */
+ 0x000007c0, /* src_mask */
+ 0x000007c0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A 6 bit shift field. */
+ /* FIXME: This is not handled correctly; a special function is
+ needed to put the most significant bit in the right place. */
+ HOWTO (R_MIPS_SHIFT6, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 6, /* bitsize */
+ FALSE, /* pc_relative */
+ 6, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_SHIFT6", /* name */
+ TRUE, /* partial_inplace */
+ 0x000007c4, /* src_mask */
+ 0x000007c4, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A 64 bit relocation. */
+ HOWTO (R_MIPS_64, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ mips32_64bit_reloc, /* special_function */
+ "R_MIPS_64", /* name */
+ TRUE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Displacement in the global offset table. */
+ HOWTO (R_MIPS_GOT_DISP, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_DISP", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Displacement to page pointer in the global offset table. */
+ HOWTO (R_MIPS_GOT_PAGE, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_PAGE", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Offset from page pointer in the global offset table. */
+ HOWTO (R_MIPS_GOT_OFST, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_OFST", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_MIPS_GOT_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_HI16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_MIPS_GOT_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_LO16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 64 bit subtraction. Used in the N32 ABI. */
+ HOWTO (R_MIPS_SUB, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_SUB", /* name */
+ TRUE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Used to cause the linker to insert and delete instructions? */
+ EMPTY_HOWTO (R_MIPS_INSERT_A),
+ EMPTY_HOWTO (R_MIPS_INSERT_B),
+ EMPTY_HOWTO (R_MIPS_DELETE),
+
+ /* Get the higher value of a 64 bit addend. */
+ HOWTO (R_MIPS_HIGHER, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_HIGHER", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Get the highest value of a 64 bit addend. */
+ HOWTO (R_MIPS_HIGHEST, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_HIGHEST", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_MIPS_CALL_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_CALL_HI16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_MIPS_CALL_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_CALL_LO16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Section displacement. */
+ HOWTO (R_MIPS_SCN_DISP, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_SCN_DISP", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (R_MIPS_REL16),
+ EMPTY_HOWTO (R_MIPS_ADD_IMMEDIATE),
+ EMPTY_HOWTO (R_MIPS_PJUMP),
+ EMPTY_HOWTO (R_MIPS_RELGOT),
+
+ /* Protected jump conversion. This is an optimization hint. No
+ relocation is required for correctness. */
+ HOWTO (R_MIPS_JALR, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_JALR", /* name */
+ FALSE, /* partial_inplace */
+ 0x00000000, /* src_mask */
+ 0x00000000, /* dst_mask */
+ FALSE), /* pcrel_offset */
+};
+
+/* The reloc used for BFD_RELOC_CTOR when doing a 64 bit link. This
+ is a hack to make the linker think that we need 64 bit values. */
+static reloc_howto_type elf_mips_ctor64_howto =
+ HOWTO (R_MIPS_64, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips32_64bit_reloc, /* special_function */
+ "R_MIPS_64", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+/* The reloc used for the mips16 jump instruction. */
+static reloc_howto_type elf_mips16_jump_howto =
+ HOWTO (R_MIPS16_26, /* type */
+ 2, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 26, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ /* This needs complex overflow
+ detection, because the upper four
+ bits must match the PC. */
+ mips16_jump_reloc, /* special_function */
+ "R_MIPS16_26", /* name */
+ TRUE, /* partial_inplace */
+ 0x3ffffff, /* src_mask */
+ 0x3ffffff, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+/* The reloc used for the mips16 gprel instruction. */
+static reloc_howto_type elf_mips16_gprel_howto =
+ HOWTO (R_MIPS16_GPREL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips16_gprel_reloc, /* special_function */
+ "R_MIPS16_GPREL", /* name */
+ TRUE, /* partial_inplace */
+ 0x07ff001f, /* src_mask */
+ 0x07ff001f, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+/* GNU extensions for embedded-pic. */
+/* High 16 bits of symbol value, pc-relative. */
+static reloc_howto_type elf_mips_gnu_rel_hi16 =
+ HOWTO (R_MIPS_GNU_REL_HI16, /* type */
+ 16, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_hi16_reloc, /* special_function */
+ "R_MIPS_GNU_REL_HI16", /* name */
+ TRUE, /* partial_inplace */
+ 0xffff, /* src_mask */
+ 0xffff, /* dst_mask */
+ TRUE); /* pcrel_offset */
+
+/* Low 16 bits of symbol value, pc-relative. */
+static reloc_howto_type elf_mips_gnu_rel_lo16 =
+ HOWTO (R_MIPS_GNU_REL_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_lo16_reloc, /* special_function */
+ "R_MIPS_GNU_REL_LO16", /* name */
+ TRUE, /* partial_inplace */
+ 0xffff, /* src_mask */
+ 0xffff, /* dst_mask */
+ TRUE); /* pcrel_offset */
+
+/* 16 bit offset for pc-relative branches. */
+static reloc_howto_type elf_mips_gnu_rel16_s2 =
+ HOWTO (R_MIPS_GNU_REL16_S2, /* type */
+ 2, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GNU_REL16_S2", /* name */
+ TRUE, /* partial_inplace */
+ 0xffff, /* src_mask */
+ 0xffff, /* dst_mask */
+ TRUE); /* pcrel_offset */
+
+/* 64 bit pc-relative. */
+static reloc_howto_type elf_mips_gnu_pcrel64 =
+ HOWTO (R_MIPS_PC64, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_PC64", /* name */
+ TRUE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ TRUE); /* pcrel_offset */
+
+/* 32 bit pc-relative. */
+static reloc_howto_type elf_mips_gnu_pcrel32 =
+ HOWTO (R_MIPS_PC32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_PC32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ TRUE); /* pcrel_offset */
+
+/* GNU extension to record C++ vtable hierarchy */
+static reloc_howto_type elf_mips_gnu_vtinherit_howto =
+ HOWTO (R_MIPS_GNU_VTINHERIT, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 0, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ NULL, /* special_function */
+ "R_MIPS_GNU_VTINHERIT", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+/* GNU extension to record C++ vtable member usage */
+static reloc_howto_type elf_mips_gnu_vtentry_howto =
+ HOWTO (R_MIPS_GNU_VTENTRY, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 0, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_elf_rel_vtable_reloc_fn, /* special_function */
+ "R_MIPS_GNU_VTENTRY", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+/* Set the GP value for OUTPUT_BFD. Returns FALSE if this is a
+ dangerous relocation. */
+
+static bfd_boolean
+mips_elf_assign_gp (bfd *output_bfd, bfd_vma *pgp)
+{
+ unsigned int count;
+ asymbol **sym;
+ unsigned int i;
+
+ /* If we've already figured out what GP will be, just return it. */
+ *pgp = _bfd_get_gp_value (output_bfd);
+ if (*pgp)
+ return TRUE;
+
+ count = bfd_get_symcount (output_bfd);
+ sym = bfd_get_outsymbols (output_bfd);
+
+ /* The linker script will have created a symbol named `_gp' with the
+ appropriate value. */
+ if (sym == NULL)
+ i = count;
+ else
+ {
+ for (i = 0; i < count; i++, sym++)
+ {
+ register const char *name;
+
+ name = bfd_asymbol_name (*sym);
+ if (*name == '_' && strcmp (name, "_gp") == 0)
+ {
+ *pgp = bfd_asymbol_value (*sym);
+ _bfd_set_gp_value (output_bfd, *pgp);
+ break;
+ }
+ }
+ }
+
+ if (i >= count)
+ {
+ /* Only get the error once. */
+ *pgp = 4;
+ _bfd_set_gp_value (output_bfd, *pgp);
+ return FALSE;
+ }
+
+ 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
+mips_elf_final_gp (bfd *output_bfd, asymbol *symbol, bfd_boolean relocatable,
+ char **error_message, bfd_vma *pgp)
+{
+ if (bfd_is_und_section (symbol->section)
+ && ! relocatable)
+ {
+ *pgp = 0;
+ return bfd_reloc_undefined;
+ }
+
+ *pgp = _bfd_get_gp_value (output_bfd);
+ if (*pgp == 0
+ && (! relocatable
+ || (symbol->flags & BSF_SECTION_SYM) != 0))
+ {
+ if (relocatable)
+ {
+ /* Make up a value. */
+ *pgp = symbol->section->output_section->vma + 0x4000;
+ _bfd_set_gp_value (output_bfd, *pgp);
+ }
+ else if (!mips_elf_assign_gp (output_bfd, pgp))
+ {
+ *error_message =
+ (char *) _("GP relative relocation when _gp not defined");
+ return bfd_reloc_dangerous;
+ }
+ }
+
+ return bfd_reloc_ok;
+}
+
+/* Do a R_MIPS_GPREL16 relocation. This is a 16 bit value which must
+ become the offset from the gp register. This function also handles
+ R_MIPS_LITERAL relocations, although those can be handled more
+ cleverly because the entries in the .lit8 and .lit4 sections can be
+ merged. */
+
+bfd_reloc_status_type
+_bfd_mips_elf32_gprel16_reloc (bfd *abfd, arelent *reloc_entry,
+ asymbol *symbol, void *data,
+ asection *input_section, bfd *output_bfd,
+ char **error_message)
+{
+ bfd_boolean relocatable;
+ bfd_reloc_status_type ret;
+ bfd_vma gp;
+
+ if (output_bfd != NULL)
+ relocatable = TRUE;
+ else
+ {
+ relocatable = FALSE;
+ output_bfd = symbol->section->output_section->owner;
+ }
+
+ ret = mips_elf_final_gp (output_bfd, symbol, relocatable, error_message,
+ &gp);
+ if (ret != bfd_reloc_ok)
+ return ret;
+
+ return _bfd_mips_elf_gprel16_with_gp (abfd, symbol, reloc_entry,
+ input_section, relocatable,
+ data, gp);
+}
+
+/* Do a R_MIPS_GPREL32 relocation. This is a 32 bit value which must
+ become the offset from the gp register. */
+
+static bfd_reloc_status_type
+mips_elf_gprel32_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+ void *data, asection *input_section, bfd *output_bfd,
+ char **error_message)
+{
+ bfd_boolean relocatable;
+ bfd_reloc_status_type ret;
+ bfd_vma gp;
+
+ if (output_bfd != NULL)
+ relocatable = TRUE;
+ else
+ {
+ relocatable = FALSE;
+ output_bfd = symbol->section->output_section->owner;
+ }
+
+ ret = mips_elf_final_gp (output_bfd, symbol, relocatable,
+ error_message, &gp);
+ if (ret != bfd_reloc_ok)
+ return ret;
+
+ return gprel32_with_gp (abfd, symbol, reloc_entry, input_section,
+ relocatable, data, gp);
+}
+
+static bfd_reloc_status_type
+gprel32_with_gp (bfd *abfd, asymbol *symbol, arelent *reloc_entry,
+ asection *input_section, bfd_boolean relocatable,
+ void *data, bfd_vma gp)
+{
+ bfd_vma relocation;
+ bfd_vma val;
+
+ if (bfd_is_com_section (symbol->section))
+ relocation = 0;
+ else
+ relocation = symbol->value;
+
+ relocation += symbol->section->output_section->vma;
+ relocation += symbol->section->output_offset;
+
+ if (reloc_entry->address > input_section->_cooked_size)
+ return bfd_reloc_outofrange;
+
+ /* Set val to the offset into the section or symbol. */
+ val = reloc_entry->addend;
+
+ if (reloc_entry->howto->partial_inplace)
+ val += bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
+
+ /* Adjust val for the final section location and GP value. If we
+ are producing relocatable output, we don't want to do this for
+ an external symbol. */
+ if (! relocatable
+ || (symbol->flags & BSF_SECTION_SYM) != 0)
+ val += relocation - gp;
+
+ if (reloc_entry->howto->partial_inplace)
+ bfd_put_32 (abfd, val, (bfd_byte *) data + reloc_entry->address);
+ else
+ reloc_entry->addend = val;
+
+ if (relocatable)
+ reloc_entry->address += input_section->output_offset;
+
+ return bfd_reloc_ok;
+}
+
+/* Handle a 64 bit reloc in a 32 bit MIPS ELF file. These are
+ generated when addresses are 64 bits. The upper 32 bits are a simple
+ sign extension. */
+
+static bfd_reloc_status_type
+mips32_64bit_reloc (bfd *abfd, arelent *reloc_entry,
+ asymbol *symbol ATTRIBUTE_UNUSED,
+ void *data, asection *input_section,
+ bfd *output_bfd, char **error_message)
+{
+ bfd_reloc_status_type r;
+ arelent reloc32;
+ unsigned long val;
+ bfd_size_type addr;
+
+ /* Do a normal 32 bit relocation on the lower 32 bits. */
+ reloc32 = *reloc_entry;
+ if (bfd_big_endian (abfd))
+ reloc32.address += 4;
+ reloc32.howto = &elf_mips_howto_table_rel[R_MIPS_32];
+ r = bfd_perform_relocation (abfd, &reloc32, data, input_section,
+ output_bfd, error_message);
+
+ /* Sign extend into the upper 32 bits. */
+ val = bfd_get_32 (abfd, (bfd_byte *) data + reloc32.address);
+ if ((val & 0x80000000) != 0)
+ val = 0xffffffff;
+ else
+ val = 0;
+ addr = reloc_entry->address;
+ if (bfd_little_endian (abfd))
+ addr += 4;
+ bfd_put_32 (abfd, val, (bfd_byte *) data + addr);
+
+ return r;
+}
+
+/* Handle a mips16 jump. */
+
+static bfd_reloc_status_type
+mips16_jump_reloc (bfd *abfd ATTRIBUTE_UNUSED, arelent *reloc_entry,
+ asymbol *symbol, void *data ATTRIBUTE_UNUSED,
+ asection *input_section, bfd *output_bfd,
+ char **error_message ATTRIBUTE_UNUSED)
+{
+ if (output_bfd != NULL
+ && (symbol->flags & BSF_SECTION_SYM) == 0
+ && reloc_entry->addend == 0)
+ {
+ reloc_entry->address += input_section->output_offset;
+ return bfd_reloc_ok;
+ }
+
+ /* FIXME. */
+ {
+ static bfd_boolean warned;
+
+ if (! warned)
+ (*_bfd_error_handler)
+ (_("Linking mips16 objects into %s format is not supported"),
+ bfd_get_target (input_section->output_section->owner));
+ warned = TRUE;
+ }
+
+ return bfd_reloc_undefined;
+}
+
+/* Handle a mips16 GP relative reloc. */
+
+static bfd_reloc_status_type
+mips16_gprel_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+ void *data, asection *input_section, bfd *output_bfd,
+ char **error_message)
+{
+ bfd_boolean relocatable;
+ bfd_reloc_status_type ret;
+ bfd_vma gp;
+ unsigned short extend = 0;
+ unsigned short insn = 0;
+ bfd_signed_vma val;
+ bfd_vma relocation;
+
+ /* If we're relocating, and this is an external symbol, we don't want
+ to change anything. */
+ if (output_bfd != NULL
+ && (symbol->flags & BSF_SECTION_SYM) == 0
+ && (symbol->flags & BSF_LOCAL) != 0)
+ {
+ reloc_entry->address += input_section->output_offset;
+ return bfd_reloc_ok;
+ }
+
+ if (output_bfd != NULL)
+ relocatable = TRUE;
+ else
+ {
+ relocatable = FALSE;
+ output_bfd = symbol->section->output_section->owner;
+ }
+
+ ret = mips_elf_final_gp (output_bfd, symbol, relocatable, error_message,
+ &gp);
+ if (ret != bfd_reloc_ok)
+ return ret;
+
+ if (reloc_entry->address > input_section->_cooked_size)
+ return bfd_reloc_outofrange;
+
+ if (bfd_is_com_section (symbol->section))
+ relocation = 0;
+ else
+ relocation = symbol->value;
+
+ relocation += symbol->section->output_section->vma;
+ relocation += symbol->section->output_offset;
+
+ /* Set val to the offset into the section or symbol. */
+ val = reloc_entry->addend;
+
+ if (reloc_entry->howto->partial_inplace)
+ {
+ /* Pick up the mips16 extend instruction and the real instruction. */
+ extend = bfd_get_16 (abfd, (bfd_byte *) data + reloc_entry->address);
+ insn = bfd_get_16 (abfd, (bfd_byte *) data + reloc_entry->address + 2);
+ val += ((extend & 0x1f) << 11) | (extend & 0x7e0) | (insn & 0x1f);
+ }
+
+ _bfd_mips_elf_sign_extend(val, 16);
+
+ /* Adjust val for the final section location and GP value. If we
+ are producing relocatable output, we don't want to do this for
+ an external symbol. */
+ if (! relocatable
+ || (symbol->flags & BSF_SECTION_SYM) != 0)
+ val += relocation - gp;
+
+ if (reloc_entry->howto->partial_inplace)
+ {
+ bfd_put_16 (abfd,
+ (extend & 0xf800) | ((val >> 11) & 0x1f) | (val & 0x7e0),
+ (bfd_byte *) data + reloc_entry->address);
+ bfd_put_16 (abfd,
+ (insn & 0xffe0) | (val & 0x1f),
+ (bfd_byte *) data + reloc_entry->address + 2);
+ }
+ else
+ reloc_entry->addend = val;
+
+ if (relocatable)
+ reloc_entry->address += input_section->output_offset;
+ else if (((val & ~0xffff) != ~0xffff) && ((val & ~0xffff) != 0))
+ return bfd_reloc_overflow;
+
+ return bfd_reloc_ok;
+}
+
+/* A mapping from BFD reloc types to MIPS ELF reloc types. */
+
+struct elf_reloc_map {
+ bfd_reloc_code_real_type bfd_val;
+ enum elf_mips_reloc_type elf_val;
+};
+
+static const struct elf_reloc_map mips_reloc_map[] =
+{
+ { BFD_RELOC_NONE, R_MIPS_NONE },
+ { BFD_RELOC_16, R_MIPS_16 },
+ { BFD_RELOC_32, R_MIPS_32 },
+ /* There is no BFD reloc for R_MIPS_REL32. */
+ { BFD_RELOC_64, R_MIPS_64 },
+ { BFD_RELOC_MIPS_JMP, R_MIPS_26 },
+ { BFD_RELOC_HI16_S, R_MIPS_HI16 },
+ { BFD_RELOC_LO16, R_MIPS_LO16 },
+ { BFD_RELOC_GPREL16, R_MIPS_GPREL16 },
+ { BFD_RELOC_MIPS_LITERAL, R_MIPS_LITERAL },
+ { BFD_RELOC_MIPS_GOT16, R_MIPS_GOT16 },
+ { BFD_RELOC_16_PCREL, R_MIPS_PC16 },
+ { BFD_RELOC_MIPS_CALL16, R_MIPS_CALL16 },
+ { BFD_RELOC_GPREL32, R_MIPS_GPREL32 },
+ { BFD_RELOC_MIPS_GOT_HI16, R_MIPS_GOT_HI16 },
+ { BFD_RELOC_MIPS_GOT_LO16, R_MIPS_GOT_LO16 },
+ { BFD_RELOC_MIPS_CALL_HI16, R_MIPS_CALL_HI16 },
+ { BFD_RELOC_MIPS_CALL_LO16, R_MIPS_CALL_LO16 },
+ { BFD_RELOC_MIPS_SUB, R_MIPS_SUB },
+ { BFD_RELOC_MIPS_GOT_PAGE, R_MIPS_GOT_PAGE },
+ { BFD_RELOC_MIPS_GOT_OFST, R_MIPS_GOT_OFST },
+ { BFD_RELOC_MIPS_GOT_DISP, R_MIPS_GOT_DISP }
+};
+
+/* Given a BFD reloc type, return a howto structure. */
+
+static reloc_howto_type *
+bfd_elf32_bfd_reloc_type_lookup (bfd *abfd, bfd_reloc_code_real_type code)
+{
+ unsigned int i;
+ reloc_howto_type *howto_table = elf_mips_howto_table_rel;
+
+ for (i = 0; i < sizeof (mips_reloc_map) / sizeof (struct elf_reloc_map);
+ i++)
+ {
+ if (mips_reloc_map[i].bfd_val == code)
+ return &howto_table[(int) mips_reloc_map[i].elf_val];
+ }
+
+ switch (code)
+ {
+ default:
+ bfd_set_error (bfd_error_bad_value);
+ return NULL;
+
+ case BFD_RELOC_CTOR:
+ /* We need to handle BFD_RELOC_CTOR specially.
+ Select the right relocation (R_MIPS_32 or R_MIPS_64) based on the
+ size of addresses of the ABI. */
+ if ((elf_elfheader (abfd)->e_flags & (E_MIPS_ABI_O64
+ | E_MIPS_ABI_EABI64)) != 0)
+ return &elf_mips_ctor64_howto;
+ else
+ return &howto_table[(int) R_MIPS_32];
+
+ case BFD_RELOC_MIPS16_JMP:
+ return &elf_mips16_jump_howto;
+ case BFD_RELOC_MIPS16_GPREL:
+ return &elf_mips16_gprel_howto;
+ case BFD_RELOC_VTABLE_INHERIT:
+ return &elf_mips_gnu_vtinherit_howto;
+ case BFD_RELOC_VTABLE_ENTRY:
+ return &elf_mips_gnu_vtentry_howto;
+ case BFD_RELOC_PCREL_HI16_S:
+ return &elf_mips_gnu_rel_hi16;
+ case BFD_RELOC_PCREL_LO16:
+ return &elf_mips_gnu_rel_lo16;
+ case BFD_RELOC_16_PCREL_S2:
+ return &elf_mips_gnu_rel16_s2;
+ case BFD_RELOC_64_PCREL:
+ return &elf_mips_gnu_pcrel64;
+ case BFD_RELOC_32_PCREL:
+ return &elf_mips_gnu_pcrel32;
+ }
+}
+
+/* Given a MIPS Elf_Internal_Rel, fill in an arelent structure. */
+
+static reloc_howto_type *
+mips_elf32_rtype_to_howto (unsigned int r_type,
+ bfd_boolean rela_p ATTRIBUTE_UNUSED)
+{
+ switch (r_type)
+ {
+ case R_MIPS16_26:
+ return &elf_mips16_jump_howto;
+ case R_MIPS16_GPREL:
+ return &elf_mips16_gprel_howto;
+ case R_MIPS_GNU_VTINHERIT:
+ return &elf_mips_gnu_vtinherit_howto;
+ case R_MIPS_GNU_VTENTRY:
+ return &elf_mips_gnu_vtentry_howto;
+ case R_MIPS_GNU_REL_HI16:
+ return &elf_mips_gnu_rel_hi16;
+ case R_MIPS_GNU_REL_LO16:
+ return &elf_mips_gnu_rel_lo16;
+ case R_MIPS_GNU_REL16_S2:
+ return &elf_mips_gnu_rel16_s2;
+ case R_MIPS_PC64:
+ return &elf_mips_gnu_pcrel64;
+ case R_MIPS_PC32:
+ return &elf_mips_gnu_pcrel32;
+ default:
+ BFD_ASSERT (r_type < (unsigned int) R_MIPS_max);
+ return &elf_mips_howto_table_rel[r_type];
+ }
+}
+
+/* Given a MIPS Elf_Internal_Rel, fill in an arelent structure. */
+
+static void
+mips_info_to_howto_rel (bfd *abfd, arelent *cache_ptr, Elf_Internal_Rela *dst)
+{
+ unsigned int r_type;
+
+ r_type = ELF32_R_TYPE (dst->r_info);
+ cache_ptr->howto = mips_elf32_rtype_to_howto (r_type, FALSE);
+
+ /* The addend for a GPREL16 or LITERAL relocation comes from the GP
+ value for the object file. We get the addend now, rather than
+ when we do the relocation, because the symbol manipulations done
+ by the linker may cause us to lose track of the input BFD. */
+ if (((*cache_ptr->sym_ptr_ptr)->flags & BSF_SECTION_SYM) != 0
+ && (r_type == (unsigned int) R_MIPS_GPREL16
+ || r_type == (unsigned int) R_MIPS_LITERAL))
+ cache_ptr->addend = elf_gp (abfd);
+}
+
+/* Given a MIPS Elf_Internal_Rela, fill in an arelent structure. */
+
+static void
+mips_info_to_howto_rela (bfd *abfd, arelent *cache_ptr, Elf_Internal_Rela *dst)
+{
+ mips_info_to_howto_rel (abfd, cache_ptr, dst);
+
+ /* If we ever need to do any extra processing with dst->r_addend
+ (the field omitted in an Elf_Internal_Rel) we can do it here. */
+}
+
+/* Determine whether a symbol is global for the purposes of splitting
+ the symbol table into global symbols and local symbols. At least
+ on Irix 5, this split must be between section symbols and all other
+ symbols. On most ELF targets the split is between static symbols
+ and externally visible symbols. */
+
+static bfd_boolean
+mips_elf_sym_is_global (bfd *abfd ATTRIBUTE_UNUSED, asymbol *sym)
+{
+ if (SGI_COMPAT (abfd))
+ return (sym->flags & BSF_SECTION_SYM) == 0;
+ else
+ return ((sym->flags & (BSF_GLOBAL | BSF_WEAK)) != 0
+ || bfd_is_und_section (bfd_get_section (sym))
+ || bfd_is_com_section (bfd_get_section (sym)));
+}
+
+/* Set the right machine number for a MIPS ELF file. */
+
+static bfd_boolean
+mips_elf32_object_p (bfd *abfd)
+{
+ unsigned long mach;
+
+ /* Irix 5 and 6 are broken. Object file symbol tables are not always
+ sorted correctly such that local symbols precede global symbols,
+ and the sh_info field in the symbol table is not always right. */
+ if (SGI_COMPAT (abfd))
+ elf_bad_symtab (abfd) = TRUE;
+
+ if (ABI_N32_P (abfd))
+ return FALSE;
+
+ mach = _bfd_elf_mips_mach (elf_elfheader (abfd)->e_flags);
+ bfd_default_set_arch_mach (abfd, bfd_arch_mips, mach);
+
+ return TRUE;
+}
+
+/* MIPS ELF local labels start with '$', not 'L'. */
+
+static bfd_boolean
+mips_elf_is_local_label_name (bfd *abfd, const char *name)
+{
+ if (name[0] == '$')
+ return TRUE;
+
+ /* On Irix 6, the labels go back to starting with '.', so we accept
+ the generic ELF local label syntax as well. */
+ return _bfd_elf_is_local_label_name (abfd, name);
+}
+
+/* Support for core dump NOTE sections. */
+static bfd_boolean
+elf32_mips_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
+{
+ int offset;
+ unsigned int raw_size;
+
+ switch (note->descsz)
+ {
+ default:
+ return FALSE;
+
+ case 256: /* Linux/MIPS */
+ /* 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;
+ raw_size = 180;
+
+ break;
+ }
+
+ /* Make a ".reg/999" section. */
+ return _bfd_elfcore_make_pseudosection (abfd, ".reg",
+ raw_size, note->descpos + offset);
+}
+
+static bfd_boolean
+elf32_mips_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
+{
+ switch (note->descsz)
+ {
+ default:
+ return FALSE;
+
+ case 128: /* Linux/MIPS elf_prpsinfo */
+ elf_tdata (abfd)->core_program
+ = _bfd_elfcore_strndup (abfd, note->descdata + 32, 16);
+ elf_tdata (abfd)->core_command
+ = _bfd_elfcore_strndup (abfd, note->descdata + 48, 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;
+}
+
+/* Depending on the target vector we generate some version of Irix
+ executables or "normal" MIPS ELF ABI executables. */
+static irix_compat_t
+elf32_mips_irix_compat (bfd *abfd)
+{
+ if ((abfd->xvec == &bfd_elf32_bigmips_vec)
+ || (abfd->xvec == &bfd_elf32_littlemips_vec))
+ return ict_irix5;
+ else
+ return ict_none;
+}
+
+/* Given a data section and an in-memory embedded reloc section, store
+ relocation information into the embedded reloc section which can be
+ used at runtime to relocate the data section. This is called by the
+ linker when the --embedded-relocs switch is used. This is called
+ after the add_symbols entry point has been called for all the
+ objects, and before the final_link entry point is called. */
+
+bfd_boolean
+bfd_mips_elf32_create_embedded_relocs (bfd *abfd, struct bfd_link_info *info,
+ asection *datasec, asection *relsec,
+ char **errmsg)
+{
+ Elf_Internal_Shdr *symtab_hdr;
+ Elf_Internal_Sym *isymbuf = NULL;
+ Elf_Internal_Rela *internal_relocs = NULL;
+ Elf_Internal_Rela *irel, *irelend;
+ bfd_byte *p;
+
+ BFD_ASSERT (! info->relocatable);
+
+ *errmsg = NULL;
+
+ if (datasec->reloc_count == 0)
+ return TRUE;
+
+ /* Read this BFD's symbols if we haven't done so already, or get the cached
+ copy if it exists. */
+ symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+ if (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 a copy of the native relocations. */
+ internal_relocs = _bfd_elf_link_read_relocs (abfd, datasec, NULL, NULL,
+ info->keep_memory);
+ if (internal_relocs == NULL)
+ goto error_return;
+
+ relsec->contents = bfd_alloc (abfd, datasec->reloc_count * 12);
+ if (relsec->contents == NULL)
+ goto error_return;
+
+ p = relsec->contents;
+
+ irelend = internal_relocs + datasec->reloc_count;
+
+ for (irel = internal_relocs; irel < irelend; irel++, p += 12)
+ {
+ asection *targetsec;
+
+ /* We are going to write a four byte longword into the runtime
+ reloc section. The longword will be the address in the data
+ section which must be relocated. It is followed by the name
+ of the target section NUL-padded or truncated to 8
+ characters. */
+
+ /* We can only relocate absolute longword relocs at run time. */
+ if ((ELF32_R_TYPE (irel->r_info) != (int) R_MIPS_32) &&
+ (ELF32_R_TYPE (irel->r_info) != (int) R_MIPS_64))
+ {
+ *errmsg = _("unsupported reloc type");
+ bfd_set_error (bfd_error_bad_value);
+ goto error_return;
+ }
+ /* Get the target section referred to by the reloc. */
+ if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
+ {
+ Elf_Internal_Sym *isym;
+
+ /* A local symbol. */
+ isym = isymbuf + ELF32_R_SYM (irel->r_info);
+ targetsec = bfd_section_from_elf_index (abfd, isym->st_shndx);
+ }
+ else
+ {
+ unsigned long indx;
+ struct elf_link_hash_entry *h;
+
+ /* An external symbol. */
+ indx = ELF32_R_SYM (irel->r_info);
+ h = elf_sym_hashes (abfd)[indx];
+ targetsec = NULL;
+ /*
+ For some reason, in certain programs, the symbol will
+ not be in the hash table. It seems to happen when you
+ declare a static table of pointers to const external structures.
+ In this case, the relocs are relative to data, not
+ text, so just treating it like an undefined link
+ should be sufficient. */
+ BFD_ASSERT(h != NULL);
+ if (h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ targetsec = h->root.u.def.section;
+ }
+
+
+ /*
+ Set the low bit of the relocation offset if it's a MIPS64 reloc.
+ Relocations will always be on (at least) 32-bit boundaries. */
+
+ bfd_put_32 (abfd, ((irel->r_offset + datasec->output_offset) +
+ ((ELF32_R_TYPE (irel->r_info) == (int) R_MIPS_64) ? 1 : 0)),
+ p);
+ memset (p + 4, 0, 8);
+ if (targetsec != NULL)
+ strncpy (p + 4, targetsec->output_section->name, 8);
+ }
+
+ if (internal_relocs != NULL
+ && elf_section_data (datasec)->relocs != internal_relocs)
+ free (internal_relocs);
+ if (isymbuf != NULL
+ && symtab_hdr->contents != (unsigned char *) isymbuf)
+ free (isymbuf);
+ return TRUE;
+
+ error_return:
+ if (internal_relocs != NULL
+ && elf_section_data (datasec)->relocs != internal_relocs)
+ free (internal_relocs);
+ if (isymbuf != NULL
+ && symtab_hdr->contents != (unsigned char *) isymbuf)
+ free (isymbuf);
+ return FALSE;
+}
+
+/* ECOFF swapping routines. These are used when dealing with the
+ .mdebug section, which is in the ECOFF debugging format. */
+static const struct ecoff_debug_swap mips_elf32_ecoff_debug_swap = {
+ /* Symbol table magic number. */
+ magicSym,
+ /* Alignment of debugging information. E.g., 4. */
+ 4,
+ /* Sizes of external symbolic information. */
+ sizeof (struct hdr_ext),
+ sizeof (struct dnr_ext),
+ sizeof (struct pdr_ext),
+ sizeof (struct sym_ext),
+ sizeof (struct opt_ext),
+ sizeof (struct fdr_ext),
+ sizeof (struct rfd_ext),
+ sizeof (struct ext_ext),
+ /* Functions to swap in external symbolic data. */
+ ecoff_swap_hdr_in,
+ ecoff_swap_dnr_in,
+ ecoff_swap_pdr_in,
+ ecoff_swap_sym_in,
+ ecoff_swap_opt_in,
+ ecoff_swap_fdr_in,
+ ecoff_swap_rfd_in,
+ ecoff_swap_ext_in,
+ _bfd_ecoff_swap_tir_in,
+ _bfd_ecoff_swap_rndx_in,
+ /* Functions to swap out external symbolic data. */
+ ecoff_swap_hdr_out,
+ ecoff_swap_dnr_out,
+ ecoff_swap_pdr_out,
+ ecoff_swap_sym_out,
+ ecoff_swap_opt_out,
+ ecoff_swap_fdr_out,
+ ecoff_swap_rfd_out,
+ ecoff_swap_ext_out,
+ _bfd_ecoff_swap_tir_out,
+ _bfd_ecoff_swap_rndx_out,
+ /* Function to read in symbolic data. */
+ _bfd_mips_elf_read_ecoff_info
+};
+
+#define ELF_ARCH bfd_arch_mips
+#define ELF_MACHINE_CODE EM_MIPS
+
+#define elf_backend_collect TRUE
+#define elf_backend_type_change_ok TRUE
+#define elf_backend_can_gc_sections TRUE
+#define elf_info_to_howto mips_info_to_howto_rela
+#define elf_info_to_howto_rel mips_info_to_howto_rel
+#define elf_backend_sym_is_global mips_elf_sym_is_global
+#define elf_backend_object_p mips_elf32_object_p
+#define elf_backend_symbol_processing _bfd_mips_elf_symbol_processing
+#define elf_backend_section_processing _bfd_mips_elf_section_processing
+#define elf_backend_section_from_shdr _bfd_mips_elf_section_from_shdr
+#define elf_backend_fake_sections _bfd_mips_elf_fake_sections
+#define elf_backend_section_from_bfd_section \
+ _bfd_mips_elf_section_from_bfd_section
+#define elf_backend_add_symbol_hook _bfd_mips_elf_add_symbol_hook
+#define elf_backend_link_output_symbol_hook \
+ _bfd_mips_elf_link_output_symbol_hook
+#define elf_backend_create_dynamic_sections \
+ _bfd_mips_elf_create_dynamic_sections
+#define elf_backend_check_relocs _bfd_mips_elf_check_relocs
+#define elf_backend_adjust_dynamic_symbol \
+ _bfd_mips_elf_adjust_dynamic_symbol
+#define elf_backend_always_size_sections \
+ _bfd_mips_elf_always_size_sections
+#define elf_backend_size_dynamic_sections \
+ _bfd_mips_elf_size_dynamic_sections
+#define elf_backend_relocate_section _bfd_mips_elf_relocate_section
+#define elf_backend_finish_dynamic_symbol \
+ _bfd_mips_elf_finish_dynamic_symbol
+#define elf_backend_finish_dynamic_sections \
+ _bfd_mips_elf_finish_dynamic_sections
+#define elf_backend_final_write_processing \
+ _bfd_mips_elf_final_write_processing
+#define elf_backend_additional_program_headers \
+ _bfd_mips_elf_additional_program_headers
+#define elf_backend_modify_segment_map _bfd_mips_elf_modify_segment_map
+#define elf_backend_gc_mark_hook _bfd_mips_elf_gc_mark_hook
+#define elf_backend_gc_sweep_hook _bfd_mips_elf_gc_sweep_hook
+#define elf_backend_copy_indirect_symbol \
+ _bfd_mips_elf_copy_indirect_symbol
+#define elf_backend_hide_symbol _bfd_mips_elf_hide_symbol
+#define elf_backend_grok_prstatus elf32_mips_grok_prstatus
+#define elf_backend_grok_psinfo elf32_mips_grok_psinfo
+#define elf_backend_ecoff_debug_swap &mips_elf32_ecoff_debug_swap
+
+#define elf_backend_got_header_size (4 * MIPS_RESERVED_GOTNO)
+#define elf_backend_may_use_rel_p 1
+#define elf_backend_may_use_rela_p 0
+#define elf_backend_default_use_rela_p 0
+#define elf_backend_sign_extend_vma TRUE
+
+#define elf_backend_discard_info _bfd_mips_elf_discard_info
+#define elf_backend_ignore_discarded_relocs \
+ _bfd_mips_elf_ignore_discarded_relocs
+#define elf_backend_mips_irix_compat elf32_mips_irix_compat
+#define elf_backend_mips_rtype_to_howto mips_elf32_rtype_to_howto
+#define bfd_elf32_bfd_is_local_label_name \
+ mips_elf_is_local_label_name
+#define bfd_elf32_find_nearest_line _bfd_mips_elf_find_nearest_line
+#define bfd_elf32_new_section_hook _bfd_mips_elf_new_section_hook
+#define bfd_elf32_set_section_contents _bfd_mips_elf_set_section_contents
+#define bfd_elf32_bfd_get_relocated_section_contents \
+ _bfd_elf_mips_get_relocated_section_contents
+#define bfd_elf32_bfd_link_hash_table_create \
+ _bfd_mips_elf_link_hash_table_create
+#define bfd_elf32_bfd_final_link _bfd_mips_elf_final_link
+#define bfd_elf32_bfd_merge_private_bfd_data \
+ _bfd_mips_elf_merge_private_bfd_data
+#define bfd_elf32_bfd_set_private_flags _bfd_mips_elf_set_private_flags
+#define bfd_elf32_bfd_print_private_bfd_data \
+ _bfd_mips_elf_print_private_bfd_data
+
+/* Support for SGI-ish mips targets. */
+#define TARGET_LITTLE_SYM bfd_elf32_littlemips_vec
+#define TARGET_LITTLE_NAME "elf32-littlemips"
+#define TARGET_BIG_SYM bfd_elf32_bigmips_vec
+#define TARGET_BIG_NAME "elf32-bigmips"
+
+/* The SVR4 MIPS ABI says that this should be 0x10000, but Irix 5 uses
+ a value of 0x1000, and we are compatible. */
+#define ELF_MAXPAGESIZE 0x1000
+
+#include "elf32-target.h"
+
+/* Support for traditional mips targets. */
+#undef TARGET_LITTLE_SYM
+#undef TARGET_LITTLE_NAME
+#undef TARGET_BIG_SYM
+#undef TARGET_BIG_NAME
+
+#undef ELF_MAXPAGESIZE
+
+#define TARGET_LITTLE_SYM bfd_elf32_tradlittlemips_vec
+#define TARGET_LITTLE_NAME "elf32-tradlittlemips"
+#define TARGET_BIG_SYM bfd_elf32_tradbigmips_vec
+#define TARGET_BIG_NAME "elf32-tradbigmips"
+
+/* The SVR4 MIPS ABI says that this should be 0x10000, and Linux uses
+ page sizes of up to that limit, so we need to respect it. */
+#define ELF_MAXPAGESIZE 0x10000
+#define elf32_bed elf32_tradbed
+
+/* Include the target file again for this target. */
+#include "elf32-target.h"
diff --git a/contrib/binutils/bfd/elf64-mips.c b/contrib/binutils/bfd/elf64-mips.c
new file mode 100644
index 0000000..fa3b494
--- /dev/null
+++ b/contrib/binutils/bfd/elf64-mips.c
@@ -0,0 +1,2815 @@
+/* MIPS-specific support for 64-bit ELF
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
+ Free Software Foundation, Inc.
+ Ian Lance Taylor, Cygnus Support
+ Linker support added by Mark Mitchell, CodeSourcery, LLC.
+ <mark@codesourcery.com>
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* This file supports the 64-bit MIPS ELF ABI.
+
+ The MIPS 64-bit ELF ABI uses an unusual reloc format. This file
+ overrides the usual ELF reloc handling, and handles reading and
+ writing the relocations here. */
+
+/* TODO: Many things are unsupported, even if there is some code for it
+ . (which was mostly stolen from elf32-mips.c and slightly adapted).
+ .
+ . - Relocation handling for REL relocs is wrong in many cases and
+ . generally untested.
+ . - Relocation handling for RELA relocs related to GOT support are
+ . also likely to be wrong.
+ . - Support for MIPS16 is untested.
+ . - Combined relocs with RSS_* entries are unsupported.
+ . - The whole GOT handling for NewABI is missing, some parts of
+ . the OldABI version is still lying around and should be removed.
+ */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+#include "aout/ar.h"
+#include "bfdlink.h"
+#include "genlink.h"
+#include "elf-bfd.h"
+#include "elfxx-mips.h"
+#include "elf/mips.h"
+
+/* Get the ECOFF swapping routines. The 64-bit ABI is not supposed to
+ use ECOFF. However, we support it anyhow for an easier changeover. */
+#include "coff/sym.h"
+#include "coff/symconst.h"
+#include "coff/internal.h"
+#include "coff/ecoff.h"
+/* The 64 bit versions of the mdebug data structures are in alpha.h. */
+#include "coff/alpha.h"
+#define ECOFF_SIGNED_64
+#include "ecoffswap.h"
+
+static void mips_elf64_swap_reloc_in
+ (bfd *, const Elf64_Mips_External_Rel *, Elf64_Mips_Internal_Rela *);
+static void mips_elf64_swap_reloca_in
+ (bfd *, const Elf64_Mips_External_Rela *, Elf64_Mips_Internal_Rela *);
+static void mips_elf64_swap_reloc_out
+ (bfd *, const Elf64_Mips_Internal_Rela *, Elf64_Mips_External_Rel *);
+static void mips_elf64_swap_reloca_out
+ (bfd *, const Elf64_Mips_Internal_Rela *, Elf64_Mips_External_Rela *);
+static void mips_elf64_be_swap_reloc_in
+ (bfd *, const bfd_byte *, Elf_Internal_Rela *);
+static void mips_elf64_be_swap_reloc_out
+ (bfd *, const Elf_Internal_Rela *, bfd_byte *);
+static void mips_elf64_be_swap_reloca_in
+ (bfd *, const bfd_byte *, Elf_Internal_Rela *);
+static void mips_elf64_be_swap_reloca_out
+ (bfd *, const Elf_Internal_Rela *, bfd_byte *);
+static reloc_howto_type *bfd_elf64_bfd_reloc_type_lookup
+ (bfd *, bfd_reloc_code_real_type);
+static reloc_howto_type *mips_elf64_rtype_to_howto
+ (unsigned int, bfd_boolean);
+static void mips_elf64_info_to_howto_rel
+ (bfd *, arelent *, Elf_Internal_Rela *);
+static void mips_elf64_info_to_howto_rela
+ (bfd *, arelent *, Elf_Internal_Rela *);
+static long mips_elf64_get_reloc_upper_bound
+ (bfd *, asection *);
+static long mips_elf64_canonicalize_reloc
+ (bfd *, asection *, arelent **, asymbol **);
+static long mips_elf64_get_dynamic_reloc_upper_bound
+ (bfd *);
+static long mips_elf64_canonicalize_dynamic_reloc
+ (bfd *, arelent **, asymbol **);
+static bfd_boolean mips_elf64_slurp_one_reloc_table
+ (bfd *, asection *, Elf_Internal_Shdr *, bfd_size_type, arelent *,
+ asymbol **, bfd_boolean);
+static bfd_boolean mips_elf64_slurp_reloc_table
+ (bfd *, asection *, asymbol **, bfd_boolean);
+static void mips_elf64_write_relocs
+ (bfd *, asection *, void *);
+static void mips_elf64_write_rel
+ (bfd *, asection *, Elf_Internal_Shdr *, int *, void *);
+static void mips_elf64_write_rela
+ (bfd *, asection *, Elf_Internal_Shdr *, int *, void *);
+static bfd_reloc_status_type mips_elf64_gprel16_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type mips_elf64_literal_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type mips_elf64_gprel32_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type mips_elf64_shift6_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type mips16_jump_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type mips16_gprel_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_boolean mips_elf64_assign_gp
+ (bfd *, bfd_vma *);
+static bfd_reloc_status_type mips_elf64_final_gp
+ (bfd *, asymbol *, bfd_boolean, char **, bfd_vma *);
+static bfd_boolean mips_elf64_object_p
+ (bfd *);
+static irix_compat_t elf64_mips_irix_compat
+ (bfd *);
+static bfd_boolean elf64_mips_grok_prstatus
+ (bfd *, Elf_Internal_Note *);
+static bfd_boolean elf64_mips_grok_psinfo
+ (bfd *, Elf_Internal_Note *);
+
+extern const bfd_target bfd_elf64_bigmips_vec;
+extern const bfd_target bfd_elf64_littlemips_vec;
+
+/* In case we're on a 32-bit machine, construct a 64-bit "-1" value
+ from smaller values. Start with zero, widen, *then* decrement. */
+#define MINUS_ONE (((bfd_vma)0) - 1)
+
+/* The number of local .got entries we reserve. */
+#define MIPS_RESERVED_GOTNO (2)
+
+/* The relocation table used for SHT_REL sections. */
+
+static reloc_howto_type mips_elf64_howto_table_rel[] =
+{
+ /* No relocation. */
+ HOWTO (R_MIPS_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_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_NONE", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 16 bit relocation. */
+ HOWTO (R_MIPS_16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit relocation. */
+ HOWTO (R_MIPS_32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit symbol relative relocation. */
+ HOWTO (R_MIPS_REL32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_REL32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 26 bit jump address. */
+ HOWTO (R_MIPS_26, /* type */
+ 2, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 26, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ /* This needs complex overflow
+ detection, because the upper 36
+ bits must match the PC + 4. */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_26", /* name */
+ TRUE, /* partial_inplace */
+ 0x03ffffff, /* src_mask */
+ 0x03ffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* R_MIPS_HI16 and R_MIPS_LO16 are unsupported for NewABI REL.
+ However, the native IRIX6 tools use them, so we try our best. */
+
+ /* High 16 bits of symbol value. */
+ HOWTO (R_MIPS_HI16, /* type */
+ 16, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_hi16_reloc, /* special_function */
+ "R_MIPS_HI16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of symbol value. */
+ HOWTO (R_MIPS_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_lo16_reloc, /* special_function */
+ "R_MIPS_LO16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* GP relative reference. */
+ HOWTO (R_MIPS_GPREL16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips_elf64_gprel16_reloc, /* special_function */
+ "R_MIPS_GPREL16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Reference to literal section. */
+ HOWTO (R_MIPS_LITERAL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips_elf64_literal_reloc, /* special_function */
+ "R_MIPS_LITERAL", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Reference to global offset table. */
+ HOWTO (R_MIPS_GOT16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_got16_reloc, /* special_function */
+ "R_MIPS_GOT16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 16 bit PC relative reference. */
+ HOWTO (R_MIPS_PC16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_PC16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* 16 bit call through global offset table. */
+ HOWTO (R_MIPS_CALL16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_CALL16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit GP relative reference. */
+ HOWTO (R_MIPS_GPREL32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ mips_elf64_gprel32_reloc, /* special_function */
+ "R_MIPS_GPREL32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (13),
+ EMPTY_HOWTO (14),
+ EMPTY_HOWTO (15),
+
+ /* A 5 bit shift field. */
+ HOWTO (R_MIPS_SHIFT5, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 5, /* bitsize */
+ FALSE, /* pc_relative */
+ 6, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_SHIFT5", /* name */
+ TRUE, /* partial_inplace */
+ 0x000007c0, /* src_mask */
+ 0x000007c0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A 6 bit shift field. */
+ HOWTO (R_MIPS_SHIFT6, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 6, /* bitsize */
+ FALSE, /* pc_relative */
+ 6, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ mips_elf64_shift6_reloc, /* special_function */
+ "R_MIPS_SHIFT6", /* name */
+ TRUE, /* partial_inplace */
+ 0x000007c4, /* src_mask */
+ 0x000007c4, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 64 bit relocation. */
+ HOWTO (R_MIPS_64, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_64", /* name */
+ TRUE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Displacement in the global offset table. */
+ HOWTO (R_MIPS_GOT_DISP, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_DISP", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Displacement to page pointer in the global offset table. */
+ HOWTO (R_MIPS_GOT_PAGE, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_PAGE", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Offset from page pointer in the global offset table. */
+ HOWTO (R_MIPS_GOT_OFST, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_OFST", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_MIPS_GOT_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_HI16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_MIPS_GOT_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_LO16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 64 bit subtraction. */
+ HOWTO (R_MIPS_SUB, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_SUB", /* name */
+ TRUE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Insert the addend as an instruction. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_MIPS_INSERT_A, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_INSERT_A", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Insert the addend as an instruction, and change all relocations
+ to refer to the old instruction at the address. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_MIPS_INSERT_B, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_INSERT_B", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Delete a 32 bit instruction. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_MIPS_DELETE, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_DELETE", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* The MIPS ELF64 ABI Draft wants us to support these for REL relocations.
+ We don't, because
+ a) It means building the addend from a R_MIPS_HIGHEST/R_MIPS_HIGHER/
+ R_MIPS_HI16/R_MIPS_LO16 sequence with varying ordering, using
+ fallable heuristics.
+ b) No other NewABI toolchain actually emits such relocations. */
+ EMPTY_HOWTO (R_MIPS_HIGHER),
+ EMPTY_HOWTO (R_MIPS_HIGHEST),
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_MIPS_CALL_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_CALL_HI16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_MIPS_CALL_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_CALL_LO16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Section displacement, used by an associated event location section. */
+ HOWTO (R_MIPS_SCN_DISP, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_SCN_DISP", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_MIPS_REL16, /* type */
+ 0, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_REL16", /* name */
+ TRUE, /* partial_inplace */
+ 0xffff, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* These two are obsolete. */
+ EMPTY_HOWTO (R_MIPS_ADD_IMMEDIATE),
+ EMPTY_HOWTO (R_MIPS_PJUMP),
+
+ /* Similiar to R_MIPS_REL32, but used for relocations in a GOT section.
+ It must be used for multigot GOT's (and only there). */
+ HOWTO (R_MIPS_RELGOT, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_RELGOT", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Protected jump conversion. This is an optimization hint. No
+ relocation is required for correctness. */
+ HOWTO (R_MIPS_JALR, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_JALR", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x00000000, /* dst_mask */
+ FALSE), /* pcrel_offset */
+};
+
+/* The relocation table used for SHT_RELA sections. */
+
+static reloc_howto_type mips_elf64_howto_table_rela[] =
+{
+ /* No relocation. */
+ HOWTO (R_MIPS_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_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_NONE", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 16 bit relocation. */
+ HOWTO (R_MIPS_16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit relocation. */
+ HOWTO (R_MIPS_32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_32", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit symbol relative relocation. */
+ HOWTO (R_MIPS_REL32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_REL32", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 26 bit jump address. */
+ HOWTO (R_MIPS_26, /* type */
+ 2, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 26, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ /* This needs complex overflow
+ detection, because the upper 36
+ bits must match the PC + 4. */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_26", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x03ffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of symbol value. */
+ HOWTO (R_MIPS_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_HI16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of symbol value. */
+ HOWTO (R_MIPS_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_LO16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* GP relative reference. */
+ HOWTO (R_MIPS_GPREL16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips_elf64_gprel16_reloc, /* special_function */
+ "R_MIPS_GPREL16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Reference to literal section. */
+ HOWTO (R_MIPS_LITERAL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips_elf64_literal_reloc, /* special_function */
+ "R_MIPS_LITERAL", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Reference to global offset table. */
+ HOWTO (R_MIPS_GOT16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 16 bit PC relative reference. */
+ HOWTO (R_MIPS_PC16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_PC16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* 16 bit call through global offset table. */
+ HOWTO (R_MIPS_CALL16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_CALL16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit GP relative reference. */
+ HOWTO (R_MIPS_GPREL32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ mips_elf64_gprel32_reloc, /* special_function */
+ "R_MIPS_GPREL32", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (13),
+ EMPTY_HOWTO (14),
+ EMPTY_HOWTO (15),
+
+ /* A 5 bit shift field. */
+ HOWTO (R_MIPS_SHIFT5, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 5, /* bitsize */
+ FALSE, /* pc_relative */
+ 6, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_SHIFT5", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x000007c0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A 6 bit shift field. */
+ HOWTO (R_MIPS_SHIFT6, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 6, /* bitsize */
+ FALSE, /* pc_relative */
+ 6, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ mips_elf64_shift6_reloc, /* special_function */
+ "R_MIPS_SHIFT6", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x000007c4, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 64 bit relocation. */
+ HOWTO (R_MIPS_64, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_64", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Displacement in the global offset table. */
+ HOWTO (R_MIPS_GOT_DISP, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_DISP", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Displacement to page pointer in the global offset table. */
+ HOWTO (R_MIPS_GOT_PAGE, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_PAGE", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Offset from page pointer in the global offset table. */
+ HOWTO (R_MIPS_GOT_OFST, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_OFST", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_MIPS_GOT_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_HI16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_MIPS_GOT_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_LO16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 64 bit subtraction. */
+ HOWTO (R_MIPS_SUB, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_SUB", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Insert the addend as an instruction. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_MIPS_INSERT_A, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_INSERT_A", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Insert the addend as an instruction, and change all relocations
+ to refer to the old instruction at the address. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_MIPS_INSERT_B, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_INSERT_B", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Delete a 32 bit instruction. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_MIPS_DELETE, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_DELETE", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Get the higher value of a 64 bit addend. */
+ HOWTO (R_MIPS_HIGHER, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_HIGHER", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Get the highest value of a 64 bit addend. */
+ HOWTO (R_MIPS_HIGHEST, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_HIGHEST", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_MIPS_CALL_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_CALL_HI16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_MIPS_CALL_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_CALL_LO16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Section displacement, used by an associated event location section. */
+ HOWTO (R_MIPS_SCN_DISP, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_SCN_DISP", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_MIPS_REL16, /* type */
+ 0, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_REL16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* These two are obsolete. */
+ EMPTY_HOWTO (R_MIPS_ADD_IMMEDIATE),
+ EMPTY_HOWTO (R_MIPS_PJUMP),
+
+ /* Similiar to R_MIPS_REL32, but used for relocations in a GOT section.
+ It must be used for multigot GOT's (and only there). */
+ HOWTO (R_MIPS_RELGOT, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_RELGOT", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Protected jump conversion. This is an optimization hint. No
+ relocation is required for correctness. */
+ HOWTO (R_MIPS_JALR, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_JALR", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x00000000, /* dst_mask */
+ FALSE), /* pcrel_offset */
+};
+
+/* The reloc used for the mips16 jump instruction. */
+static reloc_howto_type elf_mips16_jump_howto =
+ HOWTO (R_MIPS16_26, /* type */
+ 2, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 26, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ /* This needs complex overflow
+ detection, because the upper four
+ bits must match the PC. */
+ mips16_jump_reloc, /* special_function */
+ "R_MIPS16_26", /* name */
+ TRUE, /* partial_inplace */
+ 0x3ffffff, /* src_mask */
+ 0x3ffffff, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+/* The reloc used for the mips16 gprel instruction. */
+static reloc_howto_type elf_mips16_gprel_howto =
+ HOWTO (R_MIPS16_GPREL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips16_gprel_reloc, /* special_function */
+ "R_MIPS16_GPREL", /* name */
+ TRUE, /* partial_inplace */
+ 0x07ff001f, /* src_mask */
+ 0x07ff001f, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+/* GNU extension to record C++ vtable hierarchy */
+static reloc_howto_type elf_mips_gnu_vtinherit_howto =
+ HOWTO (R_MIPS_GNU_VTINHERIT, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 0, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ NULL, /* special_function */
+ "R_MIPS_GNU_VTINHERIT", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+/* GNU extension to record C++ vtable member usage */
+static reloc_howto_type elf_mips_gnu_vtentry_howto =
+ HOWTO (R_MIPS_GNU_VTENTRY, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 0, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_elf_rel_vtable_reloc_fn, /* special_function */
+ "R_MIPS_GNU_VTENTRY", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+/* 16 bit offset for pc-relative branches. */
+static reloc_howto_type elf_mips_gnu_rel16_s2 =
+ HOWTO (R_MIPS_GNU_REL16_S2, /* type */
+ 2, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GNU_REL16_S2", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ TRUE); /* pcrel_offset */
+
+/* 16 bit offset for pc-relative branches. */
+static reloc_howto_type elf_mips_gnu_rela16_s2 =
+ HOWTO (R_MIPS_GNU_REL16_S2, /* type */
+ 2, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GNU_REL16_S2", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ TRUE); /* pcrel_offset */
+
+/* Swap in a MIPS 64-bit Rel reloc. */
+
+static void
+mips_elf64_swap_reloc_in (bfd *abfd, const Elf64_Mips_External_Rel *src,
+ Elf64_Mips_Internal_Rela *dst)
+{
+ dst->r_offset = H_GET_64 (abfd, src->r_offset);
+ dst->r_sym = H_GET_32 (abfd, src->r_sym);
+ dst->r_ssym = H_GET_8 (abfd, src->r_ssym);
+ dst->r_type3 = H_GET_8 (abfd, src->r_type3);
+ dst->r_type2 = H_GET_8 (abfd, src->r_type2);
+ dst->r_type = H_GET_8 (abfd, src->r_type);
+ dst->r_addend = 0;
+}
+
+/* Swap in a MIPS 64-bit Rela reloc. */
+
+static void
+mips_elf64_swap_reloca_in (bfd *abfd, const Elf64_Mips_External_Rela *src,
+ Elf64_Mips_Internal_Rela *dst)
+{
+ dst->r_offset = H_GET_64 (abfd, src->r_offset);
+ dst->r_sym = H_GET_32 (abfd, src->r_sym);
+ dst->r_ssym = H_GET_8 (abfd, src->r_ssym);
+ dst->r_type3 = H_GET_8 (abfd, src->r_type3);
+ dst->r_type2 = H_GET_8 (abfd, src->r_type2);
+ dst->r_type = H_GET_8 (abfd, src->r_type);
+ dst->r_addend = H_GET_S64 (abfd, src->r_addend);
+}
+
+/* Swap out a MIPS 64-bit Rel reloc. */
+
+static void
+mips_elf64_swap_reloc_out (bfd *abfd, const Elf64_Mips_Internal_Rela *src,
+ Elf64_Mips_External_Rel *dst)
+{
+ H_PUT_64 (abfd, src->r_offset, dst->r_offset);
+ H_PUT_32 (abfd, src->r_sym, dst->r_sym);
+ H_PUT_8 (abfd, src->r_ssym, dst->r_ssym);
+ H_PUT_8 (abfd, src->r_type3, dst->r_type3);
+ H_PUT_8 (abfd, src->r_type2, dst->r_type2);
+ H_PUT_8 (abfd, src->r_type, dst->r_type);
+}
+
+/* Swap out a MIPS 64-bit Rela reloc. */
+
+static void
+mips_elf64_swap_reloca_out (bfd *abfd, const Elf64_Mips_Internal_Rela *src,
+ Elf64_Mips_External_Rela *dst)
+{
+ H_PUT_64 (abfd, src->r_offset, dst->r_offset);
+ H_PUT_32 (abfd, src->r_sym, dst->r_sym);
+ H_PUT_8 (abfd, src->r_ssym, dst->r_ssym);
+ H_PUT_8 (abfd, src->r_type3, dst->r_type3);
+ H_PUT_8 (abfd, src->r_type2, dst->r_type2);
+ H_PUT_8 (abfd, src->r_type, dst->r_type);
+ H_PUT_S64 (abfd, src->r_addend, dst->r_addend);
+}
+
+/* Swap in a MIPS 64-bit Rel reloc. */
+
+static void
+mips_elf64_be_swap_reloc_in (bfd *abfd, const bfd_byte *src,
+ Elf_Internal_Rela *dst)
+{
+ Elf64_Mips_Internal_Rela mirel;
+
+ mips_elf64_swap_reloc_in (abfd,
+ (const Elf64_Mips_External_Rel *) src,
+ &mirel);
+
+ dst[0].r_offset = mirel.r_offset;
+ dst[0].r_info = ELF64_R_INFO (mirel.r_sym, mirel.r_type);
+ dst[0].r_addend = 0;
+ dst[1].r_offset = mirel.r_offset;
+ dst[1].r_info = ELF64_R_INFO (mirel.r_ssym, mirel.r_type2);
+ dst[1].r_addend = 0;
+ dst[2].r_offset = mirel.r_offset;
+ dst[2].r_info = ELF64_R_INFO (STN_UNDEF, mirel.r_type3);
+ dst[2].r_addend = 0;
+}
+
+/* Swap in a MIPS 64-bit Rela reloc. */
+
+static void
+mips_elf64_be_swap_reloca_in (bfd *abfd, const bfd_byte *src,
+ Elf_Internal_Rela *dst)
+{
+ Elf64_Mips_Internal_Rela mirela;
+
+ mips_elf64_swap_reloca_in (abfd,
+ (const Elf64_Mips_External_Rela *) src,
+ &mirela);
+
+ dst[0].r_offset = mirela.r_offset;
+ dst[0].r_info = ELF64_R_INFO (mirela.r_sym, mirela.r_type);
+ dst[0].r_addend = mirela.r_addend;
+ dst[1].r_offset = mirela.r_offset;
+ dst[1].r_info = ELF64_R_INFO (mirela.r_ssym, mirela.r_type2);
+ dst[1].r_addend = 0;
+ dst[2].r_offset = mirela.r_offset;
+ dst[2].r_info = ELF64_R_INFO (STN_UNDEF, mirela.r_type3);
+ dst[2].r_addend = 0;
+}
+
+/* Swap out a MIPS 64-bit Rel reloc. */
+
+static void
+mips_elf64_be_swap_reloc_out (bfd *abfd, const Elf_Internal_Rela *src,
+ bfd_byte *dst)
+{
+ Elf64_Mips_Internal_Rela mirel;
+
+ mirel.r_offset = src[0].r_offset;
+ BFD_ASSERT(src[0].r_offset == src[1].r_offset);
+#if 0
+ BFD_ASSERT(src[0].r_offset == src[2].r_offset);
+#endif
+
+ mirel.r_type = ELF64_MIPS_R_TYPE (src[0].r_info);
+ mirel.r_sym = ELF64_R_SYM (src[0].r_info);
+ mirel.r_type2 = ELF64_MIPS_R_TYPE (src[1].r_info);
+ mirel.r_ssym = ELF64_MIPS_R_SSYM (src[1].r_info);
+ mirel.r_type3 = ELF64_MIPS_R_TYPE (src[2].r_info);
+
+ mips_elf64_swap_reloc_out (abfd, &mirel,
+ (Elf64_Mips_External_Rel *) dst);
+}
+
+/* Swap out a MIPS 64-bit Rela reloc. */
+
+static void
+mips_elf64_be_swap_reloca_out (bfd *abfd, const Elf_Internal_Rela *src,
+ bfd_byte *dst)
+{
+ Elf64_Mips_Internal_Rela mirela;
+
+ mirela.r_offset = src[0].r_offset;
+ BFD_ASSERT(src[0].r_offset == src[1].r_offset);
+ BFD_ASSERT(src[0].r_offset == src[2].r_offset);
+
+ mirela.r_type = ELF64_MIPS_R_TYPE (src[0].r_info);
+ mirela.r_sym = ELF64_R_SYM (src[0].r_info);
+ mirela.r_addend = src[0].r_addend;
+ BFD_ASSERT(src[1].r_addend == 0);
+ BFD_ASSERT(src[2].r_addend == 0);
+
+ mirela.r_type2 = ELF64_MIPS_R_TYPE (src[1].r_info);
+ mirela.r_ssym = ELF64_MIPS_R_SSYM (src[1].r_info);
+ mirela.r_type3 = ELF64_MIPS_R_TYPE (src[2].r_info);
+
+ mips_elf64_swap_reloca_out (abfd, &mirela,
+ (Elf64_Mips_External_Rela *) dst);
+}
+
+/* Set the GP value for OUTPUT_BFD. Returns FALSE if this is a
+ dangerous relocation. */
+
+static bfd_boolean
+mips_elf64_assign_gp (bfd *output_bfd, bfd_vma *pgp)
+{
+ unsigned int count;
+ asymbol **sym;
+ unsigned int i;
+
+ /* If we've already figured out what GP will be, just return it. */
+ *pgp = _bfd_get_gp_value (output_bfd);
+ if (*pgp)
+ return TRUE;
+
+ count = bfd_get_symcount (output_bfd);
+ sym = bfd_get_outsymbols (output_bfd);
+
+ /* The linker script will have created a symbol named `_gp' with the
+ appropriate value. */
+ if (sym == NULL)
+ i = count;
+ else
+ {
+ for (i = 0; i < count; i++, sym++)
+ {
+ register const char *name;
+
+ name = bfd_asymbol_name (*sym);
+ if (*name == '_' && strcmp (name, "_gp") == 0)
+ {
+ *pgp = bfd_asymbol_value (*sym);
+ _bfd_set_gp_value (output_bfd, *pgp);
+ break;
+ }
+ }
+ }
+
+ if (i >= count)
+ {
+ /* Only get the error once. */
+ *pgp = 4;
+ _bfd_set_gp_value (output_bfd, *pgp);
+ return FALSE;
+ }
+
+ 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
+mips_elf64_final_gp (bfd *output_bfd, asymbol *symbol, bfd_boolean relocatable,
+ char **error_message, bfd_vma *pgp)
+{
+ if (bfd_is_und_section (symbol->section)
+ && ! relocatable)
+ {
+ *pgp = 0;
+ return bfd_reloc_undefined;
+ }
+
+ *pgp = _bfd_get_gp_value (output_bfd);
+ if (*pgp == 0
+ && (! relocatable
+ || (symbol->flags & BSF_SECTION_SYM) != 0))
+ {
+ if (relocatable)
+ {
+ /* Make up a value. */
+ *pgp = symbol->section->output_section->vma /*+ 0x4000*/;
+ _bfd_set_gp_value (output_bfd, *pgp);
+ }
+ else if (!mips_elf64_assign_gp (output_bfd, pgp))
+ {
+ *error_message =
+ (char *) _("GP relative relocation when _gp not defined");
+ return bfd_reloc_dangerous;
+ }
+ }
+
+ return bfd_reloc_ok;
+}
+
+/* Do a R_MIPS_GPREL16 relocation. This is a 16 bit value which must
+ become the offset from the gp register. */
+
+static bfd_reloc_status_type
+mips_elf64_gprel16_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+ void *data, asection *input_section, bfd *output_bfd,
+ char **error_message)
+{
+ bfd_boolean relocatable;
+ bfd_reloc_status_type ret;
+ bfd_vma gp;
+
+ /* If we're relocating, and this is an external symbol, we don't want
+ to change anything. */
+ if (output_bfd != NULL
+ && (symbol->flags & BSF_SECTION_SYM) == 0
+ && (symbol->flags & BSF_LOCAL) != 0)
+ {
+ reloc_entry->address += input_section->output_offset;
+ return bfd_reloc_ok;
+ }
+
+ if (output_bfd != NULL)
+ relocatable = TRUE;
+ else
+ {
+ relocatable = FALSE;
+ output_bfd = symbol->section->output_section->owner;
+ }
+
+ ret = mips_elf64_final_gp (output_bfd, symbol, relocatable, error_message,
+ &gp);
+ if (ret != bfd_reloc_ok)
+ return ret;
+
+ return _bfd_mips_elf_gprel16_with_gp (abfd, symbol, reloc_entry,
+ input_section, relocatable,
+ data, gp);
+}
+
+/* Do a R_MIPS_LITERAL relocation. */
+
+static bfd_reloc_status_type
+mips_elf64_literal_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+ void *data, asection *input_section, bfd *output_bfd,
+ char **error_message)
+{
+ bfd_boolean relocatable;
+ bfd_reloc_status_type ret;
+ bfd_vma gp;
+
+ /* If we're relocating, and this is an external symbol, we don't
+ want to change anything. */
+ if (output_bfd != NULL
+ && (symbol->flags & BSF_SECTION_SYM) == 0
+ && (symbol->flags & BSF_LOCAL) != 0)
+ {
+ reloc_entry->address += input_section->output_offset;
+ return bfd_reloc_ok;
+ }
+
+ /* FIXME: The entries in the .lit8 and .lit4 sections should be merged. */
+ if (output_bfd != NULL)
+ relocatable = TRUE;
+ else
+ {
+ relocatable = FALSE;
+ output_bfd = symbol->section->output_section->owner;
+ }
+
+ ret = mips_elf64_final_gp (output_bfd, symbol, relocatable, error_message,
+ &gp);
+ if (ret != bfd_reloc_ok)
+ return ret;
+
+ return _bfd_mips_elf_gprel16_with_gp (abfd, symbol, reloc_entry,
+ input_section, relocatable,
+ data, gp);
+}
+
+/* Do a R_MIPS_GPREL32 relocation. This is a 32 bit value which must
+ become the offset from the gp register. */
+
+static bfd_reloc_status_type
+mips_elf64_gprel32_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+ void *data, asection *input_section, bfd *output_bfd,
+ char **error_message)
+{
+ bfd_boolean relocatable;
+ bfd_reloc_status_type ret;
+ bfd_vma gp;
+ bfd_vma relocation;
+ bfd_vma val;
+
+ /* If we're relocating, and this is an external symbol, we don't want
+ to change anything. */
+ if (output_bfd != NULL
+ && (symbol->flags & BSF_SECTION_SYM) == 0
+ && (symbol->flags & BSF_LOCAL) != 0)
+ {
+ *error_message = (char *)
+ _("32bits gp relative relocation occurs for an external symbol");
+ return bfd_reloc_outofrange;
+ }
+
+ if (output_bfd != NULL)
+ relocatable = TRUE;
+ else
+ {
+ relocatable = FALSE;
+ output_bfd = symbol->section->output_section->owner;
+ }
+
+ ret = mips_elf64_final_gp (output_bfd, symbol, relocatable,
+ error_message, &gp);
+ if (ret != bfd_reloc_ok)
+ return ret;
+
+ if (bfd_is_com_section (symbol->section))
+ relocation = 0;
+ else
+ relocation = symbol->value;
+
+ relocation += symbol->section->output_section->vma;
+ relocation += symbol->section->output_offset;
+
+ if (reloc_entry->address > input_section->_cooked_size)
+ return bfd_reloc_outofrange;
+
+ /* Set val to the offset into the section or symbol. */
+ val = reloc_entry->addend;
+
+ if (reloc_entry->howto->partial_inplace)
+ val += bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
+
+ /* Adjust val for the final section location and GP value. If we
+ are producing relocatable output, we don't want to do this for
+ an external symbol. */
+ if (! relocatable
+ || (symbol->flags & BSF_SECTION_SYM) != 0)
+ val += relocation - gp;
+
+ if (reloc_entry->howto->partial_inplace)
+ bfd_put_32 (abfd, val, (bfd_byte *) data + reloc_entry->address);
+ else
+ reloc_entry->addend = val;
+
+ if (relocatable)
+ reloc_entry->address += input_section->output_offset;
+
+ return bfd_reloc_ok;
+}
+
+/* Do a R_MIPS_SHIFT6 relocation. The MSB of the shift is stored at bit 2,
+ the rest is at bits 6-10. The bitpos already got right by the howto. */
+
+static bfd_reloc_status_type
+mips_elf64_shift6_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+ void *data, asection *input_section, bfd *output_bfd,
+ char **error_message)
+{
+ if (reloc_entry->howto->partial_inplace)
+ {
+ reloc_entry->addend = ((reloc_entry->addend & 0x00007c0)
+ | (reloc_entry->addend & 0x00000800) >> 9);
+ }
+
+ return _bfd_mips_elf_generic_reloc (abfd, reloc_entry, symbol, data,
+ input_section, output_bfd,
+ error_message);
+}
+
+/* Handle a mips16 jump. */
+
+static bfd_reloc_status_type
+mips16_jump_reloc (bfd *abfd ATTRIBUTE_UNUSED, arelent *reloc_entry,
+ asymbol *symbol, void *data ATTRIBUTE_UNUSED,
+ asection *input_section, bfd *output_bfd,
+ char **error_message ATTRIBUTE_UNUSED)
+{
+ if (output_bfd != NULL
+ && (symbol->flags & BSF_SECTION_SYM) == 0
+ && (! reloc_entry->howto->partial_inplace
+ || reloc_entry->addend == 0))
+ {
+ reloc_entry->address += input_section->output_offset;
+ return bfd_reloc_ok;
+ }
+
+ /* FIXME. */
+ {
+ static bfd_boolean warned;
+
+ if (! warned)
+ (*_bfd_error_handler)
+ (_("Linking mips16 objects into %s format is not supported"),
+ bfd_get_target (input_section->output_section->owner));
+ warned = TRUE;
+ }
+
+ return bfd_reloc_undefined;
+}
+
+/* Handle a mips16 GP relative reloc. */
+
+static bfd_reloc_status_type
+mips16_gprel_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+ void *data, asection *input_section, bfd *output_bfd,
+ char **error_message)
+{
+ bfd_boolean relocatable;
+ bfd_reloc_status_type ret;
+ bfd_vma gp;
+ unsigned short extend = 0;
+ unsigned short insn = 0;
+ bfd_signed_vma val;
+ bfd_vma relocation;
+
+ /* If we're relocating, and this is an external symbol with no
+ addend, we don't want to change anything. */
+ if (output_bfd != NULL
+ && (symbol->flags & BSF_SECTION_SYM) == 0
+ && (symbol->flags & BSF_LOCAL) != 0)
+ {
+ reloc_entry->address += input_section->output_offset;
+ return bfd_reloc_ok;
+ }
+
+ if (output_bfd != NULL)
+ relocatable = TRUE;
+ else
+ {
+ relocatable = FALSE;
+ output_bfd = symbol->section->output_section->owner;
+ }
+
+ ret = mips_elf64_final_gp (output_bfd, symbol, relocatable, error_message,
+ &gp);
+ if (ret != bfd_reloc_ok)
+ return ret;
+
+ if (reloc_entry->address > input_section->_cooked_size)
+ return bfd_reloc_outofrange;
+
+ if (bfd_is_com_section (symbol->section))
+ relocation = 0;
+ else
+ relocation = symbol->value;
+
+ relocation += symbol->section->output_section->vma;
+ relocation += symbol->section->output_offset;
+
+ /* Set val to the offset into the section or symbol. */
+ val = reloc_entry->addend;
+
+ if (reloc_entry->howto->partial_inplace)
+ {
+ /* Pick up the mips16 extend instruction and the real instruction. */
+ extend = bfd_get_16 (abfd, (bfd_byte *) data + reloc_entry->address);
+ insn = bfd_get_16 (abfd, (bfd_byte *) data + reloc_entry->address + 2);
+ val += ((extend & 0x1f) << 11) | (extend & 0x7e0) | (insn & 0x1f);
+ }
+
+ _bfd_mips_elf_sign_extend(val, 16);
+
+ /* Adjust val for the final section location and GP value. If we
+ are producing relocatable output, we don't want to do this for
+ an external symbol. */
+ if (! relocatable
+ || (symbol->flags & BSF_SECTION_SYM) != 0)
+ val += relocation - gp;
+
+ if (reloc_entry->howto->partial_inplace)
+ {
+ bfd_put_16 (abfd,
+ (extend & 0xf800) | ((val >> 11) & 0x1f) | (val & 0x7e0),
+ (bfd_byte *) data + reloc_entry->address);
+ bfd_put_16 (abfd,
+ (insn & 0xffe0) | (val & 0x1f),
+ (bfd_byte *) data + reloc_entry->address + 2);
+ }
+ else
+ reloc_entry->addend = val;
+
+ if (relocatable)
+ reloc_entry->address += input_section->output_offset;
+ else if (((val & ~0xffff) != ~0xffff) && ((val & ~0xffff) != 0))
+ return bfd_reloc_overflow;
+
+ return bfd_reloc_ok;
+}
+
+/* A mapping from BFD reloc types to MIPS ELF reloc types. */
+
+struct elf_reloc_map {
+ bfd_reloc_code_real_type bfd_val;
+ enum elf_mips_reloc_type elf_val;
+};
+
+static const struct elf_reloc_map mips_reloc_map[] =
+{
+ { BFD_RELOC_NONE, R_MIPS_NONE },
+ { BFD_RELOC_16, R_MIPS_16 },
+ { BFD_RELOC_32, R_MIPS_32 },
+ /* There is no BFD reloc for R_MIPS_REL32. */
+ { BFD_RELOC_64, R_MIPS_64 },
+ { BFD_RELOC_CTOR, R_MIPS_64 },
+ { BFD_RELOC_16_PCREL, R_MIPS_PC16 },
+ { BFD_RELOC_HI16_S, R_MIPS_HI16 },
+ { BFD_RELOC_LO16, R_MIPS_LO16 },
+ { BFD_RELOC_GPREL16, R_MIPS_GPREL16 },
+ { BFD_RELOC_GPREL32, R_MIPS_GPREL32 },
+ { BFD_RELOC_MIPS_JMP, R_MIPS_26 },
+ { BFD_RELOC_MIPS_LITERAL, R_MIPS_LITERAL },
+ { BFD_RELOC_MIPS_GOT16, R_MIPS_GOT16 },
+ { BFD_RELOC_MIPS_CALL16, R_MIPS_CALL16 },
+ { BFD_RELOC_MIPS_SHIFT5, R_MIPS_SHIFT5 },
+ { BFD_RELOC_MIPS_SHIFT6, R_MIPS_SHIFT6 },
+ { BFD_RELOC_MIPS_GOT_DISP, R_MIPS_GOT_DISP },
+ { BFD_RELOC_MIPS_GOT_PAGE, R_MIPS_GOT_PAGE },
+ { BFD_RELOC_MIPS_GOT_OFST, R_MIPS_GOT_OFST },
+ { BFD_RELOC_MIPS_GOT_HI16, R_MIPS_GOT_HI16 },
+ { BFD_RELOC_MIPS_GOT_LO16, R_MIPS_GOT_LO16 },
+ { BFD_RELOC_MIPS_SUB, R_MIPS_SUB },
+ { BFD_RELOC_MIPS_INSERT_A, R_MIPS_INSERT_A },
+ { BFD_RELOC_MIPS_INSERT_B, R_MIPS_INSERT_B },
+ { BFD_RELOC_MIPS_DELETE, R_MIPS_DELETE },
+ { BFD_RELOC_MIPS_HIGHEST, R_MIPS_HIGHEST },
+ { BFD_RELOC_MIPS_HIGHER, R_MIPS_HIGHER },
+ { BFD_RELOC_MIPS_CALL_HI16, R_MIPS_CALL_HI16 },
+ { BFD_RELOC_MIPS_CALL_LO16, R_MIPS_CALL_LO16 },
+ { BFD_RELOC_MIPS_SCN_DISP, R_MIPS_SCN_DISP },
+ { BFD_RELOC_MIPS_REL16, R_MIPS_REL16 },
+ /* Use of R_MIPS_ADD_IMMEDIATE and R_MIPS_PJUMP is deprecated. */
+ { BFD_RELOC_MIPS_RELGOT, R_MIPS_RELGOT },
+ { BFD_RELOC_MIPS_JALR, R_MIPS_JALR }
+};
+
+/* Given a BFD reloc type, return a howto structure. */
+
+static reloc_howto_type *
+bfd_elf64_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+ bfd_reloc_code_real_type code)
+{
+ unsigned int i;
+ /* FIXME: We default to RELA here instead of choosing the right
+ relocation variant. */
+ reloc_howto_type *howto_table = mips_elf64_howto_table_rela;
+
+ for (i = 0; i < sizeof (mips_reloc_map) / sizeof (struct elf_reloc_map);
+ i++)
+ {
+ if (mips_reloc_map[i].bfd_val == code)
+ return &howto_table[(int) mips_reloc_map[i].elf_val];
+ }
+
+ switch (code)
+ {
+ case BFD_RELOC_MIPS16_JMP:
+ return &elf_mips16_jump_howto;
+ case BFD_RELOC_MIPS16_GPREL:
+ return &elf_mips16_gprel_howto;
+ case BFD_RELOC_VTABLE_INHERIT:
+ return &elf_mips_gnu_vtinherit_howto;
+ case BFD_RELOC_VTABLE_ENTRY:
+ return &elf_mips_gnu_vtentry_howto;
+ case BFD_RELOC_16_PCREL_S2:
+ return &elf_mips_gnu_rela16_s2;
+ default:
+ bfd_set_error (bfd_error_bad_value);
+ return NULL;
+ }
+}
+
+/* Given a MIPS Elf_Internal_Rel, fill in an arelent structure. */
+
+static reloc_howto_type *
+mips_elf64_rtype_to_howto (unsigned int r_type, bfd_boolean rela_p)
+{
+ switch (r_type)
+ {
+ case R_MIPS16_26:
+ return &elf_mips16_jump_howto;
+ case R_MIPS16_GPREL:
+ return &elf_mips16_gprel_howto;
+ case R_MIPS_GNU_VTINHERIT:
+ return &elf_mips_gnu_vtinherit_howto;
+ case R_MIPS_GNU_VTENTRY:
+ return &elf_mips_gnu_vtentry_howto;
+ case R_MIPS_GNU_REL16_S2:
+ if (rela_p)
+ return &elf_mips_gnu_rela16_s2;
+ else
+ return &elf_mips_gnu_rel16_s2;
+ default:
+ BFD_ASSERT (r_type < (unsigned int) R_MIPS_max);
+ if (rela_p)
+ return &mips_elf64_howto_table_rela[r_type];
+ else
+ return &mips_elf64_howto_table_rel[r_type];
+ break;
+ }
+}
+
+/* Prevent relocation handling by bfd for MIPS ELF64. */
+
+static void
+mips_elf64_info_to_howto_rel (bfd *abfd ATTRIBUTE_UNUSED,
+ arelent *cache_ptr ATTRIBUTE_UNUSED,
+ Elf_Internal_Rela *dst ATTRIBUTE_UNUSED)
+{
+ BFD_ASSERT (0);
+}
+
+static void
+mips_elf64_info_to_howto_rela (bfd *abfd ATTRIBUTE_UNUSED,
+ arelent *cache_ptr ATTRIBUTE_UNUSED,
+ Elf_Internal_Rela *dst ATTRIBUTE_UNUSED)
+{
+ BFD_ASSERT (0);
+}
+
+/* Since each entry in an SHT_REL or SHT_RELA section can represent up
+ to three relocs, we must tell the user to allocate more space. */
+
+static long
+mips_elf64_get_reloc_upper_bound (bfd *abfd ATTRIBUTE_UNUSED, asection *sec)
+{
+ return (sec->reloc_count * 3 + 1) * sizeof (arelent *);
+}
+
+static long
+mips_elf64_get_dynamic_reloc_upper_bound (bfd *abfd)
+{
+ return _bfd_elf_get_dynamic_reloc_upper_bound (abfd) * 3;
+}
+
+/* We must also copy more relocations than the corresponding functions
+ in elf.c would, so the two following functions are slightly
+ modified from elf.c, that multiply the external relocation count by
+ 3 to obtain the internal relocation count. */
+
+static long
+mips_elf64_canonicalize_reloc (bfd *abfd, sec_ptr section,
+ arelent **relptr, asymbol **symbols)
+{
+ arelent *tblptr;
+ unsigned int i;
+ const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+
+ if (! bed->s->slurp_reloc_table (abfd, section, symbols, FALSE))
+ return -1;
+
+ tblptr = section->relocation;
+ for (i = 0; i < section->reloc_count * 3; i++)
+ *relptr++ = tblptr++;
+
+ *relptr = NULL;
+
+ return section->reloc_count * 3;
+}
+
+static long
+mips_elf64_canonicalize_dynamic_reloc (bfd *abfd, arelent **storage,
+ asymbol **syms)
+{
+ bfd_boolean (*slurp_relocs) (bfd *, asection *, asymbol **, bfd_boolean);
+ asection *s;
+ long ret;
+
+ if (elf_dynsymtab (abfd) == 0)
+ {
+ bfd_set_error (bfd_error_invalid_operation);
+ return -1;
+ }
+
+ slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table;
+ ret = 0;
+ for (s = abfd->sections; s != NULL; s = s->next)
+ {
+ if (elf_section_data (s)->this_hdr.sh_link == elf_dynsymtab (abfd)
+ && (elf_section_data (s)->this_hdr.sh_type == SHT_REL
+ || elf_section_data (s)->this_hdr.sh_type == SHT_RELA))
+ {
+ arelent *p;
+ long count, i;
+
+ if (! (*slurp_relocs) (abfd, s, syms, TRUE))
+ return -1;
+ count = s->_raw_size / elf_section_data (s)->this_hdr.sh_entsize * 3;
+ p = s->relocation;
+ for (i = 0; i < count; i++)
+ *storage++ = p++;
+ ret += count;
+ }
+ }
+
+ *storage = NULL;
+
+ return ret;
+}
+
+/* Read the relocations from one reloc section. This is mostly copied
+ from elfcode.h, except for the changes to expand one external
+ relocation to 3 internal ones. We must unfortunately set
+ reloc_count to the number of external relocations, because a lot of
+ generic code seems to depend on this. */
+
+static bfd_boolean
+mips_elf64_slurp_one_reloc_table (bfd *abfd, asection *asect,
+ Elf_Internal_Shdr *rel_hdr,
+ bfd_size_type reloc_count,
+ arelent *relents, asymbol **symbols,
+ bfd_boolean dynamic)
+{
+ void *allocated;
+ bfd_byte *native_relocs;
+ arelent *relent;
+ bfd_vma i;
+ int entsize;
+ bfd_boolean rela_p;
+
+ allocated = bfd_malloc (rel_hdr->sh_size);
+ if (allocated == NULL)
+ return FALSE;
+
+ if (bfd_seek (abfd, rel_hdr->sh_offset, SEEK_SET) != 0
+ || (bfd_bread (allocated, rel_hdr->sh_size, abfd)
+ != rel_hdr->sh_size))
+ goto error_return;
+
+ native_relocs = allocated;
+
+ entsize = rel_hdr->sh_entsize;
+ BFD_ASSERT (entsize == sizeof (Elf64_Mips_External_Rel)
+ || entsize == sizeof (Elf64_Mips_External_Rela));
+
+ if (entsize == sizeof (Elf64_Mips_External_Rel))
+ rela_p = FALSE;
+ else
+ rela_p = TRUE;
+
+ for (i = 0, relent = relents;
+ i < reloc_count;
+ i++, native_relocs += entsize)
+ {
+ Elf64_Mips_Internal_Rela rela;
+ bfd_boolean used_sym, used_ssym;
+ int ir;
+
+ if (entsize == sizeof (Elf64_Mips_External_Rela))
+ mips_elf64_swap_reloca_in (abfd,
+ (Elf64_Mips_External_Rela *) native_relocs,
+ &rela);
+ else
+ mips_elf64_swap_reloc_in (abfd,
+ (Elf64_Mips_External_Rel *) native_relocs,
+ &rela);
+
+ /* Each entry represents exactly three actual relocations. */
+
+ used_sym = FALSE;
+ used_ssym = FALSE;
+ for (ir = 0; ir < 3; ir++)
+ {
+ enum elf_mips_reloc_type type;
+
+ switch (ir)
+ {
+ default:
+ abort ();
+ case 0:
+ type = (enum elf_mips_reloc_type) rela.r_type;
+ break;
+ case 1:
+ type = (enum elf_mips_reloc_type) rela.r_type2;
+ break;
+ case 2:
+ type = (enum elf_mips_reloc_type) rela.r_type3;
+ break;
+ }
+
+ /* Some types require symbols, whereas some do not. */
+ switch (type)
+ {
+ case R_MIPS_NONE:
+ case R_MIPS_LITERAL:
+ case R_MIPS_INSERT_A:
+ case R_MIPS_INSERT_B:
+ case R_MIPS_DELETE:
+ relent->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
+ break;
+
+ default:
+ if (! used_sym)
+ {
+ if (rela.r_sym == 0)
+ relent->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
+ else
+ {
+ asymbol **ps, *s;
+
+ ps = symbols + rela.r_sym - 1;
+ s = *ps;
+ if ((s->flags & BSF_SECTION_SYM) == 0)
+ relent->sym_ptr_ptr = ps;
+ else
+ relent->sym_ptr_ptr = s->section->symbol_ptr_ptr;
+ }
+
+ used_sym = TRUE;
+ }
+ else if (! used_ssym)
+ {
+ switch (rela.r_ssym)
+ {
+ case RSS_UNDEF:
+ relent->sym_ptr_ptr =
+ bfd_abs_section_ptr->symbol_ptr_ptr;
+ break;
+
+ case RSS_GP:
+ case RSS_GP0:
+ case RSS_LOC:
+ /* FIXME: I think these need to be handled using
+ special howto structures. */
+ BFD_ASSERT (0);
+ break;
+
+ default:
+ BFD_ASSERT (0);
+ break;
+ }
+
+ used_ssym = TRUE;
+ }
+ else
+ relent->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
+
+ break;
+ }
+
+ /* The address of an ELF reloc is section relative for an
+ object file, and absolute for an executable file or
+ shared library. The address of a BFD reloc is always
+ section relative. */
+ if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0 || dynamic)
+ relent->address = rela.r_offset;
+ else
+ relent->address = rela.r_offset - asect->vma;
+
+ relent->addend = rela.r_addend;
+
+ relent->howto = mips_elf64_rtype_to_howto (type, rela_p);
+
+ ++relent;
+ }
+ }
+
+ asect->reloc_count += (relent - relents) / 3;
+
+ if (allocated != NULL)
+ free (allocated);
+
+ return TRUE;
+
+ error_return:
+ if (allocated != NULL)
+ free (allocated);
+ return FALSE;
+}
+
+/* Read the relocations. On Irix 6, there can be two reloc sections
+ associated with a single data section. This is copied from
+ elfcode.h as well, with changes as small as accounting for 3
+ internal relocs per external reloc and resetting reloc_count to
+ zero before processing the relocs of a section. */
+
+static bfd_boolean
+mips_elf64_slurp_reloc_table (bfd *abfd, asection *asect,
+ asymbol **symbols, bfd_boolean dynamic)
+{
+ struct bfd_elf_section_data * const d = elf_section_data (asect);
+ Elf_Internal_Shdr *rel_hdr;
+ Elf_Internal_Shdr *rel_hdr2;
+ bfd_size_type reloc_count;
+ bfd_size_type reloc_count2;
+ arelent *relents;
+ bfd_size_type amt;
+
+ if (asect->relocation != NULL)
+ return TRUE;
+
+ if (! dynamic)
+ {
+ if ((asect->flags & SEC_RELOC) == 0
+ || asect->reloc_count == 0)
+ return TRUE;
+
+ rel_hdr = &d->rel_hdr;
+ reloc_count = NUM_SHDR_ENTRIES (rel_hdr);
+ rel_hdr2 = d->rel_hdr2;
+ reloc_count2 = (rel_hdr2 ? NUM_SHDR_ENTRIES (rel_hdr2) : 0);
+
+ BFD_ASSERT (asect->reloc_count == reloc_count + reloc_count2);
+ BFD_ASSERT (asect->rel_filepos == rel_hdr->sh_offset
+ || (rel_hdr2 && asect->rel_filepos == rel_hdr2->sh_offset));
+
+ }
+ else
+ {
+ /* Note that ASECT->RELOC_COUNT tends not to be accurate in this
+ case because relocations against this section may use the
+ dynamic symbol table, and in that case bfd_section_from_shdr
+ in elf.c does not update the RELOC_COUNT. */
+ if (asect->_raw_size == 0)
+ return TRUE;
+
+ rel_hdr = &d->this_hdr;
+ reloc_count = NUM_SHDR_ENTRIES (rel_hdr);
+ rel_hdr2 = NULL;
+ reloc_count2 = 0;
+ }
+
+ /* Allocate space for 3 arelent structures for each Rel structure. */
+ amt = (reloc_count + reloc_count2) * 3 * sizeof (arelent);
+ relents = bfd_alloc (abfd, amt);
+ if (relents == NULL)
+ return FALSE;
+
+ /* The slurp_one_reloc_table routine increments reloc_count. */
+ asect->reloc_count = 0;
+
+ if (! mips_elf64_slurp_one_reloc_table (abfd, asect,
+ rel_hdr, reloc_count,
+ relents,
+ symbols, dynamic))
+ return FALSE;
+ if (d->rel_hdr2 != NULL)
+ {
+ if (! mips_elf64_slurp_one_reloc_table (abfd, asect,
+ rel_hdr2, reloc_count2,
+ relents + reloc_count * 3,
+ symbols, dynamic))
+ return FALSE;
+ }
+
+ asect->relocation = relents;
+ return TRUE;
+}
+
+/* Write out the relocations. */
+
+static void
+mips_elf64_write_relocs (bfd *abfd, asection *sec, void *data)
+{
+ bfd_boolean *failedp = data;
+ int count;
+ Elf_Internal_Shdr *rel_hdr;
+ unsigned int idx;
+
+ /* If we have already failed, don't do anything. */
+ if (*failedp)
+ return;
+
+ if ((sec->flags & SEC_RELOC) == 0)
+ return;
+
+ /* The linker backend writes the relocs out itself, and sets the
+ reloc_count field to zero to inhibit writing them here. Also,
+ sometimes the SEC_RELOC flag gets set even when there aren't any
+ relocs. */
+ if (sec->reloc_count == 0)
+ return;
+
+ /* We can combine up to three relocs that refer to the same address
+ if the latter relocs have no associated symbol. */
+ count = 0;
+ for (idx = 0; idx < sec->reloc_count; idx++)
+ {
+ bfd_vma addr;
+ unsigned int i;
+
+ ++count;
+
+ addr = sec->orelocation[idx]->address;
+ for (i = 0; i < 2; i++)
+ {
+ arelent *r;
+
+ if (idx + 1 >= sec->reloc_count)
+ break;
+ r = sec->orelocation[idx + 1];
+ if (r->address != addr
+ || ! bfd_is_abs_section ((*r->sym_ptr_ptr)->section)
+ || (*r->sym_ptr_ptr)->value != 0)
+ break;
+
+ /* We can merge the reloc at IDX + 1 with the reloc at IDX. */
+
+ ++idx;
+ }
+ }
+
+ rel_hdr = &elf_section_data (sec)->rel_hdr;
+
+ /* Do the actual relocation. */
+
+ if (rel_hdr->sh_entsize == sizeof(Elf64_Mips_External_Rel))
+ mips_elf64_write_rel (abfd, sec, rel_hdr, &count, data);
+ else if (rel_hdr->sh_entsize == sizeof(Elf64_Mips_External_Rela))
+ mips_elf64_write_rela (abfd, sec, rel_hdr, &count, data);
+ else
+ BFD_ASSERT (0);
+}
+
+static void
+mips_elf64_write_rel (bfd *abfd, asection *sec,
+ Elf_Internal_Shdr *rel_hdr,
+ int *count, void *data)
+{
+ bfd_boolean *failedp = data;
+ Elf64_Mips_External_Rel *ext_rel;
+ unsigned int idx;
+ asymbol *last_sym = 0;
+ int last_sym_idx = 0;
+
+ rel_hdr->sh_size = rel_hdr->sh_entsize * *count;
+ rel_hdr->contents = bfd_alloc (abfd, rel_hdr->sh_size);
+ if (rel_hdr->contents == NULL)
+ {
+ *failedp = TRUE;
+ return;
+ }
+
+ ext_rel = (Elf64_Mips_External_Rel *) rel_hdr->contents;
+ for (idx = 0; idx < sec->reloc_count; idx++, ext_rel++)
+ {
+ arelent *ptr;
+ Elf64_Mips_Internal_Rela int_rel;
+ asymbol *sym;
+ int n;
+ unsigned int i;
+
+ ptr = sec->orelocation[idx];
+
+ /* The address of an ELF reloc is section relative for an object
+ file, and absolute for an executable file or shared library.
+ The address of a BFD reloc is always section relative. */
+ if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0)
+ int_rel.r_offset = ptr->address;
+ else
+ int_rel.r_offset = ptr->address + sec->vma;
+
+ sym = *ptr->sym_ptr_ptr;
+ if (sym == last_sym)
+ n = last_sym_idx;
+ else
+ {
+ last_sym = sym;
+ n = _bfd_elf_symbol_from_bfd_symbol (abfd, &sym);
+ if (n < 0)
+ {
+ *failedp = TRUE;
+ return;
+ }
+ last_sym_idx = n;
+ }
+
+ int_rel.r_sym = n;
+ int_rel.r_ssym = RSS_UNDEF;
+
+ if ((*ptr->sym_ptr_ptr)->the_bfd->xvec != abfd->xvec
+ && ! _bfd_elf_validate_reloc (abfd, ptr))
+ {
+ *failedp = TRUE;
+ return;
+ }
+
+ int_rel.r_type = ptr->howto->type;
+ int_rel.r_type2 = (int) R_MIPS_NONE;
+ int_rel.r_type3 = (int) R_MIPS_NONE;
+
+ for (i = 0; i < 2; i++)
+ {
+ arelent *r;
+
+ if (idx + 1 >= sec->reloc_count)
+ break;
+ r = sec->orelocation[idx + 1];
+ if (r->address != ptr->address
+ || ! bfd_is_abs_section ((*r->sym_ptr_ptr)->section)
+ || (*r->sym_ptr_ptr)->value != 0)
+ break;
+
+ /* We can merge the reloc at IDX + 1 with the reloc at IDX. */
+
+ if (i == 0)
+ int_rel.r_type2 = r->howto->type;
+ else
+ int_rel.r_type3 = r->howto->type;
+
+ ++idx;
+ }
+
+ mips_elf64_swap_reloc_out (abfd, &int_rel, ext_rel);
+ }
+
+ BFD_ASSERT (ext_rel - (Elf64_Mips_External_Rel *) rel_hdr->contents
+ == *count);
+}
+
+static void
+mips_elf64_write_rela (bfd *abfd, asection *sec,
+ Elf_Internal_Shdr *rela_hdr,
+ int *count, void *data)
+{
+ bfd_boolean *failedp = data;
+ Elf64_Mips_External_Rela *ext_rela;
+ unsigned int idx;
+ asymbol *last_sym = 0;
+ int last_sym_idx = 0;
+
+ rela_hdr->sh_size = rela_hdr->sh_entsize * *count;
+ rela_hdr->contents = bfd_alloc (abfd, rela_hdr->sh_size);
+ if (rela_hdr->contents == NULL)
+ {
+ *failedp = TRUE;
+ return;
+ }
+
+ ext_rela = (Elf64_Mips_External_Rela *) rela_hdr->contents;
+ for (idx = 0; idx < sec->reloc_count; idx++, ext_rela++)
+ {
+ arelent *ptr;
+ Elf64_Mips_Internal_Rela int_rela;
+ asymbol *sym;
+ int n;
+ unsigned int i;
+
+ ptr = sec->orelocation[idx];
+
+ /* The address of an ELF reloc is section relative for an object
+ file, and absolute for an executable file or shared library.
+ The address of a BFD reloc is always section relative. */
+ if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0)
+ int_rela.r_offset = ptr->address;
+ else
+ int_rela.r_offset = ptr->address + sec->vma;
+
+ sym = *ptr->sym_ptr_ptr;
+ if (sym == last_sym)
+ n = last_sym_idx;
+ else
+ {
+ last_sym = sym;
+ n = _bfd_elf_symbol_from_bfd_symbol (abfd, &sym);
+ if (n < 0)
+ {
+ *failedp = TRUE;
+ return;
+ }
+ last_sym_idx = n;
+ }
+
+ int_rela.r_sym = n;
+ int_rela.r_addend = ptr->addend;
+ int_rela.r_ssym = RSS_UNDEF;
+
+ if ((*ptr->sym_ptr_ptr)->the_bfd->xvec != abfd->xvec
+ && ! _bfd_elf_validate_reloc (abfd, ptr))
+ {
+ *failedp = TRUE;
+ return;
+ }
+
+ int_rela.r_type = ptr->howto->type;
+ int_rela.r_type2 = (int) R_MIPS_NONE;
+ int_rela.r_type3 = (int) R_MIPS_NONE;
+
+ for (i = 0; i < 2; i++)
+ {
+ arelent *r;
+
+ if (idx + 1 >= sec->reloc_count)
+ break;
+ r = sec->orelocation[idx + 1];
+ if (r->address != ptr->address
+ || ! bfd_is_abs_section ((*r->sym_ptr_ptr)->section)
+ || (*r->sym_ptr_ptr)->value != 0)
+ break;
+
+ /* We can merge the reloc at IDX + 1 with the reloc at IDX. */
+
+ if (i == 0)
+ int_rela.r_type2 = r->howto->type;
+ else
+ int_rela.r_type3 = r->howto->type;
+
+ ++idx;
+ }
+
+ mips_elf64_swap_reloca_out (abfd, &int_rela, ext_rela);
+ }
+
+ BFD_ASSERT (ext_rela - (Elf64_Mips_External_Rela *) rela_hdr->contents
+ == *count);
+}
+
+/* Set the right machine number for a MIPS ELF file. */
+
+static bfd_boolean
+mips_elf64_object_p (bfd *abfd)
+{
+ unsigned long mach;
+
+ /* Irix 6 is broken. Object file symbol tables are not always
+ sorted correctly such that local symbols precede global symbols,
+ and the sh_info field in the symbol table is not always right. */
+ if (elf64_mips_irix_compat (abfd) != ict_none)
+ elf_bad_symtab (abfd) = TRUE;
+
+ mach = _bfd_elf_mips_mach (elf_elfheader (abfd)->e_flags);
+ bfd_default_set_arch_mach (abfd, bfd_arch_mips, mach);
+ return TRUE;
+}
+
+/* Depending on the target vector we generate some version of Irix
+ executables or "normal" MIPS ELF ABI executables. */
+static irix_compat_t
+elf64_mips_irix_compat (bfd *abfd)
+{
+ if ((abfd->xvec == &bfd_elf64_bigmips_vec)
+ || (abfd->xvec == &bfd_elf64_littlemips_vec))
+ return ict_irix6;
+ else
+ return ict_none;
+}
+
+/* Support for core dump NOTE sections. */
+static bfd_boolean
+elf64_mips_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
+{
+ int offset;
+ unsigned int raw_size;
+
+ switch (note->descsz)
+ {
+ default:
+ return FALSE;
+
+ case 480: /* Linux/MIPS - N64 kernel */
+ /* 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 + 32);
+
+ /* pr_reg */
+ offset = 112;
+ raw_size = 360;
+
+ break;
+ }
+
+ /* Make a ".reg/999" section. */
+ return _bfd_elfcore_make_pseudosection (abfd, ".reg",
+ raw_size, note->descpos + offset);
+}
+
+static bfd_boolean
+elf64_mips_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
+{
+ switch (note->descsz)
+ {
+ default:
+ return FALSE;
+
+ case 136: /* Linux/MIPS - N64 kernel elf_prpsinfo */
+ elf_tdata (abfd)->core_program
+ = _bfd_elfcore_strndup (abfd, note->descdata + 40, 16);
+ elf_tdata (abfd)->core_command
+ = _bfd_elfcore_strndup (abfd, note->descdata + 56, 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;
+}
+
+/* ECOFF swapping routines. These are used when dealing with the
+ .mdebug section, which is in the ECOFF debugging format. */
+static const struct ecoff_debug_swap mips_elf64_ecoff_debug_swap =
+{
+ /* Symbol table magic number. */
+ magicSym2,
+ /* Alignment of debugging information. E.g., 4. */
+ 8,
+ /* Sizes of external symbolic information. */
+ sizeof (struct hdr_ext),
+ sizeof (struct dnr_ext),
+ sizeof (struct pdr_ext),
+ sizeof (struct sym_ext),
+ sizeof (struct opt_ext),
+ sizeof (struct fdr_ext),
+ sizeof (struct rfd_ext),
+ sizeof (struct ext_ext),
+ /* Functions to swap in external symbolic data. */
+ ecoff_swap_hdr_in,
+ ecoff_swap_dnr_in,
+ ecoff_swap_pdr_in,
+ ecoff_swap_sym_in,
+ ecoff_swap_opt_in,
+ ecoff_swap_fdr_in,
+ ecoff_swap_rfd_in,
+ ecoff_swap_ext_in,
+ _bfd_ecoff_swap_tir_in,
+ _bfd_ecoff_swap_rndx_in,
+ /* Functions to swap out external symbolic data. */
+ ecoff_swap_hdr_out,
+ ecoff_swap_dnr_out,
+ ecoff_swap_pdr_out,
+ ecoff_swap_sym_out,
+ ecoff_swap_opt_out,
+ ecoff_swap_fdr_out,
+ ecoff_swap_rfd_out,
+ ecoff_swap_ext_out,
+ _bfd_ecoff_swap_tir_out,
+ _bfd_ecoff_swap_rndx_out,
+ /* Function to read in symbolic data. */
+ _bfd_mips_elf_read_ecoff_info
+};
+
+/* Relocations in the 64 bit MIPS ELF ABI are more complex than in
+ standard ELF. This structure is used to redirect the relocation
+ handling routines. */
+
+const struct elf_size_info mips_elf64_size_info =
+{
+ sizeof (Elf64_External_Ehdr),
+ sizeof (Elf64_External_Phdr),
+ sizeof (Elf64_External_Shdr),
+ sizeof (Elf64_Mips_External_Rel),
+ sizeof (Elf64_Mips_External_Rela),
+ sizeof (Elf64_External_Sym),
+ sizeof (Elf64_External_Dyn),
+ sizeof (Elf_External_Note),
+ 4, /* hash-table entry size */
+ 3, /* internal relocations per external relocations */
+ 64, /* arch_size */
+ 3, /* log_file_align */
+ ELFCLASS64,
+ EV_CURRENT,
+ bfd_elf64_write_out_phdrs,
+ bfd_elf64_write_shdrs_and_ehdr,
+ mips_elf64_write_relocs,
+ bfd_elf64_swap_symbol_in,
+ bfd_elf64_swap_symbol_out,
+ mips_elf64_slurp_reloc_table,
+ bfd_elf64_slurp_symbol_table,
+ bfd_elf64_swap_dyn_in,
+ bfd_elf64_swap_dyn_out,
+ mips_elf64_be_swap_reloc_in,
+ mips_elf64_be_swap_reloc_out,
+ mips_elf64_be_swap_reloca_in,
+ mips_elf64_be_swap_reloca_out
+};
+
+#define ELF_ARCH bfd_arch_mips
+#define ELF_MACHINE_CODE EM_MIPS
+
+#define elf_backend_collect TRUE
+#define elf_backend_type_change_ok TRUE
+#define elf_backend_can_gc_sections TRUE
+#define elf_info_to_howto mips_elf64_info_to_howto_rela
+#define elf_info_to_howto_rel mips_elf64_info_to_howto_rel
+#define elf_backend_object_p mips_elf64_object_p
+#define elf_backend_symbol_processing _bfd_mips_elf_symbol_processing
+#define elf_backend_section_processing _bfd_mips_elf_section_processing
+#define elf_backend_section_from_shdr _bfd_mips_elf_section_from_shdr
+#define elf_backend_fake_sections _bfd_mips_elf_fake_sections
+#define elf_backend_section_from_bfd_section \
+ _bfd_mips_elf_section_from_bfd_section
+#define elf_backend_add_symbol_hook _bfd_mips_elf_add_symbol_hook
+#define elf_backend_link_output_symbol_hook \
+ _bfd_mips_elf_link_output_symbol_hook
+#define elf_backend_create_dynamic_sections \
+ _bfd_mips_elf_create_dynamic_sections
+#define elf_backend_check_relocs _bfd_mips_elf_check_relocs
+#define elf_backend_adjust_dynamic_symbol \
+ _bfd_mips_elf_adjust_dynamic_symbol
+#define elf_backend_always_size_sections \
+ _bfd_mips_elf_always_size_sections
+#define elf_backend_size_dynamic_sections \
+ _bfd_mips_elf_size_dynamic_sections
+#define elf_backend_relocate_section _bfd_mips_elf_relocate_section
+#define elf_backend_finish_dynamic_symbol \
+ _bfd_mips_elf_finish_dynamic_symbol
+#define elf_backend_finish_dynamic_sections \
+ _bfd_mips_elf_finish_dynamic_sections
+#define elf_backend_final_write_processing \
+ _bfd_mips_elf_final_write_processing
+#define elf_backend_additional_program_headers \
+ _bfd_mips_elf_additional_program_headers
+#define elf_backend_modify_segment_map _bfd_mips_elf_modify_segment_map
+#define elf_backend_gc_mark_hook _bfd_mips_elf_gc_mark_hook
+#define elf_backend_gc_sweep_hook _bfd_mips_elf_gc_sweep_hook
+#define elf_backend_copy_indirect_symbol \
+ _bfd_mips_elf_copy_indirect_symbol
+#define elf_backend_hide_symbol _bfd_mips_elf_hide_symbol
+#define elf_backend_ignore_discarded_relocs \
+ _bfd_mips_elf_ignore_discarded_relocs
+#define elf_backend_mips_irix_compat elf64_mips_irix_compat
+#define elf_backend_mips_rtype_to_howto mips_elf64_rtype_to_howto
+#define elf_backend_ecoff_debug_swap &mips_elf64_ecoff_debug_swap
+#define elf_backend_size_info mips_elf64_size_info
+
+#define elf_backend_grok_prstatus elf64_mips_grok_prstatus
+#define elf_backend_grok_psinfo elf64_mips_grok_psinfo
+
+#define elf_backend_got_header_size (4 * MIPS_RESERVED_GOTNO)
+
+/* MIPS ELF64 can use a mixture of REL and RELA, but some Relocations
+ work better/work only in RELA, so we default to this. */
+#define elf_backend_may_use_rel_p 1
+#define elf_backend_may_use_rela_p 1
+#define elf_backend_default_use_rela_p 1
+
+#define elf_backend_write_section _bfd_mips_elf_write_section
+
+/* We don't set bfd_elf64_bfd_is_local_label_name because the 32-bit
+ MIPS-specific function only applies to IRIX5, which had no 64-bit
+ ABI. */
+#define bfd_elf64_find_nearest_line _bfd_mips_elf_find_nearest_line
+#define bfd_elf64_new_section_hook _bfd_mips_elf_new_section_hook
+#define bfd_elf64_set_section_contents _bfd_mips_elf_set_section_contents
+#define bfd_elf64_bfd_get_relocated_section_contents \
+ _bfd_elf_mips_get_relocated_section_contents
+#define bfd_elf64_bfd_link_hash_table_create \
+ _bfd_mips_elf_link_hash_table_create
+#define bfd_elf64_bfd_final_link _bfd_mips_elf_final_link
+#define bfd_elf64_bfd_merge_private_bfd_data \
+ _bfd_mips_elf_merge_private_bfd_data
+#define bfd_elf64_bfd_set_private_flags _bfd_mips_elf_set_private_flags
+#define bfd_elf64_bfd_print_private_bfd_data \
+ _bfd_mips_elf_print_private_bfd_data
+
+#define bfd_elf64_get_reloc_upper_bound mips_elf64_get_reloc_upper_bound
+#define bfd_elf64_canonicalize_reloc mips_elf64_canonicalize_reloc
+#define bfd_elf64_get_dynamic_reloc_upper_bound mips_elf64_get_dynamic_reloc_upper_bound
+#define bfd_elf64_canonicalize_dynamic_reloc mips_elf64_canonicalize_dynamic_reloc
+#define bfd_elf64_bfd_relax_section _bfd_mips_relax_section
+
+/* MIPS ELF64 archive functions. */
+#define bfd_elf64_archive_functions
+extern bfd_boolean bfd_elf64_archive_slurp_armap
+ (bfd *);
+extern bfd_boolean bfd_elf64_archive_write_armap
+ (bfd *, unsigned int, struct orl *, unsigned int, int);
+#define bfd_elf64_archive_slurp_extended_name_table \
+ _bfd_archive_coff_slurp_extended_name_table
+#define bfd_elf64_archive_construct_extended_name_table \
+ _bfd_archive_coff_construct_extended_name_table
+#define bfd_elf64_archive_truncate_arname \
+ _bfd_archive_coff_truncate_arname
+#define bfd_elf64_archive_read_ar_hdr _bfd_archive_coff_read_ar_hdr
+#define bfd_elf64_archive_openr_next_archived_file \
+ _bfd_archive_coff_openr_next_archived_file
+#define bfd_elf64_archive_get_elt_at_index \
+ _bfd_archive_coff_get_elt_at_index
+#define bfd_elf64_archive_generic_stat_arch_elt \
+ _bfd_archive_coff_generic_stat_arch_elt
+#define bfd_elf64_archive_update_armap_timestamp \
+ _bfd_archive_coff_update_armap_timestamp
+
+/* The SGI style (n)64 NewABI. */
+#define TARGET_LITTLE_SYM bfd_elf64_littlemips_vec
+#define TARGET_LITTLE_NAME "elf64-littlemips"
+#define TARGET_BIG_SYM bfd_elf64_bigmips_vec
+#define TARGET_BIG_NAME "elf64-bigmips"
+
+/* The SVR4 MIPS ABI says that this should be 0x10000, but Irix 5 uses
+ a value of 0x1000, and we are compatible.
+ FIXME: How does this affect NewABI? */
+#define ELF_MAXPAGESIZE 0x1000
+
+#include "elf64-target.h"
+
+/* The SYSV-style 'traditional' (n)64 NewABI. */
+#undef TARGET_LITTLE_SYM
+#undef TARGET_LITTLE_NAME
+#undef TARGET_BIG_SYM
+#undef TARGET_BIG_NAME
+
+#undef ELF_MAXPAGESIZE
+
+#define TARGET_LITTLE_SYM bfd_elf64_tradlittlemips_vec
+#define TARGET_LITTLE_NAME "elf64-tradlittlemips"
+#define TARGET_BIG_SYM bfd_elf64_tradbigmips_vec
+#define TARGET_BIG_NAME "elf64-tradbigmips"
+
+/* The SVR4 MIPS ABI says that this should be 0x10000, and Linux uses
+ page sizes of up to that limit, so we need to respect it. */
+#define ELF_MAXPAGESIZE 0x10000
+#define elf64_bed elf64_tradbed
+
+/* Include the target file again for this target. */
+#include "elf64-target.h"
diff --git a/contrib/binutils/bfd/elfn32-mips.c b/contrib/binutils/bfd/elfn32-mips.c
new file mode 100644
index 0000000..973edd5
--- /dev/null
+++ b/contrib/binutils/bfd/elfn32-mips.c
@@ -0,0 +1,1983 @@
+/* MIPS-specific support for 32-bit ELF
+ Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+ 2003 Free Software Foundation, Inc.
+
+ Most of the information added by Ian Lance Taylor, Cygnus Support,
+ <ian@cygnus.com>.
+ N32/64 ABI support added by Mark Mitchell, CodeSourcery, LLC.
+ <mark@codesourcery.com>
+ Traditional MIPS targets support added by Koundinya.K, Dansk Data
+ Elektronik & Operations Research Group. <kk@ddeorg.soft.net>
+
+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 MIPS ELF targets. SGI Irix 5 uses a slightly
+ different MIPS ELF from other targets. This matters when linking.
+ This file supports both, switching at runtime. */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+#include "bfdlink.h"
+#include "genlink.h"
+#include "elf-bfd.h"
+#include "elfxx-mips.h"
+#include "elf/mips.h"
+
+/* Get the ECOFF swapping routines. */
+#include "coff/sym.h"
+#include "coff/symconst.h"
+#include "coff/internal.h"
+#include "coff/ecoff.h"
+#include "coff/mips.h"
+#define ECOFF_SIGNED_32
+#include "ecoffswap.h"
+
+static bfd_boolean mips_elf_assign_gp
+ (bfd *, bfd_vma *);
+static bfd_reloc_status_type mips_elf_final_gp
+ (bfd *, asymbol *, bfd_boolean, char **, bfd_vma *);
+static bfd_reloc_status_type mips_elf_gprel16_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type mips_elf_literal_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type mips_elf_gprel32_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type gprel32_with_gp
+ (bfd *, asymbol *, arelent *, asection *, bfd_boolean, void *, bfd_vma);
+static bfd_reloc_status_type mips_elf_shift6_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type mips16_jump_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_reloc_status_type mips16_gprel_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static reloc_howto_type *bfd_elf32_bfd_reloc_type_lookup
+ (bfd *, bfd_reloc_code_real_type);
+static reloc_howto_type *mips_elf_n32_rtype_to_howto
+ (unsigned int, bfd_boolean);
+static void mips_info_to_howto_rel
+ (bfd *, arelent *, Elf_Internal_Rela *);
+static void mips_info_to_howto_rela
+ (bfd *, arelent *, Elf_Internal_Rela *);
+static bfd_boolean mips_elf_sym_is_global
+ (bfd *, asymbol *);
+static bfd_boolean mips_elf_n32_object_p
+ (bfd *);
+static bfd_boolean elf32_mips_grok_prstatus
+ (bfd *, Elf_Internal_Note *);
+static bfd_boolean elf32_mips_grok_psinfo
+ (bfd *, Elf_Internal_Note *);
+static irix_compat_t elf_n32_mips_irix_compat
+ (bfd *);
+
+extern const bfd_target bfd_elf32_nbigmips_vec;
+extern const bfd_target bfd_elf32_nlittlemips_vec;
+
+/* Nonzero if ABFD is using the N32 ABI. */
+#define ABI_N32_P(abfd) \
+ ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI2) != 0)
+
+/* Whether we are trying to be compatible with IRIX at all. */
+#define SGI_COMPAT(abfd) \
+ (elf_n32_mips_irix_compat (abfd) != ict_none)
+
+/* The number of local .got entries we reserve. */
+#define MIPS_RESERVED_GOTNO (2)
+
+/* In case we're on a 32-bit machine, construct a 64-bit "-1" value
+ from smaller values. Start with zero, widen, *then* decrement. */
+#define MINUS_ONE (((bfd_vma)0) - 1)
+
+/* The relocation table used for SHT_REL sections. */
+
+static reloc_howto_type elf_mips_howto_table_rel[] =
+{
+ /* No relocation. */
+ HOWTO (R_MIPS_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_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_NONE", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 16 bit relocation. */
+ HOWTO (R_MIPS_16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit relocation. */
+ HOWTO (R_MIPS_32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit symbol relative relocation. */
+ HOWTO (R_MIPS_REL32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_REL32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 26 bit jump address. */
+ HOWTO (R_MIPS_26, /* type */
+ 2, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 26, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ /* This needs complex overflow
+ detection, because the upper four
+ bits must match the PC + 4. */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_26", /* name */
+ TRUE, /* partial_inplace */
+ 0x03ffffff, /* src_mask */
+ 0x03ffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* R_MIPS_HI16 and R_MIPS_LO16 are unsupported for NewABI REL.
+ However, the native IRIX6 tools use them, so we try our best. */
+
+ /* High 16 bits of symbol value. */
+ HOWTO (R_MIPS_HI16, /* type */
+ 16, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_hi16_reloc, /* special_function */
+ "R_MIPS_HI16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of symbol value. */
+ HOWTO (R_MIPS_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_lo16_reloc, /* special_function */
+ "R_MIPS_LO16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* GP relative reference. */
+ HOWTO (R_MIPS_GPREL16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips_elf_gprel16_reloc, /* special_function */
+ "R_MIPS_GPREL16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Reference to literal section. */
+ HOWTO (R_MIPS_LITERAL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips_elf_literal_reloc, /* special_function */
+ "R_MIPS_LITERAL", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Reference to global offset table. */
+ HOWTO (R_MIPS_GOT16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_got16_reloc, /* special_function */
+ "R_MIPS_GOT16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 16 bit PC relative reference. */
+ HOWTO (R_MIPS_PC16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_PC16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* 16 bit call through global offset table. */
+ HOWTO (R_MIPS_CALL16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_CALL16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit GP relative reference. */
+ HOWTO (R_MIPS_GPREL32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ mips_elf_gprel32_reloc, /* special_function */
+ "R_MIPS_GPREL32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* The remaining relocs are defined on Irix 5, although they are
+ not defined by the ABI. */
+ EMPTY_HOWTO (13),
+ EMPTY_HOWTO (14),
+ EMPTY_HOWTO (15),
+
+ /* A 5 bit shift field. */
+ HOWTO (R_MIPS_SHIFT5, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 5, /* bitsize */
+ FALSE, /* pc_relative */
+ 6, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_SHIFT5", /* name */
+ TRUE, /* partial_inplace */
+ 0x000007c0, /* src_mask */
+ 0x000007c0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A 6 bit shift field. */
+ HOWTO (R_MIPS_SHIFT6, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 6, /* bitsize */
+ FALSE, /* pc_relative */
+ 6, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ mips_elf_shift6_reloc, /* special_function */
+ "R_MIPS_SHIFT6", /* name */
+ TRUE, /* partial_inplace */
+ 0x000007c4, /* src_mask */
+ 0x000007c4, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A 64 bit relocation. */
+ HOWTO (R_MIPS_64, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_64", /* name */
+ TRUE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Displacement in the global offset table. */
+ HOWTO (R_MIPS_GOT_DISP, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_DISP", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Displacement to page pointer in the global offset table. */
+ HOWTO (R_MIPS_GOT_PAGE, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_PAGE", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Offset from page pointer in the global offset table. */
+ HOWTO (R_MIPS_GOT_OFST, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_OFST", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_MIPS_GOT_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_HI16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_MIPS_GOT_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_LO16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 64 bit subtraction. */
+ HOWTO (R_MIPS_SUB, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_SUB", /* name */
+ TRUE, /* partial_inplace */
+ MINUS_ONE, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Insert the addend as an instruction. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_MIPS_INSERT_A, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_INSERT_A", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Insert the addend as an instruction, and change all relocations
+ to refer to the old instruction at the address. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_MIPS_INSERT_B, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_INSERT_B", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Delete a 32 bit instruction. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_MIPS_DELETE, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_DELETE", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* The MIPS ELF64 ABI Draft wants us to support these for REL relocations.
+ We don't, because
+ a) It means building the addend from a R_MIPS_HIGHEST/R_MIPS_HIGHER/
+ R_MIPS_HI16/R_MIPS_LO16 sequence with varying ordering, using
+ fallable heuristics.
+ b) No other NEwABI toolchain actually emits such relocations. */
+ EMPTY_HOWTO (R_MIPS_HIGHER),
+ EMPTY_HOWTO (R_MIPS_HIGHEST),
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_MIPS_CALL_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_CALL_HI16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_MIPS_CALL_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_CALL_LO16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Section displacement. */
+ HOWTO (R_MIPS_SCN_DISP, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_SCN_DISP", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_MIPS_REL16, /* type */
+ 0, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_REL16", /* name */
+ TRUE, /* partial_inplace */
+ 0xffff, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* These two are obsolete. */
+ EMPTY_HOWTO (R_MIPS_ADD_IMMEDIATE),
+ EMPTY_HOWTO (R_MIPS_PJUMP),
+
+ /* Similiar to R_MIPS_REL32, but used for relocations in a GOT section.
+ It must be used for multigot GOT's (and only there). */
+ HOWTO (R_MIPS_RELGOT, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_RELGOT", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Protected jump conversion. This is an optimization hint. No
+ relocation is required for correctness. */
+ HOWTO (R_MIPS_JALR, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_JALR", /* name */
+ FALSE, /* partial_inplace */
+ 0x00000000, /* src_mask */
+ 0x00000000, /* dst_mask */
+ FALSE), /* pcrel_offset */
+};
+
+/* The relocation table used for SHT_RELA sections. */
+
+static reloc_howto_type elf_mips_howto_table_rela[] =
+{
+ /* No relocation. */
+ HOWTO (R_MIPS_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_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_NONE", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 16 bit relocation. */
+ HOWTO (R_MIPS_16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit relocation. */
+ HOWTO (R_MIPS_32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_32", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit symbol relative relocation. */
+ HOWTO (R_MIPS_REL32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_REL32", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 26 bit jump address. */
+ HOWTO (R_MIPS_26, /* type */
+ 2, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 26, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ /* This needs complex overflow
+ detection, because the upper 36
+ bits must match the PC + 4. */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_26", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x03ffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of symbol value. */
+ HOWTO (R_MIPS_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_HI16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of symbol value. */
+ HOWTO (R_MIPS_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_LO16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* GP relative reference. */
+ HOWTO (R_MIPS_GPREL16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips_elf_gprel16_reloc, /* special_function */
+ "R_MIPS_GPREL16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Reference to literal section. */
+ HOWTO (R_MIPS_LITERAL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips_elf_literal_reloc, /* special_function */
+ "R_MIPS_LITERAL", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Reference to global offset table. */
+ HOWTO (R_MIPS_GOT16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 16 bit PC relative reference. */
+ HOWTO (R_MIPS_PC16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_PC16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* 16 bit call through global offset table. */
+ HOWTO (R_MIPS_CALL16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_CALL16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 32 bit GP relative reference. */
+ HOWTO (R_MIPS_GPREL32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ mips_elf_gprel32_reloc, /* special_function */
+ "R_MIPS_GPREL32", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (13),
+ EMPTY_HOWTO (14),
+ EMPTY_HOWTO (15),
+
+ /* A 5 bit shift field. */
+ HOWTO (R_MIPS_SHIFT5, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 5, /* bitsize */
+ FALSE, /* pc_relative */
+ 6, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_SHIFT5", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x000007c0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A 6 bit shift field. */
+ HOWTO (R_MIPS_SHIFT6, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 6, /* bitsize */
+ FALSE, /* pc_relative */
+ 6, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ mips_elf_shift6_reloc, /* special_function */
+ "R_MIPS_SHIFT6", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x000007c4, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 64 bit relocation. */
+ HOWTO (R_MIPS_64, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_64", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Displacement in the global offset table. */
+ HOWTO (R_MIPS_GOT_DISP, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_DISP", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Displacement to page pointer in the global offset table. */
+ HOWTO (R_MIPS_GOT_PAGE, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_PAGE", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Offset from page pointer in the global offset table. */
+ HOWTO (R_MIPS_GOT_OFST, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_OFST", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_MIPS_GOT_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_HI16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_MIPS_GOT_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GOT_LO16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 64 bit subtraction. */
+ HOWTO (R_MIPS_SUB, /* type */
+ 0, /* rightshift */
+ 4, /* size (0 = byte, 1 = short, 2 = long) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_SUB", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Insert the addend as an instruction. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_MIPS_INSERT_A, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_INSERT_A", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Insert the addend as an instruction, and change all relocations
+ to refer to the old instruction at the address. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_MIPS_INSERT_B, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_INSERT_B", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Delete a 32 bit instruction. */
+ /* FIXME: Not handled correctly. */
+ HOWTO (R_MIPS_DELETE, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_DELETE", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Get the higher value of a 64 bit addend. */
+ HOWTO (R_MIPS_HIGHER, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_HIGHER", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Get the highest value of a 64 bit addend. */
+ HOWTO (R_MIPS_HIGHEST, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_HIGHEST", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* High 16 bits of displacement in global offset table. */
+ HOWTO (R_MIPS_CALL_HI16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_CALL_HI16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Low 16 bits of displacement in global offset table. */
+ HOWTO (R_MIPS_CALL_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_CALL_LO16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Section displacement, used by an associated event location section. */
+ HOWTO (R_MIPS_SCN_DISP, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_SCN_DISP", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* 16 bit relocation. */
+ HOWTO (R_MIPS_REL16, /* type */
+ 0, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_REL16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* These two are obsolete. */
+ EMPTY_HOWTO (R_MIPS_ADD_IMMEDIATE),
+ EMPTY_HOWTO (R_MIPS_PJUMP),
+
+ /* Similiar to R_MIPS_REL32, but used for relocations in a GOT section.
+ It must be used for multigot GOT's (and only there). */
+ HOWTO (R_MIPS_RELGOT, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_RELGOT", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Protected jump conversion. This is an optimization hint. No
+ relocation is required for correctness. */
+ HOWTO (R_MIPS_JALR, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_JALR", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+};
+
+/* The reloc used for the mips16 jump instruction. */
+static reloc_howto_type elf_mips16_jump_howto =
+ HOWTO (R_MIPS16_26, /* type */
+ 2, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 26, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ /* This needs complex overflow
+ detection, because the upper four
+ bits must match the PC. */
+ mips16_jump_reloc, /* special_function */
+ "R_MIPS16_26", /* name */
+ TRUE, /* partial_inplace */
+ 0x3ffffff, /* src_mask */
+ 0x3ffffff, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+/* The reloc used for the mips16 gprel instruction. */
+static reloc_howto_type elf_mips16_gprel_howto =
+ HOWTO (R_MIPS16_GPREL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips16_gprel_reloc, /* special_function */
+ "R_MIPS16_GPREL", /* name */
+ TRUE, /* partial_inplace */
+ 0x07ff001f, /* src_mask */
+ 0x07ff001f, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+/* GNU extension to record C++ vtable hierarchy */
+static reloc_howto_type elf_mips_gnu_vtinherit_howto =
+ HOWTO (R_MIPS_GNU_VTINHERIT, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 0, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ NULL, /* special_function */
+ "R_MIPS_GNU_VTINHERIT", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+/* GNU extension to record C++ vtable member usage */
+static reloc_howto_type elf_mips_gnu_vtentry_howto =
+ HOWTO (R_MIPS_GNU_VTENTRY, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 0, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_elf_rel_vtable_reloc_fn, /* special_function */
+ "R_MIPS_GNU_VTENTRY", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE); /* pcrel_offset */
+
+/* 16 bit offset for pc-relative branches. */
+static reloc_howto_type elf_mips_gnu_rel16_s2 =
+ HOWTO (R_MIPS_GNU_REL16_S2, /* type */
+ 2, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GNU_REL16_S2", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ TRUE); /* pcrel_offset */
+
+/* 16 bit offset for pc-relative branches. */
+static reloc_howto_type elf_mips_gnu_rela16_s2 =
+ HOWTO (R_MIPS_GNU_REL16_S2, /* type */
+ 2, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ _bfd_mips_elf_generic_reloc, /* special_function */
+ "R_MIPS_GNU_REL16_S2", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ TRUE); /* pcrel_offset */
+
+/* Set the GP value for OUTPUT_BFD. Returns FALSE if this is a
+ dangerous relocation. */
+
+static bfd_boolean
+mips_elf_assign_gp (bfd *output_bfd, bfd_vma *pgp)
+{
+ unsigned int count;
+ asymbol **sym;
+ unsigned int i;
+
+ /* If we've already figured out what GP will be, just return it. */
+ *pgp = _bfd_get_gp_value (output_bfd);
+ if (*pgp)
+ return TRUE;
+
+ count = bfd_get_symcount (output_bfd);
+ sym = bfd_get_outsymbols (output_bfd);
+
+ /* The linker script will have created a symbol named `_gp' with the
+ appropriate value. */
+ if (sym == NULL)
+ i = count;
+ else
+ {
+ for (i = 0; i < count; i++, sym++)
+ {
+ register const char *name;
+
+ name = bfd_asymbol_name (*sym);
+ if (*name == '_' && strcmp (name, "_gp") == 0)
+ {
+ *pgp = bfd_asymbol_value (*sym);
+ _bfd_set_gp_value (output_bfd, *pgp);
+ break;
+ }
+ }
+ }
+
+ if (i >= count)
+ {
+ /* Only get the error once. */
+ *pgp = 4;
+ _bfd_set_gp_value (output_bfd, *pgp);
+ return FALSE;
+ }
+
+ 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
+mips_elf_final_gp (bfd *output_bfd, asymbol *symbol, bfd_boolean relocatable,
+ char **error_message, bfd_vma *pgp)
+{
+ if (bfd_is_und_section (symbol->section)
+ && ! relocatable)
+ {
+ *pgp = 0;
+ return bfd_reloc_undefined;
+ }
+
+ *pgp = _bfd_get_gp_value (output_bfd);
+ if (*pgp == 0
+ && (! relocatable
+ || (symbol->flags & BSF_SECTION_SYM) != 0))
+ {
+ if (relocatable)
+ {
+ /* Make up a value. */
+ *pgp = symbol->section->output_section->vma /*+ 0x4000*/;
+ _bfd_set_gp_value (output_bfd, *pgp);
+ }
+ else if (!mips_elf_assign_gp (output_bfd, pgp))
+ {
+ *error_message =
+ (char *) _("GP relative relocation when _gp not defined");
+ return bfd_reloc_dangerous;
+ }
+ }
+
+ return bfd_reloc_ok;
+}
+
+/* Do a R_MIPS_GPREL16 relocation. This is a 16 bit value which must
+ become the offset from the gp register. */
+
+static bfd_reloc_status_type
+mips_elf_gprel16_reloc (bfd *abfd ATTRIBUTE_UNUSED, arelent *reloc_entry,
+ asymbol *symbol, void *data ATTRIBUTE_UNUSED,
+ asection *input_section, bfd *output_bfd,
+ char **error_message ATTRIBUTE_UNUSED)
+{
+ bfd_boolean relocatable;
+ bfd_reloc_status_type ret;
+ bfd_vma gp;
+
+ if (output_bfd != NULL)
+ relocatable = TRUE;
+ else
+ {
+ relocatable = FALSE;
+ output_bfd = symbol->section->output_section->owner;
+ }
+
+ ret = mips_elf_final_gp (output_bfd, symbol, relocatable, error_message,
+ &gp);
+ if (ret != bfd_reloc_ok)
+ return ret;
+
+ return _bfd_mips_elf_gprel16_with_gp (abfd, symbol, reloc_entry,
+ input_section, relocatable,
+ data, gp);
+}
+
+/* Do a R_MIPS_LITERAL relocation. */
+
+static bfd_reloc_status_type
+mips_elf_literal_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+ void *data, asection *input_section, bfd *output_bfd,
+ char **error_message)
+{
+ bfd_boolean relocatable;
+ bfd_reloc_status_type ret;
+ bfd_vma gp;
+
+ /* FIXME: The entries in the .lit8 and .lit4 sections should be merged. */
+ if (output_bfd != NULL)
+ relocatable = TRUE;
+ else
+ {
+ relocatable = FALSE;
+ output_bfd = symbol->section->output_section->owner;
+ }
+
+ ret = mips_elf_final_gp (output_bfd, symbol, relocatable, error_message,
+ &gp);
+ if (ret != bfd_reloc_ok)
+ return ret;
+
+ return _bfd_mips_elf_gprel16_with_gp (abfd, symbol, reloc_entry,
+ input_section, relocatable,
+ data, gp);
+}
+
+/* Do a R_MIPS_GPREL32 relocation. This is a 32 bit value which must
+ become the offset from the gp register. */
+
+static bfd_reloc_status_type
+mips_elf_gprel32_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+ void *data, asection *input_section, bfd *output_bfd,
+ char **error_message)
+{
+ bfd_boolean relocatable;
+ bfd_reloc_status_type ret;
+ bfd_vma gp;
+
+ /* R_MIPS_GPREL32 relocations are defined for local symbols only. */
+ if (output_bfd != NULL
+ && (symbol->flags & BSF_SECTION_SYM) == 0
+ && (symbol->flags & BSF_LOCAL) != 0)
+ {
+ *error_message = (char *)
+ _("32bits gp relative relocation occurs for an external symbol");
+ return bfd_reloc_outofrange;
+ }
+
+ if (output_bfd != NULL)
+ {
+ relocatable = TRUE;
+ gp = _bfd_get_gp_value (output_bfd);
+ }
+ else
+ {
+ relocatable = FALSE;
+ output_bfd = symbol->section->output_section->owner;
+
+ ret = mips_elf_final_gp (output_bfd, symbol, relocatable,
+ error_message, &gp);
+ if (ret != bfd_reloc_ok)
+ return ret;
+ }
+
+ return gprel32_with_gp (abfd, symbol, reloc_entry, input_section,
+ relocatable, data, gp);
+}
+
+static bfd_reloc_status_type
+gprel32_with_gp (bfd *abfd, asymbol *symbol, arelent *reloc_entry,
+ asection *input_section, bfd_boolean relocatable,
+ void *data, bfd_vma gp)
+{
+ bfd_vma relocation;
+ unsigned long val;
+
+ if (bfd_is_com_section (symbol->section))
+ relocation = 0;
+ else
+ relocation = symbol->value;
+
+ relocation += symbol->section->output_section->vma;
+ relocation += symbol->section->output_offset;
+
+ if (reloc_entry->address > input_section->_cooked_size)
+ return bfd_reloc_outofrange;
+
+ if (reloc_entry->howto->src_mask == 0)
+ val = 0;
+ else
+ val = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
+
+ /* Set val to the offset into the section or symbol. */
+ val += reloc_entry->addend;
+
+ /* Adjust val for the final section location and GP value. If we
+ are producing relocatable output, we don't want to do this for
+ an external symbol. */
+ if (! relocatable
+ || (symbol->flags & BSF_SECTION_SYM) != 0)
+ val += relocation - gp;
+
+ bfd_put_32 (abfd, val, (bfd_byte *) data + reloc_entry->address);
+
+ if (relocatable)
+ reloc_entry->address += input_section->output_offset;
+
+ return bfd_reloc_ok;
+}
+
+/* Do a R_MIPS_SHIFT6 relocation. The MSB of the shift is stored at bit 2,
+ the rest is at bits 6-10. The bitpos already got right by the howto. */
+
+static bfd_reloc_status_type
+mips_elf_shift6_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+ void *data, asection *input_section, bfd *output_bfd,
+ char **error_message)
+{
+ if (reloc_entry->howto->partial_inplace)
+ {
+ reloc_entry->addend = ((reloc_entry->addend & 0x00007c0)
+ | (reloc_entry->addend & 0x00000800) >> 9);
+ }
+
+ return _bfd_mips_elf_generic_reloc (abfd, reloc_entry, symbol, data,
+ input_section, output_bfd,
+ error_message);
+}
+
+/* Handle a mips16 jump. */
+
+static bfd_reloc_status_type
+mips16_jump_reloc (bfd *abfd ATTRIBUTE_UNUSED,
+ arelent *reloc_entry ATTRIBUTE_UNUSED,
+ asymbol *symbol ATTRIBUTE_UNUSED,
+ void *data ATTRIBUTE_UNUSED,
+ asection *input_section, bfd *output_bfd ATTRIBUTE_UNUSED,
+ char **error_message ATTRIBUTE_UNUSED)
+{
+ static bfd_boolean warned = FALSE;
+
+ /* FIXME. */
+ if (! warned)
+ (*_bfd_error_handler)
+ (_("Linking mips16 objects into %s format is not supported"),
+ bfd_get_target (input_section->output_section->owner));
+ warned = TRUE;
+
+ return bfd_reloc_undefined;
+}
+
+/* Handle a mips16 GP relative reloc. */
+
+static bfd_reloc_status_type
+mips16_gprel_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+ void *data, asection *input_section, bfd *output_bfd,
+ char **error_message)
+{
+ bfd_boolean relocatable;
+ bfd_reloc_status_type ret;
+ bfd_vma gp;
+ unsigned short extend = 0;
+ unsigned short insn = 0;
+ bfd_signed_vma val;
+ bfd_vma relocation;
+
+ if (output_bfd != NULL)
+ relocatable = TRUE;
+ else
+ {
+ relocatable = FALSE;
+ output_bfd = symbol->section->output_section->owner;
+ }
+
+ ret = mips_elf_final_gp (output_bfd, symbol, relocatable, error_message,
+ &gp);
+ if (ret != bfd_reloc_ok)
+ return ret;
+
+ if (reloc_entry->address > input_section->_cooked_size)
+ return bfd_reloc_outofrange;
+
+ if (bfd_is_com_section (symbol->section))
+ relocation = 0;
+ else
+ relocation = symbol->value;
+
+ relocation += symbol->section->output_section->vma;
+ relocation += symbol->section->output_offset;
+
+ /* Set val to the offset into the section or symbol. */
+ val = reloc_entry->addend;
+
+ if (reloc_entry->howto->partial_inplace)
+ {
+ /* Pick up the mips16 extend instruction and the real instruction. */
+ extend = bfd_get_16 (abfd, (bfd_byte *) data + reloc_entry->address);
+ insn = bfd_get_16 (abfd, (bfd_byte *) data + reloc_entry->address + 2);
+ val += ((extend & 0x1f) << 11) | (extend & 0x7e0) | (insn & 0x1f);
+ }
+
+ _bfd_mips_elf_sign_extend(val, 16);
+
+ /* Adjust val for the final section location and GP value. If we
+ are producing relocatable output, we don't want to do this for
+ an external symbol. */
+ if (! relocatable
+ || (symbol->flags & BSF_SECTION_SYM) != 0)
+ val += relocation - gp;
+
+ if (reloc_entry->howto->partial_inplace)
+ {
+ bfd_put_16 (abfd,
+ (extend & 0xf800) | ((val >> 11) & 0x1f) | (val & 0x7e0),
+ (bfd_byte *) data + reloc_entry->address);
+ bfd_put_16 (abfd,
+ (insn & 0xffe0) | (val & 0x1f),
+ (bfd_byte *) data + reloc_entry->address + 2);
+ }
+ else
+ reloc_entry->addend = val;
+
+ if (relocatable)
+ reloc_entry->address += input_section->output_offset;
+ else if (((val & ~0xffff) != ~0xffff) && ((val & ~0xffff) != 0))
+ return bfd_reloc_overflow;
+
+ return bfd_reloc_ok;
+}
+
+/* A mapping from BFD reloc types to MIPS ELF reloc types. */
+
+struct elf_reloc_map {
+ bfd_reloc_code_real_type bfd_val;
+ enum elf_mips_reloc_type elf_val;
+};
+
+static const struct elf_reloc_map mips_reloc_map[] =
+{
+ { BFD_RELOC_NONE, R_MIPS_NONE },
+ { BFD_RELOC_16, R_MIPS_16 },
+ { BFD_RELOC_32, R_MIPS_32 },
+ /* There is no BFD reloc for R_MIPS_REL32. */
+ { BFD_RELOC_CTOR, R_MIPS_32 },
+ { BFD_RELOC_64, R_MIPS_64 },
+ { BFD_RELOC_16_PCREL, R_MIPS_PC16 },
+ { BFD_RELOC_HI16_S, R_MIPS_HI16 },
+ { BFD_RELOC_LO16, R_MIPS_LO16 },
+ { BFD_RELOC_GPREL16, R_MIPS_GPREL16 },
+ { BFD_RELOC_GPREL32, R_MIPS_GPREL32 },
+ { BFD_RELOC_MIPS_JMP, R_MIPS_26 },
+ { BFD_RELOC_MIPS_LITERAL, R_MIPS_LITERAL },
+ { BFD_RELOC_MIPS_GOT16, R_MIPS_GOT16 },
+ { BFD_RELOC_MIPS_CALL16, R_MIPS_CALL16 },
+ { BFD_RELOC_MIPS_SHIFT5, R_MIPS_SHIFT5 },
+ { BFD_RELOC_MIPS_SHIFT6, R_MIPS_SHIFT6 },
+ { BFD_RELOC_MIPS_GOT_DISP, R_MIPS_GOT_DISP },
+ { BFD_RELOC_MIPS_GOT_PAGE, R_MIPS_GOT_PAGE },
+ { BFD_RELOC_MIPS_GOT_OFST, R_MIPS_GOT_OFST },
+ { BFD_RELOC_MIPS_GOT_HI16, R_MIPS_GOT_HI16 },
+ { BFD_RELOC_MIPS_GOT_LO16, R_MIPS_GOT_LO16 },
+ { BFD_RELOC_MIPS_SUB, R_MIPS_SUB },
+ { BFD_RELOC_MIPS_INSERT_A, R_MIPS_INSERT_A },
+ { BFD_RELOC_MIPS_INSERT_B, R_MIPS_INSERT_B },
+ { BFD_RELOC_MIPS_DELETE, R_MIPS_DELETE },
+ { BFD_RELOC_MIPS_HIGHEST, R_MIPS_HIGHEST },
+ { BFD_RELOC_MIPS_HIGHER, R_MIPS_HIGHER },
+ { BFD_RELOC_MIPS_CALL_HI16, R_MIPS_CALL_HI16 },
+ { BFD_RELOC_MIPS_CALL_LO16, R_MIPS_CALL_LO16 },
+ { BFD_RELOC_MIPS_SCN_DISP, R_MIPS_SCN_DISP },
+ { BFD_RELOC_MIPS_REL16, R_MIPS_REL16 },
+ /* Use of R_MIPS_ADD_IMMEDIATE and R_MIPS_PJUMP is deprecated. */
+ { BFD_RELOC_MIPS_RELGOT, R_MIPS_RELGOT },
+ { BFD_RELOC_MIPS_JALR, R_MIPS_JALR }
+};
+
+/* Given a BFD reloc type, return a howto structure. */
+
+static reloc_howto_type *
+bfd_elf32_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+ bfd_reloc_code_real_type code)
+{
+ unsigned int i;
+ /* FIXME: We default to RELA here instead of choosing the right
+ relocation variant. */
+ reloc_howto_type *howto_table = elf_mips_howto_table_rela;
+
+ for (i = 0; i < sizeof (mips_reloc_map) / sizeof (struct elf_reloc_map);
+ i++)
+ {
+ if (mips_reloc_map[i].bfd_val == code)
+ return &howto_table[(int) mips_reloc_map[i].elf_val];
+ }
+
+ switch (code)
+ {
+ case BFD_RELOC_MIPS16_JMP:
+ return &elf_mips16_jump_howto;
+ case BFD_RELOC_MIPS16_GPREL:
+ return &elf_mips16_gprel_howto;
+ case BFD_RELOC_VTABLE_INHERIT:
+ return &elf_mips_gnu_vtinherit_howto;
+ case BFD_RELOC_VTABLE_ENTRY:
+ return &elf_mips_gnu_vtentry_howto;
+ case BFD_RELOC_16_PCREL_S2:
+ return &elf_mips_gnu_rela16_s2;
+ default:
+ bfd_set_error (bfd_error_bad_value);
+ return NULL;
+ }
+}
+
+/* Given a MIPS Elf_Internal_Rel, fill in an arelent structure. */
+
+static reloc_howto_type *
+mips_elf_n32_rtype_to_howto (unsigned int r_type, bfd_boolean rela_p)
+{
+ switch (r_type)
+ {
+ case R_MIPS16_26:
+ return &elf_mips16_jump_howto;
+ case R_MIPS16_GPREL:
+ return &elf_mips16_gprel_howto;
+ case R_MIPS_GNU_VTINHERIT:
+ return &elf_mips_gnu_vtinherit_howto;
+ case R_MIPS_GNU_VTENTRY:
+ return &elf_mips_gnu_vtentry_howto;
+ case R_MIPS_GNU_REL16_S2:
+ if (rela_p)
+ return &elf_mips_gnu_rela16_s2;
+ else
+ return &elf_mips_gnu_rel16_s2;
+ default:
+ BFD_ASSERT (r_type < (unsigned int) R_MIPS_max);
+ if (rela_p)
+ return &elf_mips_howto_table_rela[r_type];
+ else
+ return &elf_mips_howto_table_rel[r_type];
+ break;
+ }
+}
+
+/* Given a MIPS Elf_Internal_Rel, fill in an arelent structure. */
+
+static void
+mips_info_to_howto_rel (bfd *abfd, arelent *cache_ptr, Elf_Internal_Rela *dst)
+{
+ unsigned int r_type;
+
+ r_type = ELF32_R_TYPE (dst->r_info);
+ cache_ptr->howto = mips_elf_n32_rtype_to_howto (r_type, FALSE);
+
+ /* The addend for a GPREL16 or LITERAL relocation comes from the GP
+ value for the object file. We get the addend now, rather than
+ when we do the relocation, because the symbol manipulations done
+ by the linker may cause us to lose track of the input BFD. */
+ if (((*cache_ptr->sym_ptr_ptr)->flags & BSF_SECTION_SYM) != 0
+ && (r_type == (unsigned int) R_MIPS_GPREL16
+ || r_type == (unsigned int) R_MIPS_LITERAL))
+ cache_ptr->addend = elf_gp (abfd);
+}
+
+/* Given a MIPS Elf_Internal_Rela, fill in an arelent structure. */
+
+static void
+mips_info_to_howto_rela (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 = mips_elf_n32_rtype_to_howto (r_type, TRUE);
+ cache_ptr->addend = dst->r_addend;
+}
+
+/* Determine whether a symbol is global for the purposes of splitting
+ the symbol table into global symbols and local symbols. At least
+ on Irix 5, this split must be between section symbols and all other
+ symbols. On most ELF targets the split is between static symbols
+ and externally visible symbols. */
+
+static bfd_boolean
+mips_elf_sym_is_global (bfd *abfd ATTRIBUTE_UNUSED, asymbol *sym)
+{
+ if (SGI_COMPAT (abfd))
+ return (sym->flags & BSF_SECTION_SYM) == 0;
+ else
+ return ((sym->flags & (BSF_GLOBAL | BSF_WEAK)) != 0
+ || bfd_is_und_section (bfd_get_section (sym))
+ || bfd_is_com_section (bfd_get_section (sym)));
+}
+
+/* Set the right machine number for a MIPS ELF file. */
+
+static bfd_boolean
+mips_elf_n32_object_p (bfd *abfd)
+{
+ unsigned long mach;
+
+ /* Irix 5 and 6 are broken. Object file symbol tables are not always
+ sorted correctly such that local symbols precede global symbols,
+ and the sh_info field in the symbol table is not always right. */
+ if (SGI_COMPAT (abfd))
+ elf_bad_symtab (abfd) = TRUE;
+
+ mach = _bfd_elf_mips_mach (elf_elfheader (abfd)->e_flags);
+ bfd_default_set_arch_mach (abfd, bfd_arch_mips, mach);
+
+ if (! ABI_N32_P(abfd))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Support for core dump NOTE sections. */
+static bfd_boolean
+elf32_mips_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
+{
+ int offset;
+ unsigned int raw_size;
+
+ switch (note->descsz)
+ {
+ default:
+ return FALSE;
+
+ case 440: /* Linux/MIPS N32 */
+ /* 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;
+ raw_size = 360;
+
+ break;
+ }
+
+ /* Make a ".reg/999" section. */
+ return _bfd_elfcore_make_pseudosection (abfd, ".reg", raw_size,
+ note->descpos + offset);
+}
+
+static bfd_boolean
+elf32_mips_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
+{
+ switch (note->descsz)
+ {
+ default:
+ return FALSE;
+
+ case 128: /* Linux/MIPS elf_prpsinfo */
+ elf_tdata (abfd)->core_program
+ = _bfd_elfcore_strndup (abfd, note->descdata + 32, 16);
+ elf_tdata (abfd)->core_command
+ = _bfd_elfcore_strndup (abfd, note->descdata + 48, 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;
+}
+
+/* Depending on the target vector we generate some version of Irix
+ executables or "normal" MIPS ELF ABI executables. */
+static irix_compat_t
+elf_n32_mips_irix_compat (bfd *abfd)
+{
+ if ((abfd->xvec == &bfd_elf32_nbigmips_vec)
+ || (abfd->xvec == &bfd_elf32_nlittlemips_vec))
+ return ict_irix6;
+ else
+ return ict_none;
+}
+
+/* ECOFF swapping routines. These are used when dealing with the
+ .mdebug section, which is in the ECOFF debugging format. */
+static const struct ecoff_debug_swap mips_elf32_ecoff_debug_swap = {
+ /* Symbol table magic number. */
+ magicSym,
+ /* Alignment of debugging information. E.g., 4. */
+ 4,
+ /* Sizes of external symbolic information. */
+ sizeof (struct hdr_ext),
+ sizeof (struct dnr_ext),
+ sizeof (struct pdr_ext),
+ sizeof (struct sym_ext),
+ sizeof (struct opt_ext),
+ sizeof (struct fdr_ext),
+ sizeof (struct rfd_ext),
+ sizeof (struct ext_ext),
+ /* Functions to swap in external symbolic data. */
+ ecoff_swap_hdr_in,
+ ecoff_swap_dnr_in,
+ ecoff_swap_pdr_in,
+ ecoff_swap_sym_in,
+ ecoff_swap_opt_in,
+ ecoff_swap_fdr_in,
+ ecoff_swap_rfd_in,
+ ecoff_swap_ext_in,
+ _bfd_ecoff_swap_tir_in,
+ _bfd_ecoff_swap_rndx_in,
+ /* Functions to swap out external symbolic data. */
+ ecoff_swap_hdr_out,
+ ecoff_swap_dnr_out,
+ ecoff_swap_pdr_out,
+ ecoff_swap_sym_out,
+ ecoff_swap_opt_out,
+ ecoff_swap_fdr_out,
+ ecoff_swap_rfd_out,
+ ecoff_swap_ext_out,
+ _bfd_ecoff_swap_tir_out,
+ _bfd_ecoff_swap_rndx_out,
+ /* Function to read in symbolic data. */
+ _bfd_mips_elf_read_ecoff_info
+};
+
+#define ELF_ARCH bfd_arch_mips
+#define ELF_MACHINE_CODE EM_MIPS
+
+#define elf_backend_collect TRUE
+#define elf_backend_type_change_ok TRUE
+#define elf_backend_can_gc_sections TRUE
+#define elf_info_to_howto mips_info_to_howto_rela
+#define elf_info_to_howto_rel mips_info_to_howto_rel
+#define elf_backend_sym_is_global mips_elf_sym_is_global
+#define elf_backend_object_p mips_elf_n32_object_p
+#define elf_backend_symbol_processing _bfd_mips_elf_symbol_processing
+#define elf_backend_section_processing _bfd_mips_elf_section_processing
+#define elf_backend_section_from_shdr _bfd_mips_elf_section_from_shdr
+#define elf_backend_fake_sections _bfd_mips_elf_fake_sections
+#define elf_backend_section_from_bfd_section \
+ _bfd_mips_elf_section_from_bfd_section
+#define elf_backend_add_symbol_hook _bfd_mips_elf_add_symbol_hook
+#define elf_backend_link_output_symbol_hook \
+ _bfd_mips_elf_link_output_symbol_hook
+#define elf_backend_create_dynamic_sections \
+ _bfd_mips_elf_create_dynamic_sections
+#define elf_backend_check_relocs _bfd_mips_elf_check_relocs
+#define elf_backend_adjust_dynamic_symbol \
+ _bfd_mips_elf_adjust_dynamic_symbol
+#define elf_backend_always_size_sections \
+ _bfd_mips_elf_always_size_sections
+#define elf_backend_size_dynamic_sections \
+ _bfd_mips_elf_size_dynamic_sections
+#define elf_backend_relocate_section _bfd_mips_elf_relocate_section
+#define elf_backend_finish_dynamic_symbol \
+ _bfd_mips_elf_finish_dynamic_symbol
+#define elf_backend_finish_dynamic_sections \
+ _bfd_mips_elf_finish_dynamic_sections
+#define elf_backend_final_write_processing \
+ _bfd_mips_elf_final_write_processing
+#define elf_backend_additional_program_headers \
+ _bfd_mips_elf_additional_program_headers
+#define elf_backend_modify_segment_map _bfd_mips_elf_modify_segment_map
+#define elf_backend_gc_mark_hook _bfd_mips_elf_gc_mark_hook
+#define elf_backend_gc_sweep_hook _bfd_mips_elf_gc_sweep_hook
+#define elf_backend_copy_indirect_symbol \
+ _bfd_mips_elf_copy_indirect_symbol
+#define elf_backend_hide_symbol _bfd_mips_elf_hide_symbol
+#define elf_backend_grok_prstatus elf32_mips_grok_prstatus
+#define elf_backend_grok_psinfo elf32_mips_grok_psinfo
+#define elf_backend_ecoff_debug_swap &mips_elf32_ecoff_debug_swap
+
+#define elf_backend_got_header_size (4 * MIPS_RESERVED_GOTNO)
+
+/* MIPS n32 ELF can use a mixture of REL and RELA, but some Relocations
+ work better/work only in RELA, so we default to this. */
+#define elf_backend_may_use_rel_p 1
+#define elf_backend_may_use_rela_p 1
+#define elf_backend_default_use_rela_p 1
+#define elf_backend_sign_extend_vma TRUE
+
+#define elf_backend_discard_info _bfd_mips_elf_discard_info
+#define elf_backend_ignore_discarded_relocs \
+ _bfd_mips_elf_ignore_discarded_relocs
+#define elf_backend_write_section _bfd_mips_elf_write_section
+#define elf_backend_mips_irix_compat elf_n32_mips_irix_compat
+#define elf_backend_mips_rtype_to_howto mips_elf_n32_rtype_to_howto
+#define bfd_elf32_find_nearest_line _bfd_mips_elf_find_nearest_line
+#define bfd_elf32_new_section_hook _bfd_mips_elf_new_section_hook
+#define bfd_elf32_set_section_contents _bfd_mips_elf_set_section_contents
+#define bfd_elf32_bfd_get_relocated_section_contents \
+ _bfd_elf_mips_get_relocated_section_contents
+#define bfd_elf32_bfd_link_hash_table_create \
+ _bfd_mips_elf_link_hash_table_create
+#define bfd_elf32_bfd_final_link _bfd_mips_elf_final_link
+#define bfd_elf32_bfd_merge_private_bfd_data \
+ _bfd_mips_elf_merge_private_bfd_data
+#define bfd_elf32_bfd_set_private_flags _bfd_mips_elf_set_private_flags
+#define bfd_elf32_bfd_print_private_bfd_data \
+ _bfd_mips_elf_print_private_bfd_data
+#define bfd_elf32_bfd_relax_section _bfd_mips_relax_section
+
+/* Support for SGI-ish mips targets using n32 ABI. */
+
+#define TARGET_LITTLE_SYM bfd_elf32_nlittlemips_vec
+#define TARGET_LITTLE_NAME "elf32-nlittlemips"
+#define TARGET_BIG_SYM bfd_elf32_nbigmips_vec
+#define TARGET_BIG_NAME "elf32-nbigmips"
+
+/* The SVR4 MIPS ABI says that this should be 0x10000, but Irix 5 uses
+ a value of 0x1000, and we are compatible.
+ FIXME: How does this affect NewABI? */
+#define ELF_MAXPAGESIZE 0x1000
+
+#include "elf32-target.h"
+
+/* Support for traditional mips targets using n32 ABI. */
+#undef TARGET_LITTLE_SYM
+#undef TARGET_LITTLE_NAME
+#undef TARGET_BIG_SYM
+#undef TARGET_BIG_NAME
+
+#undef ELF_MAXPAGESIZE
+
+#define TARGET_LITTLE_SYM bfd_elf32_ntradlittlemips_vec
+#define TARGET_LITTLE_NAME "elf32-ntradlittlemips"
+#define TARGET_BIG_SYM bfd_elf32_ntradbigmips_vec
+#define TARGET_BIG_NAME "elf32-ntradbigmips"
+
+/* The SVR4 MIPS ABI says that this should be 0x10000, and Linux uses
+ page sizes of up to that limit, so we need to respect it. */
+#define ELF_MAXPAGESIZE 0x10000
+#define elf32_bed elf32_tradbed
+
+/* Include the target file again for this target. */
+#include "elf32-target.h"
diff --git a/contrib/binutils/bfd/elfxx-mips.c b/contrib/binutils/bfd/elfxx-mips.c
new file mode 100644
index 0000000..8fb20d8
--- /dev/null
+++ b/contrib/binutils/bfd/elfxx-mips.c
@@ -0,0 +1,9234 @@
+/* MIPS-specific support for ELF
+ Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+ 2003 Free Software Foundation, Inc.
+
+ Most of the information added by Ian Lance Taylor, Cygnus Support,
+ <ian@cygnus.com>.
+ N32/64 ABI support added by Mark Mitchell, CodeSourcery, LLC.
+ <mark@codesourcery.com>
+ Traditional MIPS targets support added by Koundinya.K, Dansk Data
+ Elektronik & Operations Research Group. <kk@ddeorg.soft.net>
+
+ 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 functionality common to the different MIPS ABI's. */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+#include "libiberty.h"
+#include "elf-bfd.h"
+#include "elfxx-mips.h"
+#include "elf/mips.h"
+
+/* Get the ECOFF swapping routines. */
+#include "coff/sym.h"
+#include "coff/symconst.h"
+#include "coff/ecoff.h"
+#include "coff/mips.h"
+
+#include "hashtab.h"
+
+/* This structure is used to hold .got entries while estimating got
+ sizes. */
+struct mips_got_entry
+{
+ /* The input bfd in which the symbol is defined. */
+ bfd *abfd;
+ /* The index of the symbol, as stored in the relocation r_info, if
+ we have a local symbol; -1 otherwise. */
+ long symndx;
+ union
+ {
+ /* If abfd == NULL, an address that must be stored in the got. */
+ bfd_vma address;
+ /* If abfd != NULL && symndx != -1, the addend of the relocation
+ that should be added to the symbol value. */
+ bfd_vma addend;
+ /* If abfd != NULL && symndx == -1, the hash table entry
+ corresponding to a global symbol in the got (or, local, if
+ h->forced_local). */
+ struct mips_elf_link_hash_entry *h;
+ } d;
+ /* The offset from the beginning of the .got section to the entry
+ corresponding to this symbol+addend. If it's a global symbol
+ whose offset is yet to be decided, it's going to be -1. */
+ long gotidx;
+};
+
+/* This structure is used to hold .got information when linking. */
+
+struct mips_got_info
+{
+ /* The global symbol in the GOT with the lowest index in the dynamic
+ symbol table. */
+ struct elf_link_hash_entry *global_gotsym;
+ /* The number of global .got entries. */
+ unsigned int global_gotno;
+ /* The number of local .got entries. */
+ unsigned int local_gotno;
+ /* The number of local .got entries we have used. */
+ unsigned int assigned_gotno;
+ /* A hash table holding members of the got. */
+ struct htab *got_entries;
+ /* A hash table mapping input bfds to other mips_got_info. NULL
+ unless multi-got was necessary. */
+ struct htab *bfd2got;
+ /* In multi-got links, a pointer to the next got (err, rather, most
+ of the time, it points to the previous got). */
+ struct mips_got_info *next;
+};
+
+/* Map an input bfd to a got in a multi-got link. */
+
+struct mips_elf_bfd2got_hash {
+ bfd *bfd;
+ struct mips_got_info *g;
+};
+
+/* Structure passed when traversing the bfd2got hash table, used to
+ create and merge bfd's gots. */
+
+struct mips_elf_got_per_bfd_arg
+{
+ /* A hashtable that maps bfds to gots. */
+ htab_t bfd2got;
+ /* The output bfd. */
+ bfd *obfd;
+ /* The link information. */
+ struct bfd_link_info *info;
+ /* A pointer to the primary got, i.e., the one that's going to get
+ the implicit relocations from DT_MIPS_LOCAL_GOTNO and
+ DT_MIPS_GOTSYM. */
+ struct mips_got_info *primary;
+ /* A non-primary got we're trying to merge with other input bfd's
+ gots. */
+ struct mips_got_info *current;
+ /* The maximum number of got entries that can be addressed with a
+ 16-bit offset. */
+ unsigned int max_count;
+ /* The number of local and global entries in the primary got. */
+ unsigned int primary_count;
+ /* The number of local and global entries in the current got. */
+ unsigned int current_count;
+};
+
+/* Another structure used to pass arguments for got entries traversal. */
+
+struct mips_elf_set_global_got_offset_arg
+{
+ struct mips_got_info *g;
+ int value;
+ unsigned int needed_relocs;
+ struct bfd_link_info *info;
+};
+
+struct _mips_elf_section_data
+{
+ struct bfd_elf_section_data elf;
+ union
+ {
+ struct mips_got_info *got_info;
+ bfd_byte *tdata;
+ } u;
+};
+
+#define mips_elf_section_data(sec) \
+ ((struct _mips_elf_section_data *) elf_section_data (sec))
+
+/* This structure is passed to mips_elf_sort_hash_table_f when sorting
+ the dynamic symbols. */
+
+struct mips_elf_hash_sort_data
+{
+ /* The symbol in the global GOT with the lowest dynamic symbol table
+ index. */
+ struct elf_link_hash_entry *low;
+ /* The least dynamic symbol table index corresponding to a symbol
+ with a GOT entry. */
+ long min_got_dynindx;
+ /* The greatest dynamic symbol table index corresponding to a symbol
+ with a GOT entry that is not referenced (e.g., a dynamic symbol
+ with dynamic relocations pointing to it from non-primary GOTs). */
+ long max_unref_got_dynindx;
+ /* The greatest dynamic symbol table index not corresponding to a
+ symbol without a GOT entry. */
+ long max_non_got_dynindx;
+};
+
+/* The MIPS ELF linker needs additional information for each symbol in
+ the global hash table. */
+
+struct mips_elf_link_hash_entry
+{
+ struct elf_link_hash_entry root;
+
+ /* External symbol information. */
+ EXTR esym;
+
+ /* Number of R_MIPS_32, R_MIPS_REL32, or R_MIPS_64 relocs against
+ this symbol. */
+ unsigned int possibly_dynamic_relocs;
+
+ /* If the R_MIPS_32, R_MIPS_REL32, or R_MIPS_64 reloc is against
+ a readonly section. */
+ bfd_boolean readonly_reloc;
+
+ /* We must not create a stub for a symbol that has relocations
+ related to taking the function's address, i.e. any but
+ R_MIPS_CALL*16 ones -- see "MIPS ABI Supplement, 3rd Edition",
+ p. 4-20. */
+ bfd_boolean no_fn_stub;
+
+ /* If there is a stub that 32 bit functions should use to call this
+ 16 bit function, this points to the section containing the stub. */
+ asection *fn_stub;
+
+ /* Whether we need the fn_stub; this is set if this symbol appears
+ in any relocs other than a 16 bit call. */
+ bfd_boolean need_fn_stub;
+
+ /* If there is a stub that 16 bit functions should use to call this
+ 32 bit function, this points to the section containing the stub. */
+ asection *call_stub;
+
+ /* This is like the call_stub field, but it is used if the function
+ being called returns a floating point value. */
+ asection *call_fp_stub;
+
+ /* Are we forced local? .*/
+ bfd_boolean forced_local;
+};
+
+/* MIPS ELF linker hash table. */
+
+struct mips_elf_link_hash_table
+{
+ struct elf_link_hash_table root;
+#if 0
+ /* We no longer use this. */
+ /* String section indices for the dynamic section symbols. */
+ bfd_size_type dynsym_sec_strindex[SIZEOF_MIPS_DYNSYM_SECNAMES];
+#endif
+ /* The number of .rtproc entries. */
+ bfd_size_type procedure_count;
+ /* The size of the .compact_rel section (if SGI_COMPAT). */
+ bfd_size_type compact_rel_size;
+ /* This flag indicates that the value of DT_MIPS_RLD_MAP dynamic
+ entry is set to the address of __rld_obj_head as in IRIX5. */
+ bfd_boolean use_rld_obj_head;
+ /* This is the value of the __rld_map or __rld_obj_head symbol. */
+ bfd_vma rld_value;
+ /* This is set if we see any mips16 stub sections. */
+ bfd_boolean mips16_stubs_seen;
+};
+
+/* Structure used to pass information to mips_elf_output_extsym. */
+
+struct extsym_info
+{
+ bfd *abfd;
+ struct bfd_link_info *info;
+ struct ecoff_debug_info *debug;
+ const struct ecoff_debug_swap *swap;
+ bfd_boolean failed;
+};
+
+/* The names of the runtime procedure table symbols used on IRIX5. */
+
+static const char * const mips_elf_dynsym_rtproc_names[] =
+{
+ "_procedure_table",
+ "_procedure_string_table",
+ "_procedure_table_size",
+ NULL
+};
+
+/* These structures are used to generate the .compact_rel section on
+ IRIX5. */
+
+typedef struct
+{
+ unsigned long id1; /* Always one? */
+ unsigned long num; /* Number of compact relocation entries. */
+ unsigned long id2; /* Always two? */
+ unsigned long offset; /* The file offset of the first relocation. */
+ unsigned long reserved0; /* Zero? */
+ unsigned long reserved1; /* Zero? */
+} Elf32_compact_rel;
+
+typedef struct
+{
+ bfd_byte id1[4];
+ bfd_byte num[4];
+ bfd_byte id2[4];
+ bfd_byte offset[4];
+ bfd_byte reserved0[4];
+ bfd_byte reserved1[4];
+} Elf32_External_compact_rel;
+
+typedef struct
+{
+ unsigned int ctype : 1; /* 1: long 0: short format. See below. */
+ unsigned int rtype : 4; /* Relocation types. See below. */
+ unsigned int dist2to : 8;
+ unsigned int relvaddr : 19; /* (VADDR - vaddr of the previous entry)/ 4 */
+ unsigned long konst; /* KONST field. See below. */
+ unsigned long vaddr; /* VADDR to be relocated. */
+} Elf32_crinfo;
+
+typedef struct
+{
+ unsigned int ctype : 1; /* 1: long 0: short format. See below. */
+ unsigned int rtype : 4; /* Relocation types. See below. */
+ unsigned int dist2to : 8;
+ unsigned int relvaddr : 19; /* (VADDR - vaddr of the previous entry)/ 4 */
+ unsigned long konst; /* KONST field. See below. */
+} Elf32_crinfo2;
+
+typedef struct
+{
+ bfd_byte info[4];
+ bfd_byte konst[4];
+ bfd_byte vaddr[4];
+} Elf32_External_crinfo;
+
+typedef struct
+{
+ bfd_byte info[4];
+ bfd_byte konst[4];
+} Elf32_External_crinfo2;
+
+/* These are the constants used to swap the bitfields in a crinfo. */
+
+#define CRINFO_CTYPE (0x1)
+#define CRINFO_CTYPE_SH (31)
+#define CRINFO_RTYPE (0xf)
+#define CRINFO_RTYPE_SH (27)
+#define CRINFO_DIST2TO (0xff)
+#define CRINFO_DIST2TO_SH (19)
+#define CRINFO_RELVADDR (0x7ffff)
+#define CRINFO_RELVADDR_SH (0)
+
+/* A compact relocation info has long (3 words) or short (2 words)
+ formats. A short format doesn't have VADDR field and relvaddr
+ fields contains ((VADDR - vaddr of the previous entry) >> 2). */
+#define CRF_MIPS_LONG 1
+#define CRF_MIPS_SHORT 0
+
+/* There are 4 types of compact relocation at least. The value KONST
+ has different meaning for each type:
+
+ (type) (konst)
+ CT_MIPS_REL32 Address in data
+ CT_MIPS_WORD Address in word (XXX)
+ CT_MIPS_GPHI_LO GP - vaddr
+ CT_MIPS_JMPAD Address to jump
+ */
+
+#define CRT_MIPS_REL32 0xa
+#define CRT_MIPS_WORD 0xb
+#define CRT_MIPS_GPHI_LO 0xc
+#define CRT_MIPS_JMPAD 0xd
+
+#define mips_elf_set_cr_format(x,format) ((x).ctype = (format))
+#define mips_elf_set_cr_type(x,type) ((x).rtype = (type))
+#define mips_elf_set_cr_dist2to(x,v) ((x).dist2to = (v))
+#define mips_elf_set_cr_relvaddr(x,d) ((x).relvaddr = (d)<<2)
+
+/* The structure of the runtime procedure descriptor created by the
+ loader for use by the static exception system. */
+
+typedef struct runtime_pdr {
+ bfd_vma adr; /* Memory address of start of procedure. */
+ long regmask; /* Save register mask. */
+ long regoffset; /* Save register offset. */
+ long fregmask; /* Save floating point register mask. */
+ long fregoffset; /* Save floating point register offset. */
+ long frameoffset; /* Frame size. */
+ short framereg; /* Frame pointer register. */
+ short pcreg; /* Offset or reg of return pc. */
+ long irpss; /* Index into the runtime string table. */
+ long reserved;
+ struct exception_info *exception_info;/* Pointer to exception array. */
+} RPDR, *pRPDR;
+#define cbRPDR sizeof (RPDR)
+#define rpdNil ((pRPDR) 0)
+
+static struct bfd_hash_entry *mips_elf_link_hash_newfunc
+ (struct bfd_hash_entry *, struct bfd_hash_table *, const char *);
+static void ecoff_swap_rpdr_out
+ (bfd *, const RPDR *, struct rpdr_ext *);
+static bfd_boolean mips_elf_create_procedure_table
+ (void *, bfd *, struct bfd_link_info *, asection *,
+ struct ecoff_debug_info *);
+static bfd_boolean mips_elf_check_mips16_stubs
+ (struct mips_elf_link_hash_entry *, void *);
+static void bfd_mips_elf32_swap_gptab_in
+ (bfd *, const Elf32_External_gptab *, Elf32_gptab *);
+static void bfd_mips_elf32_swap_gptab_out
+ (bfd *, const Elf32_gptab *, Elf32_External_gptab *);
+static void bfd_elf32_swap_compact_rel_out
+ (bfd *, const Elf32_compact_rel *, Elf32_External_compact_rel *);
+static void bfd_elf32_swap_crinfo_out
+ (bfd *, const Elf32_crinfo *, Elf32_External_crinfo *);
+static int sort_dynamic_relocs
+ (const void *, const void *);
+static int sort_dynamic_relocs_64
+ (const void *, const void *);
+static bfd_boolean mips_elf_output_extsym
+ (struct mips_elf_link_hash_entry *, void *);
+static int gptab_compare
+ (const void *, const void *);
+static asection *mips_elf_rel_dyn_section
+ (bfd *, bfd_boolean);
+static asection *mips_elf_got_section
+ (bfd *, bfd_boolean);
+static struct mips_got_info *mips_elf_got_info
+ (bfd *, asection **);
+static long mips_elf_get_global_gotsym_index
+ (bfd *abfd);
+static bfd_vma mips_elf_local_got_index
+ (bfd *, bfd *, struct bfd_link_info *, bfd_vma);
+static bfd_vma mips_elf_global_got_index
+ (bfd *, bfd *, struct elf_link_hash_entry *);
+static bfd_vma mips_elf_got_page
+ (bfd *, bfd *, struct bfd_link_info *, bfd_vma, bfd_vma *);
+static bfd_vma mips_elf_got16_entry
+ (bfd *, bfd *, struct bfd_link_info *, bfd_vma, bfd_boolean);
+static bfd_vma mips_elf_got_offset_from_index
+ (bfd *, bfd *, bfd *, bfd_vma);
+static struct mips_got_entry *mips_elf_create_local_got_entry
+ (bfd *, bfd *, struct mips_got_info *, asection *, bfd_vma);
+static bfd_boolean mips_elf_sort_hash_table
+ (struct bfd_link_info *, unsigned long);
+static bfd_boolean mips_elf_sort_hash_table_f
+ (struct mips_elf_link_hash_entry *, void *);
+static bfd_boolean mips_elf_record_local_got_symbol
+ (bfd *, long, bfd_vma, struct mips_got_info *);
+static bfd_boolean mips_elf_record_global_got_symbol
+ (struct elf_link_hash_entry *, bfd *, struct bfd_link_info *,
+ struct mips_got_info *);
+static const Elf_Internal_Rela *mips_elf_next_relocation
+ (bfd *, unsigned int, const Elf_Internal_Rela *, const Elf_Internal_Rela *);
+static bfd_boolean mips_elf_local_relocation_p
+ (bfd *, const Elf_Internal_Rela *, asection **, bfd_boolean);
+static bfd_boolean mips_elf_overflow_p
+ (bfd_vma, int);
+static bfd_vma mips_elf_high
+ (bfd_vma);
+static bfd_vma mips_elf_higher
+ (bfd_vma);
+static bfd_vma mips_elf_highest
+ (bfd_vma);
+static bfd_boolean mips_elf_create_compact_rel_section
+ (bfd *, struct bfd_link_info *);
+static bfd_boolean mips_elf_create_got_section
+ (bfd *, struct bfd_link_info *, bfd_boolean);
+static bfd_reloc_status_type mips_elf_calculate_relocation
+ (bfd *, bfd *, asection *, struct bfd_link_info *,
+ const Elf_Internal_Rela *, bfd_vma, reloc_howto_type *,
+ Elf_Internal_Sym *, asection **, bfd_vma *, const char **,
+ bfd_boolean *, bfd_boolean);
+static bfd_vma mips_elf_obtain_contents
+ (reloc_howto_type *, const Elf_Internal_Rela *, bfd *, bfd_byte *);
+static bfd_boolean mips_elf_perform_relocation
+ (struct bfd_link_info *, reloc_howto_type *, const Elf_Internal_Rela *,
+ bfd_vma, bfd *, asection *, bfd_byte *, bfd_boolean);
+static bfd_boolean mips_elf_stub_section_p
+ (bfd *, asection *);
+static void mips_elf_allocate_dynamic_relocations
+ (bfd *, unsigned int);
+static bfd_boolean mips_elf_create_dynamic_relocation
+ (bfd *, struct bfd_link_info *, const Elf_Internal_Rela *,
+ struct mips_elf_link_hash_entry *, asection *, bfd_vma,
+ bfd_vma *, asection *);
+static void mips_set_isa_flags
+ (bfd *);
+static INLINE char *elf_mips_abi_name
+ (bfd *);
+static void mips_elf_irix6_finish_dynamic_symbol
+ (bfd *, const char *, Elf_Internal_Sym *);
+static bfd_boolean mips_mach_extends_p
+ (unsigned long, unsigned long);
+static bfd_boolean mips_32bit_flags_p
+ (flagword);
+static INLINE hashval_t mips_elf_hash_bfd_vma
+ (bfd_vma);
+static hashval_t mips_elf_got_entry_hash
+ (const void *);
+static int mips_elf_got_entry_eq
+ (const void *, const void *);
+
+static bfd_boolean mips_elf_multi_got
+ (bfd *, struct bfd_link_info *, struct mips_got_info *,
+ asection *, bfd_size_type);
+static hashval_t mips_elf_multi_got_entry_hash
+ (const void *);
+static int mips_elf_multi_got_entry_eq
+ (const void *, const void *);
+static hashval_t mips_elf_bfd2got_entry_hash
+ (const void *);
+static int mips_elf_bfd2got_entry_eq
+ (const void *, const void *);
+static int mips_elf_make_got_per_bfd
+ (void **, void *);
+static int mips_elf_merge_gots
+ (void **, void *);
+static int mips_elf_set_global_got_offset
+ (void **, void *);
+static int mips_elf_set_no_stub
+ (void **, void *);
+static int mips_elf_resolve_final_got_entry
+ (void **, void *);
+static void mips_elf_resolve_final_got_entries
+ (struct mips_got_info *);
+static bfd_vma mips_elf_adjust_gp
+ (bfd *, struct mips_got_info *, bfd *);
+static struct mips_got_info *mips_elf_got_for_ibfd
+ (struct mips_got_info *, bfd *);
+
+/* This will be used when we sort the dynamic relocation records. */
+static bfd *reldyn_sorting_bfd;
+
+/* Nonzero if ABFD is using the N32 ABI. */
+
+#define ABI_N32_P(abfd) \
+ ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI2) != 0)
+
+/* Nonzero if ABFD is using the N64 ABI. */
+#define ABI_64_P(abfd) \
+ (get_elf_backend_data (abfd)->s->elfclass == ELFCLASS64)
+
+/* Nonzero if ABFD is using NewABI conventions. */
+#define NEWABI_P(abfd) (ABI_N32_P (abfd) || ABI_64_P (abfd))
+
+/* The IRIX compatibility level we are striving for. */
+#define IRIX_COMPAT(abfd) \
+ (get_elf_backend_data (abfd)->elf_backend_mips_irix_compat (abfd))
+
+/* Whether we are trying to be compatible with IRIX at all. */
+#define SGI_COMPAT(abfd) \
+ (IRIX_COMPAT (abfd) != ict_none)
+
+/* The name of the options section. */
+#define MIPS_ELF_OPTIONS_SECTION_NAME(abfd) \
+ (NEWABI_P (abfd) ? ".MIPS.options" : ".options")
+
+/* The name of the stub section. */
+#define MIPS_ELF_STUB_SECTION_NAME(abfd) ".MIPS.stubs"
+
+/* The size of an external REL relocation. */
+#define MIPS_ELF_REL_SIZE(abfd) \
+ (get_elf_backend_data (abfd)->s->sizeof_rel)
+
+/* The size of an external dynamic table entry. */
+#define MIPS_ELF_DYN_SIZE(abfd) \
+ (get_elf_backend_data (abfd)->s->sizeof_dyn)
+
+/* The size of a GOT entry. */
+#define MIPS_ELF_GOT_SIZE(abfd) \
+ (get_elf_backend_data (abfd)->s->arch_size / 8)
+
+/* The size of a symbol-table entry. */
+#define MIPS_ELF_SYM_SIZE(abfd) \
+ (get_elf_backend_data (abfd)->s->sizeof_sym)
+
+/* The default alignment for sections, as a power of two. */
+#define MIPS_ELF_LOG_FILE_ALIGN(abfd) \
+ (get_elf_backend_data (abfd)->s->log_file_align)
+
+/* Get word-sized data. */
+#define MIPS_ELF_GET_WORD(abfd, ptr) \
+ (ABI_64_P (abfd) ? bfd_get_64 (abfd, ptr) : bfd_get_32 (abfd, ptr))
+
+/* Put out word-sized data. */
+#define MIPS_ELF_PUT_WORD(abfd, val, ptr) \
+ (ABI_64_P (abfd) \
+ ? bfd_put_64 (abfd, val, ptr) \
+ : bfd_put_32 (abfd, val, ptr))
+
+/* Add a dynamic symbol table-entry. */
+#define MIPS_ELF_ADD_DYNAMIC_ENTRY(info, tag, val) \
+ _bfd_elf_add_dynamic_entry (info, tag, val)
+
+#define MIPS_ELF_RTYPE_TO_HOWTO(abfd, rtype, rela) \
+ (get_elf_backend_data (abfd)->elf_backend_mips_rtype_to_howto (rtype, rela))
+
+/* Determine whether the internal relocation of index REL_IDX is REL
+ (zero) or RELA (non-zero). The assumption is that, if there are
+ two relocation sections for this section, one of them is REL and
+ the other is RELA. If the index of the relocation we're testing is
+ in range for the first relocation section, check that the external
+ relocation size is that for RELA. It is also assumed that, if
+ rel_idx is not in range for the first section, and this first
+ section contains REL relocs, then the relocation is in the second
+ section, that is RELA. */
+#define MIPS_RELOC_RELA_P(abfd, sec, rel_idx) \
+ ((NUM_SHDR_ENTRIES (&elf_section_data (sec)->rel_hdr) \
+ * get_elf_backend_data (abfd)->s->int_rels_per_ext_rel \
+ > (bfd_vma)(rel_idx)) \
+ == (elf_section_data (sec)->rel_hdr.sh_entsize \
+ == (ABI_64_P (abfd) ? sizeof (Elf64_External_Rela) \
+ : sizeof (Elf32_External_Rela))))
+
+/* In case we're on a 32-bit machine, construct a 64-bit "-1" value
+ from smaller values. Start with zero, widen, *then* decrement. */
+#define MINUS_ONE (((bfd_vma)0) - 1)
+
+/* The number of local .got entries we reserve. */
+#define MIPS_RESERVED_GOTNO (2)
+
+/* The offset of $gp from the beginning of the .got section. */
+#define ELF_MIPS_GP_OFFSET(abfd) (0x7ff0)
+
+/* The maximum size of the GOT for it to be addressable using 16-bit
+ offsets from $gp. */
+#define MIPS_ELF_GOT_MAX_SIZE(abfd) (ELF_MIPS_GP_OFFSET(abfd) + 0x7fff)
+
+/* Instructions which appear in a stub. */
+#define STUB_LW(abfd) \
+ ((ABI_64_P (abfd) \
+ ? 0xdf998010 /* ld t9,0x8010(gp) */ \
+ : 0x8f998010)) /* lw t9,0x8010(gp) */
+#define STUB_MOVE(abfd) \
+ ((ABI_64_P (abfd) \
+ ? 0x03e0782d /* daddu t7,ra */ \
+ : 0x03e07821)) /* addu t7,ra */
+#define STUB_JALR 0x0320f809 /* jalr t9,ra */
+#define STUB_LI16(abfd) \
+ ((ABI_64_P (abfd) \
+ ? 0x64180000 /* daddiu t8,zero,0 */ \
+ : 0x24180000)) /* addiu t8,zero,0 */
+#define MIPS_FUNCTION_STUB_SIZE (16)
+
+/* The name of the dynamic interpreter. This is put in the .interp
+ section. */
+
+#define ELF_DYNAMIC_INTERPRETER(abfd) \
+ (ABI_N32_P (abfd) ? "/usr/lib32/libc.so.1" \
+ : ABI_64_P (abfd) ? "/usr/lib64/libc.so.1" \
+ : "/usr/lib/libc.so.1")
+
+#ifdef BFD64
+#define MNAME(bfd,pre,pos) \
+ (ABI_64_P (bfd) ? CONCAT4 (pre,64,_,pos) : CONCAT4 (pre,32,_,pos))
+#define ELF_R_SYM(bfd, i) \
+ (ABI_64_P (bfd) ? ELF64_R_SYM (i) : ELF32_R_SYM (i))
+#define ELF_R_TYPE(bfd, i) \
+ (ABI_64_P (bfd) ? ELF64_MIPS_R_TYPE (i) : ELF32_R_TYPE (i))
+#define ELF_R_INFO(bfd, s, t) \
+ (ABI_64_P (bfd) ? ELF64_R_INFO (s, t) : ELF32_R_INFO (s, t))
+#else
+#define MNAME(bfd,pre,pos) CONCAT4 (pre,32,_,pos)
+#define ELF_R_SYM(bfd, i) \
+ (ELF32_R_SYM (i))
+#define ELF_R_TYPE(bfd, i) \
+ (ELF32_R_TYPE (i))
+#define ELF_R_INFO(bfd, s, t) \
+ (ELF32_R_INFO (s, t))
+#endif
+
+ /* The mips16 compiler uses a couple of special sections to handle
+ floating point arguments.
+
+ Section names that look like .mips16.fn.FNNAME contain stubs that
+ copy floating point arguments from the fp regs to the gp regs and
+ then jump to FNNAME. If any 32 bit function calls FNNAME, the
+ call should be redirected to the stub instead. If no 32 bit
+ function calls FNNAME, the stub should be discarded. We need to
+ consider any reference to the function, not just a call, because
+ if the address of the function is taken we will need the stub,
+ since the address might be passed to a 32 bit function.
+
+ Section names that look like .mips16.call.FNNAME contain stubs
+ that copy floating point arguments from the gp regs to the fp
+ regs and then jump to FNNAME. If FNNAME is a 32 bit function,
+ then any 16 bit function that calls FNNAME should be redirected
+ to the stub instead. If FNNAME is not a 32 bit function, the
+ stub should be discarded.
+
+ .mips16.call.fp.FNNAME sections are similar, but contain stubs
+ which call FNNAME and then copy the return value from the fp regs
+ to the gp regs. These stubs store the return value in $18 while
+ calling FNNAME; any function which might call one of these stubs
+ must arrange to save $18 around the call. (This case is not
+ needed for 32 bit functions that call 16 bit functions, because
+ 16 bit functions always return floating point values in both
+ $f0/$f1 and $2/$3.)
+
+ Note that in all cases FNNAME might be defined statically.
+ Therefore, FNNAME is not used literally. Instead, the relocation
+ information will indicate which symbol the section is for.
+
+ We record any stubs that we find in the symbol table. */
+
+#define FN_STUB ".mips16.fn."
+#define CALL_STUB ".mips16.call."
+#define CALL_FP_STUB ".mips16.call.fp."
+
+/* Look up an entry in a MIPS ELF linker hash table. */
+
+#define mips_elf_link_hash_lookup(table, string, create, copy, follow) \
+ ((struct mips_elf_link_hash_entry *) \
+ elf_link_hash_lookup (&(table)->root, (string), (create), \
+ (copy), (follow)))
+
+/* Traverse a MIPS ELF linker hash table. */
+
+#define mips_elf_link_hash_traverse(table, func, info) \
+ (elf_link_hash_traverse \
+ (&(table)->root, \
+ (bfd_boolean (*) (struct elf_link_hash_entry *, void *)) (func), \
+ (info)))
+
+/* Get the MIPS ELF linker hash table from a link_info structure. */
+
+#define mips_elf_hash_table(p) \
+ ((struct mips_elf_link_hash_table *) ((p)->hash))
+
+/* Create an entry in a MIPS ELF linker hash table. */
+
+static struct bfd_hash_entry *
+mips_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
+ struct bfd_hash_table *table, const char *string)
+{
+ struct mips_elf_link_hash_entry *ret =
+ (struct mips_elf_link_hash_entry *) entry;
+
+ /* Allocate the structure if it has not already been allocated by a
+ subclass. */
+ if (ret == NULL)
+ ret = bfd_hash_allocate (table, sizeof (struct mips_elf_link_hash_entry));
+ if (ret == NULL)
+ return (struct bfd_hash_entry *) ret;
+
+ /* Call the allocation method of the superclass. */
+ ret = ((struct mips_elf_link_hash_entry *)
+ _bfd_elf_link_hash_newfunc ((struct bfd_hash_entry *) ret,
+ table, string));
+ if (ret != NULL)
+ {
+ /* Set local fields. */
+ memset (&ret->esym, 0, sizeof (EXTR));
+ /* We use -2 as a marker to indicate that the information has
+ not been set. -1 means there is no associated ifd. */
+ ret->esym.ifd = -2;
+ ret->possibly_dynamic_relocs = 0;
+ ret->readonly_reloc = FALSE;
+ ret->no_fn_stub = FALSE;
+ ret->fn_stub = NULL;
+ ret->need_fn_stub = FALSE;
+ ret->call_stub = NULL;
+ ret->call_fp_stub = NULL;
+ ret->forced_local = FALSE;
+ }
+
+ return (struct bfd_hash_entry *) ret;
+}
+
+bfd_boolean
+_bfd_mips_elf_new_section_hook (bfd *abfd, asection *sec)
+{
+ struct _mips_elf_section_data *sdata;
+ bfd_size_type amt = sizeof (*sdata);
+
+ sdata = bfd_zalloc (abfd, amt);
+ if (sdata == NULL)
+ return FALSE;
+ sec->used_by_bfd = sdata;
+
+ return _bfd_elf_new_section_hook (abfd, sec);
+}
+
+/* Read ECOFF debugging information from a .mdebug section into a
+ ecoff_debug_info structure. */
+
+bfd_boolean
+_bfd_mips_elf_read_ecoff_info (bfd *abfd, asection *section,
+ struct ecoff_debug_info *debug)
+{
+ HDRR *symhdr;
+ const struct ecoff_debug_swap *swap;
+ char *ext_hdr;
+
+ swap = get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap;
+ memset (debug, 0, sizeof (*debug));
+
+ ext_hdr = bfd_malloc (swap->external_hdr_size);
+ if (ext_hdr == NULL && swap->external_hdr_size != 0)
+ goto error_return;
+
+ if (! bfd_get_section_contents (abfd, section, ext_hdr, 0,
+ swap->external_hdr_size))
+ goto error_return;
+
+ symhdr = &debug->symbolic_header;
+ (*swap->swap_hdr_in) (abfd, ext_hdr, symhdr);
+
+ /* The symbolic header contains absolute file offsets and sizes to
+ read. */
+#define READ(ptr, offset, count, size, type) \
+ if (symhdr->count == 0) \
+ debug->ptr = NULL; \
+ else \
+ { \
+ bfd_size_type amt = (bfd_size_type) size * symhdr->count; \
+ debug->ptr = bfd_malloc (amt); \
+ if (debug->ptr == NULL) \
+ goto error_return; \
+ if (bfd_seek (abfd, symhdr->offset, SEEK_SET) != 0 \
+ || bfd_bread (debug->ptr, amt, abfd) != amt) \
+ goto error_return; \
+ }
+
+ READ (line, cbLineOffset, cbLine, sizeof (unsigned char), unsigned char *);
+ READ (external_dnr, cbDnOffset, idnMax, swap->external_dnr_size, void *);
+ READ (external_pdr, cbPdOffset, ipdMax, swap->external_pdr_size, void *);
+ READ (external_sym, cbSymOffset, isymMax, swap->external_sym_size, void *);
+ READ (external_opt, cbOptOffset, ioptMax, swap->external_opt_size, void *);
+ READ (external_aux, cbAuxOffset, iauxMax, sizeof (union aux_ext),
+ union aux_ext *);
+ READ (ss, cbSsOffset, issMax, sizeof (char), char *);
+ READ (ssext, cbSsExtOffset, issExtMax, sizeof (char), char *);
+ READ (external_fdr, cbFdOffset, ifdMax, swap->external_fdr_size, void *);
+ READ (external_rfd, cbRfdOffset, crfd, swap->external_rfd_size, void *);
+ READ (external_ext, cbExtOffset, iextMax, swap->external_ext_size, void *);
+#undef READ
+
+ debug->fdr = NULL;
+ debug->adjust = NULL;
+
+ return TRUE;
+
+ error_return:
+ if (ext_hdr != NULL)
+ free (ext_hdr);
+ if (debug->line != NULL)
+ free (debug->line);
+ if (debug->external_dnr != NULL)
+ free (debug->external_dnr);
+ if (debug->external_pdr != NULL)
+ free (debug->external_pdr);
+ if (debug->external_sym != NULL)
+ free (debug->external_sym);
+ if (debug->external_opt != NULL)
+ free (debug->external_opt);
+ if (debug->external_aux != NULL)
+ free (debug->external_aux);
+ if (debug->ss != NULL)
+ free (debug->ss);
+ if (debug->ssext != NULL)
+ free (debug->ssext);
+ if (debug->external_fdr != NULL)
+ free (debug->external_fdr);
+ if (debug->external_rfd != NULL)
+ free (debug->external_rfd);
+ if (debug->external_ext != NULL)
+ free (debug->external_ext);
+ return FALSE;
+}
+
+/* Swap RPDR (runtime procedure table entry) for output. */
+
+static void
+ecoff_swap_rpdr_out (bfd *abfd, const RPDR *in, struct rpdr_ext *ex)
+{
+ H_PUT_S32 (abfd, in->adr, ex->p_adr);
+ H_PUT_32 (abfd, in->regmask, ex->p_regmask);
+ H_PUT_32 (abfd, in->regoffset, ex->p_regoffset);
+ H_PUT_32 (abfd, in->fregmask, ex->p_fregmask);
+ H_PUT_32 (abfd, in->fregoffset, ex->p_fregoffset);
+ H_PUT_32 (abfd, in->frameoffset, ex->p_frameoffset);
+
+ H_PUT_16 (abfd, in->framereg, ex->p_framereg);
+ H_PUT_16 (abfd, in->pcreg, ex->p_pcreg);
+
+ H_PUT_32 (abfd, in->irpss, ex->p_irpss);
+#if 0 /* FIXME */
+ H_PUT_S32 (abfd, in->exception_info, ex->p_exception_info);
+#endif
+}
+
+/* Create a runtime procedure table from the .mdebug section. */
+
+static bfd_boolean
+mips_elf_create_procedure_table (void *handle, bfd *abfd,
+ struct bfd_link_info *info, asection *s,
+ struct ecoff_debug_info *debug)
+{
+ const struct ecoff_debug_swap *swap;
+ HDRR *hdr = &debug->symbolic_header;
+ RPDR *rpdr, *rp;
+ struct rpdr_ext *erp;
+ void *rtproc;
+ struct pdr_ext *epdr;
+ struct sym_ext *esym;
+ char *ss, **sv;
+ char *str;
+ bfd_size_type size;
+ bfd_size_type count;
+ unsigned long sindex;
+ unsigned long i;
+ PDR pdr;
+ SYMR sym;
+ const char *no_name_func = _("static procedure (no name)");
+
+ epdr = NULL;
+ rpdr = NULL;
+ esym = NULL;
+ ss = NULL;
+ sv = NULL;
+
+ swap = get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap;
+
+ sindex = strlen (no_name_func) + 1;
+ count = hdr->ipdMax;
+ if (count > 0)
+ {
+ size = swap->external_pdr_size;
+
+ epdr = bfd_malloc (size * count);
+ if (epdr == NULL)
+ goto error_return;
+
+ if (! _bfd_ecoff_get_accumulated_pdr (handle, (bfd_byte *) epdr))
+ goto error_return;
+
+ size = sizeof (RPDR);
+ rp = rpdr = bfd_malloc (size * count);
+ if (rpdr == NULL)
+ goto error_return;
+
+ size = sizeof (char *);
+ sv = bfd_malloc (size * count);
+ if (sv == NULL)
+ goto error_return;
+
+ count = hdr->isymMax;
+ size = swap->external_sym_size;
+ esym = bfd_malloc (size * count);
+ if (esym == NULL)
+ goto error_return;
+
+ if (! _bfd_ecoff_get_accumulated_sym (handle, (bfd_byte *) esym))
+ goto error_return;
+
+ count = hdr->issMax;
+ ss = bfd_malloc (count);
+ if (ss == NULL)
+ goto error_return;
+ if (! _bfd_ecoff_get_accumulated_ss (handle, ss))
+ goto error_return;
+
+ count = hdr->ipdMax;
+ for (i = 0; i < (unsigned long) count; i++, rp++)
+ {
+ (*swap->swap_pdr_in) (abfd, epdr + i, &pdr);
+ (*swap->swap_sym_in) (abfd, &esym[pdr.isym], &sym);
+ rp->adr = sym.value;
+ rp->regmask = pdr.regmask;
+ rp->regoffset = pdr.regoffset;
+ rp->fregmask = pdr.fregmask;
+ rp->fregoffset = pdr.fregoffset;
+ rp->frameoffset = pdr.frameoffset;
+ rp->framereg = pdr.framereg;
+ rp->pcreg = pdr.pcreg;
+ rp->irpss = sindex;
+ sv[i] = ss + sym.iss;
+ sindex += strlen (sv[i]) + 1;
+ }
+ }
+
+ size = sizeof (struct rpdr_ext) * (count + 2) + sindex;
+ size = BFD_ALIGN (size, 16);
+ rtproc = bfd_alloc (abfd, size);
+ if (rtproc == NULL)
+ {
+ mips_elf_hash_table (info)->procedure_count = 0;
+ goto error_return;
+ }
+
+ mips_elf_hash_table (info)->procedure_count = count + 2;
+
+ erp = rtproc;
+ memset (erp, 0, sizeof (struct rpdr_ext));
+ erp++;
+ str = (char *) rtproc + sizeof (struct rpdr_ext) * (count + 2);
+ strcpy (str, no_name_func);
+ str += strlen (no_name_func) + 1;
+ for (i = 0; i < count; i++)
+ {
+ ecoff_swap_rpdr_out (abfd, rpdr + i, erp + i);
+ strcpy (str, sv[i]);
+ str += strlen (sv[i]) + 1;
+ }
+ H_PUT_S32 (abfd, -1, (erp + count)->p_adr);
+
+ /* Set the size and contents of .rtproc section. */
+ s->_raw_size = size;
+ s->contents = rtproc;
+
+ /* Skip this section later on (I don't think this currently
+ matters, but someday it might). */
+ s->link_order_head = NULL;
+
+ if (epdr != NULL)
+ free (epdr);
+ if (rpdr != NULL)
+ free (rpdr);
+ if (esym != NULL)
+ free (esym);
+ if (ss != NULL)
+ free (ss);
+ if (sv != NULL)
+ free (sv);
+
+ return TRUE;
+
+ error_return:
+ if (epdr != NULL)
+ free (epdr);
+ if (rpdr != NULL)
+ free (rpdr);
+ if (esym != NULL)
+ free (esym);
+ if (ss != NULL)
+ free (ss);
+ if (sv != NULL)
+ free (sv);
+ return FALSE;
+}
+
+/* Check the mips16 stubs for a particular symbol, and see if we can
+ discard them. */
+
+static bfd_boolean
+mips_elf_check_mips16_stubs (struct mips_elf_link_hash_entry *h,
+ void *data ATTRIBUTE_UNUSED)
+{
+ if (h->root.root.type == bfd_link_hash_warning)
+ h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
+
+ if (h->fn_stub != NULL
+ && ! h->need_fn_stub)
+ {
+ /* We don't need the fn_stub; the only references to this symbol
+ are 16 bit calls. Clobber the size to 0 to prevent it from
+ being included in the link. */
+ h->fn_stub->_raw_size = 0;
+ h->fn_stub->_cooked_size = 0;
+ h->fn_stub->flags &= ~SEC_RELOC;
+ h->fn_stub->reloc_count = 0;
+ h->fn_stub->flags |= SEC_EXCLUDE;
+ }
+
+ if (h->call_stub != NULL
+ && h->root.other == STO_MIPS16)
+ {
+ /* We don't need the call_stub; this is a 16 bit function, so
+ calls from other 16 bit functions are OK. Clobber the size
+ to 0 to prevent it from being included in the link. */
+ h->call_stub->_raw_size = 0;
+ h->call_stub->_cooked_size = 0;
+ h->call_stub->flags &= ~SEC_RELOC;
+ h->call_stub->reloc_count = 0;
+ h->call_stub->flags |= SEC_EXCLUDE;
+ }
+
+ if (h->call_fp_stub != NULL
+ && h->root.other == STO_MIPS16)
+ {
+ /* We don't need the call_stub; this is a 16 bit function, so
+ calls from other 16 bit functions are OK. Clobber the size
+ to 0 to prevent it from being included in the link. */
+ h->call_fp_stub->_raw_size = 0;
+ h->call_fp_stub->_cooked_size = 0;
+ h->call_fp_stub->flags &= ~SEC_RELOC;
+ h->call_fp_stub->reloc_count = 0;
+ h->call_fp_stub->flags |= SEC_EXCLUDE;
+ }
+
+ return TRUE;
+}
+
+bfd_reloc_status_type
+_bfd_mips_elf_gprel16_with_gp (bfd *abfd, asymbol *symbol,
+ arelent *reloc_entry, asection *input_section,
+ bfd_boolean relocatable, void *data, bfd_vma gp)
+{
+ bfd_vma relocation;
+ bfd_signed_vma val;
+ bfd_reloc_status_type status;
+
+ if (bfd_is_com_section (symbol->section))
+ relocation = 0;
+ else
+ relocation = symbol->value;
+
+ relocation += symbol->section->output_section->vma;
+ relocation += symbol->section->output_offset;
+
+ if (reloc_entry->address > input_section->_cooked_size)
+ return bfd_reloc_outofrange;
+
+ /* Set val to the offset into the section or symbol. */
+ val = reloc_entry->addend;
+
+ _bfd_mips_elf_sign_extend (val, 16);
+
+ /* Adjust val for the final section location and GP value. If we
+ are producing relocatable output, we don't want to do this for
+ an external symbol. */
+ if (! relocatable
+ || (symbol->flags & BSF_SECTION_SYM) != 0)
+ val += relocation - gp;
+
+ if (reloc_entry->howto->partial_inplace)
+ {
+ status = _bfd_relocate_contents (reloc_entry->howto, abfd, val,
+ (bfd_byte *) data
+ + reloc_entry->address);
+ if (status != bfd_reloc_ok)
+ return status;
+ }
+ else
+ reloc_entry->addend = val;
+
+ if (relocatable)
+ reloc_entry->address += input_section->output_offset;
+
+ return bfd_reloc_ok;
+}
+
+/* Used to store a REL high-part relocation such as R_MIPS_HI16 or
+ R_MIPS_GOT16. REL is the relocation, INPUT_SECTION is the section
+ that contains the relocation field and DATA points to the start of
+ INPUT_SECTION. */
+
+struct mips_hi16
+{
+ struct mips_hi16 *next;
+ bfd_byte *data;
+ asection *input_section;
+ arelent rel;
+};
+
+/* FIXME: This should not be a static variable. */
+
+static struct mips_hi16 *mips_hi16_list;
+
+/* A howto special_function for REL *HI16 relocations. We can only
+ calculate the correct value once we've seen the partnering
+ *LO16 relocation, so just save the information for later.
+
+ The ABI requires that the *LO16 immediately follow the *HI16.
+ However, as a GNU extension, we permit an arbitrary number of
+ *HI16s to be associated with a single *LO16. This significantly
+ simplies the relocation handling in gcc. */
+
+bfd_reloc_status_type
+_bfd_mips_elf_hi16_reloc (bfd *abfd ATTRIBUTE_UNUSED, arelent *reloc_entry,
+ asymbol *symbol ATTRIBUTE_UNUSED, void *data,
+ asection *input_section, bfd *output_bfd,
+ char **error_message ATTRIBUTE_UNUSED)
+{
+ struct mips_hi16 *n;
+
+ if (reloc_entry->address > input_section->_cooked_size)
+ return bfd_reloc_outofrange;
+
+ n = bfd_malloc (sizeof *n);
+ if (n == NULL)
+ return bfd_reloc_outofrange;
+
+ n->next = mips_hi16_list;
+ n->data = data;
+ n->input_section = input_section;
+ n->rel = *reloc_entry;
+ mips_hi16_list = n;
+
+ if (output_bfd != NULL)
+ reloc_entry->address += input_section->output_offset;
+
+ return bfd_reloc_ok;
+}
+
+/* A howto special_function for REL R_MIPS_GOT16 relocations. This is just
+ like any other 16-bit relocation when applied to global symbols, but is
+ treated in the same as R_MIPS_HI16 when applied to local symbols. */
+
+bfd_reloc_status_type
+_bfd_mips_elf_got16_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+ void *data, asection *input_section,
+ bfd *output_bfd, char **error_message)
+{
+ if ((symbol->flags & (BSF_GLOBAL | BSF_WEAK)) != 0
+ || bfd_is_und_section (bfd_get_section (symbol))
+ || bfd_is_com_section (bfd_get_section (symbol)))
+ /* The relocation is against a global symbol. */
+ return _bfd_mips_elf_generic_reloc (abfd, reloc_entry, symbol, data,
+ input_section, output_bfd,
+ error_message);
+
+ return _bfd_mips_elf_hi16_reloc (abfd, reloc_entry, symbol, data,
+ input_section, output_bfd, error_message);
+}
+
+/* A howto special_function for REL *LO16 relocations. The *LO16 itself
+ is a straightforward 16 bit inplace relocation, but we must deal with
+ any partnering high-part relocations as well. */
+
+bfd_reloc_status_type
+_bfd_mips_elf_lo16_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+ void *data, asection *input_section,
+ bfd *output_bfd, char **error_message)
+{
+ bfd_vma vallo;
+
+ if (reloc_entry->address > input_section->_cooked_size)
+ return bfd_reloc_outofrange;
+
+ vallo = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
+ while (mips_hi16_list != NULL)
+ {
+ bfd_reloc_status_type ret;
+ struct mips_hi16 *hi;
+
+ hi = mips_hi16_list;
+
+ /* R_MIPS_GOT16 relocations are something of a special case. We
+ want to install the addend in the same way as for a R_MIPS_HI16
+ relocation (with a rightshift of 16). However, since GOT16
+ relocations can also be used with global symbols, their howto
+ has a rightshift of 0. */
+ if (hi->rel.howto->type == R_MIPS_GOT16)
+ hi->rel.howto = MIPS_ELF_RTYPE_TO_HOWTO (abfd, R_MIPS_HI16, FALSE);
+
+ /* VALLO is a signed 16-bit number. Bias it by 0x8000 so that any
+ carry or borrow will induce a change of +1 or -1 in the high part. */
+ hi->rel.addend += (vallo + 0x8000) & 0xffff;
+
+ /* R_MIPS_GNU_REL_HI16 relocations are relative to the address of the
+ lo16 relocation, not their own address. If we're calculating the
+ final value, and hence subtracting the "PC", subtract the offset
+ of the lo16 relocation from here. */
+ if (output_bfd == NULL && hi->rel.howto->type == R_MIPS_GNU_REL_HI16)
+ hi->rel.addend -= reloc_entry->address - hi->rel.address;
+
+ ret = _bfd_mips_elf_generic_reloc (abfd, &hi->rel, symbol, hi->data,
+ hi->input_section, output_bfd,
+ error_message);
+ if (ret != bfd_reloc_ok)
+ return ret;
+
+ mips_hi16_list = hi->next;
+ free (hi);
+ }
+
+ return _bfd_mips_elf_generic_reloc (abfd, reloc_entry, symbol, data,
+ input_section, output_bfd,
+ error_message);
+}
+
+/* A generic howto special_function. This calculates and installs the
+ relocation itself, thus avoiding the oft-discussed problems in
+ bfd_perform_relocation and bfd_install_relocation. */
+
+bfd_reloc_status_type
+_bfd_mips_elf_generic_reloc (bfd *abfd ATTRIBUTE_UNUSED, arelent *reloc_entry,
+ asymbol *symbol, void *data ATTRIBUTE_UNUSED,
+ asection *input_section, bfd *output_bfd,
+ char **error_message ATTRIBUTE_UNUSED)
+{
+ bfd_signed_vma val;
+ bfd_reloc_status_type status;
+ bfd_boolean relocatable;
+
+ relocatable = (output_bfd != NULL);
+
+ if (reloc_entry->address > input_section->_cooked_size)
+ return bfd_reloc_outofrange;
+
+ /* Build up the field adjustment in VAL. */
+ val = 0;
+ if (!relocatable || (symbol->flags & BSF_SECTION_SYM) != 0)
+ {
+ /* Either we're calculating the final field value or we have a
+ relocation against a section symbol. Add in the section's
+ offset or address. */
+ val += symbol->section->output_section->vma;
+ val += symbol->section->output_offset;
+ }
+
+ if (!relocatable)
+ {
+ /* We're calculating the final field value. Add in the symbol's value
+ and, if pc-relative, subtract the address of the field itself. */
+ val += symbol->value;
+ if (reloc_entry->howto->pc_relative)
+ {
+ val -= input_section->output_section->vma;
+ val -= input_section->output_offset;
+ val -= reloc_entry->address;
+ }
+ }
+
+ /* VAL is now the final adjustment. If we're keeping this relocation
+ in the output file, and if the relocation uses a separate addend,
+ we just need to add VAL to that addend. Otherwise we need to add
+ VAL to the relocation field itself. */
+ if (relocatable && !reloc_entry->howto->partial_inplace)
+ reloc_entry->addend += val;
+ else
+ {
+ /* Add in the separate addend, if any. */
+ val += reloc_entry->addend;
+
+ /* Add VAL to the relocation field. */
+ status = _bfd_relocate_contents (reloc_entry->howto, abfd, val,
+ (bfd_byte *) data
+ + reloc_entry->address);
+ if (status != bfd_reloc_ok)
+ return status;
+ }
+
+ if (relocatable)
+ reloc_entry->address += input_section->output_offset;
+
+ return bfd_reloc_ok;
+}
+
+/* Swap an entry in a .gptab section. Note that these routines rely
+ on the equivalence of the two elements of the union. */
+
+static void
+bfd_mips_elf32_swap_gptab_in (bfd *abfd, const Elf32_External_gptab *ex,
+ Elf32_gptab *in)
+{
+ in->gt_entry.gt_g_value = H_GET_32 (abfd, ex->gt_entry.gt_g_value);
+ in->gt_entry.gt_bytes = H_GET_32 (abfd, ex->gt_entry.gt_bytes);
+}
+
+static void
+bfd_mips_elf32_swap_gptab_out (bfd *abfd, const Elf32_gptab *in,
+ Elf32_External_gptab *ex)
+{
+ H_PUT_32 (abfd, in->gt_entry.gt_g_value, ex->gt_entry.gt_g_value);
+ H_PUT_32 (abfd, in->gt_entry.gt_bytes, ex->gt_entry.gt_bytes);
+}
+
+static void
+bfd_elf32_swap_compact_rel_out (bfd *abfd, const Elf32_compact_rel *in,
+ Elf32_External_compact_rel *ex)
+{
+ H_PUT_32 (abfd, in->id1, ex->id1);
+ H_PUT_32 (abfd, in->num, ex->num);
+ H_PUT_32 (abfd, in->id2, ex->id2);
+ H_PUT_32 (abfd, in->offset, ex->offset);
+ H_PUT_32 (abfd, in->reserved0, ex->reserved0);
+ H_PUT_32 (abfd, in->reserved1, ex->reserved1);
+}
+
+static void
+bfd_elf32_swap_crinfo_out (bfd *abfd, const Elf32_crinfo *in,
+ Elf32_External_crinfo *ex)
+{
+ unsigned long l;
+
+ l = (((in->ctype & CRINFO_CTYPE) << CRINFO_CTYPE_SH)
+ | ((in->rtype & CRINFO_RTYPE) << CRINFO_RTYPE_SH)
+ | ((in->dist2to & CRINFO_DIST2TO) << CRINFO_DIST2TO_SH)
+ | ((in->relvaddr & CRINFO_RELVADDR) << CRINFO_RELVADDR_SH));
+ H_PUT_32 (abfd, l, ex->info);
+ H_PUT_32 (abfd, in->konst, ex->konst);
+ H_PUT_32 (abfd, in->vaddr, ex->vaddr);
+}
+
+/* A .reginfo section holds a single Elf32_RegInfo structure. These
+ routines swap this structure in and out. They are used outside of
+ BFD, so they are globally visible. */
+
+void
+bfd_mips_elf32_swap_reginfo_in (bfd *abfd, const Elf32_External_RegInfo *ex,
+ Elf32_RegInfo *in)
+{
+ in->ri_gprmask = H_GET_32 (abfd, ex->ri_gprmask);
+ in->ri_cprmask[0] = H_GET_32 (abfd, ex->ri_cprmask[0]);
+ in->ri_cprmask[1] = H_GET_32 (abfd, ex->ri_cprmask[1]);
+ in->ri_cprmask[2] = H_GET_32 (abfd, ex->ri_cprmask[2]);
+ in->ri_cprmask[3] = H_GET_32 (abfd, ex->ri_cprmask[3]);
+ in->ri_gp_value = H_GET_32 (abfd, ex->ri_gp_value);
+}
+
+void
+bfd_mips_elf32_swap_reginfo_out (bfd *abfd, const Elf32_RegInfo *in,
+ Elf32_External_RegInfo *ex)
+{
+ H_PUT_32 (abfd, in->ri_gprmask, ex->ri_gprmask);
+ H_PUT_32 (abfd, in->ri_cprmask[0], ex->ri_cprmask[0]);
+ H_PUT_32 (abfd, in->ri_cprmask[1], ex->ri_cprmask[1]);
+ H_PUT_32 (abfd, in->ri_cprmask[2], ex->ri_cprmask[2]);
+ H_PUT_32 (abfd, in->ri_cprmask[3], ex->ri_cprmask[3]);
+ H_PUT_32 (abfd, in->ri_gp_value, ex->ri_gp_value);
+}
+
+/* In the 64 bit ABI, the .MIPS.options section holds register
+ information in an Elf64_Reginfo structure. These routines swap
+ them in and out. They are globally visible because they are used
+ outside of BFD. These routines are here so that gas can call them
+ without worrying about whether the 64 bit ABI has been included. */
+
+void
+bfd_mips_elf64_swap_reginfo_in (bfd *abfd, const Elf64_External_RegInfo *ex,
+ Elf64_Internal_RegInfo *in)
+{
+ in->ri_gprmask = H_GET_32 (abfd, ex->ri_gprmask);
+ in->ri_pad = H_GET_32 (abfd, ex->ri_pad);
+ in->ri_cprmask[0] = H_GET_32 (abfd, ex->ri_cprmask[0]);
+ in->ri_cprmask[1] = H_GET_32 (abfd, ex->ri_cprmask[1]);
+ in->ri_cprmask[2] = H_GET_32 (abfd, ex->ri_cprmask[2]);
+ in->ri_cprmask[3] = H_GET_32 (abfd, ex->ri_cprmask[3]);
+ in->ri_gp_value = H_GET_64 (abfd, ex->ri_gp_value);
+}
+
+void
+bfd_mips_elf64_swap_reginfo_out (bfd *abfd, const Elf64_Internal_RegInfo *in,
+ Elf64_External_RegInfo *ex)
+{
+ H_PUT_32 (abfd, in->ri_gprmask, ex->ri_gprmask);
+ H_PUT_32 (abfd, in->ri_pad, ex->ri_pad);
+ H_PUT_32 (abfd, in->ri_cprmask[0], ex->ri_cprmask[0]);
+ H_PUT_32 (abfd, in->ri_cprmask[1], ex->ri_cprmask[1]);
+ H_PUT_32 (abfd, in->ri_cprmask[2], ex->ri_cprmask[2]);
+ H_PUT_32 (abfd, in->ri_cprmask[3], ex->ri_cprmask[3]);
+ H_PUT_64 (abfd, in->ri_gp_value, ex->ri_gp_value);
+}
+
+/* Swap in an options header. */
+
+void
+bfd_mips_elf_swap_options_in (bfd *abfd, const Elf_External_Options *ex,
+ Elf_Internal_Options *in)
+{
+ in->kind = H_GET_8 (abfd, ex->kind);
+ in->size = H_GET_8 (abfd, ex->size);
+ in->section = H_GET_16 (abfd, ex->section);
+ in->info = H_GET_32 (abfd, ex->info);
+}
+
+/* Swap out an options header. */
+
+void
+bfd_mips_elf_swap_options_out (bfd *abfd, const Elf_Internal_Options *in,
+ Elf_External_Options *ex)
+{
+ H_PUT_8 (abfd, in->kind, ex->kind);
+ H_PUT_8 (abfd, in->size, ex->size);
+ H_PUT_16 (abfd, in->section, ex->section);
+ H_PUT_32 (abfd, in->info, ex->info);
+}
+
+/* This function is called via qsort() to sort the dynamic relocation
+ entries by increasing r_symndx value. */
+
+static int
+sort_dynamic_relocs (const void *arg1, const void *arg2)
+{
+ Elf_Internal_Rela int_reloc1;
+ Elf_Internal_Rela int_reloc2;
+
+ bfd_elf32_swap_reloc_in (reldyn_sorting_bfd, arg1, &int_reloc1);
+ bfd_elf32_swap_reloc_in (reldyn_sorting_bfd, arg2, &int_reloc2);
+
+ return ELF32_R_SYM (int_reloc1.r_info) - ELF32_R_SYM (int_reloc2.r_info);
+}
+
+/* Like sort_dynamic_relocs, but used for elf64 relocations. */
+
+static int
+sort_dynamic_relocs_64 (const void *arg1, const void *arg2)
+{
+ Elf_Internal_Rela int_reloc1[3];
+ Elf_Internal_Rela int_reloc2[3];
+
+ (*get_elf_backend_data (reldyn_sorting_bfd)->s->swap_reloc_in)
+ (reldyn_sorting_bfd, arg1, int_reloc1);
+ (*get_elf_backend_data (reldyn_sorting_bfd)->s->swap_reloc_in)
+ (reldyn_sorting_bfd, arg2, int_reloc2);
+
+ return (ELF64_R_SYM (int_reloc1[0].r_info)
+ - ELF64_R_SYM (int_reloc2[0].r_info));
+}
+
+
+/* This routine is used to write out ECOFF debugging external symbol
+ information. It is called via mips_elf_link_hash_traverse. The
+ ECOFF external symbol information must match the ELF external
+ symbol information. Unfortunately, at this point we don't know
+ whether a symbol is required by reloc information, so the two
+ tables may wind up being different. We must sort out the external
+ symbol information before we can set the final size of the .mdebug
+ section, and we must set the size of the .mdebug section before we
+ can relocate any sections, and we can't know which symbols are
+ required by relocation until we relocate the sections.
+ Fortunately, it is relatively unlikely that any symbol will be
+ stripped but required by a reloc. In particular, it can not happen
+ when generating a final executable. */
+
+static bfd_boolean
+mips_elf_output_extsym (struct mips_elf_link_hash_entry *h, void *data)
+{
+ struct extsym_info *einfo = data;
+ bfd_boolean strip;
+ asection *sec, *output_section;
+
+ if (h->root.root.type == bfd_link_hash_warning)
+ h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
+
+ if (h->root.indx == -2)
+ strip = FALSE;
+ else if (((h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0
+ || (h->root.elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) != 0)
+ && (h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0
+ && (h->root.elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) == 0)
+ strip = TRUE;
+ else if (einfo->info->strip == strip_all
+ || (einfo->info->strip == strip_some
+ && bfd_hash_lookup (einfo->info->keep_hash,
+ h->root.root.root.string,
+ FALSE, FALSE) == NULL))
+ strip = TRUE;
+ else
+ strip = FALSE;
+
+ if (strip)
+ return TRUE;
+
+ if (h->esym.ifd == -2)
+ {
+ h->esym.jmptbl = 0;
+ h->esym.cobol_main = 0;
+ h->esym.weakext = 0;
+ h->esym.reserved = 0;
+ h->esym.ifd = ifdNil;
+ h->esym.asym.value = 0;
+ h->esym.asym.st = stGlobal;
+
+ if (h->root.root.type == bfd_link_hash_undefined
+ || h->root.root.type == bfd_link_hash_undefweak)
+ {
+ const char *name;
+
+ /* Use undefined class. Also, set class and type for some
+ special symbols. */
+ name = h->root.root.root.string;
+ if (strcmp (name, mips_elf_dynsym_rtproc_names[0]) == 0
+ || strcmp (name, mips_elf_dynsym_rtproc_names[1]) == 0)
+ {
+ h->esym.asym.sc = scData;
+ h->esym.asym.st = stLabel;
+ h->esym.asym.value = 0;
+ }
+ else if (strcmp (name, mips_elf_dynsym_rtproc_names[2]) == 0)
+ {
+ h->esym.asym.sc = scAbs;
+ h->esym.asym.st = stLabel;
+ h->esym.asym.value =
+ mips_elf_hash_table (einfo->info)->procedure_count;
+ }
+ else if (strcmp (name, "_gp_disp") == 0 && ! NEWABI_P (einfo->abfd))
+ {
+ h->esym.asym.sc = scAbs;
+ h->esym.asym.st = stLabel;
+ h->esym.asym.value = elf_gp (einfo->abfd);
+ }
+ else
+ h->esym.asym.sc = scUndefined;
+ }
+ else if (h->root.root.type != bfd_link_hash_defined
+ && h->root.root.type != bfd_link_hash_defweak)
+ h->esym.asym.sc = scAbs;
+ else
+ {
+ const char *name;
+
+ sec = h->root.root.u.def.section;
+ output_section = sec->output_section;
+
+ /* When making a shared library and symbol h is the one from
+ the another shared library, OUTPUT_SECTION may be null. */
+ if (output_section == NULL)
+ h->esym.asym.sc = scUndefined;
+ else
+ {
+ name = bfd_section_name (output_section->owner, output_section);
+
+ if (strcmp (name, ".text") == 0)
+ h->esym.asym.sc = scText;
+ else if (strcmp (name, ".data") == 0)
+ h->esym.asym.sc = scData;
+ else if (strcmp (name, ".sdata") == 0)
+ h->esym.asym.sc = scSData;
+ else if (strcmp (name, ".rodata") == 0
+ || strcmp (name, ".rdata") == 0)
+ h->esym.asym.sc = scRData;
+ else if (strcmp (name, ".bss") == 0)
+ h->esym.asym.sc = scBss;
+ else if (strcmp (name, ".sbss") == 0)
+ h->esym.asym.sc = scSBss;
+ else if (strcmp (name, ".init") == 0)
+ h->esym.asym.sc = scInit;
+ else if (strcmp (name, ".fini") == 0)
+ h->esym.asym.sc = scFini;
+ else
+ h->esym.asym.sc = scAbs;
+ }
+ }
+
+ h->esym.asym.reserved = 0;
+ h->esym.asym.index = indexNil;
+ }
+
+ if (h->root.root.type == bfd_link_hash_common)
+ h->esym.asym.value = h->root.root.u.c.size;
+ else if (h->root.root.type == bfd_link_hash_defined
+ || h->root.root.type == bfd_link_hash_defweak)
+ {
+ if (h->esym.asym.sc == scCommon)
+ h->esym.asym.sc = scBss;
+ else if (h->esym.asym.sc == scSCommon)
+ h->esym.asym.sc = scSBss;
+
+ sec = h->root.root.u.def.section;
+ output_section = sec->output_section;
+ if (output_section != NULL)
+ h->esym.asym.value = (h->root.root.u.def.value
+ + sec->output_offset
+ + output_section->vma);
+ else
+ h->esym.asym.value = 0;
+ }
+ else if ((h->root.elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0)
+ {
+ struct mips_elf_link_hash_entry *hd = h;
+ bfd_boolean no_fn_stub = h->no_fn_stub;
+
+ while (hd->root.root.type == bfd_link_hash_indirect)
+ {
+ hd = (struct mips_elf_link_hash_entry *)h->root.root.u.i.link;
+ no_fn_stub = no_fn_stub || hd->no_fn_stub;
+ }
+
+ if (!no_fn_stub)
+ {
+ /* Set type and value for a symbol with a function stub. */
+ h->esym.asym.st = stProc;
+ sec = hd->root.root.u.def.section;
+ if (sec == NULL)
+ h->esym.asym.value = 0;
+ else
+ {
+ output_section = sec->output_section;
+ if (output_section != NULL)
+ h->esym.asym.value = (hd->root.plt.offset
+ + sec->output_offset
+ + output_section->vma);
+ else
+ h->esym.asym.value = 0;
+ }
+#if 0 /* FIXME? */
+ h->esym.ifd = 0;
+#endif
+ }
+ }
+
+ if (! bfd_ecoff_debug_one_external (einfo->abfd, einfo->debug, einfo->swap,
+ h->root.root.root.string,
+ &h->esym))
+ {
+ einfo->failed = TRUE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* A comparison routine used to sort .gptab entries. */
+
+static int
+gptab_compare (const void *p1, const void *p2)
+{
+ const Elf32_gptab *a1 = p1;
+ const Elf32_gptab *a2 = p2;
+
+ return a1->gt_entry.gt_g_value - a2->gt_entry.gt_g_value;
+}
+
+/* Functions to manage the got entry hash table. */
+
+/* Use all 64 bits of a bfd_vma for the computation of a 32-bit
+ hash number. */
+
+static INLINE hashval_t
+mips_elf_hash_bfd_vma (bfd_vma addr)
+{
+#ifdef BFD64
+ return addr + (addr >> 32);
+#else
+ return addr;
+#endif
+}
+
+/* got_entries only match if they're identical, except for gotidx, so
+ use all fields to compute the hash, and compare the appropriate
+ union members. */
+
+static hashval_t
+mips_elf_got_entry_hash (const void *entry_)
+{
+ const struct mips_got_entry *entry = (struct mips_got_entry *)entry_;
+
+ return entry->symndx
+ + (! entry->abfd ? mips_elf_hash_bfd_vma (entry->d.address)
+ : entry->abfd->id
+ + (entry->symndx >= 0 ? mips_elf_hash_bfd_vma (entry->d.addend)
+ : entry->d.h->root.root.root.hash));
+}
+
+static int
+mips_elf_got_entry_eq (const void *entry1, const void *entry2)
+{
+ const struct mips_got_entry *e1 = (struct mips_got_entry *)entry1;
+ const struct mips_got_entry *e2 = (struct mips_got_entry *)entry2;
+
+ return e1->abfd == e2->abfd && e1->symndx == e2->symndx
+ && (! e1->abfd ? e1->d.address == e2->d.address
+ : e1->symndx >= 0 ? e1->d.addend == e2->d.addend
+ : e1->d.h == e2->d.h);
+}
+
+/* multi_got_entries are still a match in the case of global objects,
+ even if the input bfd in which they're referenced differs, so the
+ hash computation and compare functions are adjusted
+ accordingly. */
+
+static hashval_t
+mips_elf_multi_got_entry_hash (const void *entry_)
+{
+ const struct mips_got_entry *entry = (struct mips_got_entry *)entry_;
+
+ return entry->symndx
+ + (! entry->abfd
+ ? mips_elf_hash_bfd_vma (entry->d.address)
+ : entry->symndx >= 0
+ ? (entry->abfd->id
+ + mips_elf_hash_bfd_vma (entry->d.addend))
+ : entry->d.h->root.root.root.hash);
+}
+
+static int
+mips_elf_multi_got_entry_eq (const void *entry1, const void *entry2)
+{
+ const struct mips_got_entry *e1 = (struct mips_got_entry *)entry1;
+ const struct mips_got_entry *e2 = (struct mips_got_entry *)entry2;
+
+ return e1->symndx == e2->symndx
+ && (e1->symndx >= 0 ? e1->abfd == e2->abfd && e1->d.addend == e2->d.addend
+ : e1->abfd == NULL || e2->abfd == NULL
+ ? e1->abfd == e2->abfd && e1->d.address == e2->d.address
+ : e1->d.h == e2->d.h);
+}
+
+/* Returns the dynamic relocation section for DYNOBJ. */
+
+static asection *
+mips_elf_rel_dyn_section (bfd *dynobj, bfd_boolean create_p)
+{
+ static const char dname[] = ".rel.dyn";
+ asection *sreloc;
+
+ sreloc = bfd_get_section_by_name (dynobj, dname);
+ if (sreloc == NULL && create_p)
+ {
+ sreloc = bfd_make_section (dynobj, dname);
+ if (sreloc == NULL
+ || ! bfd_set_section_flags (dynobj, sreloc,
+ (SEC_ALLOC
+ | SEC_LOAD
+ | SEC_HAS_CONTENTS
+ | SEC_IN_MEMORY
+ | SEC_LINKER_CREATED
+ | SEC_READONLY))
+ || ! bfd_set_section_alignment (dynobj, sreloc,
+ MIPS_ELF_LOG_FILE_ALIGN (dynobj)))
+ return NULL;
+ }
+ return sreloc;
+}
+
+/* Returns the GOT section for ABFD. */
+
+static asection *
+mips_elf_got_section (bfd *abfd, bfd_boolean maybe_excluded)
+{
+ asection *sgot = bfd_get_section_by_name (abfd, ".got");
+ if (sgot == NULL
+ || (! maybe_excluded && (sgot->flags & SEC_EXCLUDE) != 0))
+ return NULL;
+ return sgot;
+}
+
+/* Returns the GOT information associated with the link indicated by
+ INFO. If SGOTP is non-NULL, it is filled in with the GOT
+ section. */
+
+static struct mips_got_info *
+mips_elf_got_info (bfd *abfd, asection **sgotp)
+{
+ asection *sgot;
+ struct mips_got_info *g;
+
+ sgot = mips_elf_got_section (abfd, TRUE);
+ BFD_ASSERT (sgot != NULL);
+ BFD_ASSERT (mips_elf_section_data (sgot) != NULL);
+ g = mips_elf_section_data (sgot)->u.got_info;
+ BFD_ASSERT (g != NULL);
+
+ if (sgotp)
+ *sgotp = (sgot->flags & SEC_EXCLUDE) == 0 ? sgot : NULL;
+
+ return g;
+}
+
+/* Obtain the lowest dynamic index of a symbol that was assigned a
+ global GOT entry. */
+static long
+mips_elf_get_global_gotsym_index (bfd *abfd)
+{
+ asection *sgot;
+ struct mips_got_info *g;
+
+ if (abfd == NULL)
+ return 0;
+
+ sgot = mips_elf_got_section (abfd, TRUE);
+ if (sgot == NULL || mips_elf_section_data (sgot) == NULL)
+ return 0;
+
+ g = mips_elf_section_data (sgot)->u.got_info;
+ if (g == NULL || g->global_gotsym == NULL)
+ return 0;
+
+ return g->global_gotsym->dynindx;
+}
+
+/* Returns the GOT offset at which the indicated address can be found.
+ If there is not yet a GOT entry for this value, create one. Returns
+ -1 if no satisfactory GOT offset can be found. */
+
+static bfd_vma
+mips_elf_local_got_index (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
+ bfd_vma value)
+{
+ asection *sgot;
+ struct mips_got_info *g;
+ struct mips_got_entry *entry;
+
+ g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot);
+
+ entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value);
+ if (entry)
+ return entry->gotidx;
+ else
+ return MINUS_ONE;
+}
+
+/* Returns the GOT index for the global symbol indicated by H. */
+
+static bfd_vma
+mips_elf_global_got_index (bfd *abfd, bfd *ibfd, struct elf_link_hash_entry *h)
+{
+ bfd_vma index;
+ asection *sgot;
+ struct mips_got_info *g, *gg;
+ long global_got_dynindx = 0;
+
+ gg = g = mips_elf_got_info (abfd, &sgot);
+ if (g->bfd2got && ibfd)
+ {
+ struct mips_got_entry e, *p;
+
+ BFD_ASSERT (h->dynindx >= 0);
+
+ g = mips_elf_got_for_ibfd (g, ibfd);
+ if (g->next != gg)
+ {
+ e.abfd = ibfd;
+ e.symndx = -1;
+ e.d.h = (struct mips_elf_link_hash_entry *)h;
+
+ p = htab_find (g->got_entries, &e);
+
+ BFD_ASSERT (p->gotidx > 0);
+ return p->gotidx;
+ }
+ }
+
+ if (gg->global_gotsym != NULL)
+ global_got_dynindx = gg->global_gotsym->dynindx;
+
+ /* Once we determine the global GOT entry with the lowest dynamic
+ symbol table index, we must put all dynamic symbols with greater
+ indices into the GOT. That makes it easy to calculate the GOT
+ offset. */
+ BFD_ASSERT (h->dynindx >= global_got_dynindx);
+ index = ((h->dynindx - global_got_dynindx + g->local_gotno)
+ * MIPS_ELF_GOT_SIZE (abfd));
+ BFD_ASSERT (index < sgot->_raw_size);
+
+ return index;
+}
+
+/* Find a GOT entry that is within 32KB of the VALUE. These entries
+ are supposed to be placed at small offsets in the GOT, i.e.,
+ within 32KB of GP. Return the index into the GOT for this page,
+ and store the offset from this entry to the desired address in
+ OFFSETP, if it is non-NULL. */
+
+static bfd_vma
+mips_elf_got_page (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
+ bfd_vma value, bfd_vma *offsetp)
+{
+ asection *sgot;
+ struct mips_got_info *g;
+ bfd_vma index;
+ struct mips_got_entry *entry;
+
+ g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot);
+
+ entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot,
+ (value + 0x8000)
+ & (~(bfd_vma)0xffff));
+
+ if (!entry)
+ return MINUS_ONE;
+
+ index = entry->gotidx;
+
+ if (offsetp)
+ *offsetp = value - entry->d.address;
+
+ return index;
+}
+
+/* Find a GOT entry whose higher-order 16 bits are the same as those
+ for value. Return the index into the GOT for this entry. */
+
+static bfd_vma
+mips_elf_got16_entry (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
+ bfd_vma value, bfd_boolean external)
+{
+ asection *sgot;
+ struct mips_got_info *g;
+ struct mips_got_entry *entry;
+
+ if (! external)
+ {
+ /* Although the ABI says that it is "the high-order 16 bits" that we
+ want, it is really the %high value. The complete value is
+ calculated with a `addiu' of a LO16 relocation, just as with a
+ HI16/LO16 pair. */
+ value = mips_elf_high (value) << 16;
+ }
+
+ g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot);
+
+ entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value);
+ if (entry)
+ return entry->gotidx;
+ else
+ return MINUS_ONE;
+}
+
+/* Returns the offset for the entry at the INDEXth position
+ in the GOT. */
+
+static bfd_vma
+mips_elf_got_offset_from_index (bfd *dynobj, bfd *output_bfd,
+ bfd *input_bfd, bfd_vma index)
+{
+ asection *sgot;
+ bfd_vma gp;
+ struct mips_got_info *g;
+
+ g = mips_elf_got_info (dynobj, &sgot);
+ gp = _bfd_get_gp_value (output_bfd)
+ + mips_elf_adjust_gp (output_bfd, g, input_bfd);
+
+ return sgot->output_section->vma + sgot->output_offset + index - gp;
+}
+
+/* Create a local GOT entry for VALUE. Return the index of the entry,
+ or -1 if it could not be created. */
+
+static struct mips_got_entry *
+mips_elf_create_local_got_entry (bfd *abfd, bfd *ibfd,
+ struct mips_got_info *gg,
+ asection *sgot, bfd_vma value)
+{
+ struct mips_got_entry entry, **loc;
+ struct mips_got_info *g;
+
+ entry.abfd = NULL;
+ entry.symndx = -1;
+ entry.d.address = value;
+
+ g = mips_elf_got_for_ibfd (gg, ibfd);
+ if (g == NULL)
+ {
+ g = mips_elf_got_for_ibfd (gg, abfd);
+ BFD_ASSERT (g != NULL);
+ }
+
+ loc = (struct mips_got_entry **) htab_find_slot (g->got_entries, &entry,
+ INSERT);
+ if (*loc)
+ return *loc;
+
+ entry.gotidx = MIPS_ELF_GOT_SIZE (abfd) * g->assigned_gotno++;
+
+ *loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
+
+ if (! *loc)
+ return NULL;
+
+ memcpy (*loc, &entry, sizeof entry);
+
+ if (g->assigned_gotno >= g->local_gotno)
+ {
+ (*loc)->gotidx = -1;
+ /* We didn't allocate enough space in the GOT. */
+ (*_bfd_error_handler)
+ (_("not enough GOT space for local GOT entries"));
+ bfd_set_error (bfd_error_bad_value);
+ return NULL;
+ }
+
+ MIPS_ELF_PUT_WORD (abfd, value,
+ (sgot->contents + entry.gotidx));
+
+ return *loc;
+}
+
+/* Sort the dynamic symbol table so that symbols that need GOT entries
+ appear towards the end. This reduces the amount of GOT space
+ required. MAX_LOCAL is used to set the number of local symbols
+ known to be in the dynamic symbol table. During
+ _bfd_mips_elf_size_dynamic_sections, this value is 1. Afterward, the
+ section symbols are added and the count is higher. */
+
+static bfd_boolean
+mips_elf_sort_hash_table (struct bfd_link_info *info, unsigned long max_local)
+{
+ struct mips_elf_hash_sort_data hsd;
+ struct mips_got_info *g;
+ bfd *dynobj;
+
+ dynobj = elf_hash_table (info)->dynobj;
+
+ g = mips_elf_got_info (dynobj, NULL);
+
+ hsd.low = NULL;
+ hsd.max_unref_got_dynindx =
+ hsd.min_got_dynindx = elf_hash_table (info)->dynsymcount
+ /* In the multi-got case, assigned_gotno of the master got_info
+ indicate the number of entries that aren't referenced in the
+ primary GOT, but that must have entries because there are
+ dynamic relocations that reference it. Since they aren't
+ referenced, we move them to the end of the GOT, so that they
+ don't prevent other entries that are referenced from getting
+ too large offsets. */
+ - (g->next ? g->assigned_gotno : 0);
+ hsd.max_non_got_dynindx = max_local;
+ mips_elf_link_hash_traverse (((struct mips_elf_link_hash_table *)
+ elf_hash_table (info)),
+ mips_elf_sort_hash_table_f,
+ &hsd);
+
+ /* There should have been enough room in the symbol table to
+ accommodate both the GOT and non-GOT symbols. */
+ BFD_ASSERT (hsd.max_non_got_dynindx <= hsd.min_got_dynindx);
+ BFD_ASSERT ((unsigned long)hsd.max_unref_got_dynindx
+ <= elf_hash_table (info)->dynsymcount);
+
+ /* Now we know which dynamic symbol has the lowest dynamic symbol
+ table index in the GOT. */
+ g->global_gotsym = hsd.low;
+
+ return TRUE;
+}
+
+/* If H needs a GOT entry, assign it the highest available dynamic
+ index. Otherwise, assign it the lowest available dynamic
+ index. */
+
+static bfd_boolean
+mips_elf_sort_hash_table_f (struct mips_elf_link_hash_entry *h, void *data)
+{
+ struct mips_elf_hash_sort_data *hsd = data;
+
+ if (h->root.root.type == bfd_link_hash_warning)
+ h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
+
+ /* Symbols without dynamic symbol table entries aren't interesting
+ at all. */
+ if (h->root.dynindx == -1)
+ return TRUE;
+
+ /* Global symbols that need GOT entries that are not explicitly
+ referenced are marked with got offset 2. Those that are
+ referenced get a 1, and those that don't need GOT entries get
+ -1. */
+ if (h->root.got.offset == 2)
+ {
+ if (hsd->max_unref_got_dynindx == hsd->min_got_dynindx)
+ hsd->low = (struct elf_link_hash_entry *) h;
+ h->root.dynindx = hsd->max_unref_got_dynindx++;
+ }
+ else if (h->root.got.offset != 1)
+ h->root.dynindx = hsd->max_non_got_dynindx++;
+ else
+ {
+ h->root.dynindx = --hsd->min_got_dynindx;
+ hsd->low = (struct elf_link_hash_entry *) h;
+ }
+
+ return TRUE;
+}
+
+/* If H is a symbol that needs a global GOT entry, but has a dynamic
+ symbol table index lower than any we've seen to date, record it for
+ posterity. */
+
+static bfd_boolean
+mips_elf_record_global_got_symbol (struct elf_link_hash_entry *h,
+ bfd *abfd, struct bfd_link_info *info,
+ struct mips_got_info *g)
+{
+ struct mips_got_entry entry, **loc;
+
+ /* A global symbol in the GOT must also be in the dynamic symbol
+ table. */
+ if (h->dynindx == -1)
+ {
+ switch (ELF_ST_VISIBILITY (h->other))
+ {
+ case STV_INTERNAL:
+ case STV_HIDDEN:
+ _bfd_mips_elf_hide_symbol (info, h, TRUE);
+ break;
+ }
+ if (!bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+ }
+
+ entry.abfd = abfd;
+ entry.symndx = -1;
+ entry.d.h = (struct mips_elf_link_hash_entry *) h;
+
+ loc = (struct mips_got_entry **) htab_find_slot (g->got_entries, &entry,
+ INSERT);
+
+ /* If we've already marked this entry as needing GOT space, we don't
+ need to do it again. */
+ if (*loc)
+ return TRUE;
+
+ *loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
+
+ if (! *loc)
+ return FALSE;
+
+ entry.gotidx = -1;
+ memcpy (*loc, &entry, sizeof entry);
+
+ if (h->got.offset != MINUS_ONE)
+ return TRUE;
+
+ /* By setting this to a value other than -1, we are indicating that
+ there needs to be a GOT entry for H. Avoid using zero, as the
+ generic ELF copy_indirect_symbol tests for <= 0. */
+ h->got.offset = 1;
+
+ return TRUE;
+}
+
+/* Reserve space in G for a GOT entry containing the value of symbol
+ SYMNDX in input bfd ABDF, plus ADDEND. */
+
+static bfd_boolean
+mips_elf_record_local_got_symbol (bfd *abfd, long symndx, bfd_vma addend,
+ struct mips_got_info *g)
+{
+ struct mips_got_entry entry, **loc;
+
+ entry.abfd = abfd;
+ entry.symndx = symndx;
+ entry.d.addend = addend;
+ loc = (struct mips_got_entry **)
+ htab_find_slot (g->got_entries, &entry, INSERT);
+
+ if (*loc)
+ return TRUE;
+
+ entry.gotidx = g->local_gotno++;
+
+ *loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
+
+ if (! *loc)
+ return FALSE;
+
+ memcpy (*loc, &entry, sizeof entry);
+
+ return TRUE;
+}
+
+/* Compute the hash value of the bfd in a bfd2got hash entry. */
+
+static hashval_t
+mips_elf_bfd2got_entry_hash (const void *entry_)
+{
+ const struct mips_elf_bfd2got_hash *entry
+ = (struct mips_elf_bfd2got_hash *)entry_;
+
+ return entry->bfd->id;
+}
+
+/* Check whether two hash entries have the same bfd. */
+
+static int
+mips_elf_bfd2got_entry_eq (const void *entry1, const void *entry2)
+{
+ const struct mips_elf_bfd2got_hash *e1
+ = (const struct mips_elf_bfd2got_hash *)entry1;
+ const struct mips_elf_bfd2got_hash *e2
+ = (const struct mips_elf_bfd2got_hash *)entry2;
+
+ return e1->bfd == e2->bfd;
+}
+
+/* In a multi-got link, determine the GOT to be used for IBDF. G must
+ be the master GOT data. */
+
+static struct mips_got_info *
+mips_elf_got_for_ibfd (struct mips_got_info *g, bfd *ibfd)
+{
+ struct mips_elf_bfd2got_hash e, *p;
+
+ if (! g->bfd2got)
+ return g;
+
+ e.bfd = ibfd;
+ p = htab_find (g->bfd2got, &e);
+ return p ? p->g : NULL;
+}
+
+/* Create one separate got for each bfd that has entries in the global
+ got, such that we can tell how many local and global entries each
+ bfd requires. */
+
+static int
+mips_elf_make_got_per_bfd (void **entryp, void *p)
+{
+ struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
+ struct mips_elf_got_per_bfd_arg *arg = (struct mips_elf_got_per_bfd_arg *)p;
+ htab_t bfd2got = arg->bfd2got;
+ struct mips_got_info *g;
+ struct mips_elf_bfd2got_hash bfdgot_entry, *bfdgot;
+ void **bfdgotp;
+
+ /* Find the got_info for this GOT entry's input bfd. Create one if
+ none exists. */
+ bfdgot_entry.bfd = entry->abfd;
+ bfdgotp = htab_find_slot (bfd2got, &bfdgot_entry, INSERT);
+ bfdgot = (struct mips_elf_bfd2got_hash *)*bfdgotp;
+
+ if (bfdgot != NULL)
+ g = bfdgot->g;
+ else
+ {
+ bfdgot = (struct mips_elf_bfd2got_hash *)bfd_alloc
+ (arg->obfd, sizeof (struct mips_elf_bfd2got_hash));
+
+ if (bfdgot == NULL)
+ {
+ arg->obfd = 0;
+ return 0;
+ }
+
+ *bfdgotp = bfdgot;
+
+ bfdgot->bfd = entry->abfd;
+ bfdgot->g = g = (struct mips_got_info *)
+ bfd_alloc (arg->obfd, sizeof (struct mips_got_info));
+ if (g == NULL)
+ {
+ arg->obfd = 0;
+ return 0;
+ }
+
+ g->global_gotsym = NULL;
+ g->global_gotno = 0;
+ g->local_gotno = 0;
+ g->assigned_gotno = -1;
+ g->got_entries = htab_try_create (1, mips_elf_multi_got_entry_hash,
+ mips_elf_multi_got_entry_eq, NULL);
+ if (g->got_entries == NULL)
+ {
+ arg->obfd = 0;
+ return 0;
+ }
+
+ g->bfd2got = NULL;
+ g->next = NULL;
+ }
+
+ /* Insert the GOT entry in the bfd's got entry hash table. */
+ entryp = htab_find_slot (g->got_entries, entry, INSERT);
+ if (*entryp != NULL)
+ return 1;
+
+ *entryp = entry;
+
+ if (entry->symndx >= 0 || entry->d.h->forced_local)
+ ++g->local_gotno;
+ else
+ ++g->global_gotno;
+
+ return 1;
+}
+
+/* Attempt to merge gots of different input bfds. Try to use as much
+ as possible of the primary got, since it doesn't require explicit
+ dynamic relocations, but don't use bfds that would reference global
+ symbols out of the addressable range. Failing the primary got,
+ attempt to merge with the current got, or finish the current got
+ and then make make the new got current. */
+
+static int
+mips_elf_merge_gots (void **bfd2got_, void *p)
+{
+ struct mips_elf_bfd2got_hash *bfd2got
+ = (struct mips_elf_bfd2got_hash *)*bfd2got_;
+ struct mips_elf_got_per_bfd_arg *arg = (struct mips_elf_got_per_bfd_arg *)p;
+ unsigned int lcount = bfd2got->g->local_gotno;
+ unsigned int gcount = bfd2got->g->global_gotno;
+ unsigned int maxcnt = arg->max_count;
+
+ /* If we don't have a primary GOT and this is not too big, use it as
+ a starting point for the primary GOT. */
+ if (! arg->primary && lcount + gcount <= maxcnt)
+ {
+ arg->primary = bfd2got->g;
+ arg->primary_count = lcount + gcount;
+ }
+ /* If it looks like we can merge this bfd's entries with those of
+ the primary, merge them. The heuristics is conservative, but we
+ don't have to squeeze it too hard. */
+ else if (arg->primary
+ && (arg->primary_count + lcount + gcount) <= maxcnt)
+ {
+ struct mips_got_info *g = bfd2got->g;
+ int old_lcount = arg->primary->local_gotno;
+ int old_gcount = arg->primary->global_gotno;
+
+ bfd2got->g = arg->primary;
+
+ htab_traverse (g->got_entries,
+ mips_elf_make_got_per_bfd,
+ arg);
+ if (arg->obfd == NULL)
+ return 0;
+
+ htab_delete (g->got_entries);
+ /* We don't have to worry about releasing memory of the actual
+ got entries, since they're all in the master got_entries hash
+ table anyway. */
+
+ BFD_ASSERT (old_lcount + lcount >= arg->primary->local_gotno);
+ BFD_ASSERT (old_gcount + gcount >= arg->primary->global_gotno);
+
+ arg->primary_count = arg->primary->local_gotno
+ + arg->primary->global_gotno;
+ }
+ /* If we can merge with the last-created got, do it. */
+ else if (arg->current
+ && arg->current_count + lcount + gcount <= maxcnt)
+ {
+ struct mips_got_info *g = bfd2got->g;
+ int old_lcount = arg->current->local_gotno;
+ int old_gcount = arg->current->global_gotno;
+
+ bfd2got->g = arg->current;
+
+ htab_traverse (g->got_entries,
+ mips_elf_make_got_per_bfd,
+ arg);
+ if (arg->obfd == NULL)
+ return 0;
+
+ htab_delete (g->got_entries);
+
+ BFD_ASSERT (old_lcount + lcount >= arg->current->local_gotno);
+ BFD_ASSERT (old_gcount + gcount >= arg->current->global_gotno);
+
+ arg->current_count = arg->current->local_gotno
+ + arg->current->global_gotno;
+ }
+ /* Well, we couldn't merge, so create a new GOT. Don't check if it
+ fits; if it turns out that it doesn't, we'll get relocation
+ overflows anyway. */
+ else
+ {
+ bfd2got->g->next = arg->current;
+ arg->current = bfd2got->g;
+
+ arg->current_count = lcount + gcount;
+ }
+
+ return 1;
+}
+
+/* If passed a NULL mips_got_info in the argument, set the marker used
+ to tell whether a global symbol needs a got entry (in the primary
+ got) to the given VALUE.
+
+ If passed a pointer G to a mips_got_info in the argument (it must
+ not be the primary GOT), compute the offset from the beginning of
+ the (primary) GOT section to the entry in G corresponding to the
+ global symbol. G's assigned_gotno must contain the index of the
+ first available global GOT entry in G. VALUE must contain the size
+ of a GOT entry in bytes. For each global GOT entry that requires a
+ dynamic relocation, NEEDED_RELOCS is incremented, and the symbol is
+ marked as not eligible for lazy resolution through a function
+ stub. */
+static int
+mips_elf_set_global_got_offset (void **entryp, void *p)
+{
+ struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
+ struct mips_elf_set_global_got_offset_arg *arg
+ = (struct mips_elf_set_global_got_offset_arg *)p;
+ struct mips_got_info *g = arg->g;
+
+ if (entry->abfd != NULL && entry->symndx == -1
+ && entry->d.h->root.dynindx != -1)
+ {
+ if (g)
+ {
+ BFD_ASSERT (g->global_gotsym == NULL);
+
+ entry->gotidx = arg->value * (long) g->assigned_gotno++;
+ if (arg->info->shared
+ || (elf_hash_table (arg->info)->dynamic_sections_created
+ && ((entry->d.h->root.elf_link_hash_flags
+ & ELF_LINK_HASH_DEF_DYNAMIC) != 0)
+ && ((entry->d.h->root.elf_link_hash_flags
+ & ELF_LINK_HASH_DEF_REGULAR) == 0)))
+ ++arg->needed_relocs;
+ }
+ else
+ entry->d.h->root.got.offset = arg->value;
+ }
+
+ return 1;
+}
+
+/* Mark any global symbols referenced in the GOT we are iterating over
+ as inelligible for lazy resolution stubs. */
+static int
+mips_elf_set_no_stub (void **entryp, void *p ATTRIBUTE_UNUSED)
+{
+ struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
+
+ if (entry->abfd != NULL
+ && entry->symndx == -1
+ && entry->d.h->root.dynindx != -1)
+ entry->d.h->no_fn_stub = TRUE;
+
+ return 1;
+}
+
+/* Follow indirect and warning hash entries so that each got entry
+ points to the final symbol definition. P must point to a pointer
+ to the hash table we're traversing. Since this traversal may
+ modify the hash table, we set this pointer to NULL to indicate
+ we've made a potentially-destructive change to the hash table, so
+ the traversal must be restarted. */
+static int
+mips_elf_resolve_final_got_entry (void **entryp, void *p)
+{
+ struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
+ htab_t got_entries = *(htab_t *)p;
+
+ if (entry->abfd != NULL && entry->symndx == -1)
+ {
+ struct mips_elf_link_hash_entry *h = entry->d.h;
+
+ while (h->root.root.type == bfd_link_hash_indirect
+ || h->root.root.type == bfd_link_hash_warning)
+ h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
+
+ if (entry->d.h == h)
+ return 1;
+
+ entry->d.h = h;
+
+ /* If we can't find this entry with the new bfd hash, re-insert
+ it, and get the traversal restarted. */
+ if (! htab_find (got_entries, entry))
+ {
+ htab_clear_slot (got_entries, entryp);
+ entryp = htab_find_slot (got_entries, entry, INSERT);
+ if (! *entryp)
+ *entryp = entry;
+ /* Abort the traversal, since the whole table may have
+ moved, and leave it up to the parent to restart the
+ process. */
+ *(htab_t *)p = NULL;
+ return 0;
+ }
+ /* We might want to decrement the global_gotno count, but it's
+ either too early or too late for that at this point. */
+ }
+
+ return 1;
+}
+
+/* Turn indirect got entries in a got_entries table into their final
+ locations. */
+static void
+mips_elf_resolve_final_got_entries (struct mips_got_info *g)
+{
+ htab_t got_entries;
+
+ do
+ {
+ got_entries = g->got_entries;
+
+ htab_traverse (got_entries,
+ mips_elf_resolve_final_got_entry,
+ &got_entries);
+ }
+ while (got_entries == NULL);
+}
+
+/* Return the offset of an input bfd IBFD's GOT from the beginning of
+ the primary GOT. */
+static bfd_vma
+mips_elf_adjust_gp (bfd *abfd, struct mips_got_info *g, bfd *ibfd)
+{
+ if (g->bfd2got == NULL)
+ return 0;
+
+ g = mips_elf_got_for_ibfd (g, ibfd);
+ if (! g)
+ return 0;
+
+ BFD_ASSERT (g->next);
+
+ g = g->next;
+
+ return (g->local_gotno + g->global_gotno) * MIPS_ELF_GOT_SIZE (abfd);
+}
+
+/* Turn a single GOT that is too big for 16-bit addressing into
+ a sequence of GOTs, each one 16-bit addressable. */
+
+static bfd_boolean
+mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info,
+ struct mips_got_info *g, asection *got,
+ bfd_size_type pages)
+{
+ struct mips_elf_got_per_bfd_arg got_per_bfd_arg;
+ struct mips_elf_set_global_got_offset_arg set_got_offset_arg;
+ struct mips_got_info *gg;
+ unsigned int assign;
+
+ g->bfd2got = htab_try_create (1, mips_elf_bfd2got_entry_hash,
+ mips_elf_bfd2got_entry_eq, NULL);
+ if (g->bfd2got == NULL)
+ return FALSE;
+
+ got_per_bfd_arg.bfd2got = g->bfd2got;
+ got_per_bfd_arg.obfd = abfd;
+ got_per_bfd_arg.info = info;
+
+ /* Count how many GOT entries each input bfd requires, creating a
+ map from bfd to got info while at that. */
+ mips_elf_resolve_final_got_entries (g);
+ htab_traverse (g->got_entries, mips_elf_make_got_per_bfd, &got_per_bfd_arg);
+ if (got_per_bfd_arg.obfd == NULL)
+ return FALSE;
+
+ got_per_bfd_arg.current = NULL;
+ got_per_bfd_arg.primary = NULL;
+ /* Taking out PAGES entries is a worst-case estimate. We could
+ compute the maximum number of pages that each separate input bfd
+ uses, but it's probably not worth it. */
+ got_per_bfd_arg.max_count = ((MIPS_ELF_GOT_MAX_SIZE (abfd)
+ / MIPS_ELF_GOT_SIZE (abfd))
+ - MIPS_RESERVED_GOTNO - pages);
+
+ /* Try to merge the GOTs of input bfds together, as long as they
+ don't seem to exceed the maximum GOT size, choosing one of them
+ to be the primary GOT. */
+ htab_traverse (g->bfd2got, mips_elf_merge_gots, &got_per_bfd_arg);
+ if (got_per_bfd_arg.obfd == NULL)
+ return FALSE;
+
+ /* If we find any suitable primary GOT, create an empty one. */
+ if (got_per_bfd_arg.primary == NULL)
+ {
+ g->next = (struct mips_got_info *)
+ bfd_alloc (abfd, sizeof (struct mips_got_info));
+ if (g->next == NULL)
+ return FALSE;
+
+ g->next->global_gotsym = NULL;
+ g->next->global_gotno = 0;
+ g->next->local_gotno = 0;
+ g->next->assigned_gotno = 0;
+ g->next->got_entries = htab_try_create (1, mips_elf_multi_got_entry_hash,
+ mips_elf_multi_got_entry_eq,
+ NULL);
+ if (g->next->got_entries == NULL)
+ return FALSE;
+ g->next->bfd2got = NULL;
+ }
+ else
+ g->next = got_per_bfd_arg.primary;
+ g->next->next = got_per_bfd_arg.current;
+
+ /* GG is now the master GOT, and G is the primary GOT. */
+ gg = g;
+ g = g->next;
+
+ /* Map the output bfd to the primary got. That's what we're going
+ to use for bfds that use GOT16 or GOT_PAGE relocations that we
+ didn't mark in check_relocs, and we want a quick way to find it.
+ We can't just use gg->next because we're going to reverse the
+ list. */
+ {
+ struct mips_elf_bfd2got_hash *bfdgot;
+ void **bfdgotp;
+
+ bfdgot = (struct mips_elf_bfd2got_hash *)bfd_alloc
+ (abfd, sizeof (struct mips_elf_bfd2got_hash));
+
+ if (bfdgot == NULL)
+ return FALSE;
+
+ bfdgot->bfd = abfd;
+ bfdgot->g = g;
+ bfdgotp = htab_find_slot (gg->bfd2got, bfdgot, INSERT);
+
+ BFD_ASSERT (*bfdgotp == NULL);
+ *bfdgotp = bfdgot;
+ }
+
+ /* The IRIX dynamic linker requires every symbol that is referenced
+ in a dynamic relocation to be present in the primary GOT, so
+ arrange for them to appear after those that are actually
+ referenced.
+
+ GNU/Linux could very well do without it, but it would slow down
+ the dynamic linker, since it would have to resolve every dynamic
+ symbol referenced in other GOTs more than once, without help from
+ the cache. Also, knowing that every external symbol has a GOT
+ helps speed up the resolution of local symbols too, so GNU/Linux
+ follows IRIX's practice.
+
+ The number 2 is used by mips_elf_sort_hash_table_f to count
+ global GOT symbols that are unreferenced in the primary GOT, with
+ an initial dynamic index computed from gg->assigned_gotno, where
+ the number of unreferenced global entries in the primary GOT is
+ preserved. */
+ if (1)
+ {
+ gg->assigned_gotno = gg->global_gotno - g->global_gotno;
+ g->global_gotno = gg->global_gotno;
+ set_got_offset_arg.value = 2;
+ }
+ else
+ {
+ /* This could be used for dynamic linkers that don't optimize
+ symbol resolution while applying relocations so as to use
+ primary GOT entries or assuming the symbol is locally-defined.
+ With this code, we assign lower dynamic indices to global
+ symbols that are not referenced in the primary GOT, so that
+ their entries can be omitted. */
+ gg->assigned_gotno = 0;
+ set_got_offset_arg.value = -1;
+ }
+
+ /* Reorder dynamic symbols as described above (which behavior
+ depends on the setting of VALUE). */
+ set_got_offset_arg.g = NULL;
+ htab_traverse (gg->got_entries, mips_elf_set_global_got_offset,
+ &set_got_offset_arg);
+ set_got_offset_arg.value = 1;
+ htab_traverse (g->got_entries, mips_elf_set_global_got_offset,
+ &set_got_offset_arg);
+ if (! mips_elf_sort_hash_table (info, 1))
+ return FALSE;
+
+ /* Now go through the GOTs assigning them offset ranges.
+ [assigned_gotno, local_gotno[ will be set to the range of local
+ entries in each GOT. We can then compute the end of a GOT by
+ adding local_gotno to global_gotno. We reverse the list and make
+ it circular since then we'll be able to quickly compute the
+ beginning of a GOT, by computing the end of its predecessor. To
+ avoid special cases for the primary GOT, while still preserving
+ assertions that are valid for both single- and multi-got links,
+ we arrange for the main got struct to have the right number of
+ global entries, but set its local_gotno such that the initial
+ offset of the primary GOT is zero. Remember that the primary GOT
+ will become the last item in the circular linked list, so it
+ points back to the master GOT. */
+ gg->local_gotno = -g->global_gotno;
+ gg->global_gotno = g->global_gotno;
+ assign = 0;
+ gg->next = gg;
+
+ do
+ {
+ struct mips_got_info *gn;
+
+ assign += MIPS_RESERVED_GOTNO;
+ g->assigned_gotno = assign;
+ g->local_gotno += assign + pages;
+ assign = g->local_gotno + g->global_gotno;
+
+ /* Take g out of the direct list, and push it onto the reversed
+ list that gg points to. */
+ gn = g->next;
+ g->next = gg->next;
+ gg->next = g;
+ g = gn;
+
+ /* Mark global symbols in every non-primary GOT as ineligible for
+ stubs. */
+ if (g)
+ htab_traverse (g->got_entries, mips_elf_set_no_stub, NULL);
+ }
+ while (g);
+
+ got->_raw_size = (gg->next->local_gotno
+ + gg->next->global_gotno) * MIPS_ELF_GOT_SIZE (abfd);
+
+ return TRUE;
+}
+
+
+/* Returns the first relocation of type r_type found, beginning with
+ RELOCATION. RELEND is one-past-the-end of the relocation table. */
+
+static const Elf_Internal_Rela *
+mips_elf_next_relocation (bfd *abfd ATTRIBUTE_UNUSED, unsigned int r_type,
+ const Elf_Internal_Rela *relocation,
+ const Elf_Internal_Rela *relend)
+{
+ /* According to the MIPS ELF ABI, the R_MIPS_LO16 relocation must be
+ immediately following. However, for the IRIX6 ABI, the next
+ relocation may be a composed relocation consisting of several
+ relocations for the same address. In that case, the R_MIPS_LO16
+ relocation may occur as one of these. We permit a similar
+ extension in general, as that is useful for GCC. */
+ while (relocation < relend)
+ {
+ if (ELF_R_TYPE (abfd, relocation->r_info) == r_type)
+ return relocation;
+
+ ++relocation;
+ }
+
+ /* We didn't find it. */
+ bfd_set_error (bfd_error_bad_value);
+ return NULL;
+}
+
+/* Return whether a relocation is against a local symbol. */
+
+static bfd_boolean
+mips_elf_local_relocation_p (bfd *input_bfd,
+ const Elf_Internal_Rela *relocation,
+ asection **local_sections,
+ bfd_boolean check_forced)
+{
+ unsigned long r_symndx;
+ Elf_Internal_Shdr *symtab_hdr;
+ struct mips_elf_link_hash_entry *h;
+ size_t extsymoff;
+
+ r_symndx = ELF_R_SYM (input_bfd, relocation->r_info);
+ symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+ extsymoff = (elf_bad_symtab (input_bfd)) ? 0 : symtab_hdr->sh_info;
+
+ if (r_symndx < extsymoff)
+ return TRUE;
+ if (elf_bad_symtab (input_bfd) && local_sections[r_symndx] != NULL)
+ return TRUE;
+
+ if (check_forced)
+ {
+ /* Look up the hash table to check whether the symbol
+ was forced local. */
+ h = (struct mips_elf_link_hash_entry *)
+ elf_sym_hashes (input_bfd) [r_symndx - extsymoff];
+ /* Find the real hash-table entry for this symbol. */
+ while (h->root.root.type == bfd_link_hash_indirect
+ || h->root.root.type == bfd_link_hash_warning)
+ h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
+ if ((h->root.elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Sign-extend VALUE, which has the indicated number of BITS. */
+
+bfd_vma
+_bfd_mips_elf_sign_extend (bfd_vma value, int bits)
+{
+ if (value & ((bfd_vma) 1 << (bits - 1)))
+ /* VALUE is negative. */
+ value |= ((bfd_vma) - 1) << bits;
+
+ return value;
+}
+
+/* Return non-zero if the indicated VALUE has overflowed the maximum
+ range expressible by a signed number with the indicated number of
+ BITS. */
+
+static bfd_boolean
+mips_elf_overflow_p (bfd_vma value, int bits)
+{
+ bfd_signed_vma svalue = (bfd_signed_vma) value;
+
+ if (svalue > (1 << (bits - 1)) - 1)
+ /* The value is too big. */
+ return TRUE;
+ else if (svalue < -(1 << (bits - 1)))
+ /* The value is too small. */
+ return TRUE;
+
+ /* All is well. */
+ return FALSE;
+}
+
+/* Calculate the %high function. */
+
+static bfd_vma
+mips_elf_high (bfd_vma value)
+{
+ return ((value + (bfd_vma) 0x8000) >> 16) & 0xffff;
+}
+
+/* Calculate the %higher function. */
+
+static bfd_vma
+mips_elf_higher (bfd_vma value ATTRIBUTE_UNUSED)
+{
+#ifdef BFD64
+ return ((value + (bfd_vma) 0x80008000) >> 32) & 0xffff;
+#else
+ abort ();
+ return (bfd_vma) -1;
+#endif
+}
+
+/* Calculate the %highest function. */
+
+static bfd_vma
+mips_elf_highest (bfd_vma value ATTRIBUTE_UNUSED)
+{
+#ifdef BFD64
+ return ((value + (((bfd_vma) 0x8000 << 32) | 0x80008000)) >> 48) & 0xffff;
+#else
+ abort ();
+ return (bfd_vma) -1;
+#endif
+}
+
+/* Create the .compact_rel section. */
+
+static bfd_boolean
+mips_elf_create_compact_rel_section
+ (bfd *abfd, struct bfd_link_info *info ATTRIBUTE_UNUSED)
+{
+ flagword flags;
+ register asection *s;
+
+ if (bfd_get_section_by_name (abfd, ".compact_rel") == NULL)
+ {
+ flags = (SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED
+ | SEC_READONLY);
+
+ s = bfd_make_section (abfd, ".compact_rel");
+ if (s == NULL
+ || ! bfd_set_section_flags (abfd, s, flags)
+ || ! bfd_set_section_alignment (abfd, s,
+ MIPS_ELF_LOG_FILE_ALIGN (abfd)))
+ return FALSE;
+
+ s->_raw_size = sizeof (Elf32_External_compact_rel);
+ }
+
+ return TRUE;
+}
+
+/* Create the .got section to hold the global offset table. */
+
+static bfd_boolean
+mips_elf_create_got_section (bfd *abfd, struct bfd_link_info *info,
+ bfd_boolean maybe_exclude)
+{
+ flagword flags;
+ register asection *s;
+ struct elf_link_hash_entry *h;
+ struct bfd_link_hash_entry *bh;
+ struct mips_got_info *g;
+ bfd_size_type amt;
+
+ /* This function may be called more than once. */
+ s = mips_elf_got_section (abfd, TRUE);
+ if (s)
+ {
+ if (! maybe_exclude)
+ s->flags &= ~SEC_EXCLUDE;
+ return TRUE;
+ }
+
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+ | SEC_LINKER_CREATED);
+
+ if (maybe_exclude)
+ flags |= SEC_EXCLUDE;
+
+ /* We have to use an alignment of 2**4 here because this is hardcoded
+ in the function stub generation and in the linker script. */
+ s = bfd_make_section (abfd, ".got");
+ if (s == NULL
+ || ! bfd_set_section_flags (abfd, s, flags)
+ || ! bfd_set_section_alignment (abfd, s, 4))
+ return FALSE;
+
+ /* Define the symbol _GLOBAL_OFFSET_TABLE_. We don't do this in the
+ linker script because we don't want to define the symbol if we
+ are not creating a global offset table. */
+ bh = NULL;
+ if (! (_bfd_generic_link_add_one_symbol
+ (info, abfd, "_GLOBAL_OFFSET_TABLE_", BSF_GLOBAL, s,
+ 0, NULL, FALSE, get_elf_backend_data (abfd)->collect, &bh)))
+ return FALSE;
+
+ h = (struct elf_link_hash_entry *) bh;
+ h->elf_link_hash_flags &= ~ELF_LINK_NON_ELF;
+ h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
+ h->type = STT_OBJECT;
+
+ if (info->shared
+ && ! bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+
+ amt = sizeof (struct mips_got_info);
+ g = bfd_alloc (abfd, amt);
+ if (g == NULL)
+ return FALSE;
+ g->global_gotsym = NULL;
+ g->global_gotno = 0;
+ g->local_gotno = MIPS_RESERVED_GOTNO;
+ g->assigned_gotno = MIPS_RESERVED_GOTNO;
+ g->bfd2got = NULL;
+ g->next = NULL;
+ g->got_entries = htab_try_create (1, mips_elf_got_entry_hash,
+ mips_elf_got_entry_eq, NULL);
+ if (g->got_entries == NULL)
+ return FALSE;
+ mips_elf_section_data (s)->u.got_info = g;
+ mips_elf_section_data (s)->elf.this_hdr.sh_flags
+ |= SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL;
+
+ return TRUE;
+}
+
+/* Calculate the value produced by the RELOCATION (which comes from
+ the INPUT_BFD). The ADDEND is the addend to use for this
+ RELOCATION; RELOCATION->R_ADDEND is ignored.
+
+ The result of the relocation calculation is stored in VALUEP.
+ REQUIRE_JALXP indicates whether or not the opcode used with this
+ relocation must be JALX.
+
+ This function returns bfd_reloc_continue if the caller need take no
+ further action regarding this relocation, bfd_reloc_notsupported if
+ something goes dramatically wrong, bfd_reloc_overflow if an
+ overflow occurs, and bfd_reloc_ok to indicate success. */
+
+static bfd_reloc_status_type
+mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
+ asection *input_section,
+ struct bfd_link_info *info,
+ const Elf_Internal_Rela *relocation,
+ bfd_vma addend, reloc_howto_type *howto,
+ Elf_Internal_Sym *local_syms,
+ asection **local_sections, bfd_vma *valuep,
+ const char **namep, bfd_boolean *require_jalxp,
+ bfd_boolean save_addend)
+{
+ /* The eventual value we will return. */
+ bfd_vma value;
+ /* The address of the symbol against which the relocation is
+ occurring. */
+ bfd_vma symbol = 0;
+ /* The final GP value to be used for the relocatable, executable, or
+ shared object file being produced. */
+ bfd_vma gp = MINUS_ONE;
+ /* The place (section offset or address) of the storage unit being
+ relocated. */
+ bfd_vma p;
+ /* The value of GP used to create the relocatable object. */
+ bfd_vma gp0 = MINUS_ONE;
+ /* The offset into the global offset table at which the address of
+ the relocation entry symbol, adjusted by the addend, resides
+ during execution. */
+ bfd_vma g = MINUS_ONE;
+ /* The section in which the symbol referenced by the relocation is
+ located. */
+ asection *sec = NULL;
+ struct mips_elf_link_hash_entry *h = NULL;
+ /* TRUE if the symbol referred to by this relocation is a local
+ symbol. */
+ bfd_boolean local_p, was_local_p;
+ /* TRUE if the symbol referred to by this relocation is "_gp_disp". */
+ bfd_boolean gp_disp_p = FALSE;
+ Elf_Internal_Shdr *symtab_hdr;
+ size_t extsymoff;
+ unsigned long r_symndx;
+ int r_type;
+ /* TRUE if overflow occurred during the calculation of the
+ relocation value. */
+ bfd_boolean overflowed_p;
+ /* TRUE if this relocation refers to a MIPS16 function. */
+ bfd_boolean target_is_16_bit_code_p = FALSE;
+
+ /* Parse the relocation. */
+ r_symndx = ELF_R_SYM (input_bfd, relocation->r_info);
+ r_type = ELF_R_TYPE (input_bfd, relocation->r_info);
+ p = (input_section->output_section->vma
+ + input_section->output_offset
+ + relocation->r_offset);
+
+ /* Assume that there will be no overflow. */
+ overflowed_p = FALSE;
+
+ /* Figure out whether or not the symbol is local, and get the offset
+ used in the array of hash table entries. */
+ symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+ local_p = mips_elf_local_relocation_p (input_bfd, relocation,
+ local_sections, FALSE);
+ was_local_p = local_p;
+ if (! elf_bad_symtab (input_bfd))
+ extsymoff = symtab_hdr->sh_info;
+ else
+ {
+ /* The symbol table does not follow the rule that local symbols
+ must come before globals. */
+ extsymoff = 0;
+ }
+
+ /* Figure out the value of the symbol. */
+ if (local_p)
+ {
+ Elf_Internal_Sym *sym;
+
+ sym = local_syms + r_symndx;
+ sec = local_sections[r_symndx];
+
+ symbol = sec->output_section->vma + sec->output_offset;
+ if (ELF_ST_TYPE (sym->st_info) != STT_SECTION
+ || (sec->flags & SEC_MERGE))
+ symbol += sym->st_value;
+ if ((sec->flags & SEC_MERGE)
+ && ELF_ST_TYPE (sym->st_info) == STT_SECTION)
+ {
+ addend = _bfd_elf_rel_local_sym (abfd, sym, &sec, addend);
+ addend -= symbol;
+ addend += sec->output_section->vma + sec->output_offset;
+ }
+
+ /* MIPS16 text labels should be treated as odd. */
+ if (sym->st_other == STO_MIPS16)
+ ++symbol;
+
+ /* Record the name of this symbol, for our caller. */
+ *namep = bfd_elf_string_from_elf_section (input_bfd,
+ symtab_hdr->sh_link,
+ sym->st_name);
+ if (*namep == '\0')
+ *namep = bfd_section_name (input_bfd, sec);
+
+ target_is_16_bit_code_p = (sym->st_other == STO_MIPS16);
+ }
+ else
+ {
+ /* ??? Could we use RELOC_FOR_GLOBAL_SYMBOL here ? */
+
+ /* For global symbols we look up the symbol in the hash-table. */
+ h = ((struct mips_elf_link_hash_entry *)
+ elf_sym_hashes (input_bfd) [r_symndx - extsymoff]);
+ /* Find the real hash-table entry for this symbol. */
+ while (h->root.root.type == bfd_link_hash_indirect
+ || h->root.root.type == bfd_link_hash_warning)
+ h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
+
+ /* Record the name of this symbol, for our caller. */
+ *namep = h->root.root.root.string;
+
+ /* See if this is the special _gp_disp symbol. Note that such a
+ symbol must always be a global symbol. */
+ if (strcmp (*namep, "_gp_disp") == 0
+ && ! NEWABI_P (input_bfd))
+ {
+ /* Relocations against _gp_disp are permitted only with
+ R_MIPS_HI16 and R_MIPS_LO16 relocations. */
+ if (r_type != R_MIPS_HI16 && r_type != R_MIPS_LO16)
+ return bfd_reloc_notsupported;
+
+ gp_disp_p = TRUE;
+ }
+ /* If this symbol is defined, calculate its address. Note that
+ _gp_disp is a magic symbol, always implicitly defined by the
+ linker, so it's inappropriate to check to see whether or not
+ its defined. */
+ else if ((h->root.root.type == bfd_link_hash_defined
+ || h->root.root.type == bfd_link_hash_defweak)
+ && h->root.root.u.def.section)
+ {
+ sec = h->root.root.u.def.section;
+ if (sec->output_section)
+ symbol = (h->root.root.u.def.value
+ + sec->output_section->vma
+ + sec->output_offset);
+ else
+ symbol = h->root.root.u.def.value;
+ }
+ else if (h->root.root.type == bfd_link_hash_undefweak)
+ /* We allow relocations against undefined weak symbols, giving
+ it the value zero, so that you can undefined weak functions
+ and check to see if they exist by looking at their
+ addresses. */
+ symbol = 0;
+ else if (info->unresolved_syms_in_objects == RM_IGNORE
+ && ELF_ST_VISIBILITY (h->root.other) == STV_DEFAULT)
+ symbol = 0;
+ else if (strcmp (*namep, "_DYNAMIC_LINK") == 0 ||
+ strcmp (*namep, "_DYNAMIC_LINKING") == 0)
+ {
+ /* If this is a dynamic link, we should have created a
+ _DYNAMIC_LINK symbol or _DYNAMIC_LINKING(for normal mips) symbol
+ in in _bfd_mips_elf_create_dynamic_sections.
+ Otherwise, we should define the symbol with a value of 0.
+ FIXME: It should probably get into the symbol table
+ somehow as well. */
+ BFD_ASSERT (! info->shared);
+ BFD_ASSERT (bfd_get_section_by_name (abfd, ".dynamic") == NULL);
+ symbol = 0;
+ }
+ else
+ {
+ if (! ((*info->callbacks->undefined_symbol)
+ (info, h->root.root.root.string, input_bfd,
+ input_section, relocation->r_offset,
+ (info->unresolved_syms_in_objects == RM_GENERATE_ERROR)
+ || ELF_ST_VISIBILITY (h->root.other))))
+ return bfd_reloc_undefined;
+ symbol = 0;
+ }
+
+ target_is_16_bit_code_p = (h->root.other == STO_MIPS16);
+ }
+
+ /* If this is a 32- or 64-bit call to a 16-bit function with a stub, we
+ need to redirect the call to the stub, unless we're already *in*
+ a stub. */
+ if (r_type != R_MIPS16_26 && !info->relocatable
+ && ((h != NULL && h->fn_stub != NULL)
+ || (local_p && elf_tdata (input_bfd)->local_stubs != NULL
+ && elf_tdata (input_bfd)->local_stubs[r_symndx] != NULL))
+ && !mips_elf_stub_section_p (input_bfd, input_section))
+ {
+ /* This is a 32- or 64-bit call to a 16-bit function. We should
+ have already noticed that we were going to need the
+ stub. */
+ if (local_p)
+ sec = elf_tdata (input_bfd)->local_stubs[r_symndx];
+ else
+ {
+ BFD_ASSERT (h->need_fn_stub);
+ sec = h->fn_stub;
+ }
+
+ symbol = sec->output_section->vma + sec->output_offset;
+ }
+ /* If this is a 16-bit call to a 32- or 64-bit function with a stub, we
+ need to redirect the call to the stub. */
+ else if (r_type == R_MIPS16_26 && !info->relocatable
+ && h != NULL
+ && (h->call_stub != NULL || h->call_fp_stub != NULL)
+ && !target_is_16_bit_code_p)
+ {
+ /* If both call_stub and call_fp_stub are defined, we can figure
+ out which one to use by seeing which one appears in the input
+ file. */
+ if (h->call_stub != NULL && h->call_fp_stub != NULL)
+ {
+ asection *o;
+
+ sec = NULL;
+ for (o = input_bfd->sections; o != NULL; o = o->next)
+ {
+ if (strncmp (bfd_get_section_name (input_bfd, o),
+ CALL_FP_STUB, sizeof CALL_FP_STUB - 1) == 0)
+ {
+ sec = h->call_fp_stub;
+ break;
+ }
+ }
+ if (sec == NULL)
+ sec = h->call_stub;
+ }
+ else if (h->call_stub != NULL)
+ sec = h->call_stub;
+ else
+ sec = h->call_fp_stub;
+
+ BFD_ASSERT (sec->_raw_size > 0);
+ symbol = sec->output_section->vma + sec->output_offset;
+ }
+
+ /* Calls from 16-bit code to 32-bit code and vice versa require the
+ special jalx instruction. */
+ *require_jalxp = (!info->relocatable
+ && (((r_type == R_MIPS16_26) && !target_is_16_bit_code_p)
+ || ((r_type == R_MIPS_26) && target_is_16_bit_code_p)));
+
+ local_p = mips_elf_local_relocation_p (input_bfd, relocation,
+ local_sections, TRUE);
+
+ /* If we haven't already determined the GOT offset, or the GP value,
+ and we're going to need it, get it now. */
+ switch (r_type)
+ {
+ case R_MIPS_GOT_PAGE:
+ case R_MIPS_GOT_OFST:
+ /* We need to decay to GOT_DISP/addend if the symbol doesn't
+ bind locally. */
+ local_p = local_p || _bfd_elf_symbol_refs_local_p (&h->root, info, 1);
+ if (local_p || r_type == R_MIPS_GOT_OFST)
+ break;
+ /* Fall through. */
+
+ case R_MIPS_CALL16:
+ case R_MIPS_GOT16:
+ case R_MIPS_GOT_DISP:
+ case R_MIPS_GOT_HI16:
+ case R_MIPS_CALL_HI16:
+ case R_MIPS_GOT_LO16:
+ case R_MIPS_CALL_LO16:
+ /* Find the index into the GOT where this value is located. */
+ if (!local_p)
+ {
+ /* GOT_PAGE may take a non-zero addend, that is ignored in a
+ GOT_PAGE relocation that decays to GOT_DISP because the
+ symbol turns out to be global. The addend is then added
+ as GOT_OFST. */
+ BFD_ASSERT (addend == 0 || r_type == R_MIPS_GOT_PAGE);
+ g = mips_elf_global_got_index (elf_hash_table (info)->dynobj,
+ input_bfd,
+ (struct elf_link_hash_entry *) h);
+ if (! elf_hash_table(info)->dynamic_sections_created
+ || (info->shared
+ && (info->symbolic || h->root.dynindx == -1)
+ && (h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)))
+ {
+ /* This is a static link or a -Bsymbolic link. The
+ symbol is defined locally, or was forced to be local.
+ We must initialize this entry in the GOT. */
+ bfd *tmpbfd = elf_hash_table (info)->dynobj;
+ asection *sgot = mips_elf_got_section (tmpbfd, FALSE);
+ MIPS_ELF_PUT_WORD (tmpbfd, symbol, sgot->contents + g);
+ }
+ }
+ else if (r_type == R_MIPS_GOT16 || r_type == R_MIPS_CALL16)
+ /* There's no need to create a local GOT entry here; the
+ calculation for a local GOT16 entry does not involve G. */
+ break;
+ else
+ {
+ g = mips_elf_local_got_index (abfd, input_bfd,
+ info, symbol + addend);
+ if (g == MINUS_ONE)
+ return bfd_reloc_outofrange;
+ }
+
+ /* Convert GOT indices to actual offsets. */
+ g = mips_elf_got_offset_from_index (elf_hash_table (info)->dynobj,
+ abfd, input_bfd, g);
+ break;
+
+ case R_MIPS_HI16:
+ case R_MIPS_LO16:
+ case R_MIPS16_GPREL:
+ case R_MIPS_GPREL16:
+ case R_MIPS_GPREL32:
+ case R_MIPS_LITERAL:
+ gp0 = _bfd_get_gp_value (input_bfd);
+ gp = _bfd_get_gp_value (abfd);
+ if (elf_hash_table (info)->dynobj)
+ gp += mips_elf_adjust_gp (abfd,
+ mips_elf_got_info
+ (elf_hash_table (info)->dynobj, NULL),
+ input_bfd);
+ break;
+
+ default:
+ break;
+ }
+
+ /* Figure out what kind of relocation is being performed. */
+ switch (r_type)
+ {
+ case R_MIPS_NONE:
+ return bfd_reloc_continue;
+
+ case R_MIPS_16:
+ value = symbol + _bfd_mips_elf_sign_extend (addend, 16);
+ overflowed_p = mips_elf_overflow_p (value, 16);
+ break;
+
+ case R_MIPS_32:
+ case R_MIPS_REL32:
+ case R_MIPS_64:
+ if ((info->shared
+ || (elf_hash_table (info)->dynamic_sections_created
+ && h != NULL
+ && ((h->root.elf_link_hash_flags
+ & ELF_LINK_HASH_DEF_DYNAMIC) != 0)
+ && ((h->root.elf_link_hash_flags
+ & ELF_LINK_HASH_DEF_REGULAR) == 0)))
+ && r_symndx != 0
+ && (input_section->flags & SEC_ALLOC) != 0)
+ {
+ /* If we're creating a shared library, or this relocation is
+ against a symbol in a shared library, then we can't know
+ where the symbol will end up. So, we create a relocation
+ record in the output, and leave the job up to the dynamic
+ linker. */
+ value = addend;
+ if (!mips_elf_create_dynamic_relocation (abfd,
+ info,
+ relocation,
+ h,
+ sec,
+ symbol,
+ &value,
+ input_section))
+ return bfd_reloc_undefined;
+ }
+ else
+ {
+ if (r_type != R_MIPS_REL32)
+ value = symbol + addend;
+ else
+ value = addend;
+ }
+ value &= howto->dst_mask;
+ break;
+
+ case R_MIPS_PC32:
+ case R_MIPS_PC64:
+ case R_MIPS_GNU_REL_LO16:
+ value = symbol + addend - p;
+ value &= howto->dst_mask;
+ break;
+
+ case R_MIPS_GNU_REL16_S2:
+ value = symbol + _bfd_mips_elf_sign_extend (addend, 18) - p;
+ overflowed_p = mips_elf_overflow_p (value, 18);
+ value = (value >> 2) & howto->dst_mask;
+ break;
+
+ case R_MIPS_GNU_REL_HI16:
+ /* Instead of subtracting 'p' here, we should be subtracting the
+ equivalent value for the LO part of the reloc, since the value
+ here is relative to that address. Because that's not easy to do,
+ we adjust 'addend' in _bfd_mips_elf_relocate_section(). See also
+ the comment there for more information. */
+ value = mips_elf_high (addend + symbol - p);
+ value &= howto->dst_mask;
+ break;
+
+ case R_MIPS16_26:
+ /* The calculation for R_MIPS16_26 is just the same as for an
+ R_MIPS_26. It's only the storage of the relocated field into
+ the output file that's different. That's handled in
+ mips_elf_perform_relocation. So, we just fall through to the
+ R_MIPS_26 case here. */
+ case R_MIPS_26:
+ if (local_p)
+ value = ((addend | ((p + 4) & 0xf0000000)) + symbol) >> 2;
+ else
+ value = (_bfd_mips_elf_sign_extend (addend, 28) + symbol) >> 2;
+ value &= howto->dst_mask;
+ break;
+
+ case R_MIPS_HI16:
+ if (!gp_disp_p)
+ {
+ value = mips_elf_high (addend + symbol);
+ value &= howto->dst_mask;
+ }
+ else
+ {
+ value = mips_elf_high (addend + gp - p);
+ overflowed_p = mips_elf_overflow_p (value, 16);
+ }
+ break;
+
+ case R_MIPS_LO16:
+ if (!gp_disp_p)
+ value = (symbol + addend) & howto->dst_mask;
+ else
+ {
+ value = addend + gp - p + 4;
+ /* The MIPS ABI requires checking the R_MIPS_LO16 relocation
+ for overflow. But, on, say, IRIX5, relocations against
+ _gp_disp are normally generated from the .cpload
+ pseudo-op. It generates code that normally looks like
+ this:
+
+ lui $gp,%hi(_gp_disp)
+ addiu $gp,$gp,%lo(_gp_disp)
+ addu $gp,$gp,$t9
+
+ Here $t9 holds the address of the function being called,
+ as required by the MIPS ELF ABI. The R_MIPS_LO16
+ relocation can easily overflow in this situation, but the
+ R_MIPS_HI16 relocation will handle the overflow.
+ Therefore, we consider this a bug in the MIPS ABI, and do
+ not check for overflow here. */
+ }
+ break;
+
+ case R_MIPS_LITERAL:
+ /* Because we don't merge literal sections, we can handle this
+ just like R_MIPS_GPREL16. In the long run, we should merge
+ shared literals, and then we will need to additional work
+ here. */
+
+ /* Fall through. */
+
+ case R_MIPS16_GPREL:
+ /* The R_MIPS16_GPREL performs the same calculation as
+ R_MIPS_GPREL16, but stores the relocated bits in a different
+ order. We don't need to do anything special here; the
+ differences are handled in mips_elf_perform_relocation. */
+ case R_MIPS_GPREL16:
+ /* Only sign-extend the addend if it was extracted from the
+ instruction. If the addend was separate, leave it alone,
+ otherwise we may lose significant bits. */
+ if (howto->partial_inplace)
+ addend = _bfd_mips_elf_sign_extend (addend, 16);
+ value = symbol + addend - gp;
+ /* If the symbol was local, any earlier relocatable links will
+ have adjusted its addend with the gp offset, so compensate
+ for that now. Don't do it for symbols forced local in this
+ link, though, since they won't have had the gp offset applied
+ to them before. */
+ if (was_local_p)
+ value += gp0;
+ overflowed_p = mips_elf_overflow_p (value, 16);
+ break;
+
+ case R_MIPS_GOT16:
+ case R_MIPS_CALL16:
+ if (local_p)
+ {
+ bfd_boolean forced;
+
+ /* The special case is when the symbol is forced to be local. We
+ need the full address in the GOT since no R_MIPS_LO16 relocation
+ follows. */
+ forced = ! mips_elf_local_relocation_p (input_bfd, relocation,
+ local_sections, FALSE);
+ value = mips_elf_got16_entry (abfd, input_bfd, info,
+ symbol + addend, forced);
+ if (value == MINUS_ONE)
+ return bfd_reloc_outofrange;
+ value
+ = mips_elf_got_offset_from_index (elf_hash_table (info)->dynobj,
+ abfd, input_bfd, value);
+ overflowed_p = mips_elf_overflow_p (value, 16);
+ break;
+ }
+
+ /* Fall through. */
+
+ case R_MIPS_GOT_DISP:
+ got_disp:
+ value = g;
+ overflowed_p = mips_elf_overflow_p (value, 16);
+ break;
+
+ case R_MIPS_GPREL32:
+ value = (addend + symbol + gp0 - gp);
+ if (!save_addend)
+ value &= howto->dst_mask;
+ break;
+
+ case R_MIPS_PC16:
+ value = _bfd_mips_elf_sign_extend (addend, 16) + symbol - p;
+ overflowed_p = mips_elf_overflow_p (value, 16);
+ break;
+
+ case R_MIPS_GOT_HI16:
+ case R_MIPS_CALL_HI16:
+ /* We're allowed to handle these two relocations identically.
+ The dynamic linker is allowed to handle the CALL relocations
+ differently by creating a lazy evaluation stub. */
+ value = g;
+ value = mips_elf_high (value);
+ value &= howto->dst_mask;
+ break;
+
+ case R_MIPS_GOT_LO16:
+ case R_MIPS_CALL_LO16:
+ value = g & howto->dst_mask;
+ break;
+
+ case R_MIPS_GOT_PAGE:
+ /* GOT_PAGE relocations that reference non-local symbols decay
+ to GOT_DISP. The corresponding GOT_OFST relocation decays to
+ 0. */
+ if (! local_p)
+ goto got_disp;
+ value = mips_elf_got_page (abfd, input_bfd, info, symbol + addend, NULL);
+ if (value == MINUS_ONE)
+ return bfd_reloc_outofrange;
+ value = mips_elf_got_offset_from_index (elf_hash_table (info)->dynobj,
+ abfd, input_bfd, value);
+ overflowed_p = mips_elf_overflow_p (value, 16);
+ break;
+
+ case R_MIPS_GOT_OFST:
+ if (local_p)
+ mips_elf_got_page (abfd, input_bfd, info, symbol + addend, &value);
+ else
+ value = addend;
+ overflowed_p = mips_elf_overflow_p (value, 16);
+ break;
+
+ case R_MIPS_SUB:
+ value = symbol - addend;
+ value &= howto->dst_mask;
+ break;
+
+ case R_MIPS_HIGHER:
+ value = mips_elf_higher (addend + symbol);
+ value &= howto->dst_mask;
+ break;
+
+ case R_MIPS_HIGHEST:
+ value = mips_elf_highest (addend + symbol);
+ value &= howto->dst_mask;
+ break;
+
+ case R_MIPS_SCN_DISP:
+ value = symbol + addend - sec->output_offset;
+ value &= howto->dst_mask;
+ break;
+
+ case R_MIPS_PJUMP:
+ case R_MIPS_JALR:
+ /* Both of these may be ignored. R_MIPS_JALR is an optimization
+ hint; we could improve performance by honoring that hint. */
+ return bfd_reloc_continue;
+
+ case R_MIPS_GNU_VTINHERIT:
+ case R_MIPS_GNU_VTENTRY:
+ /* We don't do anything with these at present. */
+ return bfd_reloc_continue;
+
+ default:
+ /* An unrecognized relocation type. */
+ return bfd_reloc_notsupported;
+ }
+
+ /* Store the VALUE for our caller. */
+ *valuep = value;
+ return overflowed_p ? bfd_reloc_overflow : bfd_reloc_ok;
+}
+
+/* Obtain the field relocated by RELOCATION. */
+
+static bfd_vma
+mips_elf_obtain_contents (reloc_howto_type *howto,
+ const Elf_Internal_Rela *relocation,
+ bfd *input_bfd, bfd_byte *contents)
+{
+ bfd_vma x;
+ bfd_byte *location = contents + relocation->r_offset;
+
+ /* Obtain the bytes. */
+ x = bfd_get ((8 * bfd_get_reloc_size (howto)), input_bfd, location);
+
+ if ((ELF_R_TYPE (input_bfd, relocation->r_info) == R_MIPS16_26
+ || ELF_R_TYPE (input_bfd, relocation->r_info) == R_MIPS16_GPREL)
+ && bfd_little_endian (input_bfd))
+ /* The two 16-bit words will be reversed on a little-endian system.
+ See mips_elf_perform_relocation for more details. */
+ x = (((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16));
+
+ return x;
+}
+
+/* It has been determined that the result of the RELOCATION is the
+ VALUE. Use HOWTO to place VALUE into the output file at the
+ appropriate position. The SECTION is the section to which the
+ relocation applies. If REQUIRE_JALX is TRUE, then the opcode used
+ for the relocation must be either JAL or JALX, and it is
+ unconditionally converted to JALX.
+
+ Returns FALSE if anything goes wrong. */
+
+static bfd_boolean
+mips_elf_perform_relocation (struct bfd_link_info *info,
+ reloc_howto_type *howto,
+ const Elf_Internal_Rela *relocation,
+ bfd_vma value, bfd *input_bfd,
+ asection *input_section, bfd_byte *contents,
+ bfd_boolean require_jalx)
+{
+ bfd_vma x;
+ bfd_byte *location;
+ int r_type = ELF_R_TYPE (input_bfd, relocation->r_info);
+
+ /* Figure out where the relocation is occurring. */
+ location = contents + relocation->r_offset;
+
+ /* Obtain the current value. */
+ x = mips_elf_obtain_contents (howto, relocation, input_bfd, contents);
+
+ /* Clear the field we are setting. */
+ x &= ~howto->dst_mask;
+
+ /* If this is the R_MIPS16_26 relocation, we must store the
+ value in a funny way. */
+ if (r_type == R_MIPS16_26)
+ {
+ /* R_MIPS16_26 is used for the mips16 jal and jalx instructions.
+ Most mips16 instructions are 16 bits, but these instructions
+ are 32 bits.
+
+ The format of these instructions is:
+
+ +--------------+--------------------------------+
+ ! JALX ! X! Imm 20:16 ! Imm 25:21 !
+ +--------------+--------------------------------+
+ ! Immediate 15:0 !
+ +-----------------------------------------------+
+
+ JALX is the 5-bit value 00011. X is 0 for jal, 1 for jalx.
+ Note that the immediate value in the first word is swapped.
+
+ When producing a relocatable object file, R_MIPS16_26 is
+ handled mostly like R_MIPS_26. In particular, the addend is
+ stored as a straight 26-bit value in a 32-bit instruction.
+ (gas makes life simpler for itself by never adjusting a
+ R_MIPS16_26 reloc to be against a section, so the addend is
+ always zero). However, the 32 bit instruction is stored as 2
+ 16-bit values, rather than a single 32-bit value. In a
+ big-endian file, the result is the same; in a little-endian
+ file, the two 16-bit halves of the 32 bit value are swapped.
+ This is so that a disassembler can recognize the jal
+ instruction.
+
+ When doing a final link, R_MIPS16_26 is treated as a 32 bit
+ instruction stored as two 16-bit values. The addend A is the
+ contents of the targ26 field. The calculation is the same as
+ R_MIPS_26. When storing the calculated value, reorder the
+ immediate value as shown above, and don't forget to store the
+ value as two 16-bit values.
+
+ To put it in MIPS ABI terms, the relocation field is T-targ26-16,
+ defined as
+
+ big-endian:
+ +--------+----------------------+
+ | | |
+ | | targ26-16 |
+ |31 26|25 0|
+ +--------+----------------------+
+
+ little-endian:
+ +----------+------+-------------+
+ | | | |
+ | sub1 | | sub2 |
+ |0 9|10 15|16 31|
+ +----------+--------------------+
+ where targ26-16 is sub1 followed by sub2 (i.e., the addend field A is
+ ((sub1 << 16) | sub2)).
+
+ When producing a relocatable object file, the calculation is
+ (((A < 2) | ((P + 4) & 0xf0000000) + S) >> 2)
+ When producing a fully linked file, the calculation is
+ let R = (((A < 2) | ((P + 4) & 0xf0000000) + S) >> 2)
+ ((R & 0x1f0000) << 5) | ((R & 0x3e00000) >> 5) | (R & 0xffff) */
+
+ if (!info->relocatable)
+ /* Shuffle the bits according to the formula above. */
+ value = (((value & 0x1f0000) << 5)
+ | ((value & 0x3e00000) >> 5)
+ | (value & 0xffff));
+ }
+ else if (r_type == R_MIPS16_GPREL)
+ {
+ /* R_MIPS16_GPREL is used for GP-relative addressing in mips16
+ mode. A typical instruction will have a format like this:
+
+ +--------------+--------------------------------+
+ ! EXTEND ! Imm 10:5 ! Imm 15:11 !
+ +--------------+--------------------------------+
+ ! Major ! rx ! ry ! Imm 4:0 !
+ +--------------+--------------------------------+
+
+ EXTEND is the five bit value 11110. Major is the instruction
+ opcode.
+
+ This is handled exactly like R_MIPS_GPREL16, except that the
+ addend is retrieved and stored as shown in this diagram; that
+ is, the Imm fields above replace the V-rel16 field.
+
+ All we need to do here is shuffle the bits appropriately. As
+ above, the two 16-bit halves must be swapped on a
+ little-endian system. */
+ value = (((value & 0x7e0) << 16)
+ | ((value & 0xf800) << 5)
+ | (value & 0x1f));
+ }
+
+ /* Set the field. */
+ x |= (value & howto->dst_mask);
+
+ /* If required, turn JAL into JALX. */
+ if (require_jalx)
+ {
+ bfd_boolean ok;
+ bfd_vma opcode = x >> 26;
+ bfd_vma jalx_opcode;
+
+ /* Check to see if the opcode is already JAL or JALX. */
+ if (r_type == R_MIPS16_26)
+ {
+ ok = ((opcode == 0x6) || (opcode == 0x7));
+ jalx_opcode = 0x7;
+ }
+ else
+ {
+ ok = ((opcode == 0x3) || (opcode == 0x1d));
+ jalx_opcode = 0x1d;
+ }
+
+ /* If the opcode is not JAL or JALX, there's a problem. */
+ if (!ok)
+ {
+ (*_bfd_error_handler)
+ (_("%s: %s+0x%lx: jump to stub routine which is not jal"),
+ bfd_archive_filename (input_bfd),
+ input_section->name,
+ (unsigned long) relocation->r_offset);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+
+ /* Make this the JALX opcode. */
+ x = (x & ~(0x3f << 26)) | (jalx_opcode << 26);
+ }
+
+ /* Swap the high- and low-order 16 bits on little-endian systems
+ when doing a MIPS16 relocation. */
+ if ((r_type == R_MIPS16_GPREL || r_type == R_MIPS16_26)
+ && bfd_little_endian (input_bfd))
+ x = (((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16));
+
+ /* Put the value into the output. */
+ bfd_put (8 * bfd_get_reloc_size (howto), input_bfd, x, location);
+ return TRUE;
+}
+
+/* Returns TRUE if SECTION is a MIPS16 stub section. */
+
+static bfd_boolean
+mips_elf_stub_section_p (bfd *abfd ATTRIBUTE_UNUSED, asection *section)
+{
+ const char *name = bfd_get_section_name (abfd, section);
+
+ return (strncmp (name, FN_STUB, sizeof FN_STUB - 1) == 0
+ || strncmp (name, CALL_STUB, sizeof CALL_STUB - 1) == 0
+ || strncmp (name, CALL_FP_STUB, sizeof CALL_FP_STUB - 1) == 0);
+}
+
+/* Add room for N relocations to the .rel.dyn section in ABFD. */
+
+static void
+mips_elf_allocate_dynamic_relocations (bfd *abfd, unsigned int n)
+{
+ asection *s;
+
+ s = mips_elf_rel_dyn_section (abfd, FALSE);
+ BFD_ASSERT (s != NULL);
+
+ if (s->_raw_size == 0)
+ {
+ /* Make room for a null element. */
+ s->_raw_size += MIPS_ELF_REL_SIZE (abfd);
+ ++s->reloc_count;
+ }
+ s->_raw_size += n * MIPS_ELF_REL_SIZE (abfd);
+}
+
+/* Create a rel.dyn relocation for the dynamic linker to resolve. REL
+ is the original relocation, which is now being transformed into a
+ dynamic relocation. The ADDENDP is adjusted if necessary; the
+ caller should store the result in place of the original addend. */
+
+static bfd_boolean
+mips_elf_create_dynamic_relocation (bfd *output_bfd,
+ struct bfd_link_info *info,
+ const Elf_Internal_Rela *rel,
+ struct mips_elf_link_hash_entry *h,
+ asection *sec, bfd_vma symbol,
+ bfd_vma *addendp, asection *input_section)
+{
+ Elf_Internal_Rela outrel[3];
+ bfd_boolean skip;
+ asection *sreloc;
+ bfd *dynobj;
+ int r_type;
+
+ r_type = ELF_R_TYPE (output_bfd, rel->r_info);
+ dynobj = elf_hash_table (info)->dynobj;
+ sreloc = mips_elf_rel_dyn_section (dynobj, FALSE);
+ BFD_ASSERT (sreloc != NULL);
+ BFD_ASSERT (sreloc->contents != NULL);
+ BFD_ASSERT (sreloc->reloc_count * MIPS_ELF_REL_SIZE (output_bfd)
+ < sreloc->_raw_size);
+
+ skip = FALSE;
+ outrel[0].r_offset =
+ _bfd_elf_section_offset (output_bfd, info, input_section, rel[0].r_offset);
+ outrel[1].r_offset =
+ _bfd_elf_section_offset (output_bfd, info, input_section, rel[1].r_offset);
+ outrel[2].r_offset =
+ _bfd_elf_section_offset (output_bfd, info, input_section, rel[2].r_offset);
+
+#if 0
+ /* We begin by assuming that the offset for the dynamic relocation
+ is the same as for the original relocation. We'll adjust this
+ later to reflect the correct output offsets. */
+ if (input_section->sec_info_type != ELF_INFO_TYPE_STABS)
+ {
+ outrel[1].r_offset = rel[1].r_offset;
+ outrel[2].r_offset = rel[2].r_offset;
+ }
+ else
+ {
+ /* Except that in a stab section things are more complex.
+ Because we compress stab information, the offset given in the
+ relocation may not be the one we want; we must let the stabs
+ machinery tell us the offset. */
+ outrel[1].r_offset = outrel[0].r_offset;
+ outrel[2].r_offset = outrel[0].r_offset;
+ /* If we didn't need the relocation at all, this value will be
+ -1. */
+ if (outrel[0].r_offset == (bfd_vma) -1)
+ skip = TRUE;
+ }
+#endif
+
+ if (outrel[0].r_offset == (bfd_vma) -1)
+ /* The relocation field has been deleted. */
+ skip = TRUE;
+ else if (outrel[0].r_offset == (bfd_vma) -2)
+ {
+ /* The relocation field has been converted into a relative value of
+ some sort. Functions like _bfd_elf_write_section_eh_frame expect
+ the field to be fully relocated, so add in the symbol's value. */
+ skip = TRUE;
+ *addendp += symbol;
+ }
+
+ /* If we've decided to skip this relocation, just output an empty
+ record. Note that R_MIPS_NONE == 0, so that this call to memset
+ is a way of setting R_TYPE to R_MIPS_NONE. */
+ if (skip)
+ memset (outrel, 0, sizeof (Elf_Internal_Rela) * 3);
+ else
+ {
+ long indx;
+ bfd_boolean defined_p;
+
+ /* We must now calculate the dynamic symbol table index to use
+ in the relocation. */
+ if (h != NULL
+ && (! info->symbolic || (h->root.elf_link_hash_flags
+ & ELF_LINK_HASH_DEF_REGULAR) == 0)
+ /* h->root.dynindx may be -1 if this symbol was marked to
+ become local. */
+ && h->root.dynindx != -1)
+ {
+ indx = h->root.dynindx;
+ if (SGI_COMPAT (output_bfd))
+ defined_p = ((h->root.elf_link_hash_flags
+ & ELF_LINK_HASH_DEF_REGULAR) != 0);
+ else
+ /* ??? glibc's ld.so just adds the final GOT entry to the
+ relocation field. It therefore treats relocs against
+ defined symbols in the same way as relocs against
+ undefined symbols. */
+ defined_p = FALSE;
+ }
+ else
+ {
+ if (sec != NULL && bfd_is_abs_section (sec))
+ indx = 0;
+ else if (sec == NULL || sec->owner == NULL)
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ else
+ {
+ indx = elf_section_data (sec->output_section)->dynindx;
+ if (indx == 0)
+ abort ();
+ }
+
+ /* Instead of generating a relocation using the section
+ symbol, we may as well make it a fully relative
+ relocation. We want to avoid generating relocations to
+ local symbols because we used to generate them
+ incorrectly, without adding the original symbol value,
+ which is mandated by the ABI for section symbols. In
+ order to give dynamic loaders and applications time to
+ phase out the incorrect use, we refrain from emitting
+ section-relative relocations. It's not like they're
+ useful, after all. This should be a bit more efficient
+ as well. */
+ /* ??? Although this behavior is compatible with glibc's ld.so,
+ the ABI says that relocations against STN_UNDEF should have
+ a symbol value of 0. Irix rld honors this, so relocations
+ against STN_UNDEF have no effect. */
+ if (!SGI_COMPAT (output_bfd))
+ indx = 0;
+ defined_p = TRUE;
+ }
+
+ /* If the relocation was previously an absolute relocation and
+ this symbol will not be referred to by the relocation, we must
+ adjust it by the value we give it in the dynamic symbol table.
+ Otherwise leave the job up to the dynamic linker. */
+ if (defined_p && r_type != R_MIPS_REL32)
+ *addendp += symbol;
+
+ /* The relocation is always an REL32 relocation because we don't
+ know where the shared library will wind up at load-time. */
+ outrel[0].r_info = ELF_R_INFO (output_bfd, (unsigned long) indx,
+ R_MIPS_REL32);
+ /* For strict adherence to the ABI specification, we should
+ generate a R_MIPS_64 relocation record by itself before the
+ _REL32/_64 record as well, such that the addend is read in as
+ a 64-bit value (REL32 is a 32-bit relocation, after all).
+ However, since none of the existing ELF64 MIPS dynamic
+ loaders seems to care, we don't waste space with these
+ artificial relocations. If this turns out to not be true,
+ mips_elf_allocate_dynamic_relocation() should be tweaked so
+ as to make room for a pair of dynamic relocations per
+ invocation if ABI_64_P, and here we should generate an
+ additional relocation record with R_MIPS_64 by itself for a
+ NULL symbol before this relocation record. */
+ outrel[1].r_info = ELF_R_INFO (output_bfd, 0,
+ ABI_64_P (output_bfd)
+ ? R_MIPS_64
+ : R_MIPS_NONE);
+ outrel[2].r_info = ELF_R_INFO (output_bfd, 0, R_MIPS_NONE);
+
+ /* Adjust the output offset of the relocation to reference the
+ correct location in the output file. */
+ outrel[0].r_offset += (input_section->output_section->vma
+ + input_section->output_offset);
+ outrel[1].r_offset += (input_section->output_section->vma
+ + input_section->output_offset);
+ outrel[2].r_offset += (input_section->output_section->vma
+ + input_section->output_offset);
+ }
+
+ /* Put the relocation back out. We have to use the special
+ relocation outputter in the 64-bit case since the 64-bit
+ relocation format is non-standard. */
+ if (ABI_64_P (output_bfd))
+ {
+ (*get_elf_backend_data (output_bfd)->s->swap_reloc_out)
+ (output_bfd, &outrel[0],
+ (sreloc->contents
+ + sreloc->reloc_count * sizeof (Elf64_Mips_External_Rel)));
+ }
+ else
+ bfd_elf32_swap_reloc_out
+ (output_bfd, &outrel[0],
+ (sreloc->contents + sreloc->reloc_count * sizeof (Elf32_External_Rel)));
+
+ /* We've now added another relocation. */
+ ++sreloc->reloc_count;
+
+ /* Make sure the output section is writable. The dynamic linker
+ will be writing to it. */
+ elf_section_data (input_section->output_section)->this_hdr.sh_flags
+ |= SHF_WRITE;
+
+ /* On IRIX5, make an entry of compact relocation info. */
+ if (! skip && IRIX_COMPAT (output_bfd) == ict_irix5)
+ {
+ asection *scpt = bfd_get_section_by_name (dynobj, ".compact_rel");
+ bfd_byte *cr;
+
+ if (scpt)
+ {
+ Elf32_crinfo cptrel;
+
+ mips_elf_set_cr_format (cptrel, CRF_MIPS_LONG);
+ cptrel.vaddr = (rel->r_offset
+ + input_section->output_section->vma
+ + input_section->output_offset);
+ if (r_type == R_MIPS_REL32)
+ mips_elf_set_cr_type (cptrel, CRT_MIPS_REL32);
+ else
+ mips_elf_set_cr_type (cptrel, CRT_MIPS_WORD);
+ mips_elf_set_cr_dist2to (cptrel, 0);
+ cptrel.konst = *addendp;
+
+ cr = (scpt->contents
+ + sizeof (Elf32_External_compact_rel));
+ bfd_elf32_swap_crinfo_out (output_bfd, &cptrel,
+ ((Elf32_External_crinfo *) cr
+ + scpt->reloc_count));
+ ++scpt->reloc_count;
+ }
+ }
+
+ return TRUE;
+}
+
+/* Return the MACH for a MIPS e_flags value. */
+
+unsigned long
+_bfd_elf_mips_mach (flagword flags)
+{
+ switch (flags & EF_MIPS_MACH)
+ {
+ case E_MIPS_MACH_3900:
+ return bfd_mach_mips3900;
+
+ case E_MIPS_MACH_4010:
+ return bfd_mach_mips4010;
+
+ case E_MIPS_MACH_4100:
+ return bfd_mach_mips4100;
+
+ case E_MIPS_MACH_4111:
+ return bfd_mach_mips4111;
+
+ case E_MIPS_MACH_4120:
+ return bfd_mach_mips4120;
+
+ case E_MIPS_MACH_4650:
+ return bfd_mach_mips4650;
+
+ case E_MIPS_MACH_5400:
+ return bfd_mach_mips5400;
+
+ case E_MIPS_MACH_5500:
+ return bfd_mach_mips5500;
+
+ case E_MIPS_MACH_SB1:
+ return bfd_mach_mips_sb1;
+
+ default:
+ switch (flags & EF_MIPS_ARCH)
+ {
+ default:
+ case E_MIPS_ARCH_1:
+ return bfd_mach_mips3000;
+ break;
+
+ case E_MIPS_ARCH_2:
+ return bfd_mach_mips6000;
+ break;
+
+ case E_MIPS_ARCH_3:
+ return bfd_mach_mips4000;
+ break;
+
+ case E_MIPS_ARCH_4:
+ return bfd_mach_mips8000;
+ break;
+
+ case E_MIPS_ARCH_5:
+ return bfd_mach_mips5;
+ break;
+
+ case E_MIPS_ARCH_32:
+ return bfd_mach_mipsisa32;
+ break;
+
+ case E_MIPS_ARCH_64:
+ return bfd_mach_mipsisa64;
+ break;
+
+ case E_MIPS_ARCH_32R2:
+ return bfd_mach_mipsisa32r2;
+ break;
+
+ case E_MIPS_ARCH_64R2:
+ return bfd_mach_mipsisa64r2;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/* Return printable name for ABI. */
+
+static INLINE char *
+elf_mips_abi_name (bfd *abfd)
+{
+ flagword flags;
+
+ flags = elf_elfheader (abfd)->e_flags;
+ switch (flags & EF_MIPS_ABI)
+ {
+ case 0:
+ if (ABI_N32_P (abfd))
+ return "N32";
+ else if (ABI_64_P (abfd))
+ return "64";
+ else
+ return "none";
+ case E_MIPS_ABI_O32:
+ return "O32";
+ case E_MIPS_ABI_O64:
+ return "O64";
+ case E_MIPS_ABI_EABI32:
+ return "EABI32";
+ case E_MIPS_ABI_EABI64:
+ return "EABI64";
+ default:
+ return "unknown abi";
+ }
+}
+
+/* MIPS ELF uses two common sections. One is the usual one, and the
+ other is for small objects. All the small objects are kept
+ together, and then referenced via the gp pointer, which yields
+ faster assembler code. This is what we use for the small common
+ section. This approach is copied from ecoff.c. */
+static asection mips_elf_scom_section;
+static asymbol mips_elf_scom_symbol;
+static asymbol *mips_elf_scom_symbol_ptr;
+
+/* MIPS ELF also uses an acommon section, which represents an
+ allocated common symbol which may be overridden by a
+ definition in a shared library. */
+static asection mips_elf_acom_section;
+static asymbol mips_elf_acom_symbol;
+static asymbol *mips_elf_acom_symbol_ptr;
+
+/* Handle the special MIPS section numbers that a symbol may use.
+ This is used for both the 32-bit and the 64-bit ABI. */
+
+void
+_bfd_mips_elf_symbol_processing (bfd *abfd, asymbol *asym)
+{
+ elf_symbol_type *elfsym;
+
+ elfsym = (elf_symbol_type *) asym;
+ switch (elfsym->internal_elf_sym.st_shndx)
+ {
+ case SHN_MIPS_ACOMMON:
+ /* This section is used in a dynamically linked executable file.
+ It is an allocated common section. The dynamic linker can
+ either resolve these symbols to something in a shared
+ library, or it can just leave them here. For our purposes,
+ we can consider these symbols to be in a new section. */
+ if (mips_elf_acom_section.name == NULL)
+ {
+ /* Initialize the acommon section. */
+ mips_elf_acom_section.name = ".acommon";
+ mips_elf_acom_section.flags = SEC_ALLOC;
+ mips_elf_acom_section.output_section = &mips_elf_acom_section;
+ mips_elf_acom_section.symbol = &mips_elf_acom_symbol;
+ mips_elf_acom_section.symbol_ptr_ptr = &mips_elf_acom_symbol_ptr;
+ mips_elf_acom_symbol.name = ".acommon";
+ mips_elf_acom_symbol.flags = BSF_SECTION_SYM;
+ mips_elf_acom_symbol.section = &mips_elf_acom_section;
+ mips_elf_acom_symbol_ptr = &mips_elf_acom_symbol;
+ }
+ asym->section = &mips_elf_acom_section;
+ break;
+
+ case SHN_COMMON:
+ /* Common symbols less than the GP size are automatically
+ treated as SHN_MIPS_SCOMMON symbols on IRIX5. */
+ if (asym->value > elf_gp_size (abfd)
+ || IRIX_COMPAT (abfd) == ict_irix6)
+ break;
+ /* Fall through. */
+ case SHN_MIPS_SCOMMON:
+ if (mips_elf_scom_section.name == NULL)
+ {
+ /* Initialize the small common section. */
+ mips_elf_scom_section.name = ".scommon";
+ mips_elf_scom_section.flags = SEC_IS_COMMON;
+ mips_elf_scom_section.output_section = &mips_elf_scom_section;
+ mips_elf_scom_section.symbol = &mips_elf_scom_symbol;
+ mips_elf_scom_section.symbol_ptr_ptr = &mips_elf_scom_symbol_ptr;
+ mips_elf_scom_symbol.name = ".scommon";
+ mips_elf_scom_symbol.flags = BSF_SECTION_SYM;
+ mips_elf_scom_symbol.section = &mips_elf_scom_section;
+ mips_elf_scom_symbol_ptr = &mips_elf_scom_symbol;
+ }
+ asym->section = &mips_elf_scom_section;
+ asym->value = elfsym->internal_elf_sym.st_size;
+ break;
+
+ case SHN_MIPS_SUNDEFINED:
+ asym->section = bfd_und_section_ptr;
+ break;
+
+#if 0 /* for SGI_COMPAT */
+ case SHN_MIPS_TEXT:
+ asym->section = mips_elf_text_section_ptr;
+ break;
+
+ case SHN_MIPS_DATA:
+ asym->section = mips_elf_data_section_ptr;
+ break;
+#endif
+ }
+}
+
+/* There appears to be a bug in the MIPSpro linker that causes GOT_DISP
+ relocations against two unnamed section symbols to resolve to the
+ same address. For example, if we have code like:
+
+ lw $4,%got_disp(.data)($gp)
+ lw $25,%got_disp(.text)($gp)
+ jalr $25
+
+ then the linker will resolve both relocations to .data and the program
+ will jump there rather than to .text.
+
+ We can work around this problem by giving names to local section symbols.
+ This is also what the MIPSpro tools do. */
+
+bfd_boolean
+_bfd_mips_elf_name_local_section_symbols (bfd *abfd)
+{
+ return SGI_COMPAT (abfd);
+}
+
+/* Work over a section just before writing it out. This routine is
+ used by both the 32-bit and the 64-bit ABI. FIXME: We recognize
+ sections that need the SHF_MIPS_GPREL flag by name; there has to be
+ a better way. */
+
+bfd_boolean
+_bfd_mips_elf_section_processing (bfd *abfd, Elf_Internal_Shdr *hdr)
+{
+ if (hdr->sh_type == SHT_MIPS_REGINFO
+ && hdr->sh_size > 0)
+ {
+ bfd_byte buf[4];
+
+ BFD_ASSERT (hdr->sh_size == sizeof (Elf32_External_RegInfo));
+ BFD_ASSERT (hdr->contents == NULL);
+
+ if (bfd_seek (abfd,
+ hdr->sh_offset + sizeof (Elf32_External_RegInfo) - 4,
+ SEEK_SET) != 0)
+ return FALSE;
+ H_PUT_32 (abfd, elf_gp (abfd), buf);
+ if (bfd_bwrite (buf, 4, abfd) != 4)
+ return FALSE;
+ }
+
+ if (hdr->sh_type == SHT_MIPS_OPTIONS
+ && hdr->bfd_section != NULL
+ && mips_elf_section_data (hdr->bfd_section) != NULL
+ && mips_elf_section_data (hdr->bfd_section)->u.tdata != NULL)
+ {
+ bfd_byte *contents, *l, *lend;
+
+ /* We stored the section contents in the tdata field in the
+ set_section_contents routine. We save the section contents
+ so that we don't have to read them again.
+ At this point we know that elf_gp is set, so we can look
+ through the section contents to see if there is an
+ ODK_REGINFO structure. */
+
+ contents = mips_elf_section_data (hdr->bfd_section)->u.tdata;
+ l = contents;
+ lend = contents + hdr->sh_size;
+ while (l + sizeof (Elf_External_Options) <= lend)
+ {
+ Elf_Internal_Options intopt;
+
+ bfd_mips_elf_swap_options_in (abfd, (Elf_External_Options *) l,
+ &intopt);
+ if (ABI_64_P (abfd) && intopt.kind == ODK_REGINFO)
+ {
+ bfd_byte buf[8];
+
+ if (bfd_seek (abfd,
+ (hdr->sh_offset
+ + (l - contents)
+ + sizeof (Elf_External_Options)
+ + (sizeof (Elf64_External_RegInfo) - 8)),
+ SEEK_SET) != 0)
+ return FALSE;
+ H_PUT_64 (abfd, elf_gp (abfd), buf);
+ if (bfd_bwrite (buf, 8, abfd) != 8)
+ return FALSE;
+ }
+ else if (intopt.kind == ODK_REGINFO)
+ {
+ bfd_byte buf[4];
+
+ if (bfd_seek (abfd,
+ (hdr->sh_offset
+ + (l - contents)
+ + sizeof (Elf_External_Options)
+ + (sizeof (Elf32_External_RegInfo) - 4)),
+ SEEK_SET) != 0)
+ return FALSE;
+ H_PUT_32 (abfd, elf_gp (abfd), buf);
+ if (bfd_bwrite (buf, 4, abfd) != 4)
+ return FALSE;
+ }
+ l += intopt.size;
+ }
+ }
+
+ if (hdr->bfd_section != NULL)
+ {
+ const char *name = bfd_get_section_name (abfd, hdr->bfd_section);
+
+ if (strcmp (name, ".sdata") == 0
+ || strcmp (name, ".lit8") == 0
+ || strcmp (name, ".lit4") == 0)
+ {
+ hdr->sh_flags |= SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL;
+ hdr->sh_type = SHT_PROGBITS;
+ }
+ else if (strcmp (name, ".sbss") == 0)
+ {
+ hdr->sh_flags |= SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL;
+ hdr->sh_type = SHT_NOBITS;
+ }
+ else if (strcmp (name, ".srdata") == 0)
+ {
+ hdr->sh_flags |= SHF_ALLOC | SHF_MIPS_GPREL;
+ hdr->sh_type = SHT_PROGBITS;
+ }
+ else if (strcmp (name, ".compact_rel") == 0)
+ {
+ hdr->sh_flags = 0;
+ hdr->sh_type = SHT_PROGBITS;
+ }
+ else if (strcmp (name, ".rtproc") == 0)
+ {
+ if (hdr->sh_addralign != 0 && hdr->sh_entsize == 0)
+ {
+ unsigned int adjust;
+
+ adjust = hdr->sh_size % hdr->sh_addralign;
+ if (adjust != 0)
+ hdr->sh_size += hdr->sh_addralign - adjust;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+/* Handle a MIPS specific section when reading an object file. This
+ is called when elfcode.h finds a section with an unknown type.
+ This routine supports both the 32-bit and 64-bit ELF ABI.
+
+ FIXME: We need to handle the SHF_MIPS_GPREL flag, but I'm not sure
+ how to. */
+
+bfd_boolean
+_bfd_mips_elf_section_from_shdr (bfd *abfd, Elf_Internal_Shdr *hdr,
+ const char *name)
+{
+ flagword flags = 0;
+
+ /* There ought to be a place to keep ELF backend specific flags, but
+ at the moment there isn't one. We just keep track of the
+ sections by their name, instead. Fortunately, the ABI gives
+ suggested names for all the MIPS specific sections, so we will
+ probably get away with this. */
+ switch (hdr->sh_type)
+ {
+ case SHT_MIPS_LIBLIST:
+ if (strcmp (name, ".liblist") != 0)
+ return FALSE;
+ break;
+ case SHT_MIPS_MSYM:
+ if (strcmp (name, ".msym") != 0)
+ return FALSE;
+ break;
+ case SHT_MIPS_CONFLICT:
+ if (strcmp (name, ".conflict") != 0)
+ return FALSE;
+ break;
+ case SHT_MIPS_GPTAB:
+ if (strncmp (name, ".gptab.", sizeof ".gptab." - 1) != 0)
+ return FALSE;
+ break;
+ case SHT_MIPS_UCODE:
+ if (strcmp (name, ".ucode") != 0)
+ return FALSE;
+ break;
+ case SHT_MIPS_DEBUG:
+ if (strcmp (name, ".mdebug") != 0)
+ return FALSE;
+ flags = SEC_DEBUGGING;
+ break;
+ case SHT_MIPS_REGINFO:
+ if (strcmp (name, ".reginfo") != 0
+ || hdr->sh_size != sizeof (Elf32_External_RegInfo))
+ return FALSE;
+ flags = (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_SAME_SIZE);
+ break;
+ case SHT_MIPS_IFACE:
+ if (strcmp (name, ".MIPS.interfaces") != 0)
+ return FALSE;
+ break;
+ case SHT_MIPS_CONTENT:
+ if (strncmp (name, ".MIPS.content", sizeof ".MIPS.content" - 1) != 0)
+ return FALSE;
+ break;
+ case SHT_MIPS_OPTIONS:
+ if (strcmp (name, MIPS_ELF_OPTIONS_SECTION_NAME (abfd)) != 0)
+ return FALSE;
+ break;
+ case SHT_MIPS_DWARF:
+ if (strncmp (name, ".debug_", sizeof ".debug_" - 1) != 0)
+ return FALSE;
+ break;
+ case SHT_MIPS_SYMBOL_LIB:
+ if (strcmp (name, ".MIPS.symlib") != 0)
+ return FALSE;
+ break;
+ case SHT_MIPS_EVENTS:
+ if (strncmp (name, ".MIPS.events", sizeof ".MIPS.events" - 1) != 0
+ && strncmp (name, ".MIPS.post_rel",
+ sizeof ".MIPS.post_rel" - 1) != 0)
+ return FALSE;
+ break;
+ default:
+ return FALSE;
+ }
+
+ if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name))
+ return FALSE;
+
+ if (flags)
+ {
+ if (! bfd_set_section_flags (abfd, hdr->bfd_section,
+ (bfd_get_section_flags (abfd,
+ hdr->bfd_section)
+ | flags)))
+ return FALSE;
+ }
+
+ /* FIXME: We should record sh_info for a .gptab section. */
+
+ /* For a .reginfo section, set the gp value in the tdata information
+ from the contents of this section. We need the gp value while
+ processing relocs, so we just get it now. The .reginfo section
+ is not used in the 64-bit MIPS ELF ABI. */
+ if (hdr->sh_type == SHT_MIPS_REGINFO)
+ {
+ Elf32_External_RegInfo ext;
+ Elf32_RegInfo s;
+
+ if (! bfd_get_section_contents (abfd, hdr->bfd_section,
+ &ext, 0, sizeof ext))
+ return FALSE;
+ bfd_mips_elf32_swap_reginfo_in (abfd, &ext, &s);
+ elf_gp (abfd) = s.ri_gp_value;
+ }
+
+ /* For a SHT_MIPS_OPTIONS section, look for a ODK_REGINFO entry, and
+ set the gp value based on what we find. We may see both
+ SHT_MIPS_REGINFO and SHT_MIPS_OPTIONS/ODK_REGINFO; in that case,
+ they should agree. */
+ if (hdr->sh_type == SHT_MIPS_OPTIONS)
+ {
+ bfd_byte *contents, *l, *lend;
+
+ contents = bfd_malloc (hdr->sh_size);
+ if (contents == NULL)
+ return FALSE;
+ if (! bfd_get_section_contents (abfd, hdr->bfd_section, contents,
+ 0, hdr->sh_size))
+ {
+ free (contents);
+ return FALSE;
+ }
+ l = contents;
+ lend = contents + hdr->sh_size;
+ while (l + sizeof (Elf_External_Options) <= lend)
+ {
+ Elf_Internal_Options intopt;
+
+ bfd_mips_elf_swap_options_in (abfd, (Elf_External_Options *) l,
+ &intopt);
+ if (ABI_64_P (abfd) && intopt.kind == ODK_REGINFO)
+ {
+ Elf64_Internal_RegInfo intreg;
+
+ bfd_mips_elf64_swap_reginfo_in
+ (abfd,
+ ((Elf64_External_RegInfo *)
+ (l + sizeof (Elf_External_Options))),
+ &intreg);
+ elf_gp (abfd) = intreg.ri_gp_value;
+ }
+ else if (intopt.kind == ODK_REGINFO)
+ {
+ Elf32_RegInfo intreg;
+
+ bfd_mips_elf32_swap_reginfo_in
+ (abfd,
+ ((Elf32_External_RegInfo *)
+ (l + sizeof (Elf_External_Options))),
+ &intreg);
+ elf_gp (abfd) = intreg.ri_gp_value;
+ }
+ l += intopt.size;
+ }
+ free (contents);
+ }
+
+ return TRUE;
+}
+
+/* Set the correct type for a MIPS ELF section. We do this by the
+ section name, which is a hack, but ought to work. This routine is
+ used by both the 32-bit and the 64-bit ABI. */
+
+bfd_boolean
+_bfd_mips_elf_fake_sections (bfd *abfd, Elf_Internal_Shdr *hdr, asection *sec)
+{
+ register const char *name;
+
+ name = bfd_get_section_name (abfd, sec);
+
+ if (strcmp (name, ".liblist") == 0)
+ {
+ hdr->sh_type = SHT_MIPS_LIBLIST;
+ hdr->sh_info = sec->_raw_size / sizeof (Elf32_Lib);
+ /* The sh_link field is set in final_write_processing. */
+ }
+ else if (strcmp (name, ".conflict") == 0)
+ hdr->sh_type = SHT_MIPS_CONFLICT;
+ else if (strncmp (name, ".gptab.", sizeof ".gptab." - 1) == 0)
+ {
+ hdr->sh_type = SHT_MIPS_GPTAB;
+ hdr->sh_entsize = sizeof (Elf32_External_gptab);
+ /* The sh_info field is set in final_write_processing. */
+ }
+ else if (strcmp (name, ".ucode") == 0)
+ hdr->sh_type = SHT_MIPS_UCODE;
+ else if (strcmp (name, ".mdebug") == 0)
+ {
+ hdr->sh_type = SHT_MIPS_DEBUG;
+ /* In a shared object on IRIX 5.3, the .mdebug section has an
+ entsize of 0. FIXME: Does this matter? */
+ if (SGI_COMPAT (abfd) && (abfd->flags & DYNAMIC) != 0)
+ hdr->sh_entsize = 0;
+ else
+ hdr->sh_entsize = 1;
+ }
+ else if (strcmp (name, ".reginfo") == 0)
+ {
+ hdr->sh_type = SHT_MIPS_REGINFO;
+ /* In a shared object on IRIX 5.3, the .reginfo section has an
+ entsize of 0x18. FIXME: Does this matter? */
+ if (SGI_COMPAT (abfd))
+ {
+ if ((abfd->flags & DYNAMIC) != 0)
+ hdr->sh_entsize = sizeof (Elf32_External_RegInfo);
+ else
+ hdr->sh_entsize = 1;
+ }
+ else
+ hdr->sh_entsize = sizeof (Elf32_External_RegInfo);
+ }
+ else if (SGI_COMPAT (abfd)
+ && (strcmp (name, ".hash") == 0
+ || strcmp (name, ".dynamic") == 0
+ || strcmp (name, ".dynstr") == 0))
+ {
+ if (SGI_COMPAT (abfd))
+ hdr->sh_entsize = 0;
+#if 0
+ /* This isn't how the IRIX6 linker behaves. */
+ hdr->sh_info = SIZEOF_MIPS_DYNSYM_SECNAMES;
+#endif
+ }
+ else if (strcmp (name, ".got") == 0
+ || strcmp (name, ".srdata") == 0
+ || strcmp (name, ".sdata") == 0
+ || strcmp (name, ".sbss") == 0
+ || strcmp (name, ".lit4") == 0
+ || strcmp (name, ".lit8") == 0)
+ hdr->sh_flags |= SHF_MIPS_GPREL;
+ else if (strcmp (name, ".MIPS.interfaces") == 0)
+ {
+ hdr->sh_type = SHT_MIPS_IFACE;
+ hdr->sh_flags |= SHF_MIPS_NOSTRIP;
+ }
+ else if (strncmp (name, ".MIPS.content", strlen (".MIPS.content")) == 0)
+ {
+ hdr->sh_type = SHT_MIPS_CONTENT;
+ hdr->sh_flags |= SHF_MIPS_NOSTRIP;
+ /* The sh_info field is set in final_write_processing. */
+ }
+ else if (strcmp (name, MIPS_ELF_OPTIONS_SECTION_NAME (abfd)) == 0)
+ {
+ hdr->sh_type = SHT_MIPS_OPTIONS;
+ hdr->sh_entsize = 1;
+ hdr->sh_flags |= SHF_MIPS_NOSTRIP;
+ }
+ else if (strncmp (name, ".debug_", sizeof ".debug_" - 1) == 0)
+ hdr->sh_type = SHT_MIPS_DWARF;
+ else if (strcmp (name, ".MIPS.symlib") == 0)
+ {
+ hdr->sh_type = SHT_MIPS_SYMBOL_LIB;
+ /* The sh_link and sh_info fields are set in
+ final_write_processing. */
+ }
+ else if (strncmp (name, ".MIPS.events", sizeof ".MIPS.events" - 1) == 0
+ || strncmp (name, ".MIPS.post_rel",
+ sizeof ".MIPS.post_rel" - 1) == 0)
+ {
+ hdr->sh_type = SHT_MIPS_EVENTS;
+ hdr->sh_flags |= SHF_MIPS_NOSTRIP;
+ /* The sh_link field is set in final_write_processing. */
+ }
+ else if (strcmp (name, ".msym") == 0)
+ {
+ hdr->sh_type = SHT_MIPS_MSYM;
+ hdr->sh_flags |= SHF_ALLOC;
+ hdr->sh_entsize = 8;
+ }
+
+ /* The generic elf_fake_sections will set up REL_HDR using the default
+ kind of relocations. We used to set up a second header for the
+ non-default kind of relocations here, but only NewABI would use
+ these, and the IRIX ld doesn't like resulting empty RELA sections.
+ Thus we create those header only on demand now. */
+
+ return TRUE;
+}
+
+/* Given a BFD section, try to locate the corresponding ELF section
+ index. This is used by both the 32-bit and the 64-bit ABI.
+ Actually, it's not clear to me that the 64-bit ABI supports these,
+ but for non-PIC objects we will certainly want support for at least
+ the .scommon section. */
+
+bfd_boolean
+_bfd_mips_elf_section_from_bfd_section (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec, int *retval)
+{
+ if (strcmp (bfd_get_section_name (abfd, sec), ".scommon") == 0)
+ {
+ *retval = SHN_MIPS_SCOMMON;
+ return TRUE;
+ }
+ if (strcmp (bfd_get_section_name (abfd, sec), ".acommon") == 0)
+ {
+ *retval = SHN_MIPS_ACOMMON;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Hook called by the linker routine which adds symbols from an object
+ file. We must handle the special MIPS section numbers here. */
+
+bfd_boolean
+_bfd_mips_elf_add_symbol_hook (bfd *abfd, struct bfd_link_info *info,
+ Elf_Internal_Sym *sym, const char **namep,
+ flagword *flagsp ATTRIBUTE_UNUSED,
+ asection **secp, bfd_vma *valp)
+{
+ if (SGI_COMPAT (abfd)
+ && (abfd->flags & DYNAMIC) != 0
+ && strcmp (*namep, "_rld_new_interface") == 0)
+ {
+ /* Skip IRIX5 rld entry name. */
+ *namep = NULL;
+ return TRUE;
+ }
+
+ switch (sym->st_shndx)
+ {
+ case SHN_COMMON:
+ /* Common symbols less than the GP size are automatically
+ treated as SHN_MIPS_SCOMMON symbols. */
+ if (sym->st_size > elf_gp_size (abfd)
+ || IRIX_COMPAT (abfd) == ict_irix6)
+ break;
+ /* Fall through. */
+ case SHN_MIPS_SCOMMON:
+ *secp = bfd_make_section_old_way (abfd, ".scommon");
+ (*secp)->flags |= SEC_IS_COMMON;
+ *valp = sym->st_size;
+ break;
+
+ case SHN_MIPS_TEXT:
+ /* This section is used in a shared object. */
+ if (elf_tdata (abfd)->elf_text_section == NULL)
+ {
+ asymbol *elf_text_symbol;
+ asection *elf_text_section;
+ bfd_size_type amt = sizeof (asection);
+
+ elf_text_section = bfd_zalloc (abfd, amt);
+ if (elf_text_section == NULL)
+ return FALSE;
+
+ amt = sizeof (asymbol);
+ elf_text_symbol = bfd_zalloc (abfd, amt);
+ if (elf_text_symbol == NULL)
+ return FALSE;
+
+ /* Initialize the section. */
+
+ elf_tdata (abfd)->elf_text_section = elf_text_section;
+ elf_tdata (abfd)->elf_text_symbol = elf_text_symbol;
+
+ elf_text_section->symbol = elf_text_symbol;
+ elf_text_section->symbol_ptr_ptr = &elf_tdata (abfd)->elf_text_symbol;
+
+ elf_text_section->name = ".text";
+ elf_text_section->flags = SEC_NO_FLAGS;
+ elf_text_section->output_section = NULL;
+ elf_text_section->owner = abfd;
+ elf_text_symbol->name = ".text";
+ elf_text_symbol->flags = BSF_SECTION_SYM | BSF_DYNAMIC;
+ elf_text_symbol->section = elf_text_section;
+ }
+ /* This code used to do *secp = bfd_und_section_ptr if
+ info->shared. I don't know why, and that doesn't make sense,
+ so I took it out. */
+ *secp = elf_tdata (abfd)->elf_text_section;
+ break;
+
+ case SHN_MIPS_ACOMMON:
+ /* Fall through. XXX Can we treat this as allocated data? */
+ case SHN_MIPS_DATA:
+ /* This section is used in a shared object. */
+ if (elf_tdata (abfd)->elf_data_section == NULL)
+ {
+ asymbol *elf_data_symbol;
+ asection *elf_data_section;
+ bfd_size_type amt = sizeof (asection);
+
+ elf_data_section = bfd_zalloc (abfd, amt);
+ if (elf_data_section == NULL)
+ return FALSE;
+
+ amt = sizeof (asymbol);
+ elf_data_symbol = bfd_zalloc (abfd, amt);
+ if (elf_data_symbol == NULL)
+ return FALSE;
+
+ /* Initialize the section. */
+
+ elf_tdata (abfd)->elf_data_section = elf_data_section;
+ elf_tdata (abfd)->elf_data_symbol = elf_data_symbol;
+
+ elf_data_section->symbol = elf_data_symbol;
+ elf_data_section->symbol_ptr_ptr = &elf_tdata (abfd)->elf_data_symbol;
+
+ elf_data_section->name = ".data";
+ elf_data_section->flags = SEC_NO_FLAGS;
+ elf_data_section->output_section = NULL;
+ elf_data_section->owner = abfd;
+ elf_data_symbol->name = ".data";
+ elf_data_symbol->flags = BSF_SECTION_SYM | BSF_DYNAMIC;
+ elf_data_symbol->section = elf_data_section;
+ }
+ /* This code used to do *secp = bfd_und_section_ptr if
+ info->shared. I don't know why, and that doesn't make sense,
+ so I took it out. */
+ *secp = elf_tdata (abfd)->elf_data_section;
+ break;
+
+ case SHN_MIPS_SUNDEFINED:
+ *secp = bfd_und_section_ptr;
+ break;
+ }
+
+ if (SGI_COMPAT (abfd)
+ && ! info->shared
+ && info->hash->creator == abfd->xvec
+ && strcmp (*namep, "__rld_obj_head") == 0)
+ {
+ struct elf_link_hash_entry *h;
+ struct bfd_link_hash_entry *bh;
+
+ /* Mark __rld_obj_head as dynamic. */
+ bh = NULL;
+ if (! (_bfd_generic_link_add_one_symbol
+ (info, abfd, *namep, BSF_GLOBAL, *secp, *valp, NULL, FALSE,
+ get_elf_backend_data (abfd)->collect, &bh)))
+ return FALSE;
+
+ h = (struct elf_link_hash_entry *) bh;
+ h->elf_link_hash_flags &= ~ELF_LINK_NON_ELF;
+ h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
+ h->type = STT_OBJECT;
+
+ if (! bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+
+ mips_elf_hash_table (info)->use_rld_obj_head = TRUE;
+ }
+
+ /* If this is a mips16 text symbol, add 1 to the value to make it
+ odd. This will cause something like .word SYM to come up with
+ the right value when it is loaded into the PC. */
+ if (sym->st_other == STO_MIPS16)
+ ++*valp;
+
+ return TRUE;
+}
+
+/* This hook function is called before the linker writes out a global
+ symbol. We mark symbols as small common if appropriate. This is
+ also where we undo the increment of the value for a mips16 symbol. */
+
+bfd_boolean
+_bfd_mips_elf_link_output_symbol_hook
+ (struct bfd_link_info *info ATTRIBUTE_UNUSED,
+ const char *name ATTRIBUTE_UNUSED, Elf_Internal_Sym *sym,
+ asection *input_sec, struct elf_link_hash_entry *h ATTRIBUTE_UNUSED)
+{
+ /* If we see a common symbol, which implies a relocatable link, then
+ if a symbol was small common in an input file, mark it as small
+ common in the output file. */
+ if (sym->st_shndx == SHN_COMMON
+ && strcmp (input_sec->name, ".scommon") == 0)
+ sym->st_shndx = SHN_MIPS_SCOMMON;
+
+ if (sym->st_other == STO_MIPS16
+ && (sym->st_value & 1) != 0)
+ --sym->st_value;
+
+ return TRUE;
+}
+
+/* Functions for the dynamic linker. */
+
+/* Create dynamic sections when linking against a dynamic object. */
+
+bfd_boolean
+_bfd_mips_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
+{
+ struct elf_link_hash_entry *h;
+ struct bfd_link_hash_entry *bh;
+ flagword flags;
+ register asection *s;
+ const char * const *namep;
+
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+ | SEC_LINKER_CREATED | SEC_READONLY);
+
+ /* Mips ABI requests the .dynamic section to be read only. */
+ s = bfd_get_section_by_name (abfd, ".dynamic");
+ if (s != NULL)
+ {
+ if (! bfd_set_section_flags (abfd, s, flags))
+ return FALSE;
+ }
+
+ /* We need to create .got section. */
+ if (! mips_elf_create_got_section (abfd, info, FALSE))
+ return FALSE;
+
+ if (! mips_elf_rel_dyn_section (elf_hash_table (info)->dynobj, TRUE))
+ return FALSE;
+
+ /* Create .stub section. */
+ if (bfd_get_section_by_name (abfd,
+ MIPS_ELF_STUB_SECTION_NAME (abfd)) == NULL)
+ {
+ s = bfd_make_section (abfd, MIPS_ELF_STUB_SECTION_NAME (abfd));
+ if (s == NULL
+ || ! bfd_set_section_flags (abfd, s, flags | SEC_CODE)
+ || ! bfd_set_section_alignment (abfd, s,
+ MIPS_ELF_LOG_FILE_ALIGN (abfd)))
+ return FALSE;
+ }
+
+ if ((IRIX_COMPAT (abfd) == ict_irix5 || IRIX_COMPAT (abfd) == ict_none)
+ && !info->shared
+ && bfd_get_section_by_name (abfd, ".rld_map") == NULL)
+ {
+ s = bfd_make_section (abfd, ".rld_map");
+ if (s == NULL
+ || ! bfd_set_section_flags (abfd, s, flags &~ (flagword) SEC_READONLY)
+ || ! bfd_set_section_alignment (abfd, s,
+ MIPS_ELF_LOG_FILE_ALIGN (abfd)))
+ return FALSE;
+ }
+
+ /* On IRIX5, we adjust add some additional symbols and change the
+ alignments of several sections. There is no ABI documentation
+ indicating that this is necessary on IRIX6, nor any evidence that
+ the linker takes such action. */
+ if (IRIX_COMPAT (abfd) == ict_irix5)
+ {
+ for (namep = mips_elf_dynsym_rtproc_names; *namep != NULL; namep++)
+ {
+ bh = NULL;
+ if (! (_bfd_generic_link_add_one_symbol
+ (info, abfd, *namep, BSF_GLOBAL, bfd_und_section_ptr, 0,
+ NULL, FALSE, get_elf_backend_data (abfd)->collect, &bh)))
+ return FALSE;
+
+ h = (struct elf_link_hash_entry *) bh;
+ h->elf_link_hash_flags &= ~ELF_LINK_NON_ELF;
+ h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
+ h->type = STT_SECTION;
+
+ if (! bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+ }
+
+ /* We need to create a .compact_rel section. */
+ if (SGI_COMPAT (abfd))
+ {
+ if (!mips_elf_create_compact_rel_section (abfd, info))
+ return FALSE;
+ }
+
+ /* Change alignments of some sections. */
+ s = bfd_get_section_by_name (abfd, ".hash");
+ if (s != NULL)
+ bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+ s = bfd_get_section_by_name (abfd, ".dynsym");
+ if (s != NULL)
+ bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+ s = bfd_get_section_by_name (abfd, ".dynstr");
+ if (s != NULL)
+ bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+ s = bfd_get_section_by_name (abfd, ".reginfo");
+ if (s != NULL)
+ bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+ s = bfd_get_section_by_name (abfd, ".dynamic");
+ if (s != NULL)
+ bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+ }
+
+ if (!info->shared)
+ {
+ const char *name;
+
+ name = SGI_COMPAT (abfd) ? "_DYNAMIC_LINK" : "_DYNAMIC_LINKING";
+ bh = NULL;
+ if (!(_bfd_generic_link_add_one_symbol
+ (info, abfd, name, BSF_GLOBAL, bfd_abs_section_ptr, 0,
+ NULL, FALSE, get_elf_backend_data (abfd)->collect, &bh)))
+ return FALSE;
+
+ h = (struct elf_link_hash_entry *) bh;
+ h->elf_link_hash_flags &= ~ELF_LINK_NON_ELF;
+ h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
+ h->type = STT_SECTION;
+
+ if (! bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+
+ if (! mips_elf_hash_table (info)->use_rld_obj_head)
+ {
+ /* __rld_map is a four byte word located in the .data section
+ and is filled in by the rtld to contain a pointer to
+ the _r_debug structure. Its symbol value will be set in
+ _bfd_mips_elf_finish_dynamic_symbol. */
+ s = bfd_get_section_by_name (abfd, ".rld_map");
+ BFD_ASSERT (s != NULL);
+
+ name = SGI_COMPAT (abfd) ? "__rld_map" : "__RLD_MAP";
+ bh = NULL;
+ if (!(_bfd_generic_link_add_one_symbol
+ (info, abfd, name, BSF_GLOBAL, s, 0, NULL, FALSE,
+ get_elf_backend_data (abfd)->collect, &bh)))
+ return FALSE;
+
+ h = (struct elf_link_hash_entry *) bh;
+ h->elf_link_hash_flags &= ~ELF_LINK_NON_ELF;
+ h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
+ h->type = STT_OBJECT;
+
+ if (! bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/* Look through the relocs for a section during the first phase, and
+ allocate space in the global offset table. */
+
+bfd_boolean
+_bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
+ asection *sec, const Elf_Internal_Rela *relocs)
+{
+ const char *name;
+ bfd *dynobj;
+ Elf_Internal_Shdr *symtab_hdr;
+ struct elf_link_hash_entry **sym_hashes;
+ struct mips_got_info *g;
+ size_t extsymoff;
+ const Elf_Internal_Rela *rel;
+ const Elf_Internal_Rela *rel_end;
+ asection *sgot;
+ asection *sreloc;
+ const struct elf_backend_data *bed;
+
+ if (info->relocatable)
+ return TRUE;
+
+ dynobj = elf_hash_table (info)->dynobj;
+ symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+ sym_hashes = elf_sym_hashes (abfd);
+ extsymoff = (elf_bad_symtab (abfd)) ? 0 : symtab_hdr->sh_info;
+
+ /* Check for the mips16 stub sections. */
+
+ name = bfd_get_section_name (abfd, sec);
+ if (strncmp (name, FN_STUB, sizeof FN_STUB - 1) == 0)
+ {
+ unsigned long r_symndx;
+
+ /* Look at the relocation information to figure out which symbol
+ this is for. */
+
+ r_symndx = ELF_R_SYM (abfd, relocs->r_info);
+
+ if (r_symndx < extsymoff
+ || sym_hashes[r_symndx - extsymoff] == NULL)
+ {
+ asection *o;
+
+ /* This stub is for a local symbol. This stub will only be
+ needed if there is some relocation in this BFD, other
+ than a 16 bit function call, which refers to this symbol. */
+ for (o = abfd->sections; o != NULL; o = o->next)
+ {
+ Elf_Internal_Rela *sec_relocs;
+ const Elf_Internal_Rela *r, *rend;
+
+ /* We can ignore stub sections when looking for relocs. */
+ if ((o->flags & SEC_RELOC) == 0
+ || o->reloc_count == 0
+ || strncmp (bfd_get_section_name (abfd, o), FN_STUB,
+ sizeof FN_STUB - 1) == 0
+ || strncmp (bfd_get_section_name (abfd, o), CALL_STUB,
+ sizeof CALL_STUB - 1) == 0
+ || strncmp (bfd_get_section_name (abfd, o), CALL_FP_STUB,
+ sizeof CALL_FP_STUB - 1) == 0)
+ continue;
+
+ sec_relocs
+ = _bfd_elf_link_read_relocs (abfd, o, NULL, NULL,
+ info->keep_memory);
+ if (sec_relocs == NULL)
+ return FALSE;
+
+ rend = sec_relocs + o->reloc_count;
+ for (r = sec_relocs; r < rend; r++)
+ if (ELF_R_SYM (abfd, r->r_info) == r_symndx
+ && ELF_R_TYPE (abfd, r->r_info) != R_MIPS16_26)
+ break;
+
+ if (elf_section_data (o)->relocs != sec_relocs)
+ free (sec_relocs);
+
+ if (r < rend)
+ break;
+ }
+
+ if (o == NULL)
+ {
+ /* There is no non-call reloc for this stub, so we do
+ not need it. Since this function is called before
+ the linker maps input sections to output sections, we
+ can easily discard it by setting the SEC_EXCLUDE
+ flag. */
+ sec->flags |= SEC_EXCLUDE;
+ return TRUE;
+ }
+
+ /* Record this stub in an array of local symbol stubs for
+ this BFD. */
+ if (elf_tdata (abfd)->local_stubs == NULL)
+ {
+ unsigned long symcount;
+ asection **n;
+ bfd_size_type amt;
+
+ if (elf_bad_symtab (abfd))
+ symcount = NUM_SHDR_ENTRIES (symtab_hdr);
+ else
+ symcount = symtab_hdr->sh_info;
+ amt = symcount * sizeof (asection *);
+ n = bfd_zalloc (abfd, amt);
+ if (n == NULL)
+ return FALSE;
+ elf_tdata (abfd)->local_stubs = n;
+ }
+
+ elf_tdata (abfd)->local_stubs[r_symndx] = sec;
+
+ /* We don't need to set mips16_stubs_seen in this case.
+ That flag is used to see whether we need to look through
+ the global symbol table for stubs. We don't need to set
+ it here, because we just have a local stub. */
+ }
+ else
+ {
+ struct mips_elf_link_hash_entry *h;
+
+ h = ((struct mips_elf_link_hash_entry *)
+ sym_hashes[r_symndx - extsymoff]);
+
+ /* H is the symbol this stub is for. */
+
+ h->fn_stub = sec;
+ mips_elf_hash_table (info)->mips16_stubs_seen = TRUE;
+ }
+ }
+ else if (strncmp (name, CALL_STUB, sizeof CALL_STUB - 1) == 0
+ || strncmp (name, CALL_FP_STUB, sizeof CALL_FP_STUB - 1) == 0)
+ {
+ unsigned long r_symndx;
+ struct mips_elf_link_hash_entry *h;
+ asection **loc;
+
+ /* Look at the relocation information to figure out which symbol
+ this is for. */
+
+ r_symndx = ELF_R_SYM (abfd, relocs->r_info);
+
+ if (r_symndx < extsymoff
+ || sym_hashes[r_symndx - extsymoff] == NULL)
+ {
+ /* This stub was actually built for a static symbol defined
+ in the same file. We assume that all static symbols in
+ mips16 code are themselves mips16, so we can simply
+ discard this stub. Since this function is called before
+ the linker maps input sections to output sections, we can
+ easily discard it by setting the SEC_EXCLUDE flag. */
+ sec->flags |= SEC_EXCLUDE;
+ return TRUE;
+ }
+
+ h = ((struct mips_elf_link_hash_entry *)
+ sym_hashes[r_symndx - extsymoff]);
+
+ /* H is the symbol this stub is for. */
+
+ if (strncmp (name, CALL_FP_STUB, sizeof CALL_FP_STUB - 1) == 0)
+ loc = &h->call_fp_stub;
+ else
+ loc = &h->call_stub;
+
+ /* If we already have an appropriate stub for this function, we
+ don't need another one, so we can discard this one. Since
+ this function is called before the linker maps input sections
+ to output sections, we can easily discard it by setting the
+ SEC_EXCLUDE flag. We can also discard this section if we
+ happen to already know that this is a mips16 function; it is
+ not necessary to check this here, as it is checked later, but
+ it is slightly faster to check now. */
+ if (*loc != NULL || h->root.other == STO_MIPS16)
+ {
+ sec->flags |= SEC_EXCLUDE;
+ return TRUE;
+ }
+
+ *loc = sec;
+ mips_elf_hash_table (info)->mips16_stubs_seen = TRUE;
+ }
+
+ if (dynobj == NULL)
+ {
+ sgot = NULL;
+ g = NULL;
+ }
+ else
+ {
+ sgot = mips_elf_got_section (dynobj, FALSE);
+ if (sgot == NULL)
+ g = NULL;
+ else
+ {
+ BFD_ASSERT (mips_elf_section_data (sgot) != NULL);
+ g = mips_elf_section_data (sgot)->u.got_info;
+ BFD_ASSERT (g != NULL);
+ }
+ }
+
+ sreloc = NULL;
+ bed = get_elf_backend_data (abfd);
+ rel_end = relocs + sec->reloc_count * bed->s->int_rels_per_ext_rel;
+ for (rel = relocs; rel < rel_end; ++rel)
+ {
+ unsigned long r_symndx;
+ unsigned int r_type;
+ struct elf_link_hash_entry *h;
+
+ r_symndx = ELF_R_SYM (abfd, rel->r_info);
+ r_type = ELF_R_TYPE (abfd, rel->r_info);
+
+ if (r_symndx < extsymoff)
+ h = NULL;
+ else if (r_symndx >= extsymoff + NUM_SHDR_ENTRIES (symtab_hdr))
+ {
+ (*_bfd_error_handler)
+ (_("%s: Malformed reloc detected for section %s"),
+ bfd_archive_filename (abfd), name);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ else
+ {
+ h = sym_hashes[r_symndx - extsymoff];
+
+ /* This may be an indirect symbol created because of a version. */
+ if (h != NULL)
+ {
+ while (h->root.type == bfd_link_hash_indirect)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ }
+ }
+
+ /* Some relocs require a global offset table. */
+ if (dynobj == NULL || sgot == NULL)
+ {
+ switch (r_type)
+ {
+ case R_MIPS_GOT16:
+ case R_MIPS_CALL16:
+ case R_MIPS_CALL_HI16:
+ case R_MIPS_CALL_LO16:
+ case R_MIPS_GOT_HI16:
+ case R_MIPS_GOT_LO16:
+ case R_MIPS_GOT_PAGE:
+ case R_MIPS_GOT_OFST:
+ case R_MIPS_GOT_DISP:
+ if (dynobj == NULL)
+ elf_hash_table (info)->dynobj = dynobj = abfd;
+ if (! mips_elf_create_got_section (dynobj, info, FALSE))
+ return FALSE;
+ g = mips_elf_got_info (dynobj, &sgot);
+ break;
+
+ case R_MIPS_32:
+ case R_MIPS_REL32:
+ case R_MIPS_64:
+ if (dynobj == NULL
+ && (info->shared || h != NULL)
+ && (sec->flags & SEC_ALLOC) != 0)
+ elf_hash_table (info)->dynobj = dynobj = abfd;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (!h && (r_type == R_MIPS_CALL_LO16
+ || r_type == R_MIPS_GOT_LO16
+ || r_type == R_MIPS_GOT_DISP))
+ {
+ /* We may need a local GOT entry for this relocation. We
+ don't count R_MIPS_GOT_PAGE because we can estimate the
+ maximum number of pages needed by looking at the size of
+ the segment. Similar comments apply to R_MIPS_GOT16 and
+ R_MIPS_CALL16. We don't count R_MIPS_GOT_HI16, or
+ R_MIPS_CALL_HI16 because these are always followed by an
+ R_MIPS_GOT_LO16 or R_MIPS_CALL_LO16. */
+ if (! mips_elf_record_local_got_symbol (abfd, r_symndx,
+ rel->r_addend, g))
+ return FALSE;
+ }
+
+ switch (r_type)
+ {
+ case R_MIPS_CALL16:
+ if (h == NULL)
+ {
+ (*_bfd_error_handler)
+ (_("%s: CALL16 reloc at 0x%lx not against global symbol"),
+ bfd_archive_filename (abfd), (unsigned long) rel->r_offset);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ /* Fall through. */
+
+ case R_MIPS_CALL_HI16:
+ case R_MIPS_CALL_LO16:
+ if (h != NULL)
+ {
+ /* This symbol requires a global offset table entry. */
+ if (! mips_elf_record_global_got_symbol (h, abfd, info, g))
+ return FALSE;
+
+ /* We need a stub, not a plt entry for the undefined
+ function. But we record it as if it needs plt. See
+ _bfd_elf_adjust_dynamic_symbol. */
+ h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
+ h->type = STT_FUNC;
+ }
+ break;
+
+ case R_MIPS_GOT_PAGE:
+ /* If this is a global, overridable symbol, GOT_PAGE will
+ decay to GOT_DISP, so we'll need a GOT entry for it. */
+ if (h == NULL)
+ break;
+ else
+ {
+ struct mips_elf_link_hash_entry *hmips =
+ (struct mips_elf_link_hash_entry *) h;
+
+ while (hmips->root.root.type == bfd_link_hash_indirect
+ || hmips->root.root.type == bfd_link_hash_warning)
+ hmips = (struct mips_elf_link_hash_entry *)
+ hmips->root.root.u.i.link;
+
+ if ((hmips->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)
+ && ! (info->shared && ! info->symbolic
+ && ! (hmips->root.elf_link_hash_flags
+ & ELF_LINK_FORCED_LOCAL)))
+ break;
+ }
+ /* Fall through. */
+
+ case R_MIPS_GOT16:
+ case R_MIPS_GOT_HI16:
+ case R_MIPS_GOT_LO16:
+ case R_MIPS_GOT_DISP:
+ /* This symbol requires a global offset table entry. */
+ if (h && ! mips_elf_record_global_got_symbol (h, abfd, info, g))
+ return FALSE;
+ break;
+
+ case R_MIPS_32:
+ case R_MIPS_REL32:
+ case R_MIPS_64:
+ if ((info->shared || h != NULL)
+ && (sec->flags & SEC_ALLOC) != 0)
+ {
+ if (sreloc == NULL)
+ {
+ sreloc = mips_elf_rel_dyn_section (dynobj, TRUE);
+ if (sreloc == NULL)
+ return FALSE;
+ }
+#define MIPS_READONLY_SECTION (SEC_ALLOC | SEC_LOAD | SEC_READONLY)
+ if (info->shared)
+ {
+ /* When creating a shared object, we must copy these
+ reloc types into the output file as R_MIPS_REL32
+ relocs. We make room for this reloc in the
+ .rel.dyn reloc section. */
+ mips_elf_allocate_dynamic_relocations (dynobj, 1);
+ if ((sec->flags & MIPS_READONLY_SECTION)
+ == MIPS_READONLY_SECTION)
+ /* We tell the dynamic linker that there are
+ relocations against the text segment. */
+ info->flags |= DF_TEXTREL;
+ }
+ else
+ {
+ struct mips_elf_link_hash_entry *hmips;
+
+ /* We only need to copy this reloc if the symbol is
+ defined in a dynamic object. */
+ hmips = (struct mips_elf_link_hash_entry *) h;
+ ++hmips->possibly_dynamic_relocs;
+ if ((sec->flags & MIPS_READONLY_SECTION)
+ == MIPS_READONLY_SECTION)
+ /* We need it to tell the dynamic linker if there
+ are relocations against the text segment. */
+ hmips->readonly_reloc = TRUE;
+ }
+
+ /* Even though we don't directly need a GOT entry for
+ this symbol, a symbol must have a dynamic symbol
+ table index greater that DT_MIPS_GOTSYM if there are
+ dynamic relocations against it. */
+ if (h != NULL)
+ {
+ if (dynobj == NULL)
+ elf_hash_table (info)->dynobj = dynobj = abfd;
+ if (! mips_elf_create_got_section (dynobj, info, TRUE))
+ return FALSE;
+ g = mips_elf_got_info (dynobj, &sgot);
+ if (! mips_elf_record_global_got_symbol (h, abfd, info, g))
+ return FALSE;
+ }
+ }
+
+ if (SGI_COMPAT (abfd))
+ mips_elf_hash_table (info)->compact_rel_size +=
+ sizeof (Elf32_External_crinfo);
+ break;
+
+ case R_MIPS_26:
+ case R_MIPS_GPREL16:
+ case R_MIPS_LITERAL:
+ case R_MIPS_GPREL32:
+ if (SGI_COMPAT (abfd))
+ mips_elf_hash_table (info)->compact_rel_size +=
+ sizeof (Elf32_External_crinfo);
+ break;
+
+ /* This relocation describes the C++ object vtable hierarchy.
+ Reconstruct it for later use during GC. */
+ case R_MIPS_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_MIPS_GNU_VTENTRY:
+ if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_offset))
+ return FALSE;
+ break;
+
+ default:
+ break;
+ }
+
+ /* We must not create a stub for a symbol that has relocations
+ related to taking the function's address. */
+ switch (r_type)
+ {
+ default:
+ if (h != NULL)
+ {
+ struct mips_elf_link_hash_entry *mh;
+
+ mh = (struct mips_elf_link_hash_entry *) h;
+ mh->no_fn_stub = TRUE;
+ }
+ break;
+ case R_MIPS_CALL16:
+ case R_MIPS_CALL_HI16:
+ case R_MIPS_CALL_LO16:
+ case R_MIPS_JALR:
+ break;
+ }
+
+ /* If this reloc is not a 16 bit call, and it has a global
+ symbol, then we will need the fn_stub if there is one.
+ References from a stub section do not count. */
+ if (h != NULL
+ && r_type != R_MIPS16_26
+ && strncmp (bfd_get_section_name (abfd, sec), FN_STUB,
+ sizeof FN_STUB - 1) != 0
+ && strncmp (bfd_get_section_name (abfd, sec), CALL_STUB,
+ sizeof CALL_STUB - 1) != 0
+ && strncmp (bfd_get_section_name (abfd, sec), CALL_FP_STUB,
+ sizeof CALL_FP_STUB - 1) != 0)
+ {
+ struct mips_elf_link_hash_entry *mh;
+
+ mh = (struct mips_elf_link_hash_entry *) h;
+ mh->need_fn_stub = TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+bfd_boolean
+_bfd_mips_relax_section (bfd *abfd, asection *sec,
+ struct bfd_link_info *link_info,
+ bfd_boolean *again)
+{
+ Elf_Internal_Rela *internal_relocs;
+ Elf_Internal_Rela *irel, *irelend;
+ Elf_Internal_Shdr *symtab_hdr;
+ bfd_byte *contents = NULL;
+ bfd_byte *free_contents = NULL;
+ size_t extsymoff;
+ bfd_boolean changed_contents = FALSE;
+ bfd_vma sec_start = sec->output_section->vma + sec->output_offset;
+ Elf_Internal_Sym *isymbuf = NULL;
+
+ /* We are not currently changing any sizes, so only one pass. */
+ *again = FALSE;
+
+ if (link_info->relocatable)
+ return TRUE;
+
+ internal_relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL,
+ link_info->keep_memory);
+ if (internal_relocs == NULL)
+ return TRUE;
+
+ irelend = internal_relocs + sec->reloc_count
+ * get_elf_backend_data (abfd)->s->int_rels_per_ext_rel;
+ symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+ extsymoff = (elf_bad_symtab (abfd)) ? 0 : symtab_hdr->sh_info;
+
+ for (irel = internal_relocs; irel < irelend; irel++)
+ {
+ bfd_vma symval;
+ bfd_signed_vma sym_offset;
+ unsigned int r_type;
+ unsigned long r_symndx;
+ asection *sym_sec;
+ unsigned long instruction;
+
+ /* Turn jalr into bgezal, and jr into beq, if they're marked
+ with a JALR relocation, that indicate where they jump to.
+ This saves some pipeline bubbles. */
+ r_type = ELF_R_TYPE (abfd, irel->r_info);
+ if (r_type != R_MIPS_JALR)
+ continue;
+
+ r_symndx = ELF_R_SYM (abfd, irel->r_info);
+ /* Compute the address of the jump target. */
+ if (r_symndx >= extsymoff)
+ {
+ struct mips_elf_link_hash_entry *h
+ = ((struct mips_elf_link_hash_entry *)
+ elf_sym_hashes (abfd) [r_symndx - extsymoff]);
+
+ while (h->root.root.type == bfd_link_hash_indirect
+ || h->root.root.type == bfd_link_hash_warning)
+ h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
+
+ /* If a symbol is undefined, or if it may be overridden,
+ skip it. */
+ if (! ((h->root.root.type == bfd_link_hash_defined
+ || h->root.root.type == bfd_link_hash_defweak)
+ && h->root.root.u.def.section)
+ || (link_info->shared && ! link_info->symbolic
+ && ! (h->root.elf_link_hash_flags & ELF_LINK_FORCED_LOCAL)))
+ continue;
+
+ sym_sec = h->root.root.u.def.section;
+ if (sym_sec->output_section)
+ symval = (h->root.root.u.def.value
+ + sym_sec->output_section->vma
+ + sym_sec->output_offset);
+ else
+ symval = h->root.root.u.def.value;
+ }
+ else
+ {
+ Elf_Internal_Sym *isym;
+
+ /* Read this BFD's 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 relax_return;
+ }
+
+ isym = isymbuf + r_symndx;
+ if (isym->st_shndx == SHN_UNDEF)
+ continue;
+ 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;
+ }
+
+ /* Compute branch offset, from delay slot of the jump to the
+ branch target. */
+ sym_offset = (symval + irel->r_addend)
+ - (sec_start + irel->r_offset + 4);
+
+ /* Branch offset must be properly aligned. */
+ if ((sym_offset & 3) != 0)
+ continue;
+
+ sym_offset >>= 2;
+
+ /* Check that it's in range. */
+ if (sym_offset < -0x8000 || sym_offset >= 0x8000)
+ 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
+ {
+ contents = bfd_malloc (sec->_raw_size);
+ if (contents == NULL)
+ goto relax_return;
+
+ free_contents = contents;
+ if (! bfd_get_section_contents (abfd, sec, contents,
+ 0, sec->_raw_size))
+ goto relax_return;
+ }
+ }
+
+ instruction = bfd_get_32 (abfd, contents + irel->r_offset);
+
+ /* If it was jalr <reg>, turn it into bgezal $zero, <target>. */
+ if ((instruction & 0xfc1fffff) == 0x0000f809)
+ instruction = 0x04110000;
+ /* If it was jr <reg>, turn it into b <target>. */
+ else if ((instruction & 0xfc1fffff) == 0x00000008)
+ instruction = 0x10000000;
+ else
+ continue;
+
+ instruction |= (sym_offset & 0xffff);
+ bfd_put_32 (abfd, instruction, contents + irel->r_offset);
+ changed_contents = TRUE;
+ }
+
+ if (contents != NULL
+ && elf_section_data (sec)->this_hdr.contents != contents)
+ {
+ if (!changed_contents && !link_info->keep_memory)
+ free (contents);
+ else
+ {
+ /* Cache the section contents for elf_link_input_bfd. */
+ elf_section_data (sec)->this_hdr.contents = contents;
+ }
+ }
+ return TRUE;
+
+ relax_return:
+ if (free_contents != NULL)
+ free (free_contents);
+ return FALSE;
+}
+
+/* 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. */
+
+bfd_boolean
+_bfd_mips_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
+ struct elf_link_hash_entry *h)
+{
+ bfd *dynobj;
+ struct mips_elf_link_hash_entry *hmips;
+ asection *s;
+
+ dynobj = elf_hash_table (info)->dynobj;
+
+ /* Make sure we know what is going on here. */
+ BFD_ASSERT (dynobj != NULL
+ && ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT)
+ || h->weakdef != NULL
+ || ((h->elf_link_hash_flags
+ & ELF_LINK_HASH_DEF_DYNAMIC) != 0
+ && (h->elf_link_hash_flags
+ & ELF_LINK_HASH_REF_REGULAR) != 0
+ && (h->elf_link_hash_flags
+ & ELF_LINK_HASH_DEF_REGULAR) == 0)));
+
+ /* If this symbol is defined in a dynamic object, we need to copy
+ any R_MIPS_32 or R_MIPS_REL32 relocs against it into the output
+ file. */
+ hmips = (struct mips_elf_link_hash_entry *) h;
+ if (! info->relocatable
+ && hmips->possibly_dynamic_relocs != 0
+ && (h->root.type == bfd_link_hash_defweak
+ || (h->elf_link_hash_flags
+ & ELF_LINK_HASH_DEF_REGULAR) == 0))
+ {
+ mips_elf_allocate_dynamic_relocations (dynobj,
+ hmips->possibly_dynamic_relocs);
+ if (hmips->readonly_reloc)
+ /* We tell the dynamic linker that there are relocations
+ against the text segment. */
+ info->flags |= DF_TEXTREL;
+ }
+
+ /* For a function, create a stub, if allowed. */
+ if (! hmips->no_fn_stub
+ && (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0)
+ {
+ if (! elf_hash_table (info)->dynamic_sections_created)
+ return TRUE;
+
+ /* If this symbol is not defined in a regular file, then set
+ the symbol to the stub location. This is required to make
+ function pointers compare as equal between the normal
+ executable and the shared library. */
+ if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
+ {
+ /* We need .stub section. */
+ s = bfd_get_section_by_name (dynobj,
+ MIPS_ELF_STUB_SECTION_NAME (dynobj));
+ BFD_ASSERT (s != NULL);
+
+ h->root.u.def.section = s;
+ h->root.u.def.value = s->_raw_size;
+
+ /* XXX Write this stub address somewhere. */
+ h->plt.offset = s->_raw_size;
+
+ /* Make room for this stub code. */
+ s->_raw_size += MIPS_FUNCTION_STUB_SIZE;
+
+ /* The last half word of the stub will be filled with the index
+ of this symbol in .dynsym section. */
+ return TRUE;
+ }
+ }
+ else if ((h->type == STT_FUNC)
+ && (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) == 0)
+ {
+ /* This will set the entry for this symbol in the GOT to 0, and
+ the dynamic linker will take care of this. */
+ h->root.u.def.value = 0;
+ return TRUE;
+ }
+
+ /* 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->weakdef != NULL)
+ {
+ BFD_ASSERT (h->weakdef->root.type == bfd_link_hash_defined
+ || h->weakdef->root.type == bfd_link_hash_defweak);
+ h->root.u.def.section = h->weakdef->root.u.def.section;
+ h->root.u.def.value = h->weakdef->root.u.def.value;
+ return TRUE;
+ }
+
+ /* This is a reference to a symbol defined by a dynamic object which
+ is not a function. */
+
+ return TRUE;
+}
+
+/* This function is called after all the input files have been read,
+ and the input sections have been assigned to output sections. We
+ check for any mips16 stub sections that we can discard. */
+
+bfd_boolean
+_bfd_mips_elf_always_size_sections (bfd *output_bfd,
+ struct bfd_link_info *info)
+{
+ asection *ri;
+
+ bfd *dynobj;
+ asection *s;
+ struct mips_got_info *g;
+ int i;
+ bfd_size_type loadable_size = 0;
+ bfd_size_type local_gotno;
+ bfd *sub;
+
+ /* The .reginfo section has a fixed size. */
+ ri = bfd_get_section_by_name (output_bfd, ".reginfo");
+ if (ri != NULL)
+ bfd_set_section_size (output_bfd, ri, sizeof (Elf32_External_RegInfo));
+
+ if (! (info->relocatable
+ || ! mips_elf_hash_table (info)->mips16_stubs_seen))
+ mips_elf_link_hash_traverse (mips_elf_hash_table (info),
+ mips_elf_check_mips16_stubs, NULL);
+
+ dynobj = elf_hash_table (info)->dynobj;
+ if (dynobj == NULL)
+ /* Relocatable links don't have it. */
+ return TRUE;
+
+ g = mips_elf_got_info (dynobj, &s);
+ if (s == NULL)
+ return TRUE;
+
+ /* Calculate the total loadable size of the output. That
+ will give us the maximum number of GOT_PAGE entries
+ required. */
+ for (sub = info->input_bfds; sub; sub = sub->link_next)
+ {
+ asection *subsection;
+
+ for (subsection = sub->sections;
+ subsection;
+ subsection = subsection->next)
+ {
+ if ((subsection->flags & SEC_ALLOC) == 0)
+ continue;
+ loadable_size += ((subsection->_raw_size + 0xf)
+ &~ (bfd_size_type) 0xf);
+ }
+ }
+
+ /* There has to be a global GOT entry for every symbol with
+ a dynamic symbol table index of DT_MIPS_GOTSYM or
+ higher. Therefore, it make sense to put those symbols
+ that need GOT entries at the end of the symbol table. We
+ do that here. */
+ if (! mips_elf_sort_hash_table (info, 1))
+ return FALSE;
+
+ if (g->global_gotsym != NULL)
+ i = elf_hash_table (info)->dynsymcount - g->global_gotsym->dynindx;
+ else
+ /* If there are no global symbols, or none requiring
+ relocations, then GLOBAL_GOTSYM will be NULL. */
+ i = 0;
+
+ /* In the worst case, we'll get one stub per dynamic symbol, plus
+ one to account for the dummy entry at the end required by IRIX
+ rld. */
+ loadable_size += MIPS_FUNCTION_STUB_SIZE * (i + 1);
+
+ /* Assume there are two loadable segments consisting of
+ contiguous sections. Is 5 enough? */
+ local_gotno = (loadable_size >> 16) + 5;
+
+ g->local_gotno += local_gotno;
+ s->_raw_size += g->local_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
+
+ g->global_gotno = i;
+ s->_raw_size += i * MIPS_ELF_GOT_SIZE (output_bfd);
+
+ if (s->_raw_size > MIPS_ELF_GOT_MAX_SIZE (output_bfd)
+ && ! mips_elf_multi_got (output_bfd, info, g, s, local_gotno))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Set the sizes of the dynamic sections. */
+
+bfd_boolean
+_bfd_mips_elf_size_dynamic_sections (bfd *output_bfd,
+ struct bfd_link_info *info)
+{
+ bfd *dynobj;
+ asection *s;
+ bfd_boolean reltext;
+
+ dynobj = elf_hash_table (info)->dynobj;
+ BFD_ASSERT (dynobj != NULL);
+
+ 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->_raw_size
+ = strlen (ELF_DYNAMIC_INTERPRETER (output_bfd)) + 1;
+ s->contents
+ = (bfd_byte *) ELF_DYNAMIC_INTERPRETER (output_bfd);
+ }
+ }
+
+ /* The check_relocs and adjust_dynamic_symbol entry points have
+ determined the sizes of the various dynamic sections. Allocate
+ memory for them. */
+ reltext = FALSE;
+ for (s = dynobj->sections; s != NULL; s = s->next)
+ {
+ const char *name;
+ bfd_boolean strip;
+
+ /* 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 ((s->flags & SEC_LINKER_CREATED) == 0)
+ continue;
+
+ strip = FALSE;
+
+ if (strncmp (name, ".rel", 4) == 0)
+ {
+ if (s->_raw_size == 0)
+ {
+ /* We only strip the section if the output section name
+ has the same name. Otherwise, there might be several
+ input sections for this output section. FIXME: This
+ code is probably not needed these days anyhow, since
+ the linker now does not create empty output sections. */
+ if (s->output_section != NULL
+ && strcmp (name,
+ bfd_get_section_name (s->output_section->owner,
+ s->output_section)) == 0)
+ strip = TRUE;
+ }
+ else
+ {
+ const char *outname;
+ asection *target;
+
+ /* If this relocation section applies to a read only
+ section, then we probably need a DT_TEXTREL entry.
+ If the relocation section is .rel.dyn, we always
+ assert a DT_TEXTREL entry rather than testing whether
+ there exists a relocation to a read only section or
+ not. */
+ outname = bfd_get_section_name (output_bfd,
+ s->output_section);
+ target = bfd_get_section_by_name (output_bfd, outname + 4);
+ if ((target != NULL
+ && (target->flags & SEC_READONLY) != 0
+ && (target->flags & SEC_ALLOC) != 0)
+ || strcmp (outname, ".rel.dyn") == 0)
+ reltext = TRUE;
+
+ /* We use the reloc_count field as a counter if we need
+ to copy relocs into the output file. */
+ if (strcmp (name, ".rel.dyn") != 0)
+ s->reloc_count = 0;
+
+ /* If combreloc is enabled, elf_link_sort_relocs() will
+ sort relocations, but in a different way than we do,
+ and before we're done creating relocations. Also, it
+ will move them around between input sections'
+ relocation's contents, so our sorting would be
+ broken, so don't let it run. */
+ info->combreloc = 0;
+ }
+ }
+ else if (strncmp (name, ".got", 4) == 0)
+ {
+ /* _bfd_mips_elf_always_size_sections() has already done
+ most of the work, but some symbols may have been mapped
+ to versions that we must now resolve in the got_entries
+ hash tables. */
+ struct mips_got_info *gg = mips_elf_got_info (dynobj, NULL);
+ struct mips_got_info *g = gg;
+ struct mips_elf_set_global_got_offset_arg set_got_offset_arg;
+ unsigned int needed_relocs = 0;
+
+ if (gg->next)
+ {
+ set_got_offset_arg.value = MIPS_ELF_GOT_SIZE (output_bfd);
+ set_got_offset_arg.info = info;
+
+ mips_elf_resolve_final_got_entries (gg);
+ for (g = gg->next; g && g->next != gg; g = g->next)
+ {
+ unsigned int save_assign;
+
+ mips_elf_resolve_final_got_entries (g);
+
+ /* Assign offsets to global GOT entries. */
+ save_assign = g->assigned_gotno;
+ g->assigned_gotno = g->local_gotno;
+ set_got_offset_arg.g = g;
+ set_got_offset_arg.needed_relocs = 0;
+ htab_traverse (g->got_entries,
+ mips_elf_set_global_got_offset,
+ &set_got_offset_arg);
+ needed_relocs += set_got_offset_arg.needed_relocs;
+ BFD_ASSERT (g->assigned_gotno - g->local_gotno
+ <= g->global_gotno);
+
+ g->assigned_gotno = save_assign;
+ if (info->shared)
+ {
+ needed_relocs += g->local_gotno - g->assigned_gotno;
+ BFD_ASSERT (g->assigned_gotno == g->next->local_gotno
+ + g->next->global_gotno
+ + MIPS_RESERVED_GOTNO);
+ }
+ }
+
+ if (needed_relocs)
+ mips_elf_allocate_dynamic_relocations (dynobj, needed_relocs);
+ }
+ }
+ else if (strcmp (name, MIPS_ELF_STUB_SECTION_NAME (output_bfd)) == 0)
+ {
+ /* IRIX rld assumes that the function stub isn't at the end
+ of .text section. So put a dummy. XXX */
+ s->_raw_size += MIPS_FUNCTION_STUB_SIZE;
+ }
+ else if (! info->shared
+ && ! mips_elf_hash_table (info)->use_rld_obj_head
+ && strncmp (name, ".rld_map", 8) == 0)
+ {
+ /* We add a room for __rld_map. It will be filled in by the
+ rtld to contain a pointer to the _r_debug structure. */
+ s->_raw_size += 4;
+ }
+ else if (SGI_COMPAT (output_bfd)
+ && strncmp (name, ".compact_rel", 12) == 0)
+ s->_raw_size += mips_elf_hash_table (info)->compact_rel_size;
+ else if (strncmp (name, ".init", 5) != 0)
+ {
+ /* It's not one of our sections, so don't allocate space. */
+ continue;
+ }
+
+ if (strip)
+ {
+ _bfd_strip_section_from_output (info, s);
+ continue;
+ }
+
+ /* Allocate memory for the section contents. */
+ s->contents = bfd_zalloc (dynobj, s->_raw_size);
+ if (s->contents == NULL && s->_raw_size != 0)
+ {
+ bfd_set_error (bfd_error_no_memory);
+ return FALSE;
+ }
+ }
+
+ if (elf_hash_table (info)->dynamic_sections_created)
+ {
+ /* Add some entries to the .dynamic section. We fill in the
+ values later, in _bfd_mips_elf_finish_dynamic_sections, but we
+ must add the entries now so that we get the correct size for
+ the .dynamic section. The DT_DEBUG entry is filled in by the
+ dynamic linker and used by the debugger. */
+ if (! info->shared)
+ {
+ /* SGI object has the equivalence of DT_DEBUG in the
+ DT_MIPS_RLD_MAP entry. */
+ if (!MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_RLD_MAP, 0))
+ return FALSE;
+ if (!SGI_COMPAT (output_bfd))
+ {
+ if (!MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_DEBUG, 0))
+ return FALSE;
+ }
+ }
+ else
+ {
+ /* Shared libraries on traditional mips have DT_DEBUG. */
+ if (!SGI_COMPAT (output_bfd))
+ {
+ if (!MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_DEBUG, 0))
+ return FALSE;
+ }
+ }
+
+ if (reltext && SGI_COMPAT (output_bfd))
+ info->flags |= DF_TEXTREL;
+
+ if ((info->flags & DF_TEXTREL) != 0)
+ {
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_TEXTREL, 0))
+ return FALSE;
+ }
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_PLTGOT, 0))
+ return FALSE;
+
+ if (mips_elf_rel_dyn_section (dynobj, FALSE))
+ {
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_REL, 0))
+ return FALSE;
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_RELSZ, 0))
+ return FALSE;
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_RELENT, 0))
+ return FALSE;
+ }
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_RLD_VERSION, 0))
+ return FALSE;
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_FLAGS, 0))
+ return FALSE;
+
+#if 0
+ /* Time stamps in executable files are a bad idea. */
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_TIME_STAMP, 0))
+ return FALSE;
+#endif
+
+#if 0 /* FIXME */
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_ICHECKSUM, 0))
+ return FALSE;
+#endif
+
+#if 0 /* FIXME */
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_IVERSION, 0))
+ return FALSE;
+#endif
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_BASE_ADDRESS, 0))
+ return FALSE;
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_LOCAL_GOTNO, 0))
+ return FALSE;
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_SYMTABNO, 0))
+ return FALSE;
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_UNREFEXTNO, 0))
+ return FALSE;
+
+ if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_GOTSYM, 0))
+ return FALSE;
+
+ if (IRIX_COMPAT (dynobj) == ict_irix5
+ && ! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_HIPAGENO, 0))
+ return FALSE;
+
+ if (IRIX_COMPAT (dynobj) == ict_irix6
+ && (bfd_get_section_by_name
+ (dynobj, MIPS_ELF_OPTIONS_SECTION_NAME (dynobj)))
+ && !MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_OPTIONS, 0))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Relocate a MIPS ELF section. */
+
+bfd_boolean
+_bfd_mips_elf_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_Rela *rel;
+ const Elf_Internal_Rela *relend;
+ bfd_vma addend = 0;
+ bfd_boolean use_saved_addend_p = FALSE;
+ const struct elf_backend_data *bed;
+
+ bed = get_elf_backend_data (output_bfd);
+ relend = relocs + input_section->reloc_count * bed->s->int_rels_per_ext_rel;
+ for (rel = relocs; rel < relend; ++rel)
+ {
+ const char *name;
+ bfd_vma value;
+ reloc_howto_type *howto;
+ bfd_boolean require_jalx;
+ /* TRUE if the relocation is a RELA relocation, rather than a
+ REL relocation. */
+ bfd_boolean rela_relocation_p = TRUE;
+ unsigned int r_type = ELF_R_TYPE (output_bfd, rel->r_info);
+ const char *msg;
+
+ /* Find the relocation howto for this relocation. */
+ if (r_type == R_MIPS_64 && ! NEWABI_P (input_bfd))
+ {
+ /* Some 32-bit code uses R_MIPS_64. In particular, people use
+ 64-bit code, but make sure all their addresses are in the
+ lowermost or uppermost 32-bit section of the 64-bit address
+ space. Thus, when they use an R_MIPS_64 they mean what is
+ usually meant by R_MIPS_32, with the exception that the
+ stored value is sign-extended to 64 bits. */
+ howto = MIPS_ELF_RTYPE_TO_HOWTO (input_bfd, R_MIPS_32, FALSE);
+
+ /* On big-endian systems, we need to lie about the position
+ of the reloc. */
+ if (bfd_big_endian (input_bfd))
+ rel->r_offset += 4;
+ }
+ else
+ /* NewABI defaults to RELA relocations. */
+ howto = MIPS_ELF_RTYPE_TO_HOWTO (input_bfd, r_type,
+ NEWABI_P (input_bfd)
+ && (MIPS_RELOC_RELA_P
+ (input_bfd, input_section,
+ rel - relocs)));
+
+ if (!use_saved_addend_p)
+ {
+ Elf_Internal_Shdr *rel_hdr;
+
+ /* If these relocations were originally of the REL variety,
+ we must pull the addend out of the field that will be
+ relocated. Otherwise, we simply use the contents of the
+ RELA relocation. To determine which flavor or relocation
+ this is, we depend on the fact that the INPUT_SECTION's
+ REL_HDR is read before its REL_HDR2. */
+ rel_hdr = &elf_section_data (input_section)->rel_hdr;
+ if ((size_t) (rel - relocs)
+ >= (NUM_SHDR_ENTRIES (rel_hdr) * bed->s->int_rels_per_ext_rel))
+ rel_hdr = elf_section_data (input_section)->rel_hdr2;
+ if (rel_hdr->sh_entsize == MIPS_ELF_REL_SIZE (input_bfd))
+ {
+ /* Note that this is a REL relocation. */
+ rela_relocation_p = FALSE;
+
+ /* Get the addend, which is stored in the input file. */
+ addend = mips_elf_obtain_contents (howto, rel, input_bfd,
+ contents);
+ addend &= howto->src_mask;
+
+ /* For some kinds of relocations, the ADDEND is a
+ combination of the addend stored in two different
+ relocations. */
+ if (r_type == R_MIPS_HI16
+ || r_type == R_MIPS_GNU_REL_HI16
+ || (r_type == R_MIPS_GOT16
+ && mips_elf_local_relocation_p (input_bfd, rel,
+ local_sections, FALSE)))
+ {
+ bfd_vma l;
+ const Elf_Internal_Rela *lo16_relocation;
+ reloc_howto_type *lo16_howto;
+ unsigned int lo;
+
+ /* The combined value is the sum of the HI16 addend,
+ left-shifted by sixteen bits, and the LO16
+ addend, sign extended. (Usually, the code does
+ a `lui' of the HI16 value, and then an `addiu' of
+ the LO16 value.)
+
+ Scan ahead to find a matching LO16 relocation. */
+ if (r_type == R_MIPS_GNU_REL_HI16)
+ lo = R_MIPS_GNU_REL_LO16;
+ else
+ lo = R_MIPS_LO16;
+ lo16_relocation = mips_elf_next_relocation (input_bfd, lo,
+ rel, relend);
+ if (lo16_relocation == NULL)
+ return FALSE;
+
+ /* Obtain the addend kept there. */
+ lo16_howto = MIPS_ELF_RTYPE_TO_HOWTO (input_bfd, lo, FALSE);
+ l = mips_elf_obtain_contents (lo16_howto, lo16_relocation,
+ input_bfd, contents);
+ l &= lo16_howto->src_mask;
+ l <<= lo16_howto->rightshift;
+ l = _bfd_mips_elf_sign_extend (l, 16);
+
+ addend <<= 16;
+
+ /* Compute the combined addend. */
+ addend += l;
+
+ /* If PC-relative, subtract the difference between the
+ address of the LO part of the reloc and the address of
+ the HI part. The relocation is relative to the LO
+ part, but mips_elf_calculate_relocation() doesn't
+ know its address or the difference from the HI part, so
+ we subtract that difference here. See also the
+ comment in mips_elf_calculate_relocation(). */
+ if (r_type == R_MIPS_GNU_REL_HI16)
+ addend -= (lo16_relocation->r_offset - rel->r_offset);
+ }
+ else if (r_type == R_MIPS16_GPREL)
+ {
+ /* The addend is scrambled in the object file. See
+ mips_elf_perform_relocation for details on the
+ format. */
+ addend = (((addend & 0x1f0000) >> 5)
+ | ((addend & 0x7e00000) >> 16)
+ | (addend & 0x1f));
+ }
+ else
+ addend <<= howto->rightshift;
+ }
+ else
+ addend = rel->r_addend;
+ }
+
+ if (info->relocatable)
+ {
+ Elf_Internal_Sym *sym;
+ unsigned long r_symndx;
+
+ if (r_type == R_MIPS_64 && ! NEWABI_P (output_bfd)
+ && bfd_big_endian (input_bfd))
+ rel->r_offset -= 4;
+
+ /* Since we're just relocating, all we need to do is copy
+ the relocations back out to the object file, unless
+ they're against a section symbol, in which case we need
+ to adjust by the section offset, or unless they're GP
+ relative in which case we need to adjust by the amount
+ that we're adjusting GP in this relocatable object. */
+
+ if (! mips_elf_local_relocation_p (input_bfd, rel, local_sections,
+ FALSE))
+ /* There's nothing to do for non-local relocations. */
+ continue;
+
+ if (r_type == R_MIPS16_GPREL
+ || r_type == R_MIPS_GPREL16
+ || r_type == R_MIPS_GPREL32
+ || r_type == R_MIPS_LITERAL)
+ addend -= (_bfd_get_gp_value (output_bfd)
+ - _bfd_get_gp_value (input_bfd));
+
+ r_symndx = ELF_R_SYM (output_bfd, rel->r_info);
+ sym = local_syms + r_symndx;
+ if (ELF_ST_TYPE (sym->st_info) == STT_SECTION)
+ /* Adjust the addend appropriately. */
+ addend += local_sections[r_symndx]->output_offset;
+
+ if (rela_relocation_p)
+ /* If this is a RELA relocation, just update the addend. */
+ rel->r_addend = addend;
+ else
+ {
+ if (r_type == R_MIPS_HI16
+ || r_type == R_MIPS_GOT16
+ || r_type == R_MIPS_GNU_REL_HI16)
+ addend = mips_elf_high (addend);
+ else if (r_type == R_MIPS_HIGHER)
+ addend = mips_elf_higher (addend);
+ else if (r_type == R_MIPS_HIGHEST)
+ addend = mips_elf_highest (addend);
+ else
+ addend >>= howto->rightshift;
+
+ /* We use the source mask, rather than the destination
+ mask because the place to which we are writing will be
+ source of the addend in the final link. */
+ addend &= howto->src_mask;
+
+ if (r_type == R_MIPS_64 && ! NEWABI_P (output_bfd))
+ /* See the comment above about using R_MIPS_64 in the 32-bit
+ ABI. Here, we need to update the addend. It would be
+ possible to get away with just using the R_MIPS_32 reloc
+ but for endianness. */
+ {
+ bfd_vma sign_bits;
+ bfd_vma low_bits;
+ bfd_vma high_bits;
+
+ if (addend & ((bfd_vma) 1 << 31))
+#ifdef BFD64
+ sign_bits = ((bfd_vma) 1 << 32) - 1;
+#else
+ sign_bits = -1;
+#endif
+ else
+ sign_bits = 0;
+
+ /* If we don't know that we have a 64-bit type,
+ do two separate stores. */
+ if (bfd_big_endian (input_bfd))
+ {
+ /* Store the sign-bits (which are most significant)
+ first. */
+ low_bits = sign_bits;
+ high_bits = addend;
+ }
+ else
+ {
+ low_bits = addend;
+ high_bits = sign_bits;
+ }
+ bfd_put_32 (input_bfd, low_bits,
+ contents + rel->r_offset);
+ bfd_put_32 (input_bfd, high_bits,
+ contents + rel->r_offset + 4);
+ continue;
+ }
+
+ if (! mips_elf_perform_relocation (info, howto, rel, addend,
+ input_bfd, input_section,
+ contents, FALSE))
+ return FALSE;
+ }
+
+ /* Go on to the next relocation. */
+ continue;
+ }
+
+ /* In the N32 and 64-bit ABIs there may be multiple consecutive
+ relocations for the same offset. In that case we are
+ supposed to treat the output of each relocation as the addend
+ for the next. */
+ if (rel + 1 < relend
+ && rel->r_offset == rel[1].r_offset
+ && ELF_R_TYPE (input_bfd, rel[1].r_info) != R_MIPS_NONE)
+ use_saved_addend_p = TRUE;
+ else
+ use_saved_addend_p = FALSE;
+
+ /* Figure out what value we are supposed to relocate. */
+ switch (mips_elf_calculate_relocation (output_bfd, input_bfd,
+ input_section, info, rel,
+ addend, howto, local_syms,
+ local_sections, &value,
+ &name, &require_jalx,
+ use_saved_addend_p))
+ {
+ case bfd_reloc_continue:
+ /* There's nothing to do. */
+ continue;
+
+ case bfd_reloc_undefined:
+ /* mips_elf_calculate_relocation already called the
+ undefined_symbol callback. There's no real point in
+ trying to perform the relocation at this point, so we
+ just skip ahead to the next relocation. */
+ continue;
+
+ case bfd_reloc_notsupported:
+ msg = _("internal error: unsupported relocation error");
+ info->callbacks->warning
+ (info, msg, name, input_bfd, input_section, rel->r_offset);
+ return FALSE;
+
+ case bfd_reloc_overflow:
+ if (use_saved_addend_p)
+ /* Ignore overflow until we reach the last relocation for
+ a given location. */
+ ;
+ else
+ {
+ BFD_ASSERT (name != NULL);
+ if (! ((*info->callbacks->reloc_overflow)
+ (info, name, howto->name, 0,
+ input_bfd, input_section, rel->r_offset)))
+ return FALSE;
+ }
+ break;
+
+ case bfd_reloc_ok:
+ break;
+
+ default:
+ abort ();
+ break;
+ }
+
+ /* If we've got another relocation for the address, keep going
+ until we reach the last one. */
+ if (use_saved_addend_p)
+ {
+ addend = value;
+ continue;
+ }
+
+ if (r_type == R_MIPS_64 && ! NEWABI_P (output_bfd))
+ /* See the comment above about using R_MIPS_64 in the 32-bit
+ ABI. Until now, we've been using the HOWTO for R_MIPS_32;
+ that calculated the right value. Now, however, we
+ sign-extend the 32-bit result to 64-bits, and store it as a
+ 64-bit value. We are especially generous here in that we
+ go to extreme lengths to support this usage on systems with
+ only a 32-bit VMA. */
+ {
+ bfd_vma sign_bits;
+ bfd_vma low_bits;
+ bfd_vma high_bits;
+
+ if (value & ((bfd_vma) 1 << 31))
+#ifdef BFD64
+ sign_bits = ((bfd_vma) 1 << 32) - 1;
+#else
+ sign_bits = -1;
+#endif
+ else
+ sign_bits = 0;
+
+ /* If we don't know that we have a 64-bit type,
+ do two separate stores. */
+ if (bfd_big_endian (input_bfd))
+ {
+ /* Undo what we did above. */
+ rel->r_offset -= 4;
+ /* Store the sign-bits (which are most significant)
+ first. */
+ low_bits = sign_bits;
+ high_bits = value;
+ }
+ else
+ {
+ low_bits = value;
+ high_bits = sign_bits;
+ }
+ bfd_put_32 (input_bfd, low_bits,
+ contents + rel->r_offset);
+ bfd_put_32 (input_bfd, high_bits,
+ contents + rel->r_offset + 4);
+ continue;
+ }
+
+ /* Actually perform the relocation. */
+ if (! mips_elf_perform_relocation (info, howto, rel, value,
+ input_bfd, input_section,
+ contents, require_jalx))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* If NAME is one of the special IRIX6 symbols defined by the linker,
+ adjust it appropriately now. */
+
+static void
+mips_elf_irix6_finish_dynamic_symbol (bfd *abfd ATTRIBUTE_UNUSED,
+ const char *name, Elf_Internal_Sym *sym)
+{
+ /* The linker script takes care of providing names and values for
+ these, but we must place them into the right sections. */
+ static const char* const text_section_symbols[] = {
+ "_ftext",
+ "_etext",
+ "__dso_displacement",
+ "__elf_header",
+ "__program_header_table",
+ NULL
+ };
+
+ static const char* const data_section_symbols[] = {
+ "_fdata",
+ "_edata",
+ "_end",
+ "_fbss",
+ NULL
+ };
+
+ const char* const *p;
+ int i;
+
+ for (i = 0; i < 2; ++i)
+ for (p = (i == 0) ? text_section_symbols : data_section_symbols;
+ *p;
+ ++p)
+ if (strcmp (*p, name) == 0)
+ {
+ /* All of these symbols are given type STT_SECTION by the
+ IRIX6 linker. */
+ sym->st_info = ELF_ST_INFO (STB_GLOBAL, STT_SECTION);
+ sym->st_other = STO_PROTECTED;
+
+ /* The IRIX linker puts these symbols in special sections. */
+ if (i == 0)
+ sym->st_shndx = SHN_MIPS_TEXT;
+ else
+ sym->st_shndx = SHN_MIPS_DATA;
+
+ break;
+ }
+}
+
+/* Finish up dynamic symbol handling. We set the contents of various
+ dynamic sections here. */
+
+bfd_boolean
+_bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
+ struct bfd_link_info *info,
+ struct elf_link_hash_entry *h,
+ Elf_Internal_Sym *sym)
+{
+ bfd *dynobj;
+ bfd_vma gval;
+ asection *sgot;
+ struct mips_got_info *g, *gg;
+ const char *name;
+
+ dynobj = elf_hash_table (info)->dynobj;
+ gval = sym->st_value;
+
+ if (h->plt.offset != (bfd_vma) -1)
+ {
+ asection *s;
+ bfd_byte stub[MIPS_FUNCTION_STUB_SIZE];
+
+ /* This symbol has a stub. Set it up. */
+
+ BFD_ASSERT (h->dynindx != -1);
+
+ s = bfd_get_section_by_name (dynobj,
+ MIPS_ELF_STUB_SECTION_NAME (dynobj));
+ BFD_ASSERT (s != NULL);
+
+ /* FIXME: Can h->dynindex be more than 64K? */
+ if (h->dynindx & 0xffff0000)
+ return FALSE;
+
+ /* Fill the stub. */
+ bfd_put_32 (output_bfd, STUB_LW (output_bfd), stub);
+ bfd_put_32 (output_bfd, STUB_MOVE (output_bfd), stub + 4);
+ bfd_put_32 (output_bfd, STUB_JALR, stub + 8);
+ bfd_put_32 (output_bfd, STUB_LI16 (output_bfd) + h->dynindx, stub + 12);
+
+ BFD_ASSERT (h->plt.offset <= s->_raw_size);
+ memcpy (s->contents + h->plt.offset, stub, MIPS_FUNCTION_STUB_SIZE);
+
+ /* Mark the symbol as undefined. plt.offset != -1 occurs
+ only for the referenced symbol. */
+ sym->st_shndx = SHN_UNDEF;
+
+ /* The run-time linker uses the st_value field of the symbol
+ to reset the global offset table entry for this external
+ to its stub address when unlinking a shared object. */
+ gval = s->output_section->vma + s->output_offset + h->plt.offset;
+ sym->st_value = gval;
+ }
+
+ BFD_ASSERT (h->dynindx != -1
+ || (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0);
+
+ sgot = mips_elf_got_section (dynobj, FALSE);
+ BFD_ASSERT (sgot != NULL);
+ BFD_ASSERT (mips_elf_section_data (sgot) != NULL);
+ g = mips_elf_section_data (sgot)->u.got_info;
+ BFD_ASSERT (g != NULL);
+
+ /* Run through the global symbol table, creating GOT entries for all
+ the symbols that need them. */
+ if (g->global_gotsym != NULL
+ && h->dynindx >= g->global_gotsym->dynindx)
+ {
+ bfd_vma offset;
+ bfd_vma value;
+
+ value = sym->st_value;
+ offset = mips_elf_global_got_index (dynobj, output_bfd, h);
+ MIPS_ELF_PUT_WORD (output_bfd, value, sgot->contents + offset);
+ }
+
+ if (g->next && h->dynindx != -1)
+ {
+ struct mips_got_entry e, *p;
+ bfd_vma entry;
+ bfd_vma offset;
+
+ gg = g;
+
+ e.abfd = output_bfd;
+ e.symndx = -1;
+ e.d.h = (struct mips_elf_link_hash_entry *)h;
+
+ for (g = g->next; g->next != gg; g = g->next)
+ {
+ if (g->got_entries
+ && (p = (struct mips_got_entry *) htab_find (g->got_entries,
+ &e)))
+ {
+ offset = p->gotidx;
+ if (info->shared
+ || (elf_hash_table (info)->dynamic_sections_created
+ && p->d.h != NULL
+ && ((p->d.h->root.elf_link_hash_flags
+ & ELF_LINK_HASH_DEF_DYNAMIC) != 0)
+ && ((p->d.h->root.elf_link_hash_flags
+ & ELF_LINK_HASH_DEF_REGULAR) == 0)))
+ {
+ /* Create an R_MIPS_REL32 relocation for this entry. Due to
+ the various compatibility problems, it's easier to mock
+ up an R_MIPS_32 or R_MIPS_64 relocation and leave
+ mips_elf_create_dynamic_relocation to calculate the
+ appropriate addend. */
+ Elf_Internal_Rela rel[3];
+
+ memset (rel, 0, sizeof (rel));
+ if (ABI_64_P (output_bfd))
+ rel[0].r_info = ELF_R_INFO (output_bfd, 0, R_MIPS_64);
+ else
+ rel[0].r_info = ELF_R_INFO (output_bfd, 0, R_MIPS_32);
+ rel[0].r_offset = rel[1].r_offset = rel[2].r_offset = offset;
+
+ entry = 0;
+ if (! (mips_elf_create_dynamic_relocation
+ (output_bfd, info, rel,
+ e.d.h, NULL, sym->st_value, &entry, sgot)))
+ return FALSE;
+ }
+ else
+ entry = sym->st_value;
+ MIPS_ELF_PUT_WORD (output_bfd, entry, sgot->contents + offset);
+ }
+ }
+ }
+
+ /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. */
+ name = h->root.root.string;
+ if (strcmp (name, "_DYNAMIC") == 0
+ || strcmp (name, "_GLOBAL_OFFSET_TABLE_") == 0)
+ sym->st_shndx = SHN_ABS;
+ else if (strcmp (name, "_DYNAMIC_LINK") == 0
+ || strcmp (name, "_DYNAMIC_LINKING") == 0)
+ {
+ sym->st_shndx = SHN_ABS;
+ sym->st_info = ELF_ST_INFO (STB_GLOBAL, STT_SECTION);
+ sym->st_value = 1;
+ }
+ else if (strcmp (name, "_gp_disp") == 0 && ! NEWABI_P (output_bfd))
+ {
+ sym->st_shndx = SHN_ABS;
+ sym->st_info = ELF_ST_INFO (STB_GLOBAL, STT_SECTION);
+ sym->st_value = elf_gp (output_bfd);
+ }
+ else if (SGI_COMPAT (output_bfd))
+ {
+ if (strcmp (name, mips_elf_dynsym_rtproc_names[0]) == 0
+ || strcmp (name, mips_elf_dynsym_rtproc_names[1]) == 0)
+ {
+ sym->st_info = ELF_ST_INFO (STB_GLOBAL, STT_SECTION);
+ sym->st_other = STO_PROTECTED;
+ sym->st_value = 0;
+ sym->st_shndx = SHN_MIPS_DATA;
+ }
+ else if (strcmp (name, mips_elf_dynsym_rtproc_names[2]) == 0)
+ {
+ sym->st_info = ELF_ST_INFO (STB_GLOBAL, STT_SECTION);
+ sym->st_other = STO_PROTECTED;
+ sym->st_value = mips_elf_hash_table (info)->procedure_count;
+ sym->st_shndx = SHN_ABS;
+ }
+ else if (sym->st_shndx != SHN_UNDEF && sym->st_shndx != SHN_ABS)
+ {
+ if (h->type == STT_FUNC)
+ sym->st_shndx = SHN_MIPS_TEXT;
+ else if (h->type == STT_OBJECT)
+ sym->st_shndx = SHN_MIPS_DATA;
+ }
+ }
+
+ /* Handle the IRIX6-specific symbols. */
+ if (IRIX_COMPAT (output_bfd) == ict_irix6)
+ mips_elf_irix6_finish_dynamic_symbol (output_bfd, name, sym);
+
+ if (! info->shared)
+ {
+ if (! mips_elf_hash_table (info)->use_rld_obj_head
+ && (strcmp (name, "__rld_map") == 0
+ || strcmp (name, "__RLD_MAP") == 0))
+ {
+ asection *s = bfd_get_section_by_name (dynobj, ".rld_map");
+ BFD_ASSERT (s != NULL);
+ sym->st_value = s->output_section->vma + s->output_offset;
+ bfd_put_32 (output_bfd, 0, s->contents);
+ if (mips_elf_hash_table (info)->rld_value == 0)
+ mips_elf_hash_table (info)->rld_value = sym->st_value;
+ }
+ else if (mips_elf_hash_table (info)->use_rld_obj_head
+ && strcmp (name, "__rld_obj_head") == 0)
+ {
+ /* IRIX6 does not use a .rld_map section. */
+ if (IRIX_COMPAT (output_bfd) == ict_irix5
+ || IRIX_COMPAT (output_bfd) == ict_none)
+ BFD_ASSERT (bfd_get_section_by_name (dynobj, ".rld_map")
+ != NULL);
+ mips_elf_hash_table (info)->rld_value = sym->st_value;
+ }
+ }
+
+ /* If this is a mips16 symbol, force the value to be even. */
+ if (sym->st_other == STO_MIPS16
+ && (sym->st_value & 1) != 0)
+ --sym->st_value;
+
+ return TRUE;
+}
+
+/* Finish up the dynamic sections. */
+
+bfd_boolean
+_bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd,
+ struct bfd_link_info *info)
+{
+ bfd *dynobj;
+ asection *sdyn;
+ asection *sgot;
+ struct mips_got_info *gg, *g;
+
+ dynobj = elf_hash_table (info)->dynobj;
+
+ sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
+
+ sgot = mips_elf_got_section (dynobj, FALSE);
+ if (sgot == NULL)
+ gg = g = NULL;
+ else
+ {
+ BFD_ASSERT (mips_elf_section_data (sgot) != NULL);
+ gg = mips_elf_section_data (sgot)->u.got_info;
+ BFD_ASSERT (gg != NULL);
+ g = mips_elf_got_for_ibfd (gg, output_bfd);
+ BFD_ASSERT (g != NULL);
+ }
+
+ if (elf_hash_table (info)->dynamic_sections_created)
+ {
+ bfd_byte *b;
+
+ BFD_ASSERT (sdyn != NULL);
+ BFD_ASSERT (g != NULL);
+
+ for (b = sdyn->contents;
+ b < sdyn->contents + sdyn->_raw_size;
+ b += MIPS_ELF_DYN_SIZE (dynobj))
+ {
+ Elf_Internal_Dyn dyn;
+ const char *name;
+ size_t elemsize;
+ asection *s;
+ bfd_boolean swap_out_p;
+
+ /* Read in the current dynamic entry. */
+ (*get_elf_backend_data (dynobj)->s->swap_dyn_in) (dynobj, b, &dyn);
+
+ /* Assume that we're going to modify it and write it out. */
+ swap_out_p = TRUE;
+
+ switch (dyn.d_tag)
+ {
+ case DT_RELENT:
+ s = mips_elf_rel_dyn_section (dynobj, FALSE);
+ BFD_ASSERT (s != NULL);
+ dyn.d_un.d_val = MIPS_ELF_REL_SIZE (dynobj);
+ break;
+
+ case DT_STRSZ:
+ /* Rewrite DT_STRSZ. */
+ dyn.d_un.d_val =
+ _bfd_elf_strtab_size (elf_hash_table (info)->dynstr);
+ break;
+
+ case DT_PLTGOT:
+ name = ".got";
+ s = bfd_get_section_by_name (output_bfd, name);
+ BFD_ASSERT (s != NULL);
+ dyn.d_un.d_ptr = s->vma;
+ break;
+
+ case DT_MIPS_RLD_VERSION:
+ dyn.d_un.d_val = 1; /* XXX */
+ break;
+
+ case DT_MIPS_FLAGS:
+ dyn.d_un.d_val = RHF_NOTPOT; /* XXX */
+ break;
+
+ case DT_MIPS_TIME_STAMP:
+ time ((time_t *) &dyn.d_un.d_val);
+ break;
+
+ case DT_MIPS_ICHECKSUM:
+ /* XXX FIXME: */
+ swap_out_p = FALSE;
+ break;
+
+ case DT_MIPS_IVERSION:
+ /* XXX FIXME: */
+ swap_out_p = FALSE;
+ break;
+
+ case DT_MIPS_BASE_ADDRESS:
+ s = output_bfd->sections;
+ BFD_ASSERT (s != NULL);
+ dyn.d_un.d_ptr = s->vma & ~(bfd_vma) 0xffff;
+ break;
+
+ case DT_MIPS_LOCAL_GOTNO:
+ dyn.d_un.d_val = g->local_gotno;
+ break;
+
+ case DT_MIPS_UNREFEXTNO:
+ /* The index into the dynamic symbol table which is the
+ entry of the first external symbol that is not
+ referenced within the same object. */
+ dyn.d_un.d_val = bfd_count_sections (output_bfd) + 1;
+ break;
+
+ case DT_MIPS_GOTSYM:
+ if (gg->global_gotsym)
+ {
+ dyn.d_un.d_val = gg->global_gotsym->dynindx;
+ break;
+ }
+ /* In case if we don't have global got symbols we default
+ to setting DT_MIPS_GOTSYM to the same value as
+ DT_MIPS_SYMTABNO, so we just fall through. */
+
+ case DT_MIPS_SYMTABNO:
+ name = ".dynsym";
+ elemsize = MIPS_ELF_SYM_SIZE (output_bfd);
+ s = bfd_get_section_by_name (output_bfd, name);
+ BFD_ASSERT (s != NULL);
+
+ if (s->_cooked_size != 0)
+ dyn.d_un.d_val = s->_cooked_size / elemsize;
+ else
+ dyn.d_un.d_val = s->_raw_size / elemsize;
+ break;
+
+ case DT_MIPS_HIPAGENO:
+ dyn.d_un.d_val = g->local_gotno - MIPS_RESERVED_GOTNO;
+ break;
+
+ case DT_MIPS_RLD_MAP:
+ dyn.d_un.d_ptr = mips_elf_hash_table (info)->rld_value;
+ break;
+
+ case DT_MIPS_OPTIONS:
+ s = (bfd_get_section_by_name
+ (output_bfd, MIPS_ELF_OPTIONS_SECTION_NAME (output_bfd)));
+ dyn.d_un.d_ptr = s->vma;
+ break;
+
+ case DT_RELSZ:
+ /* Reduce DT_RELSZ to account for any relocations we
+ decided not to make. This is for the n64 irix rld,
+ which doesn't seem to apply any relocations if there
+ are trailing null entries. */
+ s = mips_elf_rel_dyn_section (dynobj, FALSE);
+ dyn.d_un.d_val = (s->reloc_count
+ * (ABI_64_P (output_bfd)
+ ? sizeof (Elf64_Mips_External_Rel)
+ : sizeof (Elf32_External_Rel)));
+ break;
+
+ default:
+ swap_out_p = FALSE;
+ break;
+ }
+
+ if (swap_out_p)
+ (*get_elf_backend_data (dynobj)->s->swap_dyn_out)
+ (dynobj, &dyn, b);
+ }
+ }
+
+ /* The first entry of the global offset table will be filled at
+ runtime. The second entry will be used by some runtime loaders.
+ This isn't the case of IRIX rld. */
+ if (sgot != NULL && sgot->_raw_size > 0)
+ {
+ MIPS_ELF_PUT_WORD (output_bfd, 0, sgot->contents);
+ MIPS_ELF_PUT_WORD (output_bfd, 0x80000000,
+ sgot->contents + MIPS_ELF_GOT_SIZE (output_bfd));
+ }
+
+ if (sgot != NULL)
+ elf_section_data (sgot->output_section)->this_hdr.sh_entsize
+ = MIPS_ELF_GOT_SIZE (output_bfd);
+
+ /* Generate dynamic relocations for the non-primary gots. */
+ if (gg != NULL && gg->next)
+ {
+ Elf_Internal_Rela rel[3];
+ bfd_vma addend = 0;
+
+ memset (rel, 0, sizeof (rel));
+ rel[0].r_info = ELF_R_INFO (output_bfd, 0, R_MIPS_REL32);
+
+ for (g = gg->next; g->next != gg; g = g->next)
+ {
+ bfd_vma index = g->next->local_gotno + g->next->global_gotno;
+
+ MIPS_ELF_PUT_WORD (output_bfd, 0, sgot->contents
+ + index++ * MIPS_ELF_GOT_SIZE (output_bfd));
+ MIPS_ELF_PUT_WORD (output_bfd, 0x80000000, sgot->contents
+ + index++ * MIPS_ELF_GOT_SIZE (output_bfd));
+
+ if (! info->shared)
+ continue;
+
+ while (index < g->assigned_gotno)
+ {
+ rel[0].r_offset = rel[1].r_offset = rel[2].r_offset
+ = index++ * MIPS_ELF_GOT_SIZE (output_bfd);
+ if (!(mips_elf_create_dynamic_relocation
+ (output_bfd, info, rel, NULL,
+ bfd_abs_section_ptr,
+ 0, &addend, sgot)))
+ return FALSE;
+ BFD_ASSERT (addend == 0);
+ }
+ }
+ }
+
+ {
+ asection *s;
+ Elf32_compact_rel cpt;
+
+ if (SGI_COMPAT (output_bfd))
+ {
+ /* Write .compact_rel section out. */
+ s = bfd_get_section_by_name (dynobj, ".compact_rel");
+ if (s != NULL)
+ {
+ cpt.id1 = 1;
+ cpt.num = s->reloc_count;
+ cpt.id2 = 2;
+ cpt.offset = (s->output_section->filepos
+ + sizeof (Elf32_External_compact_rel));
+ cpt.reserved0 = 0;
+ cpt.reserved1 = 0;
+ bfd_elf32_swap_compact_rel_out (output_bfd, &cpt,
+ ((Elf32_External_compact_rel *)
+ s->contents));
+
+ /* Clean up a dummy stub function entry in .text. */
+ s = bfd_get_section_by_name (dynobj,
+ MIPS_ELF_STUB_SECTION_NAME (dynobj));
+ if (s != NULL)
+ {
+ file_ptr dummy_offset;
+
+ BFD_ASSERT (s->_raw_size >= MIPS_FUNCTION_STUB_SIZE);
+ dummy_offset = s->_raw_size - MIPS_FUNCTION_STUB_SIZE;
+ memset (s->contents + dummy_offset, 0,
+ MIPS_FUNCTION_STUB_SIZE);
+ }
+ }
+ }
+
+ /* We need to sort the entries of the dynamic relocation section. */
+
+ s = mips_elf_rel_dyn_section (dynobj, FALSE);
+
+ if (s != NULL
+ && s->_raw_size > (bfd_vma)2 * MIPS_ELF_REL_SIZE (output_bfd))
+ {
+ reldyn_sorting_bfd = output_bfd;
+
+ if (ABI_64_P (output_bfd))
+ qsort ((Elf64_External_Rel *) s->contents + 1, s->reloc_count - 1,
+ sizeof (Elf64_Mips_External_Rel), sort_dynamic_relocs_64);
+ else
+ qsort ((Elf32_External_Rel *) s->contents + 1, s->reloc_count - 1,
+ sizeof (Elf32_External_Rel), sort_dynamic_relocs);
+ }
+ }
+
+ return TRUE;
+}
+
+
+/* Set ABFD's EF_MIPS_ARCH and EF_MIPS_MACH flags. */
+
+static void
+mips_set_isa_flags (bfd *abfd)
+{
+ flagword val;
+
+ switch (bfd_get_mach (abfd))
+ {
+ default:
+ case bfd_mach_mips3000:
+ val = E_MIPS_ARCH_1;
+ break;
+
+ case bfd_mach_mips3900:
+ val = E_MIPS_ARCH_1 | E_MIPS_MACH_3900;
+ break;
+
+ case bfd_mach_mips6000:
+ val = E_MIPS_ARCH_2;
+ break;
+
+ case bfd_mach_mips4000:
+ case bfd_mach_mips4300:
+ case bfd_mach_mips4400:
+ case bfd_mach_mips4600:
+ val = E_MIPS_ARCH_3;
+ break;
+
+ case bfd_mach_mips4010:
+ val = E_MIPS_ARCH_3 | E_MIPS_MACH_4010;
+ break;
+
+ case bfd_mach_mips4100:
+ val = E_MIPS_ARCH_3 | E_MIPS_MACH_4100;
+ break;
+
+ case bfd_mach_mips4111:
+ val = E_MIPS_ARCH_3 | E_MIPS_MACH_4111;
+ break;
+
+ case bfd_mach_mips4120:
+ val = E_MIPS_ARCH_3 | E_MIPS_MACH_4120;
+ break;
+
+ case bfd_mach_mips4650:
+ val = E_MIPS_ARCH_3 | E_MIPS_MACH_4650;
+ break;
+
+ case bfd_mach_mips5400:
+ val = E_MIPS_ARCH_4 | E_MIPS_MACH_5400;
+ break;
+
+ case bfd_mach_mips5500:
+ val = E_MIPS_ARCH_4 | E_MIPS_MACH_5500;
+ break;
+
+ case bfd_mach_mips5000:
+ case bfd_mach_mips7000:
+ case bfd_mach_mips8000:
+ case bfd_mach_mips10000:
+ case bfd_mach_mips12000:
+ val = E_MIPS_ARCH_4;
+ break;
+
+ case bfd_mach_mips5:
+ val = E_MIPS_ARCH_5;
+ break;
+
+ case bfd_mach_mips_sb1:
+ val = E_MIPS_ARCH_64 | E_MIPS_MACH_SB1;
+ break;
+
+ case bfd_mach_mipsisa32:
+ val = E_MIPS_ARCH_32;
+ break;
+
+ case bfd_mach_mipsisa64:
+ val = E_MIPS_ARCH_64;
+ break;
+
+ case bfd_mach_mipsisa32r2:
+ val = E_MIPS_ARCH_32R2;
+ break;
+
+ case bfd_mach_mipsisa64r2:
+ val = E_MIPS_ARCH_64R2;
+ break;
+ }
+ elf_elfheader (abfd)->e_flags &= ~(EF_MIPS_ARCH | EF_MIPS_MACH);
+ elf_elfheader (abfd)->e_flags |= val;
+
+}
+
+
+/* The final processing done just before writing out a MIPS ELF object
+ file. This gets the MIPS architecture right based on the machine
+ number. This is used by both the 32-bit and the 64-bit ABI. */
+
+void
+_bfd_mips_elf_final_write_processing (bfd *abfd,
+ bfd_boolean linker ATTRIBUTE_UNUSED)
+{
+ unsigned int i;
+ Elf_Internal_Shdr **hdrpp;
+ const char *name;
+ asection *sec;
+
+ /* Keep the existing EF_MIPS_MACH and EF_MIPS_ARCH flags if the former
+ is nonzero. This is for compatibility with old objects, which used
+ a combination of a 32-bit EF_MIPS_ARCH and a 64-bit EF_MIPS_MACH. */
+ if ((elf_elfheader (abfd)->e_flags & EF_MIPS_MACH) == 0)
+ mips_set_isa_flags (abfd);
+
+ /* Set the sh_info field for .gptab sections and other appropriate
+ info for each special section. */
+ for (i = 1, hdrpp = elf_elfsections (abfd) + 1;
+ i < elf_numsections (abfd);
+ i++, hdrpp++)
+ {
+ switch ((*hdrpp)->sh_type)
+ {
+ case SHT_MIPS_MSYM:
+ case SHT_MIPS_LIBLIST:
+ sec = bfd_get_section_by_name (abfd, ".dynstr");
+ if (sec != NULL)
+ (*hdrpp)->sh_link = elf_section_data (sec)->this_idx;
+ break;
+
+ case SHT_MIPS_GPTAB:
+ BFD_ASSERT ((*hdrpp)->bfd_section != NULL);
+ name = bfd_get_section_name (abfd, (*hdrpp)->bfd_section);
+ BFD_ASSERT (name != NULL
+ && strncmp (name, ".gptab.", sizeof ".gptab." - 1) == 0);
+ sec = bfd_get_section_by_name (abfd, name + sizeof ".gptab" - 1);
+ BFD_ASSERT (sec != NULL);
+ (*hdrpp)->sh_info = elf_section_data (sec)->this_idx;
+ break;
+
+ case SHT_MIPS_CONTENT:
+ BFD_ASSERT ((*hdrpp)->bfd_section != NULL);
+ name = bfd_get_section_name (abfd, (*hdrpp)->bfd_section);
+ BFD_ASSERT (name != NULL
+ && strncmp (name, ".MIPS.content",
+ sizeof ".MIPS.content" - 1) == 0);
+ sec = bfd_get_section_by_name (abfd,
+ name + sizeof ".MIPS.content" - 1);
+ BFD_ASSERT (sec != NULL);
+ (*hdrpp)->sh_link = elf_section_data (sec)->this_idx;
+ break;
+
+ case SHT_MIPS_SYMBOL_LIB:
+ sec = bfd_get_section_by_name (abfd, ".dynsym");
+ if (sec != NULL)
+ (*hdrpp)->sh_link = elf_section_data (sec)->this_idx;
+ sec = bfd_get_section_by_name (abfd, ".liblist");
+ if (sec != NULL)
+ (*hdrpp)->sh_info = elf_section_data (sec)->this_idx;
+ break;
+
+ case SHT_MIPS_EVENTS:
+ BFD_ASSERT ((*hdrpp)->bfd_section != NULL);
+ name = bfd_get_section_name (abfd, (*hdrpp)->bfd_section);
+ BFD_ASSERT (name != NULL);
+ if (strncmp (name, ".MIPS.events", sizeof ".MIPS.events" - 1) == 0)
+ sec = bfd_get_section_by_name (abfd,
+ name + sizeof ".MIPS.events" - 1);
+ else
+ {
+ BFD_ASSERT (strncmp (name, ".MIPS.post_rel",
+ sizeof ".MIPS.post_rel" - 1) == 0);
+ sec = bfd_get_section_by_name (abfd,
+ (name
+ + sizeof ".MIPS.post_rel" - 1));
+ }
+ BFD_ASSERT (sec != NULL);
+ (*hdrpp)->sh_link = elf_section_data (sec)->this_idx;
+ break;
+
+ }
+ }
+}
+
+/* When creating an IRIX5 executable, we need REGINFO and RTPROC
+ segments. */
+
+int
+_bfd_mips_elf_additional_program_headers (bfd *abfd)
+{
+ asection *s;
+ int ret = 0;
+
+ /* See if we need a PT_MIPS_REGINFO segment. */
+ s = bfd_get_section_by_name (abfd, ".reginfo");
+ if (s && (s->flags & SEC_LOAD))
+ ++ret;
+
+ /* See if we need a PT_MIPS_OPTIONS segment. */
+ if (IRIX_COMPAT (abfd) == ict_irix6
+ && bfd_get_section_by_name (abfd,
+ MIPS_ELF_OPTIONS_SECTION_NAME (abfd)))
+ ++ret;
+
+ /* See if we need a PT_MIPS_RTPROC segment. */
+ if (IRIX_COMPAT (abfd) == ict_irix5
+ && bfd_get_section_by_name (abfd, ".dynamic")
+ && bfd_get_section_by_name (abfd, ".mdebug"))
+ ++ret;
+
+ return ret;
+}
+
+/* Modify the segment map for an IRIX5 executable. */
+
+bfd_boolean
+_bfd_mips_elf_modify_segment_map (bfd *abfd,
+ struct bfd_link_info *info ATTRIBUTE_UNUSED)
+{
+ asection *s;
+ struct elf_segment_map *m, **pm;
+ bfd_size_type amt;
+
+ /* If there is a .reginfo section, we need a PT_MIPS_REGINFO
+ segment. */
+ s = bfd_get_section_by_name (abfd, ".reginfo");
+ if (s != NULL && (s->flags & SEC_LOAD) != 0)
+ {
+ for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
+ if (m->p_type == PT_MIPS_REGINFO)
+ break;
+ if (m == NULL)
+ {
+ amt = sizeof *m;
+ m = bfd_zalloc (abfd, amt);
+ if (m == NULL)
+ return FALSE;
+
+ m->p_type = PT_MIPS_REGINFO;
+ m->count = 1;
+ m->sections[0] = s;
+
+ /* We want to put it after the PHDR and INTERP segments. */
+ pm = &elf_tdata (abfd)->segment_map;
+ while (*pm != NULL
+ && ((*pm)->p_type == PT_PHDR
+ || (*pm)->p_type == PT_INTERP))
+ pm = &(*pm)->next;
+
+ m->next = *pm;
+ *pm = m;
+ }
+ }
+
+ /* For IRIX 6, we don't have .mdebug sections, nor does anything but
+ .dynamic end up in PT_DYNAMIC. However, we do have to insert a
+ PT_MIPS_OPTIONS segment immediately following the program header
+ table. */
+ if (NEWABI_P (abfd)
+ /* On non-IRIX6 new abi, we'll have already created a segment
+ for this section, so don't create another. I'm not sure this
+ is not also the case for IRIX 6, but I can't test it right
+ now. */
+ && IRIX_COMPAT (abfd) == ict_irix6)
+ {
+ for (s = abfd->sections; s; s = s->next)
+ if (elf_section_data (s)->this_hdr.sh_type == SHT_MIPS_OPTIONS)
+ break;
+
+ if (s)
+ {
+ struct elf_segment_map *options_segment;
+
+ pm = &elf_tdata (abfd)->segment_map;
+ while (*pm != NULL
+ && ((*pm)->p_type == PT_PHDR
+ || (*pm)->p_type == PT_INTERP))
+ pm = &(*pm)->next;
+
+ amt = sizeof (struct elf_segment_map);
+ options_segment = bfd_zalloc (abfd, amt);
+ options_segment->next = *pm;
+ options_segment->p_type = PT_MIPS_OPTIONS;
+ options_segment->p_flags = PF_R;
+ options_segment->p_flags_valid = TRUE;
+ options_segment->count = 1;
+ options_segment->sections[0] = s;
+ *pm = options_segment;
+ }
+ }
+ else
+ {
+ if (IRIX_COMPAT (abfd) == ict_irix5)
+ {
+ /* If there are .dynamic and .mdebug sections, we make a room
+ for the RTPROC header. FIXME: Rewrite without section names. */
+ if (bfd_get_section_by_name (abfd, ".interp") == NULL
+ && bfd_get_section_by_name (abfd, ".dynamic") != NULL
+ && bfd_get_section_by_name (abfd, ".mdebug") != NULL)
+ {
+ for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
+ if (m->p_type == PT_MIPS_RTPROC)
+ break;
+ if (m == NULL)
+ {
+ amt = sizeof *m;
+ m = bfd_zalloc (abfd, amt);
+ if (m == NULL)
+ return FALSE;
+
+ m->p_type = PT_MIPS_RTPROC;
+
+ s = bfd_get_section_by_name (abfd, ".rtproc");
+ if (s == NULL)
+ {
+ m->count = 0;
+ m->p_flags = 0;
+ m->p_flags_valid = 1;
+ }
+ else
+ {
+ m->count = 1;
+ m->sections[0] = s;
+ }
+
+ /* We want to put it after the DYNAMIC segment. */
+ pm = &elf_tdata (abfd)->segment_map;
+ while (*pm != NULL && (*pm)->p_type != PT_DYNAMIC)
+ pm = &(*pm)->next;
+ if (*pm != NULL)
+ pm = &(*pm)->next;
+
+ m->next = *pm;
+ *pm = m;
+ }
+ }
+ }
+ /* On IRIX5, the PT_DYNAMIC segment includes the .dynamic,
+ .dynstr, .dynsym, and .hash sections, and everything in
+ between. */
+ for (pm = &elf_tdata (abfd)->segment_map; *pm != NULL;
+ pm = &(*pm)->next)
+ if ((*pm)->p_type == PT_DYNAMIC)
+ break;
+ m = *pm;
+ if (m != NULL && IRIX_COMPAT (abfd) == ict_none)
+ {
+ /* For a normal mips executable the permissions for the PT_DYNAMIC
+ segment are read, write and execute. We do that here since
+ the code in elf.c sets only the read permission. This matters
+ sometimes for the dynamic linker. */
+ if (bfd_get_section_by_name (abfd, ".dynamic") != NULL)
+ {
+ m->p_flags = PF_R | PF_W | PF_X;
+ m->p_flags_valid = 1;
+ }
+ }
+ if (m != NULL
+ && m->count == 1 && strcmp (m->sections[0]->name, ".dynamic") == 0)
+ {
+ static const char *sec_names[] =
+ {
+ ".dynamic", ".dynstr", ".dynsym", ".hash"
+ };
+ bfd_vma low, high;
+ unsigned int i, c;
+ struct elf_segment_map *n;
+
+ low = ~(bfd_vma) 0;
+ high = 0;
+ for (i = 0; i < sizeof sec_names / sizeof sec_names[0]; i++)
+ {
+ s = bfd_get_section_by_name (abfd, sec_names[i]);
+ if (s != NULL && (s->flags & SEC_LOAD) != 0)
+ {
+ bfd_size_type sz;
+
+ if (low > s->vma)
+ low = s->vma;
+ sz = s->_cooked_size;
+ if (sz == 0)
+ sz = s->_raw_size;
+ if (high < s->vma + sz)
+ high = s->vma + sz;
+ }
+ }
+
+ c = 0;
+ for (s = abfd->sections; s != NULL; s = s->next)
+ if ((s->flags & SEC_LOAD) != 0
+ && s->vma >= low
+ && ((s->vma
+ + (s->_cooked_size !=
+ 0 ? s->_cooked_size : s->_raw_size)) <= high))
+ ++c;
+
+ amt = sizeof *n + (bfd_size_type) (c - 1) * sizeof (asection *);
+ n = bfd_zalloc (abfd, amt);
+ if (n == NULL)
+ return FALSE;
+ *n = *m;
+ n->count = c;
+
+ i = 0;
+ for (s = abfd->sections; s != NULL; s = s->next)
+ {
+ if ((s->flags & SEC_LOAD) != 0
+ && s->vma >= low
+ && ((s->vma
+ + (s->_cooked_size != 0 ?
+ s->_cooked_size : s->_raw_size)) <= high))
+ {
+ n->sections[i] = s;
+ ++i;
+ }
+ }
+
+ *pm = n;
+ }
+ }
+
+ return TRUE;
+}
+
+/* Return the section that should be marked against GC for a given
+ relocation. */
+
+asection *
+_bfd_mips_elf_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)
+{
+ /* ??? Do mips16 stub sections need to be handled special? */
+
+ if (h != NULL)
+ {
+ switch (ELF_R_TYPE (sec->owner, rel->r_info))
+ {
+ case R_MIPS_GNU_VTINHERIT:
+ case R_MIPS_GNU_VTENTRY:
+ break;
+
+ default:
+ switch (h->root.type)
+ {
+ case bfd_link_hash_defined:
+ case bfd_link_hash_defweak:
+ return h->root.u.def.section;
+
+ case bfd_link_hash_common:
+ return h->root.u.c.p->section;
+
+ default:
+ break;
+ }
+ }
+ }
+ else
+ return bfd_section_from_elf_index (sec->owner, sym->st_shndx);
+
+ return NULL;
+}
+
+/* Update the got entry reference counts for the section being removed. */
+
+bfd_boolean
+_bfd_mips_elf_gc_sweep_hook (bfd *abfd ATTRIBUTE_UNUSED,
+ struct bfd_link_info *info ATTRIBUTE_UNUSED,
+ asection *sec ATTRIBUTE_UNUSED,
+ const Elf_Internal_Rela *relocs ATTRIBUTE_UNUSED)
+{
+#if 0
+ Elf_Internal_Shdr *symtab_hdr;
+ struct elf_link_hash_entry **sym_hashes;
+ bfd_signed_vma *local_got_refcounts;
+ const Elf_Internal_Rela *rel, *relend;
+ unsigned long r_symndx;
+ struct elf_link_hash_entry *h;
+
+ 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++)
+ switch (ELF_R_TYPE (abfd, rel->r_info))
+ {
+ case R_MIPS_GOT16:
+ case R_MIPS_CALL16:
+ case R_MIPS_CALL_HI16:
+ case R_MIPS_CALL_LO16:
+ case R_MIPS_GOT_HI16:
+ case R_MIPS_GOT_LO16:
+ case R_MIPS_GOT_DISP:
+ case R_MIPS_GOT_PAGE:
+ case R_MIPS_GOT_OFST:
+ /* ??? It would seem that the existing MIPS code does no sort
+ of reference counting or whatnot on its GOT and PLT entries,
+ so it is not possible to garbage collect them at this time. */
+ break;
+
+ default:
+ break;
+ }
+#endif
+
+ return TRUE;
+}
+
+/* Copy data from a MIPS ELF indirect symbol to its direct symbol,
+ hiding the old indirect symbol. Process additional relocation
+ information. Also called for weakdefs, in which case we just let
+ _bfd_elf_link_hash_copy_indirect copy the flags for us. */
+
+void
+_bfd_mips_elf_copy_indirect_symbol (const struct elf_backend_data *bed,
+ struct elf_link_hash_entry *dir,
+ struct elf_link_hash_entry *ind)
+{
+ struct mips_elf_link_hash_entry *dirmips, *indmips;
+
+ _bfd_elf_link_hash_copy_indirect (bed, dir, ind);
+
+ if (ind->root.type != bfd_link_hash_indirect)
+ return;
+
+ dirmips = (struct mips_elf_link_hash_entry *) dir;
+ indmips = (struct mips_elf_link_hash_entry *) ind;
+ dirmips->possibly_dynamic_relocs += indmips->possibly_dynamic_relocs;
+ if (indmips->readonly_reloc)
+ dirmips->readonly_reloc = TRUE;
+ if (indmips->no_fn_stub)
+ dirmips->no_fn_stub = TRUE;
+}
+
+void
+_bfd_mips_elf_hide_symbol (struct bfd_link_info *info,
+ struct elf_link_hash_entry *entry,
+ bfd_boolean force_local)
+{
+ bfd *dynobj;
+ asection *got;
+ struct mips_got_info *g;
+ struct mips_elf_link_hash_entry *h;
+
+ h = (struct mips_elf_link_hash_entry *) entry;
+ if (h->forced_local)
+ return;
+ h->forced_local = force_local;
+
+ dynobj = elf_hash_table (info)->dynobj;
+ if (dynobj != NULL && force_local)
+ {
+ got = mips_elf_got_section (dynobj, FALSE);
+ g = mips_elf_section_data (got)->u.got_info;
+
+ if (g->next)
+ {
+ struct mips_got_entry e;
+ struct mips_got_info *gg = g;
+
+ /* Since we're turning what used to be a global symbol into a
+ local one, bump up the number of local entries of each GOT
+ that had an entry for it. This will automatically decrease
+ the number of global entries, since global_gotno is actually
+ the upper limit of global entries. */
+ e.abfd = dynobj;
+ e.symndx = -1;
+ e.d.h = h;
+
+ for (g = g->next; g != gg; g = g->next)
+ if (htab_find (g->got_entries, &e))
+ {
+ BFD_ASSERT (g->global_gotno > 0);
+ g->local_gotno++;
+ g->global_gotno--;
+ }
+
+ /* If this was a global symbol forced into the primary GOT, we
+ no longer need an entry for it. We can't release the entry
+ at this point, but we must at least stop counting it as one
+ of the symbols that required a forced got entry. */
+ if (h->root.got.offset == 2)
+ {
+ BFD_ASSERT (gg->assigned_gotno > 0);
+ gg->assigned_gotno--;
+ }
+ }
+ else if (g->global_gotno == 0 && g->global_gotsym == NULL)
+ /* If we haven't got through GOT allocation yet, just bump up the
+ number of local entries, as this symbol won't be counted as
+ global. */
+ g->local_gotno++;
+ else if (h->root.got.offset == 1)
+ {
+ /* If we're past non-multi-GOT allocation and this symbol had
+ been marked for a global got entry, give it a local entry
+ instead. */
+ BFD_ASSERT (g->global_gotno > 0);
+ g->local_gotno++;
+ g->global_gotno--;
+ }
+ }
+
+ _bfd_elf_link_hash_hide_symbol (info, &h->root, force_local);
+}
+
+#define PDR_SIZE 32
+
+bfd_boolean
+_bfd_mips_elf_discard_info (bfd *abfd, struct elf_reloc_cookie *cookie,
+ struct bfd_link_info *info)
+{
+ asection *o;
+ bfd_boolean ret = FALSE;
+ unsigned char *tdata;
+ size_t i, skip;
+
+ o = bfd_get_section_by_name (abfd, ".pdr");
+ if (! o)
+ return FALSE;
+ if (o->_raw_size == 0)
+ return FALSE;
+ if (o->_raw_size % PDR_SIZE != 0)
+ return FALSE;
+ if (o->output_section != NULL
+ && bfd_is_abs_section (o->output_section))
+ return FALSE;
+
+ tdata = bfd_zmalloc (o->_raw_size / PDR_SIZE);
+ if (! tdata)
+ return FALSE;
+
+ cookie->rels = _bfd_elf_link_read_relocs (abfd, o, NULL, NULL,
+ info->keep_memory);
+ if (!cookie->rels)
+ {
+ free (tdata);
+ return FALSE;
+ }
+
+ cookie->rel = cookie->rels;
+ cookie->relend = cookie->rels + o->reloc_count;
+
+ for (i = 0, skip = 0; i < o->_raw_size / PDR_SIZE; i ++)
+ {
+ if (bfd_elf_reloc_symbol_deleted_p (i * PDR_SIZE, cookie))
+ {
+ tdata[i] = 1;
+ skip ++;
+ }
+ }
+
+ if (skip != 0)
+ {
+ mips_elf_section_data (o)->u.tdata = tdata;
+ o->_cooked_size = o->_raw_size - skip * PDR_SIZE;
+ ret = TRUE;
+ }
+ else
+ free (tdata);
+
+ if (! info->keep_memory)
+ free (cookie->rels);
+
+ return ret;
+}
+
+bfd_boolean
+_bfd_mips_elf_ignore_discarded_relocs (asection *sec)
+{
+ if (strcmp (sec->name, ".pdr") == 0)
+ return TRUE;
+ return FALSE;
+}
+
+bfd_boolean
+_bfd_mips_elf_write_section (bfd *output_bfd, asection *sec,
+ bfd_byte *contents)
+{
+ bfd_byte *to, *from, *end;
+ int i;
+
+ if (strcmp (sec->name, ".pdr") != 0)
+ return FALSE;
+
+ if (mips_elf_section_data (sec)->u.tdata == NULL)
+ return FALSE;
+
+ to = contents;
+ end = contents + sec->_raw_size;
+ for (from = contents, i = 0;
+ from < end;
+ from += PDR_SIZE, i++)
+ {
+ if ((mips_elf_section_data (sec)->u.tdata)[i] == 1)
+ continue;
+ if (to != from)
+ memcpy (to, from, PDR_SIZE);
+ to += PDR_SIZE;
+ }
+ bfd_set_section_contents (output_bfd, sec->output_section, contents,
+ sec->output_offset, sec->_cooked_size);
+ return TRUE;
+}
+
+/* MIPS ELF uses a special find_nearest_line routine in order the
+ handle the ECOFF debugging information. */
+
+struct mips_elf_find_line
+{
+ struct ecoff_debug_info d;
+ struct ecoff_find_line i;
+};
+
+bfd_boolean
+_bfd_mips_elf_find_nearest_line (bfd *abfd, asection *section,
+ asymbol **symbols, bfd_vma offset,
+ const char **filename_ptr,
+ const char **functionname_ptr,
+ unsigned int *line_ptr)
+{
+ asection *msec;
+
+ if (_bfd_dwarf1_find_nearest_line (abfd, section, symbols, offset,
+ filename_ptr, functionname_ptr,
+ line_ptr))
+ return TRUE;
+
+ if (_bfd_dwarf2_find_nearest_line (abfd, section, symbols, offset,
+ filename_ptr, functionname_ptr,
+ line_ptr, ABI_64_P (abfd) ? 8 : 0,
+ &elf_tdata (abfd)->dwarf2_find_line_info))
+ return TRUE;
+
+ msec = bfd_get_section_by_name (abfd, ".mdebug");
+ if (msec != NULL)
+ {
+ flagword origflags;
+ struct mips_elf_find_line *fi;
+ const struct ecoff_debug_swap * const swap =
+ get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap;
+
+ /* If we are called during a link, mips_elf_final_link may have
+ cleared the SEC_HAS_CONTENTS field. We force it back on here
+ if appropriate (which it normally will be). */
+ origflags = msec->flags;
+ if (elf_section_data (msec)->this_hdr.sh_type != SHT_NOBITS)
+ msec->flags |= SEC_HAS_CONTENTS;
+
+ fi = elf_tdata (abfd)->find_line_info;
+ if (fi == NULL)
+ {
+ bfd_size_type external_fdr_size;
+ char *fraw_src;
+ char *fraw_end;
+ struct fdr *fdr_ptr;
+ bfd_size_type amt = sizeof (struct mips_elf_find_line);
+
+ fi = bfd_zalloc (abfd, amt);
+ if (fi == NULL)
+ {
+ msec->flags = origflags;
+ return FALSE;
+ }
+
+ if (! _bfd_mips_elf_read_ecoff_info (abfd, msec, &fi->d))
+ {
+ msec->flags = origflags;
+ return FALSE;
+ }
+
+ /* Swap in the FDR information. */
+ amt = fi->d.symbolic_header.ifdMax * sizeof (struct fdr);
+ fi->d.fdr = bfd_alloc (abfd, amt);
+ if (fi->d.fdr == NULL)
+ {
+ msec->flags = origflags;
+ return FALSE;
+ }
+ external_fdr_size = swap->external_fdr_size;
+ fdr_ptr = fi->d.fdr;
+ fraw_src = (char *) fi->d.external_fdr;
+ fraw_end = (fraw_src
+ + fi->d.symbolic_header.ifdMax * external_fdr_size);
+ for (; fraw_src < fraw_end; fraw_src += external_fdr_size, fdr_ptr++)
+ (*swap->swap_fdr_in) (abfd, fraw_src, fdr_ptr);
+
+ elf_tdata (abfd)->find_line_info = fi;
+
+ /* Note that we don't bother to ever free this information.
+ find_nearest_line is either called all the time, as in
+ objdump -l, so the information should be saved, or it is
+ rarely called, as in ld error messages, so the memory
+ wasted is unimportant. Still, it would probably be a
+ good idea for free_cached_info to throw it away. */
+ }
+
+ if (_bfd_ecoff_locate_line (abfd, section, offset, &fi->d, swap,
+ &fi->i, filename_ptr, functionname_ptr,
+ line_ptr))
+ {
+ msec->flags = origflags;
+ return TRUE;
+ }
+
+ msec->flags = origflags;
+ }
+
+ /* Fall back on the generic ELF find_nearest_line routine. */
+
+ return _bfd_elf_find_nearest_line (abfd, section, symbols, offset,
+ filename_ptr, functionname_ptr,
+ line_ptr);
+}
+
+/* When are writing out the .options or .MIPS.options section,
+ remember the bytes we are writing out, so that we can install the
+ GP value in the section_processing routine. */
+
+bfd_boolean
+_bfd_mips_elf_set_section_contents (bfd *abfd, sec_ptr section,
+ const void *location,
+ file_ptr offset, bfd_size_type count)
+{
+ if (strcmp (section->name, MIPS_ELF_OPTIONS_SECTION_NAME (abfd)) == 0)
+ {
+ bfd_byte *c;
+
+ if (elf_section_data (section) == NULL)
+ {
+ bfd_size_type amt = sizeof (struct bfd_elf_section_data);
+ section->used_by_bfd = bfd_zalloc (abfd, amt);
+ if (elf_section_data (section) == NULL)
+ return FALSE;
+ }
+ c = mips_elf_section_data (section)->u.tdata;
+ if (c == NULL)
+ {
+ bfd_size_type size;
+
+ if (section->_cooked_size != 0)
+ size = section->_cooked_size;
+ else
+ size = section->_raw_size;
+ c = bfd_zalloc (abfd, size);
+ if (c == NULL)
+ return FALSE;
+ mips_elf_section_data (section)->u.tdata = c;
+ }
+
+ memcpy (c + offset, location, count);
+ }
+
+ return _bfd_elf_set_section_contents (abfd, section, location, offset,
+ count);
+}
+
+/* This is almost identical to bfd_generic_get_... except that some
+ MIPS relocations need to be handled specially. Sigh. */
+
+bfd_byte *
+_bfd_elf_mips_get_relocated_section_contents
+ (bfd *abfd,
+ struct bfd_link_info *link_info,
+ struct bfd_link_order *link_order,
+ bfd_byte *data,
+ bfd_boolean relocatable,
+ asymbol **symbols)
+{
+ /* Get enough memory to hold the stuff */
+ bfd *input_bfd = link_order->u.indirect.section->owner;
+ asection *input_section = link_order->u.indirect.section;
+
+ long reloc_size = bfd_get_reloc_upper_bound (input_bfd, input_section);
+ arelent **reloc_vector = NULL;
+ long reloc_count;
+
+ if (reloc_size < 0)
+ goto error_return;
+
+ reloc_vector = bfd_malloc (reloc_size);
+ if (reloc_vector == NULL && reloc_size != 0)
+ goto error_return;
+
+ /* read in the section */
+ if (!bfd_get_section_contents (input_bfd, input_section, data, 0,
+ input_section->_raw_size))
+ goto error_return;
+
+ /* We're not relaxing the section, so just copy the size info */
+ input_section->_cooked_size = input_section->_raw_size;
+ input_section->reloc_done = TRUE;
+
+ reloc_count = bfd_canonicalize_reloc (input_bfd,
+ input_section,
+ reloc_vector,
+ symbols);
+ if (reloc_count < 0)
+ goto error_return;
+
+ if (reloc_count > 0)
+ {
+ arelent **parent;
+ /* for mips */
+ int gp_found;
+ bfd_vma gp = 0x12345678; /* initialize just to shut gcc up */
+
+ {
+ struct bfd_hash_entry *h;
+ struct bfd_link_hash_entry *lh;
+ /* Skip all this stuff if we aren't mixing formats. */
+ if (abfd && input_bfd
+ && abfd->xvec == input_bfd->xvec)
+ lh = 0;
+ else
+ {
+ h = bfd_hash_lookup (&link_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 = 0;
+ break;
+ case bfd_link_hash_defined:
+ case bfd_link_hash_defweak:
+ gp_found = 1;
+ gp = 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 = 0;
+ }
+ /* end mips */
+ for (parent = reloc_vector; *parent != NULL; parent++)
+ {
+ char *error_message = NULL;
+ bfd_reloc_status_type r;
+
+ /* Specific to MIPS: Deal with relocation types that require
+ knowing the gp of the output bfd. */
+ asymbol *sym = *(*parent)->sym_ptr_ptr;
+ if (bfd_is_abs_section (sym->section) && abfd)
+ {
+ /* The special_function wouldn't get called anyway. */
+ }
+ else if (!gp_found)
+ {
+ /* The gp isn't there; let the special function code
+ fall over on its own. */
+ }
+ else if ((*parent)->howto->special_function
+ == _bfd_mips_elf32_gprel16_reloc)
+ {
+ /* bypass special_function call */
+ r = _bfd_mips_elf_gprel16_with_gp (input_bfd, sym, *parent,
+ input_section, relocatable,
+ data, gp);
+ goto skip_bfd_perform_relocation;
+ }
+ /* end mips specific stuff */
+
+ r = bfd_perform_relocation (input_bfd, *parent, data, input_section,
+ relocatable ? abfd : NULL,
+ &error_message);
+ skip_bfd_perform_relocation:
+
+ if (relocatable)
+ {
+ asection *os = input_section->output_section;
+
+ /* A partial link, so keep the relocs */
+ os->orelocation[os->reloc_count] = *parent;
+ os->reloc_count++;
+ }
+
+ if (r != bfd_reloc_ok)
+ {
+ switch (r)
+ {
+ case bfd_reloc_undefined:
+ if (!((*link_info->callbacks->undefined_symbol)
+ (link_info, bfd_asymbol_name (*(*parent)->sym_ptr_ptr),
+ input_bfd, input_section, (*parent)->address,
+ TRUE)))
+ goto error_return;
+ break;
+ case bfd_reloc_dangerous:
+ BFD_ASSERT (error_message != NULL);
+ if (!((*link_info->callbacks->reloc_dangerous)
+ (link_info, error_message, input_bfd, input_section,
+ (*parent)->address)))
+ goto error_return;
+ break;
+ case bfd_reloc_overflow:
+ if (!((*link_info->callbacks->reloc_overflow)
+ (link_info, bfd_asymbol_name (*(*parent)->sym_ptr_ptr),
+ (*parent)->howto->name, (*parent)->addend,
+ input_bfd, input_section, (*parent)->address)))
+ goto error_return;
+ break;
+ case bfd_reloc_outofrange:
+ default:
+ abort ();
+ break;
+ }
+
+ }
+ }
+ }
+ if (reloc_vector != NULL)
+ free (reloc_vector);
+ return data;
+
+error_return:
+ if (reloc_vector != NULL)
+ free (reloc_vector);
+ return NULL;
+}
+
+/* Create a MIPS ELF linker hash table. */
+
+struct bfd_link_hash_table *
+_bfd_mips_elf_link_hash_table_create (bfd *abfd)
+{
+ struct mips_elf_link_hash_table *ret;
+ bfd_size_type amt = sizeof (struct mips_elf_link_hash_table);
+
+ ret = bfd_malloc (amt);
+ if (ret == NULL)
+ return NULL;
+
+ if (! _bfd_elf_link_hash_table_init (&ret->root, abfd,
+ mips_elf_link_hash_newfunc))
+ {
+ free (ret);
+ return NULL;
+ }
+
+#if 0
+ /* We no longer use this. */
+ for (i = 0; i < SIZEOF_MIPS_DYNSYM_SECNAMES; i++)
+ ret->dynsym_sec_strindex[i] = (bfd_size_type) -1;
+#endif
+ ret->procedure_count = 0;
+ ret->compact_rel_size = 0;
+ ret->use_rld_obj_head = FALSE;
+ ret->rld_value = 0;
+ ret->mips16_stubs_seen = FALSE;
+
+ return &ret->root.root;
+}
+
+/* We need to use a special link routine to handle the .reginfo and
+ the .mdebug sections. We need to merge all instances of these
+ sections together, not write them all out sequentially. */
+
+bfd_boolean
+_bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info)
+{
+ asection **secpp;
+ asection *o;
+ struct bfd_link_order *p;
+ asection *reginfo_sec, *mdebug_sec, *gptab_data_sec, *gptab_bss_sec;
+ asection *rtproc_sec;
+ Elf32_RegInfo reginfo;
+ struct ecoff_debug_info debug;
+ const struct ecoff_debug_swap *swap
+ = get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap;
+ HDRR *symhdr = &debug.symbolic_header;
+ void *mdebug_handle = NULL;
+ asection *s;
+ EXTR esym;
+ unsigned int i;
+ bfd_size_type amt;
+
+ static const char * const secname[] =
+ {
+ ".text", ".init", ".fini", ".data",
+ ".rodata", ".sdata", ".sbss", ".bss"
+ };
+ static const int sc[] =
+ {
+ scText, scInit, scFini, scData,
+ scRData, scSData, scSBss, scBss
+ };
+
+ /* We'd carefully arranged the dynamic symbol indices, and then the
+ generic size_dynamic_sections renumbered them out from under us.
+ Rather than trying somehow to prevent the renumbering, just do
+ the sort again. */
+ if (elf_hash_table (info)->dynamic_sections_created)
+ {
+ bfd *dynobj;
+ asection *got;
+ struct mips_got_info *g;
+
+ /* When we resort, we must tell mips_elf_sort_hash_table what
+ the lowest index it may use is. That's the number of section
+ symbols we're going to add. The generic ELF linker only
+ adds these symbols when building a shared object. Note that
+ we count the sections after (possibly) removing the .options
+ section above. */
+ if (! mips_elf_sort_hash_table (info, (info->shared
+ ? bfd_count_sections (abfd) + 1
+ : 1)))
+ return FALSE;
+
+ /* Make sure we didn't grow the global .got region. */
+ dynobj = elf_hash_table (info)->dynobj;
+ got = mips_elf_got_section (dynobj, FALSE);
+ g = mips_elf_section_data (got)->u.got_info;
+
+ if (g->global_gotsym != NULL)
+ BFD_ASSERT ((elf_hash_table (info)->dynsymcount
+ - g->global_gotsym->dynindx)
+ <= g->global_gotno);
+ }
+
+#if 0
+ /* We want to set the GP value for ld -r. */
+ /* On IRIX5, we omit the .options section. On IRIX6, however, we
+ include it, even though we don't process it quite right. (Some
+ entries are supposed to be merged.) Empirically, we seem to be
+ better off including it then not. */
+ if (IRIX_COMPAT (abfd) == ict_irix5 || IRIX_COMPAT (abfd) == ict_none)
+ for (secpp = &abfd->sections; *secpp != NULL; secpp = &(*secpp)->next)
+ {
+ if (strcmp ((*secpp)->name, MIPS_ELF_OPTIONS_SECTION_NAME (abfd)) == 0)
+ {
+ for (p = (*secpp)->link_order_head; p != NULL; p = p->next)
+ if (p->type == bfd_indirect_link_order)
+ p->u.indirect.section->flags &= ~SEC_HAS_CONTENTS;
+ (*secpp)->link_order_head = NULL;
+ bfd_section_list_remove (abfd, secpp);
+ --abfd->section_count;
+
+ break;
+ }
+ }
+
+ /* We include .MIPS.options, even though we don't process it quite right.
+ (Some entries are supposed to be merged.) At IRIX6 empirically we seem
+ to be better off including it than not. */
+ for (secpp = &abfd->sections; *secpp != NULL; secpp = &(*secpp)->next)
+ {
+ if (strcmp ((*secpp)->name, ".MIPS.options") == 0)
+ {
+ for (p = (*secpp)->link_order_head; p != NULL; p = p->next)
+ if (p->type == bfd_indirect_link_order)
+ p->u.indirect.section->flags &=~ SEC_HAS_CONTENTS;
+ (*secpp)->link_order_head = NULL;
+ bfd_section_list_remove (abfd, secpp);
+ --abfd->section_count;
+
+ break;
+ }
+ }
+#endif
+
+ /* Get a value for the GP register. */
+ if (elf_gp (abfd) == 0)
+ {
+ struct bfd_link_hash_entry *h;
+
+ h = bfd_link_hash_lookup (info->hash, "_gp", FALSE, FALSE, TRUE);
+ if (h != NULL && h->type == bfd_link_hash_defined)
+ elf_gp (abfd) = (h->u.def.value
+ + h->u.def.section->output_section->vma
+ + h->u.def.section->output_offset);
+ else if (info->relocatable)
+ {
+ bfd_vma lo = MINUS_ONE;
+
+ /* Find the GP-relative section with the lowest offset. */
+ for (o = abfd->sections; o != NULL; o = o->next)
+ if (o->vma < lo
+ && (elf_section_data (o)->this_hdr.sh_flags & SHF_MIPS_GPREL))
+ lo = o->vma;
+
+ /* And calculate GP relative to that. */
+ elf_gp (abfd) = lo + ELF_MIPS_GP_OFFSET (abfd);
+ }
+ else
+ {
+ /* If the relocate_section function needs to do a reloc
+ involving the GP value, it should make a reloc_dangerous
+ callback to warn that GP is not defined. */
+ }
+ }
+
+ /* Go through the sections and collect the .reginfo and .mdebug
+ information. */
+ reginfo_sec = NULL;
+ mdebug_sec = NULL;
+ gptab_data_sec = NULL;
+ gptab_bss_sec = NULL;
+ for (o = abfd->sections; o != NULL; o = o->next)
+ {
+ if (strcmp (o->name, ".reginfo") == 0)
+ {
+ memset (&reginfo, 0, sizeof reginfo);
+
+ /* We have found the .reginfo section in the output file.
+ Look through all the link_orders comprising it and merge
+ the information together. */
+ for (p = o->link_order_head; p != NULL; p = p->next)
+ {
+ asection *input_section;
+ bfd *input_bfd;
+ Elf32_External_RegInfo ext;
+ Elf32_RegInfo sub;
+
+ if (p->type != bfd_indirect_link_order)
+ {
+ if (p->type == bfd_data_link_order)
+ continue;
+ abort ();
+ }
+
+ input_section = p->u.indirect.section;
+ input_bfd = input_section->owner;
+
+ /* The linker emulation code has probably clobbered the
+ size to be zero bytes. */
+ if (input_section->_raw_size == 0)
+ input_section->_raw_size = sizeof (Elf32_External_RegInfo);
+
+ if (! bfd_get_section_contents (input_bfd, input_section,
+ &ext, 0, sizeof ext))
+ return FALSE;
+
+ bfd_mips_elf32_swap_reginfo_in (input_bfd, &ext, &sub);
+
+ reginfo.ri_gprmask |= sub.ri_gprmask;
+ reginfo.ri_cprmask[0] |= sub.ri_cprmask[0];
+ reginfo.ri_cprmask[1] |= sub.ri_cprmask[1];
+ reginfo.ri_cprmask[2] |= sub.ri_cprmask[2];
+ reginfo.ri_cprmask[3] |= sub.ri_cprmask[3];
+
+ /* ri_gp_value is set by the function
+ mips_elf32_section_processing when the section is
+ finally written out. */
+
+ /* Hack: reset the SEC_HAS_CONTENTS flag so that
+ elf_link_input_bfd ignores this section. */
+ input_section->flags &= ~SEC_HAS_CONTENTS;
+ }
+
+ /* Size has been set in _bfd_mips_elf_always_size_sections. */
+ BFD_ASSERT(o->_raw_size == sizeof (Elf32_External_RegInfo));
+
+ /* Skip this section later on (I don't think this currently
+ matters, but someday it might). */
+ o->link_order_head = NULL;
+
+ reginfo_sec = o;
+ }
+
+ if (strcmp (o->name, ".mdebug") == 0)
+ {
+ struct extsym_info einfo;
+ bfd_vma last;
+
+ /* We have found the .mdebug section in the output file.
+ Look through all the link_orders comprising it and merge
+ the information together. */
+ symhdr->magic = swap->sym_magic;
+ /* FIXME: What should the version stamp be? */
+ symhdr->vstamp = 0;
+ symhdr->ilineMax = 0;
+ symhdr->cbLine = 0;
+ symhdr->idnMax = 0;
+ symhdr->ipdMax = 0;
+ symhdr->isymMax = 0;
+ symhdr->ioptMax = 0;
+ symhdr->iauxMax = 0;
+ symhdr->issMax = 0;
+ symhdr->issExtMax = 0;
+ symhdr->ifdMax = 0;
+ symhdr->crfd = 0;
+ symhdr->iextMax = 0;
+
+ /* We accumulate the debugging information itself in the
+ debug_info structure. */
+ debug.line = NULL;
+ debug.external_dnr = NULL;
+ debug.external_pdr = NULL;
+ debug.external_sym = NULL;
+ debug.external_opt = NULL;
+ debug.external_aux = NULL;
+ debug.ss = NULL;
+ debug.ssext = debug.ssext_end = NULL;
+ debug.external_fdr = NULL;
+ debug.external_rfd = NULL;
+ debug.external_ext = debug.external_ext_end = NULL;
+
+ mdebug_handle = bfd_ecoff_debug_init (abfd, &debug, swap, info);
+ if (mdebug_handle == NULL)
+ return FALSE;
+
+ esym.jmptbl = 0;
+ esym.cobol_main = 0;
+ esym.weakext = 0;
+ esym.reserved = 0;
+ esym.ifd = ifdNil;
+ esym.asym.iss = issNil;
+ esym.asym.st = stLocal;
+ esym.asym.reserved = 0;
+ esym.asym.index = indexNil;
+ last = 0;
+ for (i = 0; i < sizeof (secname) / sizeof (secname[0]); i++)
+ {
+ esym.asym.sc = sc[i];
+ s = bfd_get_section_by_name (abfd, secname[i]);
+ if (s != NULL)
+ {
+ esym.asym.value = s->vma;
+ last = s->vma + s->_raw_size;
+ }
+ else
+ esym.asym.value = last;
+ if (!bfd_ecoff_debug_one_external (abfd, &debug, swap,
+ secname[i], &esym))
+ return FALSE;
+ }
+
+ for (p = o->link_order_head; p != NULL; p = p->next)
+ {
+ asection *input_section;
+ bfd *input_bfd;
+ const struct ecoff_debug_swap *input_swap;
+ struct ecoff_debug_info input_debug;
+ char *eraw_src;
+ char *eraw_end;
+
+ if (p->type != bfd_indirect_link_order)
+ {
+ if (p->type == bfd_data_link_order)
+ continue;
+ abort ();
+ }
+
+ input_section = p->u.indirect.section;
+ input_bfd = input_section->owner;
+
+ if (bfd_get_flavour (input_bfd) != bfd_target_elf_flavour
+ || (get_elf_backend_data (input_bfd)
+ ->elf_backend_ecoff_debug_swap) == NULL)
+ {
+ /* I don't know what a non MIPS ELF bfd would be
+ doing with a .mdebug section, but I don't really
+ want to deal with it. */
+ continue;
+ }
+
+ input_swap = (get_elf_backend_data (input_bfd)
+ ->elf_backend_ecoff_debug_swap);
+
+ BFD_ASSERT (p->size == input_section->_raw_size);
+
+ /* The ECOFF linking code expects that we have already
+ read in the debugging information and set up an
+ ecoff_debug_info structure, so we do that now. */
+ if (! _bfd_mips_elf_read_ecoff_info (input_bfd, input_section,
+ &input_debug))
+ return FALSE;
+
+ if (! (bfd_ecoff_debug_accumulate
+ (mdebug_handle, abfd, &debug, swap, input_bfd,
+ &input_debug, input_swap, info)))
+ return FALSE;
+
+ /* Loop through the external symbols. For each one with
+ interesting information, try to find the symbol in
+ the linker global hash table and save the information
+ for the output external symbols. */
+ eraw_src = input_debug.external_ext;
+ eraw_end = (eraw_src
+ + (input_debug.symbolic_header.iextMax
+ * input_swap->external_ext_size));
+ for (;
+ eraw_src < eraw_end;
+ eraw_src += input_swap->external_ext_size)
+ {
+ EXTR ext;
+ const char *name;
+ struct mips_elf_link_hash_entry *h;
+
+ (*input_swap->swap_ext_in) (input_bfd, eraw_src, &ext);
+ if (ext.asym.sc == scNil
+ || ext.asym.sc == scUndefined
+ || ext.asym.sc == scSUndefined)
+ continue;
+
+ name = input_debug.ssext + ext.asym.iss;
+ h = mips_elf_link_hash_lookup (mips_elf_hash_table (info),
+ name, FALSE, FALSE, TRUE);
+ if (h == NULL || h->esym.ifd != -2)
+ continue;
+
+ if (ext.ifd != -1)
+ {
+ BFD_ASSERT (ext.ifd
+ < input_debug.symbolic_header.ifdMax);
+ ext.ifd = input_debug.ifdmap[ext.ifd];
+ }
+
+ h->esym = ext;
+ }
+
+ /* Free up the information we just read. */
+ free (input_debug.line);
+ free (input_debug.external_dnr);
+ free (input_debug.external_pdr);
+ free (input_debug.external_sym);
+ free (input_debug.external_opt);
+ free (input_debug.external_aux);
+ free (input_debug.ss);
+ free (input_debug.ssext);
+ free (input_debug.external_fdr);
+ free (input_debug.external_rfd);
+ free (input_debug.external_ext);
+
+ /* Hack: reset the SEC_HAS_CONTENTS flag so that
+ elf_link_input_bfd ignores this section. */
+ input_section->flags &= ~SEC_HAS_CONTENTS;
+ }
+
+ if (SGI_COMPAT (abfd) && info->shared)
+ {
+ /* Create .rtproc section. */
+ rtproc_sec = bfd_get_section_by_name (abfd, ".rtproc");
+ if (rtproc_sec == NULL)
+ {
+ flagword flags = (SEC_HAS_CONTENTS | SEC_IN_MEMORY
+ | SEC_LINKER_CREATED | SEC_READONLY);
+
+ rtproc_sec = bfd_make_section (abfd, ".rtproc");
+ if (rtproc_sec == NULL
+ || ! bfd_set_section_flags (abfd, rtproc_sec, flags)
+ || ! bfd_set_section_alignment (abfd, rtproc_sec, 4))
+ return FALSE;
+ }
+
+ if (! mips_elf_create_procedure_table (mdebug_handle, abfd,
+ info, rtproc_sec,
+ &debug))
+ return FALSE;
+ }
+
+ /* Build the external symbol information. */
+ einfo.abfd = abfd;
+ einfo.info = info;
+ einfo.debug = &debug;
+ einfo.swap = swap;
+ einfo.failed = FALSE;
+ mips_elf_link_hash_traverse (mips_elf_hash_table (info),
+ mips_elf_output_extsym, &einfo);
+ if (einfo.failed)
+ return FALSE;
+
+ /* Set the size of the .mdebug section. */
+ o->_raw_size = bfd_ecoff_debug_size (abfd, &debug, swap);
+
+ /* Skip this section later on (I don't think this currently
+ matters, but someday it might). */
+ o->link_order_head = NULL;
+
+ mdebug_sec = o;
+ }
+
+ if (strncmp (o->name, ".gptab.", sizeof ".gptab." - 1) == 0)
+ {
+ const char *subname;
+ unsigned int c;
+ Elf32_gptab *tab;
+ Elf32_External_gptab *ext_tab;
+ unsigned int j;
+
+ /* The .gptab.sdata and .gptab.sbss sections hold
+ information describing how the small data area would
+ change depending upon the -G switch. These sections
+ not used in executables files. */
+ if (! info->relocatable)
+ {
+ for (p = o->link_order_head; p != NULL; p = p->next)
+ {
+ asection *input_section;
+
+ if (p->type != bfd_indirect_link_order)
+ {
+ if (p->type == bfd_data_link_order)
+ continue;
+ abort ();
+ }
+
+ input_section = p->u.indirect.section;
+
+ /* Hack: reset the SEC_HAS_CONTENTS flag so that
+ elf_link_input_bfd ignores this section. */
+ input_section->flags &= ~SEC_HAS_CONTENTS;
+ }
+
+ /* Skip this section later on (I don't think this
+ currently matters, but someday it might). */
+ o->link_order_head = NULL;
+
+ /* Really remove the section. */
+ for (secpp = &abfd->sections;
+ *secpp != o;
+ secpp = &(*secpp)->next)
+ ;
+ bfd_section_list_remove (abfd, secpp);
+ --abfd->section_count;
+
+ continue;
+ }
+
+ /* There is one gptab for initialized data, and one for
+ uninitialized data. */
+ if (strcmp (o->name, ".gptab.sdata") == 0)
+ gptab_data_sec = o;
+ else if (strcmp (o->name, ".gptab.sbss") == 0)
+ gptab_bss_sec = o;
+ else
+ {
+ (*_bfd_error_handler)
+ (_("%s: illegal section name `%s'"),
+ bfd_get_filename (abfd), o->name);
+ bfd_set_error (bfd_error_nonrepresentable_section);
+ return FALSE;
+ }
+
+ /* The linker script always combines .gptab.data and
+ .gptab.sdata into .gptab.sdata, and likewise for
+ .gptab.bss and .gptab.sbss. It is possible that there is
+ no .sdata or .sbss section in the output file, in which
+ case we must change the name of the output section. */
+ subname = o->name + sizeof ".gptab" - 1;
+ if (bfd_get_section_by_name (abfd, subname) == NULL)
+ {
+ if (o == gptab_data_sec)
+ o->name = ".gptab.data";
+ else
+ o->name = ".gptab.bss";
+ subname = o->name + sizeof ".gptab" - 1;
+ BFD_ASSERT (bfd_get_section_by_name (abfd, subname) != NULL);
+ }
+
+ /* Set up the first entry. */
+ c = 1;
+ amt = c * sizeof (Elf32_gptab);
+ tab = bfd_malloc (amt);
+ if (tab == NULL)
+ return FALSE;
+ tab[0].gt_header.gt_current_g_value = elf_gp_size (abfd);
+ tab[0].gt_header.gt_unused = 0;
+
+ /* Combine the input sections. */
+ for (p = o->link_order_head; p != NULL; p = p->next)
+ {
+ asection *input_section;
+ bfd *input_bfd;
+ bfd_size_type size;
+ unsigned long last;
+ bfd_size_type gpentry;
+
+ if (p->type != bfd_indirect_link_order)
+ {
+ if (p->type == bfd_data_link_order)
+ continue;
+ abort ();
+ }
+
+ input_section = p->u.indirect.section;
+ input_bfd = input_section->owner;
+
+ /* Combine the gptab entries for this input section one
+ by one. We know that the input gptab entries are
+ sorted by ascending -G value. */
+ size = bfd_section_size (input_bfd, input_section);
+ last = 0;
+ for (gpentry = sizeof (Elf32_External_gptab);
+ gpentry < size;
+ gpentry += sizeof (Elf32_External_gptab))
+ {
+ Elf32_External_gptab ext_gptab;
+ Elf32_gptab int_gptab;
+ unsigned long val;
+ unsigned long add;
+ bfd_boolean exact;
+ unsigned int look;
+
+ if (! (bfd_get_section_contents
+ (input_bfd, input_section, &ext_gptab, gpentry,
+ sizeof (Elf32_External_gptab))))
+ {
+ free (tab);
+ return FALSE;
+ }
+
+ bfd_mips_elf32_swap_gptab_in (input_bfd, &ext_gptab,
+ &int_gptab);
+ val = int_gptab.gt_entry.gt_g_value;
+ add = int_gptab.gt_entry.gt_bytes - last;
+
+ exact = FALSE;
+ for (look = 1; look < c; look++)
+ {
+ if (tab[look].gt_entry.gt_g_value >= val)
+ tab[look].gt_entry.gt_bytes += add;
+
+ if (tab[look].gt_entry.gt_g_value == val)
+ exact = TRUE;
+ }
+
+ if (! exact)
+ {
+ Elf32_gptab *new_tab;
+ unsigned int max;
+
+ /* We need a new table entry. */
+ amt = (bfd_size_type) (c + 1) * sizeof (Elf32_gptab);
+ new_tab = bfd_realloc (tab, amt);
+ if (new_tab == NULL)
+ {
+ free (tab);
+ return FALSE;
+ }
+ tab = new_tab;
+ tab[c].gt_entry.gt_g_value = val;
+ tab[c].gt_entry.gt_bytes = add;
+
+ /* Merge in the size for the next smallest -G
+ value, since that will be implied by this new
+ value. */
+ max = 0;
+ for (look = 1; look < c; look++)
+ {
+ if (tab[look].gt_entry.gt_g_value < val
+ && (max == 0
+ || (tab[look].gt_entry.gt_g_value
+ > tab[max].gt_entry.gt_g_value)))
+ max = look;
+ }
+ if (max != 0)
+ tab[c].gt_entry.gt_bytes +=
+ tab[max].gt_entry.gt_bytes;
+
+ ++c;
+ }
+
+ last = int_gptab.gt_entry.gt_bytes;
+ }
+
+ /* Hack: reset the SEC_HAS_CONTENTS flag so that
+ elf_link_input_bfd ignores this section. */
+ input_section->flags &= ~SEC_HAS_CONTENTS;
+ }
+
+ /* The table must be sorted by -G value. */
+ if (c > 2)
+ qsort (tab + 1, c - 1, sizeof (tab[0]), gptab_compare);
+
+ /* Swap out the table. */
+ amt = (bfd_size_type) c * sizeof (Elf32_External_gptab);
+ ext_tab = bfd_alloc (abfd, amt);
+ if (ext_tab == NULL)
+ {
+ free (tab);
+ return FALSE;
+ }
+
+ for (j = 0; j < c; j++)
+ bfd_mips_elf32_swap_gptab_out (abfd, tab + j, ext_tab + j);
+ free (tab);
+
+ o->_raw_size = c * sizeof (Elf32_External_gptab);
+ o->contents = (bfd_byte *) ext_tab;
+
+ /* Skip this section later on (I don't think this currently
+ matters, but someday it might). */
+ o->link_order_head = NULL;
+ }
+ }
+
+ /* Invoke the regular ELF backend linker to do all the work. */
+ if (!bfd_elf_final_link (abfd, info))
+ return FALSE;
+
+ /* Now write out the computed sections. */
+
+ if (reginfo_sec != NULL)
+ {
+ Elf32_External_RegInfo ext;
+
+ bfd_mips_elf32_swap_reginfo_out (abfd, &reginfo, &ext);
+ if (! bfd_set_section_contents (abfd, reginfo_sec, &ext, 0, sizeof ext))
+ return FALSE;
+ }
+
+ if (mdebug_sec != NULL)
+ {
+ BFD_ASSERT (abfd->output_has_begun);
+ if (! bfd_ecoff_write_accumulated_debug (mdebug_handle, abfd, &debug,
+ swap, info,
+ mdebug_sec->filepos))
+ return FALSE;
+
+ bfd_ecoff_debug_free (mdebug_handle, abfd, &debug, swap, info);
+ }
+
+ if (gptab_data_sec != NULL)
+ {
+ if (! bfd_set_section_contents (abfd, gptab_data_sec,
+ gptab_data_sec->contents,
+ 0, gptab_data_sec->_raw_size))
+ return FALSE;
+ }
+
+ if (gptab_bss_sec != NULL)
+ {
+ if (! bfd_set_section_contents (abfd, gptab_bss_sec,
+ gptab_bss_sec->contents,
+ 0, gptab_bss_sec->_raw_size))
+ return FALSE;
+ }
+
+ if (SGI_COMPAT (abfd))
+ {
+ rtproc_sec = bfd_get_section_by_name (abfd, ".rtproc");
+ if (rtproc_sec != NULL)
+ {
+ if (! bfd_set_section_contents (abfd, rtproc_sec,
+ rtproc_sec->contents,
+ 0, rtproc_sec->_raw_size))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/* Structure for saying that BFD machine EXTENSION extends BASE. */
+
+struct mips_mach_extension {
+ unsigned long extension, base;
+};
+
+
+/* An array describing how BFD machines relate to one another. The entries
+ are ordered topologically with MIPS I extensions listed last. */
+
+static const struct mips_mach_extension mips_mach_extensions[] = {
+ /* MIPS64 extensions. */
+ { bfd_mach_mipsisa64r2, bfd_mach_mipsisa64 },
+ { bfd_mach_mips_sb1, bfd_mach_mipsisa64 },
+
+ /* MIPS V extensions. */
+ { bfd_mach_mipsisa64, bfd_mach_mips5 },
+
+ /* R10000 extensions. */
+ { bfd_mach_mips12000, bfd_mach_mips10000 },
+
+ /* R5000 extensions. Note: the vr5500 ISA is an extension of the core
+ vr5400 ISA, but doesn't include the multimedia stuff. It seems
+ better to allow vr5400 and vr5500 code to be merged anyway, since
+ many libraries will just use the core ISA. Perhaps we could add
+ some sort of ASE flag if this ever proves a problem. */
+ { bfd_mach_mips5500, bfd_mach_mips5400 },
+ { bfd_mach_mips5400, bfd_mach_mips5000 },
+
+ /* MIPS IV extensions. */
+ { bfd_mach_mips5, bfd_mach_mips8000 },
+ { bfd_mach_mips10000, bfd_mach_mips8000 },
+ { bfd_mach_mips5000, bfd_mach_mips8000 },
+ { bfd_mach_mips7000, bfd_mach_mips8000 },
+
+ /* VR4100 extensions. */
+ { bfd_mach_mips4120, bfd_mach_mips4100 },
+ { bfd_mach_mips4111, bfd_mach_mips4100 },
+
+ /* MIPS III extensions. */
+ { bfd_mach_mips8000, bfd_mach_mips4000 },
+ { bfd_mach_mips4650, bfd_mach_mips4000 },
+ { bfd_mach_mips4600, bfd_mach_mips4000 },
+ { bfd_mach_mips4400, bfd_mach_mips4000 },
+ { bfd_mach_mips4300, bfd_mach_mips4000 },
+ { bfd_mach_mips4100, bfd_mach_mips4000 },
+ { bfd_mach_mips4010, bfd_mach_mips4000 },
+
+ /* MIPS32 extensions. */
+ { bfd_mach_mipsisa32r2, bfd_mach_mipsisa32 },
+
+ /* MIPS II extensions. */
+ { bfd_mach_mips4000, bfd_mach_mips6000 },
+ { bfd_mach_mipsisa32, bfd_mach_mips6000 },
+
+ /* MIPS I extensions. */
+ { bfd_mach_mips6000, bfd_mach_mips3000 },
+ { bfd_mach_mips3900, bfd_mach_mips3000 }
+};
+
+
+/* Return true if bfd machine EXTENSION is an extension of machine BASE. */
+
+static bfd_boolean
+mips_mach_extends_p (unsigned long base, unsigned long extension)
+{
+ size_t i;
+
+ for (i = 0; extension != base && i < ARRAY_SIZE (mips_mach_extensions); i++)
+ if (extension == mips_mach_extensions[i].extension)
+ extension = mips_mach_extensions[i].base;
+
+ return extension == base;
+}
+
+
+/* Return true if the given ELF header flags describe a 32-bit binary. */
+
+static bfd_boolean
+mips_32bit_flags_p (flagword flags)
+{
+ return ((flags & EF_MIPS_32BITMODE) != 0
+ || (flags & EF_MIPS_ABI) == E_MIPS_ABI_O32
+ || (flags & EF_MIPS_ABI) == E_MIPS_ABI_EABI32
+ || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_1
+ || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_2
+ || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32
+ || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32R2);
+}
+
+
+/* Merge backend specific data from an object file to the output
+ object file when linking. */
+
+bfd_boolean
+_bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
+{
+ flagword old_flags;
+ flagword new_flags;
+ bfd_boolean ok;
+ bfd_boolean null_input_bfd = TRUE;
+ asection *sec;
+
+ /* Check if we have the same endianess */
+ if (! _bfd_generic_verify_endian_match (ibfd, obfd))
+ {
+ (*_bfd_error_handler)
+ (_("%s: endianness incompatible with that of the selected emulation"),
+ bfd_archive_filename (ibfd));
+ return FALSE;
+ }
+
+ if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
+ || bfd_get_flavour (obfd) != bfd_target_elf_flavour)
+ return TRUE;
+
+ if (strcmp (bfd_get_target (ibfd), bfd_get_target (obfd)) != 0)
+ {
+ (*_bfd_error_handler)
+ (_("%s: ABI is incompatible with that of the selected emulation"),
+ bfd_archive_filename (ibfd));
+ return FALSE;
+ }
+
+ new_flags = elf_elfheader (ibfd)->e_flags;
+ elf_elfheader (obfd)->e_flags |= new_flags & EF_MIPS_NOREORDER;
+ old_flags = elf_elfheader (obfd)->e_flags;
+
+ if (! elf_flags_init (obfd))
+ {
+ elf_flags_init (obfd) = TRUE;
+ elf_elfheader (obfd)->e_flags = new_flags;
+ elf_elfheader (obfd)->e_ident[EI_CLASS]
+ = elf_elfheader (ibfd)->e_ident[EI_CLASS];
+
+ if (bfd_get_arch (obfd) == bfd_get_arch (ibfd)
+ && bfd_get_arch_info (obfd)->the_default)
+ {
+ if (! bfd_set_arch_mach (obfd, bfd_get_arch (ibfd),
+ bfd_get_mach (ibfd)))
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ /* Check flag compatibility. */
+
+ new_flags &= ~EF_MIPS_NOREORDER;
+ old_flags &= ~EF_MIPS_NOREORDER;
+
+ /* Some IRIX 6 BSD-compatibility objects have this bit set. It
+ doesn't seem to matter. */
+ new_flags &= ~EF_MIPS_XGOT;
+ old_flags &= ~EF_MIPS_XGOT;
+
+ /* MIPSpro generates ucode info in n64 objects. Again, we should
+ just be able to ignore this. */
+ new_flags &= ~EF_MIPS_UCODE;
+ old_flags &= ~EF_MIPS_UCODE;
+
+ if (new_flags == old_flags)
+ return TRUE;
+
+ /* Check to see if the input BFD actually contains any sections.
+ If not, its flags may not have been initialised either, but it cannot
+ actually cause any incompatibility. */
+ for (sec = ibfd->sections; sec != NULL; sec = sec->next)
+ {
+ /* Ignore synthetic sections and empty .text, .data and .bss sections
+ which are automatically generated by gas. */
+ if (strcmp (sec->name, ".reginfo")
+ && strcmp (sec->name, ".mdebug")
+ && (sec->_raw_size != 0
+ || (strcmp (sec->name, ".text")
+ && strcmp (sec->name, ".data")
+ && strcmp (sec->name, ".bss"))))
+ {
+ null_input_bfd = FALSE;
+ break;
+ }
+ }
+ if (null_input_bfd)
+ return TRUE;
+
+ ok = TRUE;
+
+ if (((new_flags & (EF_MIPS_PIC | EF_MIPS_CPIC)) != 0)
+ != ((old_flags & (EF_MIPS_PIC | EF_MIPS_CPIC)) != 0))
+ {
+ (*_bfd_error_handler)
+ (_("%s: warning: linking PIC files with non-PIC files"),
+ bfd_archive_filename (ibfd));
+ ok = TRUE;
+ }
+
+ if (new_flags & (EF_MIPS_PIC | EF_MIPS_CPIC))
+ elf_elfheader (obfd)->e_flags |= EF_MIPS_CPIC;
+ if (! (new_flags & EF_MIPS_PIC))
+ elf_elfheader (obfd)->e_flags &= ~EF_MIPS_PIC;
+
+ new_flags &= ~ (EF_MIPS_PIC | EF_MIPS_CPIC);
+ old_flags &= ~ (EF_MIPS_PIC | EF_MIPS_CPIC);
+
+ /* Compare the ISAs. */
+ if (mips_32bit_flags_p (old_flags) != mips_32bit_flags_p (new_flags))
+ {
+ (*_bfd_error_handler)
+ (_("%s: linking 32-bit code with 64-bit code"),
+ bfd_archive_filename (ibfd));
+ ok = FALSE;
+ }
+ else if (!mips_mach_extends_p (bfd_get_mach (ibfd), bfd_get_mach (obfd)))
+ {
+ /* OBFD's ISA isn't the same as, or an extension of, IBFD's. */
+ if (mips_mach_extends_p (bfd_get_mach (obfd), bfd_get_mach (ibfd)))
+ {
+ /* Copy the architecture info from IBFD to OBFD. Also copy
+ the 32-bit flag (if set) so that we continue to recognise
+ OBFD as a 32-bit binary. */
+ bfd_set_arch_info (obfd, bfd_get_arch_info (ibfd));
+ elf_elfheader (obfd)->e_flags &= ~(EF_MIPS_ARCH | EF_MIPS_MACH);
+ elf_elfheader (obfd)->e_flags
+ |= new_flags & (EF_MIPS_ARCH | EF_MIPS_MACH | EF_MIPS_32BITMODE);
+
+ /* Copy across the ABI flags if OBFD doesn't use them
+ and if that was what caused us to treat IBFD as 32-bit. */
+ if ((old_flags & EF_MIPS_ABI) == 0
+ && mips_32bit_flags_p (new_flags)
+ && !mips_32bit_flags_p (new_flags & ~EF_MIPS_ABI))
+ elf_elfheader (obfd)->e_flags |= new_flags & EF_MIPS_ABI;
+ }
+ else
+ {
+ /* The ISAs aren't compatible. */
+ (*_bfd_error_handler)
+ (_("%s: linking %s module with previous %s modules"),
+ bfd_archive_filename (ibfd),
+ bfd_printable_name (ibfd),
+ bfd_printable_name (obfd));
+ ok = FALSE;
+ }
+ }
+
+ new_flags &= ~(EF_MIPS_ARCH | EF_MIPS_MACH | EF_MIPS_32BITMODE);
+ old_flags &= ~(EF_MIPS_ARCH | EF_MIPS_MACH | EF_MIPS_32BITMODE);
+
+ /* Compare ABIs. The 64-bit ABI does not use EF_MIPS_ABI. But, it
+ does set EI_CLASS differently from any 32-bit ABI. */
+ if ((new_flags & EF_MIPS_ABI) != (old_flags & EF_MIPS_ABI)
+ || (elf_elfheader (ibfd)->e_ident[EI_CLASS]
+ != elf_elfheader (obfd)->e_ident[EI_CLASS]))
+ {
+ /* Only error if both are set (to different values). */
+ if (((new_flags & EF_MIPS_ABI) && (old_flags & EF_MIPS_ABI))
+ || (elf_elfheader (ibfd)->e_ident[EI_CLASS]
+ != elf_elfheader (obfd)->e_ident[EI_CLASS]))
+ {
+ (*_bfd_error_handler)
+ (_("%s: ABI mismatch: linking %s module with previous %s modules"),
+ bfd_archive_filename (ibfd),
+ elf_mips_abi_name (ibfd),
+ elf_mips_abi_name (obfd));
+ ok = FALSE;
+ }
+ new_flags &= ~EF_MIPS_ABI;
+ old_flags &= ~EF_MIPS_ABI;
+ }
+
+ /* For now, allow arbitrary mixing of ASEs (retain the union). */
+ if ((new_flags & EF_MIPS_ARCH_ASE) != (old_flags & EF_MIPS_ARCH_ASE))
+ {
+ elf_elfheader (obfd)->e_flags |= new_flags & EF_MIPS_ARCH_ASE;
+
+ new_flags &= ~ EF_MIPS_ARCH_ASE;
+ old_flags &= ~ EF_MIPS_ARCH_ASE;
+ }
+
+ /* Warn about any other mismatches */
+ if (new_flags != old_flags)
+ {
+ (*_bfd_error_handler)
+ (_("%s: uses different e_flags (0x%lx) fields than previous modules (0x%lx)"),
+ bfd_archive_filename (ibfd), (unsigned long) new_flags,
+ (unsigned long) old_flags);
+ ok = FALSE;
+ }
+
+ if (! ok)
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Function to keep MIPS specific file flags like as EF_MIPS_PIC. */
+
+bfd_boolean
+_bfd_mips_elf_set_private_flags (bfd *abfd, flagword flags)
+{
+ BFD_ASSERT (!elf_flags_init (abfd)
+ || elf_elfheader (abfd)->e_flags == flags);
+
+ elf_elfheader (abfd)->e_flags = flags;
+ elf_flags_init (abfd) = TRUE;
+ return TRUE;
+}
+
+bfd_boolean
+_bfd_mips_elf_print_private_bfd_data (bfd *abfd, void *ptr)
+{
+ FILE *file = ptr;
+
+ BFD_ASSERT (abfd != NULL && ptr != NULL);
+
+ /* Print normal ELF private data. */
+ _bfd_elf_print_private_bfd_data (abfd, ptr);
+
+ /* xgettext:c-format */
+ fprintf (file, _("private flags = %lx:"), elf_elfheader (abfd)->e_flags);
+
+ if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI) == E_MIPS_ABI_O32)
+ fprintf (file, _(" [abi=O32]"));
+ else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI) == E_MIPS_ABI_O64)
+ fprintf (file, _(" [abi=O64]"));
+ else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI) == E_MIPS_ABI_EABI32)
+ fprintf (file, _(" [abi=EABI32]"));
+ else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI) == E_MIPS_ABI_EABI64)
+ fprintf (file, _(" [abi=EABI64]"));
+ else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI))
+ fprintf (file, _(" [abi unknown]"));
+ else if (ABI_N32_P (abfd))
+ fprintf (file, _(" [abi=N32]"));
+ else if (ABI_64_P (abfd))
+ fprintf (file, _(" [abi=64]"));
+ else
+ fprintf (file, _(" [no abi set]"));
+
+ if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_1)
+ fprintf (file, _(" [mips1]"));
+ else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_2)
+ fprintf (file, _(" [mips2]"));
+ else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_3)
+ fprintf (file, _(" [mips3]"));
+ else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_4)
+ fprintf (file, _(" [mips4]"));
+ else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_5)
+ fprintf (file, _(" [mips5]"));
+ else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32)
+ fprintf (file, _(" [mips32]"));
+ else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_64)
+ fprintf (file, _(" [mips64]"));
+ else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32R2)
+ fprintf (file, _(" [mips32r2]"));
+ else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_64R2)
+ fprintf (file, _(" [mips64r2]"));
+ else
+ fprintf (file, _(" [unknown ISA]"));
+
+ if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MDMX)
+ fprintf (file, _(" [mdmx]"));
+
+ if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_M16)
+ fprintf (file, _(" [mips16]"));
+
+ if (elf_elfheader (abfd)->e_flags & EF_MIPS_32BITMODE)
+ fprintf (file, _(" [32bitmode]"));
+ else
+ fprintf (file, _(" [not 32bitmode]"));
+
+ fputc ('\n', file);
+
+ return TRUE;
+}
+
+struct bfd_elf_special_section const _bfd_mips_elf_special_sections[]=
+{
+ { ".sdata", 6, -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
+ { ".sbss", 5, -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
+ { ".lit4", 5, 0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
+ { ".lit8", 5, 0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
+ { ".ucode", 6, 0, SHT_MIPS_UCODE, 0 },
+ { ".mdebug", 7, 0, SHT_MIPS_DEBUG, 0 },
+ { NULL, 0, 0, 0, 0 }
+};
diff --git a/contrib/binutils/bfd/elfxx-mips.h b/contrib/binutils/bfd/elfxx-mips.h
new file mode 100644
index 0000000..0a684d9
--- /dev/null
+++ b/contrib/binutils/bfd/elfxx-mips.h
@@ -0,0 +1,126 @@
+/* MIPS ELF specific backend routines.
+ Copyright 2002, 2003, 2004 Free Software Foundation, 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. */
+
+#include "elf/common.h"
+#include "elf/internal.h"
+
+extern bfd_boolean _bfd_mips_elf_new_section_hook
+ (bfd *, asection *);
+extern void _bfd_mips_elf_symbol_processing
+ (bfd *, asymbol *);
+extern bfd_boolean _bfd_mips_elf_name_local_section_symbols
+ (bfd *);
+extern bfd_boolean _bfd_mips_elf_section_processing
+ (bfd *, Elf_Internal_Shdr *);
+extern bfd_boolean _bfd_mips_elf_section_from_shdr
+ (bfd *, Elf_Internal_Shdr *, const char *);
+extern bfd_boolean _bfd_mips_elf_fake_sections
+ (bfd *, Elf_Internal_Shdr *, asection *);
+extern bfd_boolean _bfd_mips_elf_section_from_bfd_section
+ (bfd *, asection *, int *);
+extern bfd_boolean _bfd_mips_elf_add_symbol_hook
+ (bfd *, struct bfd_link_info *, Elf_Internal_Sym *,
+ const char **, flagword *, asection **, bfd_vma *);
+extern bfd_boolean _bfd_mips_elf_link_output_symbol_hook
+ (struct bfd_link_info *, const char *, Elf_Internal_Sym *,
+ asection *, struct elf_link_hash_entry *);
+extern bfd_boolean _bfd_mips_elf_create_dynamic_sections
+ (bfd *, struct bfd_link_info *);
+extern bfd_boolean _bfd_mips_elf_check_relocs
+ (bfd *, struct bfd_link_info *, asection *, const Elf_Internal_Rela *);
+extern bfd_boolean _bfd_mips_elf_adjust_dynamic_symbol
+ (struct bfd_link_info *, struct elf_link_hash_entry *);
+extern bfd_boolean _bfd_mips_elf_always_size_sections
+ (bfd *, struct bfd_link_info *);
+extern bfd_boolean _bfd_mips_elf_size_dynamic_sections
+ (bfd *, struct bfd_link_info *);
+extern bfd_boolean _bfd_mips_elf_relocate_section
+ (bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
+ Elf_Internal_Rela *, Elf_Internal_Sym *, asection **);
+extern bfd_boolean _bfd_mips_elf_finish_dynamic_symbol
+ (bfd *, struct bfd_link_info *, struct elf_link_hash_entry *,
+ Elf_Internal_Sym *);
+extern bfd_boolean _bfd_mips_elf_finish_dynamic_sections
+ (bfd *, struct bfd_link_info *);
+extern void _bfd_mips_elf_final_write_processing
+ (bfd *, bfd_boolean);
+extern int _bfd_mips_elf_additional_program_headers
+ (bfd *);
+extern bfd_boolean _bfd_mips_elf_modify_segment_map
+ (bfd *, struct bfd_link_info *);
+extern asection * _bfd_mips_elf_gc_mark_hook
+ (asection *, struct bfd_link_info *, Elf_Internal_Rela *,
+ struct elf_link_hash_entry *, Elf_Internal_Sym *);
+extern bfd_boolean _bfd_mips_elf_gc_sweep_hook
+ (bfd *, struct bfd_link_info *, asection *, const Elf_Internal_Rela *);
+extern void _bfd_mips_elf_copy_indirect_symbol
+ (const struct elf_backend_data *, struct elf_link_hash_entry *,
+ struct elf_link_hash_entry *);
+extern void _bfd_mips_elf_hide_symbol
+ (struct bfd_link_info *, struct elf_link_hash_entry *, bfd_boolean);
+extern bfd_boolean _bfd_mips_elf_ignore_discarded_relocs
+ (asection *);
+extern bfd_boolean _bfd_mips_elf_find_nearest_line
+ (bfd *, asection *, asymbol **, bfd_vma, const char **,
+ const char **, unsigned int *);
+extern bfd_boolean _bfd_mips_elf_set_section_contents
+ (bfd *, asection *, const void *, file_ptr, bfd_size_type);
+extern bfd_byte *_bfd_elf_mips_get_relocated_section_contents
+ (bfd *, struct bfd_link_info *, struct bfd_link_order *,
+ bfd_byte *, bfd_boolean, asymbol **);
+extern struct bfd_link_hash_table *_bfd_mips_elf_link_hash_table_create
+ (bfd *);
+extern bfd_boolean _bfd_mips_elf_final_link
+ (bfd *, struct bfd_link_info *);
+extern bfd_boolean _bfd_mips_elf_merge_private_bfd_data
+ (bfd *, bfd *);
+extern bfd_boolean _bfd_mips_elf_set_private_flags
+ (bfd *, flagword);
+extern bfd_boolean _bfd_mips_elf_print_private_bfd_data
+ (bfd *, void *);
+extern bfd_boolean _bfd_mips_elf_discard_info
+ (bfd *, struct elf_reloc_cookie *, struct bfd_link_info *);
+extern bfd_boolean _bfd_mips_elf_write_section
+ (bfd *, asection *, bfd_byte *);
+
+extern bfd_boolean _bfd_mips_elf_read_ecoff_info
+ (bfd *, asection *, struct ecoff_debug_info *);
+extern bfd_reloc_status_type _bfd_mips_elf_gprel16_with_gp
+ (bfd *, asymbol *, arelent *, asection *, bfd_boolean, void *, bfd_vma);
+extern bfd_reloc_status_type _bfd_mips_elf32_gprel16_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+extern bfd_reloc_status_type _bfd_mips_elf_hi16_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+extern bfd_reloc_status_type _bfd_mips_elf_got16_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+extern bfd_reloc_status_type _bfd_mips_elf_lo16_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+extern bfd_reloc_status_type _bfd_mips_elf_generic_reloc
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+extern unsigned long _bfd_elf_mips_mach
+ (flagword);
+extern bfd_boolean _bfd_mips_relax_section
+ (bfd *, asection *, struct bfd_link_info *, bfd_boolean *);
+extern bfd_vma _bfd_mips_elf_sign_extend
+ (bfd_vma, int);
+
+extern struct bfd_elf_special_section const _bfd_mips_elf_special_sections[];
+#define elf_backend_name_local_section_symbols \
+ _bfd_mips_elf_name_local_section_symbols
+#define elf_backend_special_sections _bfd_mips_elf_special_sections
diff --git a/contrib/binutils/bfd/hosts/mipsbsd.h b/contrib/binutils/bfd/hosts/mipsbsd.h
new file mode 100644
index 0000000..a2fad21
--- /dev/null
+++ b/contrib/binutils/bfd/hosts/mipsbsd.h
@@ -0,0 +1,12 @@
+#include <machine/param.h>
+#include <machine/vmparam.h>
+#undef ALIGN
+
+#define HOST_PAGE_SIZE NBPG
+/* #define HOST_SEGMENT_SIZE NBPG -- we use HOST_DATA_START_ADDR */
+#define HOST_MACHINE_ARCH bfd_arch_mips
+/* #define HOST_MACHINE_MACHINE */
+
+#define HOST_TEXT_START_ADDR USRTEXT
+#define HOST_STACK_END_ADDR USRSTACK
+#define NO_CORE_COMMAND
diff --git a/contrib/binutils/bfd/hosts/mipsmach3.h b/contrib/binutils/bfd/hosts/mipsmach3.h
new file mode 100644
index 0000000..c5c468d
--- /dev/null
+++ b/contrib/binutils/bfd/hosts/mipsmach3.h
@@ -0,0 +1,10 @@
+#include <machine/vmparam.h>
+#include <machine/machparam.h>
+#include <sys/param.h>
+
+#define HOST_PAGE_SIZE NBPG
+/* #define HOST_SEGMENT_SIZE NBPG */
+#define HOST_MACHINE_ARCH bfd_arch_mips
+#define HOST_TEXT_START_ADDR USRTEXT
+#define HOST_DATA_START_ADDR USRDATA
+#define HOST_STACK_END_ADDR USRSTACK
diff --git a/contrib/binutils/bfd/hosts/news-mips.h b/contrib/binutils/bfd/hosts/news-mips.h
new file mode 100644
index 0000000..9e799be
--- /dev/null
+++ b/contrib/binutils/bfd/hosts/news-mips.h
@@ -0,0 +1,12 @@
+/* Sony News running NewsOS 3.2. */
+
+#include <sys/param.h>
+#include <machine/vmparam.h>
+
+#define HOST_PAGE_SIZE NBPG
+
+#define HOST_MACHINE_ARCH bfd_arch_mips
+
+#define HOST_TEXT_START_ADDR USRTEXT
+#define HOST_DATA_START_ADDR USRDATA
+#define HOST_STACK_END_ADDR USRSTACK
diff --git a/contrib/binutils/bfd/mipsbsd.c b/contrib/binutils/bfd/mipsbsd.c
new file mode 100644
index 0000000..cb2050f
--- /dev/null
+++ b/contrib/binutils/bfd/mipsbsd.c
@@ -0,0 +1,486 @@
+/* BFD backend for MIPS BSD (a.out) binaries.
+ Copyright 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003
+ Free Software Foundation, Inc.
+ Written by Ralph Campbell.
+
+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. */
+
+/* #define ENTRY_CAN_BE_ZERO */
+#define N_HEADER_IN_TEXT(x) 1
+#define N_SHARED_LIB(x) 0
+#define N_TXTADDR(x) \
+ (N_MAGIC(x) != ZMAGIC ? (x).a_entry : /* object file or NMAGIC */\
+ TEXT_START_ADDR + EXEC_BYTES_SIZE /* no padding */\
+ )
+#define N_DATADDR(x) (N_TXTADDR(x)+N_TXTSIZE(x))
+#define TEXT_START_ADDR 4096
+#define TARGET_PAGE_SIZE 4096
+#define SEGMENT_SIZE TARGET_PAGE_SIZE
+#define DEFAULT_ARCH bfd_arch_mips
+#define MACHTYPE_OK(mtype) ((mtype) == M_UNKNOWN \
+ || (mtype) == M_MIPS1 || (mtype) == M_MIPS2)
+#define MY_symbol_leading_char '\0'
+
+/* Do not "beautify" the CONCAT* macro args. Traditional C will not
+ remove whitespace added here, and thus will fail to concatenate
+ the tokens. */
+#define MY(OP) CONCAT2 (mipsbsd_,OP)
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+#include "libaout.h"
+
+#define SET_ARCH_MACH(ABFD, EXEC) \
+ MY(set_arch_mach) (ABFD, N_MACHTYPE (EXEC)); \
+ MY(choose_reloc_size) (ABFD);
+static void MY(set_arch_mach) PARAMS ((bfd *abfd, unsigned long machtype));
+static void MY(choose_reloc_size) PARAMS ((bfd *abfd));
+
+#define MY_write_object_contents MY(write_object_contents)
+static bfd_boolean MY(write_object_contents) PARAMS ((bfd *abfd));
+
+/* We can't use MY(x) here because it leads to a recursive call to CONCAT2
+ when expanded inside JUMP_TABLE. */
+#define MY_bfd_reloc_type_lookup mipsbsd_reloc_howto_type_lookup
+#define MY_canonicalize_reloc mipsbsd_canonicalize_reloc
+
+#define MY_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
+#define MY_bfd_link_add_symbols _bfd_generic_link_add_symbols
+#define MY_final_link_callback unused
+#define MY_bfd_final_link _bfd_generic_final_link
+
+#define MY_backend_data &MY(backend_data)
+#define MY_BFD_TARGET
+
+#include "aout-target.h"
+
+static bfd_reloc_status_type mips_fix_jmp_addr
+ PARAMS ((bfd *, arelent *, struct bfd_symbol *, PTR, asection *,
+ bfd *, char **));
+static reloc_howto_type *MY(reloc_howto_type_lookup)
+ PARAMS ((bfd *, bfd_reloc_code_real_type));
+
+long MY(canonicalize_reloc) PARAMS ((bfd *, sec_ptr, arelent **, asymbol **));
+
+static void
+MY(set_arch_mach) (abfd, machtype)
+ bfd *abfd;
+ unsigned long machtype;
+{
+ enum bfd_architecture arch;
+ unsigned int machine;
+
+ /* Determine the architecture and machine type of the object file. */
+ switch (machtype)
+ {
+ case M_MIPS1:
+ arch = bfd_arch_mips;
+ machine = bfd_mach_mips3000;
+ break;
+
+ case M_MIPS2:
+ arch = bfd_arch_mips;
+ machine = bfd_mach_mips4000;
+ break;
+
+ default:
+ arch = bfd_arch_obscure;
+ machine = 0;
+ break;
+ }
+
+ bfd_set_arch_mach (abfd, arch, machine);
+}
+
+/* Determine the size of a relocation entry, based on the architecture */
+static void
+MY (choose_reloc_size) (abfd)
+ bfd *abfd;
+{
+ switch (bfd_get_arch (abfd))
+ {
+ case bfd_arch_sparc:
+ case bfd_arch_a29k:
+ case bfd_arch_mips:
+ obj_reloc_entry_size (abfd) = RELOC_EXT_SIZE;
+ break;
+ default:
+ obj_reloc_entry_size (abfd) = RELOC_STD_SIZE;
+ break;
+ }
+}
+
+/* Write an object file in BSD a.out format.
+ Section contents have already been written. We write the
+ file header, symbols, and relocation. */
+
+static bfd_boolean
+MY (write_object_contents) (abfd)
+ bfd *abfd;
+{
+ struct external_exec exec_bytes;
+ struct internal_exec *execp = exec_hdr (abfd);
+
+ /* Magic number, maestro, please! */
+ switch (bfd_get_arch (abfd))
+ {
+ case bfd_arch_m68k:
+ switch (bfd_get_mach (abfd))
+ {
+ case bfd_mach_m68010:
+ N_SET_MACHTYPE (*execp, M_68010);
+ break;
+ default:
+ case bfd_mach_m68020:
+ N_SET_MACHTYPE (*execp, M_68020);
+ break;
+ }
+ break;
+ case bfd_arch_sparc:
+ N_SET_MACHTYPE (*execp, M_SPARC);
+ break;
+ case bfd_arch_i386:
+ N_SET_MACHTYPE (*execp, M_386);
+ break;
+ case bfd_arch_a29k:
+ N_SET_MACHTYPE (*execp, M_29K);
+ break;
+ case bfd_arch_mips:
+ switch (bfd_get_mach (abfd))
+ {
+ case bfd_mach_mips4000:
+ case bfd_mach_mips6000:
+ N_SET_MACHTYPE (*execp, M_MIPS2);
+ break;
+ default:
+ N_SET_MACHTYPE (*execp, M_MIPS1);
+ break;
+ }
+ break;
+ default:
+ N_SET_MACHTYPE (*execp, M_UNKNOWN);
+ }
+
+ MY (choose_reloc_size) (abfd);
+
+ WRITE_HEADERS (abfd, execp);
+
+ return TRUE;
+}
+
+/* MIPS relocation types. */
+#define MIPS_RELOC_32 0
+#define MIPS_RELOC_JMP 1
+#define MIPS_RELOC_WDISP16 2
+#define MIPS_RELOC_HI16 3
+#define MIPS_RELOC_HI16_S 4
+#define MIPS_RELOC_LO16 5
+
+/* This is only called when performing a BFD_RELOC_MIPS_JMP relocation.
+ The jump destination address is formed from the upper 4 bits of the
+ "current" program counter concatenated with the jump instruction's
+ 26 bit field and two trailing zeros.
+ If the destination address is not in the same segment as the "current"
+ program counter, then we need to signal an error. */
+
+static bfd_reloc_status_type
+mips_fix_jmp_addr (abfd, reloc_entry, symbol, data, input_section, output_bfd,
+ error_message)
+ bfd *abfd ATTRIBUTE_UNUSED;
+ arelent *reloc_entry;
+ struct bfd_symbol *symbol;
+ PTR data ATTRIBUTE_UNUSED;
+ asection *input_section;
+ bfd *output_bfd;
+ char **error_message ATTRIBUTE_UNUSED;
+{
+ bfd_vma relocation, pc;
+
+ /* If this is a partial relocation, just continue. */
+ if (output_bfd != (bfd *)NULL)
+ return bfd_reloc_continue;
+
+ /* If this is an undefined symbol, return error */
+ if (bfd_is_und_section (symbol->section)
+ && (symbol->flags & BSF_WEAK) == 0)
+ return bfd_reloc_undefined;
+
+ /* Work out which section the relocation is targeted at and the
+ initial relocation command value. */
+ if (bfd_is_com_section (symbol->section))
+ relocation = 0;
+ else
+ relocation = symbol->value;
+
+ relocation += symbol->section->output_section->vma;
+ relocation += symbol->section->output_offset;
+ relocation += reloc_entry->addend;
+
+ pc = input_section->output_section->vma + input_section->output_offset +
+ reloc_entry->address + 4;
+
+ if ((relocation & 0xF0000000) != (pc & 0xF0000000))
+ return bfd_reloc_overflow;
+
+ return bfd_reloc_continue;
+}
+
+/* This is only called when performing a BFD_RELOC_HI16_S relocation.
+ We need to see if bit 15 is set in the result. If it is, we add
+ 0x10000 and continue normally. This will compensate for the sign extension
+ when the low bits are added at run time. */
+
+static bfd_reloc_status_type
+mips_fix_hi16_s PARAMS ((bfd *, arelent *, asymbol *, PTR,
+ asection *, bfd *, char **));
+
+static bfd_reloc_status_type
+mips_fix_hi16_s (abfd, reloc_entry, symbol, data, input_section,
+ output_bfd, error_message)
+ bfd *abfd ATTRIBUTE_UNUSED;
+ arelent *reloc_entry;
+ asymbol *symbol;
+ PTR data ATTRIBUTE_UNUSED;
+ asection *input_section ATTRIBUTE_UNUSED;
+ bfd *output_bfd;
+ char **error_message ATTRIBUTE_UNUSED;
+{
+ bfd_vma relocation;
+
+ /* If this is a partial relocation, just continue. */
+ if (output_bfd != (bfd *)NULL)
+ return bfd_reloc_continue;
+
+ /* If this is an undefined symbol, return error. */
+ if (bfd_is_und_section (symbol->section)
+ && (symbol->flags & BSF_WEAK) == 0)
+ return bfd_reloc_undefined;
+
+ /* Work out which section the relocation is targeted at and the
+ initial relocation command value. */
+ if (bfd_is_com_section (symbol->section))
+ relocation = 0;
+ else
+ relocation = symbol->value;
+
+ relocation += symbol->section->output_section->vma;
+ relocation += symbol->section->output_offset;
+ relocation += reloc_entry->addend;
+
+ if (relocation & 0x8000)
+ reloc_entry->addend += 0x10000;
+
+ return bfd_reloc_continue;
+}
+
+static reloc_howto_type mips_howto_table_ext[] = {
+ {MIPS_RELOC_32, 0, 2, 32, FALSE, 0, complain_overflow_bitfield, 0,
+ "32", FALSE, 0, 0xffffffff, FALSE},
+ {MIPS_RELOC_JMP, 2, 2, 26, FALSE, 0, complain_overflow_dont,
+ mips_fix_jmp_addr,
+ "MIPS_JMP", FALSE, 0, 0x03ffffff, FALSE},
+ {MIPS_RELOC_WDISP16, 2, 2, 16, TRUE, 0, complain_overflow_signed, 0,
+ "WDISP16", FALSE, 0, 0x0000ffff, FALSE},
+ {MIPS_RELOC_HI16, 16, 2, 16, FALSE, 0, complain_overflow_bitfield, 0,
+ "HI16", FALSE, 0, 0x0000ffff, FALSE},
+ {MIPS_RELOC_HI16_S, 16, 2, 16, FALSE, 0, complain_overflow_bitfield,
+ mips_fix_hi16_s,
+ "HI16_S", FALSE, 0, 0x0000ffff, FALSE},
+ {MIPS_RELOC_LO16, 0, 2, 16, FALSE, 0, complain_overflow_dont, 0,
+ "LO16", FALSE, 0, 0x0000ffff, FALSE},
+};
+
+static reloc_howto_type *
+MY(reloc_howto_type_lookup) (abfd, code)
+ bfd *abfd;
+ bfd_reloc_code_real_type code;
+{
+
+ if (bfd_get_arch (abfd) != bfd_arch_mips)
+ return 0;
+
+ switch (code)
+ {
+ case BFD_RELOC_CTOR:
+ case BFD_RELOC_32:
+ return (&mips_howto_table_ext[MIPS_RELOC_32]);
+ case BFD_RELOC_MIPS_JMP:
+ return (&mips_howto_table_ext[MIPS_RELOC_JMP]);
+ case BFD_RELOC_16_PCREL_S2:
+ return (&mips_howto_table_ext[MIPS_RELOC_WDISP16]);
+ case BFD_RELOC_HI16:
+ return (&mips_howto_table_ext[MIPS_RELOC_HI16]);
+ case BFD_RELOC_HI16_S:
+ return (&mips_howto_table_ext[MIPS_RELOC_HI16_S]);
+ case BFD_RELOC_LO16:
+ return (&mips_howto_table_ext[MIPS_RELOC_LO16]);
+ default:
+ return 0;
+ }
+}
+
+/* This is just like the standard aoutx.h version but we need to do our
+ own mapping of external reloc type values to howto entries. */
+long
+MY(canonicalize_reloc) (abfd, section, relptr, symbols)
+ bfd *abfd;
+ sec_ptr section;
+ arelent **relptr;
+ asymbol **symbols;
+{
+ arelent *tblptr = section->relocation;
+ unsigned int count, c;
+ extern reloc_howto_type NAME(aout,ext_howto_table)[];
+
+ /* If we have already read in the relocation table, return the values. */
+ if (section->flags & SEC_CONSTRUCTOR)
+ {
+ arelent_chain *chain = section->constructor_chain;
+
+ for (count = 0; count < section->reloc_count; count++)
+ {
+ *relptr++ = &chain->relent;
+ chain = chain->next;
+ }
+ *relptr = 0;
+ return section->reloc_count;
+ }
+
+ if (tblptr && section->reloc_count)
+ {
+ for (count = 0; count++ < section->reloc_count;)
+ *relptr++ = tblptr++;
+ *relptr = 0;
+ return section->reloc_count;
+ }
+
+ if (!NAME(aout,slurp_reloc_table) (abfd, section, symbols))
+ return -1;
+ tblptr = section->relocation;
+
+ /* fix up howto entries. */
+ for (count = 0; count++ < section->reloc_count;)
+ {
+ c = tblptr->howto - NAME(aout,ext_howto_table);
+ tblptr->howto = &mips_howto_table_ext[c];
+
+ *relptr++ = tblptr++;
+ }
+ *relptr = 0;
+ return section->reloc_count;
+}
+
+static const struct aout_backend_data MY(backend_data) = {
+ 0, /* zmagic contiguous */
+ 1, /* text incl header */
+ 0, /* entry is text address */
+ 0, /* exec_hdr_flags */
+ TARGET_PAGE_SIZE, /* text vma */
+ MY_set_sizes,
+ 0, /* text size includes exec header */
+ 0, /* add_dynamic_symbols */
+ 0, /* add_one_symbol */
+ 0, /* link_dynamic_object */
+ 0, /* write_dynamic_symbol */
+ 0, /* check_dynamic_reloc */
+ 0 /* finish_dynamic_link */
+};
+
+extern const bfd_target aout_mips_big_vec;
+
+const bfd_target aout_mips_little_vec =
+ {
+ "a.out-mips-little", /* name */
+ bfd_target_aout_flavour,
+ BFD_ENDIAN_LITTLE, /* target byte order (little) */
+ BFD_ENDIAN_LITTLE, /* target headers byte order (little) */
+ (HAS_RELOC | EXEC_P | /* object flags */
+ HAS_LINENO | HAS_DEBUG |
+ HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED),
+ (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_CODE | SEC_DATA),
+ MY_symbol_leading_char,
+ ' ', /* ar_pad_char */
+ 15, /* ar_max_namelen */
+ bfd_getl64, bfd_getl_signed_64, bfd_putl64,
+ bfd_getl32, bfd_getl_signed_32, bfd_putl32,
+ bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* data */
+ bfd_getl64, bfd_getl_signed_64, bfd_putl64,
+ bfd_getl32, bfd_getl_signed_32, bfd_putl32,
+ bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* hdrs */
+ {_bfd_dummy_target, MY_object_p, /* bfd_check_format */
+ bfd_generic_archive_p, MY_core_file_p},
+ {bfd_false, MY_mkobject, /* bfd_set_format */
+ _bfd_generic_mkarchive, bfd_false},
+ {bfd_false, MY_write_object_contents, /* bfd_write_contents */
+ _bfd_write_archive_contents, bfd_false},
+
+ BFD_JUMP_TABLE_GENERIC (MY),
+ BFD_JUMP_TABLE_COPY (MY),
+ BFD_JUMP_TABLE_CORE (MY),
+ BFD_JUMP_TABLE_ARCHIVE (MY),
+ BFD_JUMP_TABLE_SYMBOLS (MY),
+ BFD_JUMP_TABLE_RELOCS (MY),
+ BFD_JUMP_TABLE_WRITE (MY),
+ BFD_JUMP_TABLE_LINK (MY),
+ BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
+
+ & aout_mips_big_vec,
+
+ (PTR) MY_backend_data
+ };
+
+const bfd_target aout_mips_big_vec =
+ {
+ "a.out-mips-big", /* name */
+ bfd_target_aout_flavour,
+ BFD_ENDIAN_BIG, /* target byte order (big) */
+ BFD_ENDIAN_BIG, /* target headers byte order (big) */
+ (HAS_RELOC | EXEC_P | /* object flags */
+ HAS_LINENO | HAS_DEBUG |
+ HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED),
+ (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_CODE | SEC_DATA),
+ MY_symbol_leading_char,
+ ' ', /* ar_pad_char */
+ 15, /* ar_max_namelen */
+ bfd_getb64, bfd_getb_signed_64, bfd_putb64,
+ bfd_getb32, bfd_getb_signed_32, bfd_putb32,
+ bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* data */
+ bfd_getb64, bfd_getb_signed_64, bfd_putb64,
+ bfd_getb32, bfd_getb_signed_32, bfd_putb32,
+ bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* hdrs */
+ {_bfd_dummy_target, MY_object_p, /* bfd_check_format */
+ bfd_generic_archive_p, MY_core_file_p},
+ {bfd_false, MY_mkobject, /* bfd_set_format */
+ _bfd_generic_mkarchive, bfd_false},
+ {bfd_false, MY_write_object_contents, /* bfd_write_contents */
+ _bfd_write_archive_contents, bfd_false},
+
+ BFD_JUMP_TABLE_GENERIC (MY),
+ BFD_JUMP_TABLE_COPY (MY),
+ BFD_JUMP_TABLE_CORE (MY),
+ BFD_JUMP_TABLE_ARCHIVE (MY),
+ BFD_JUMP_TABLE_SYMBOLS (MY),
+ BFD_JUMP_TABLE_RELOCS (MY),
+ BFD_JUMP_TABLE_WRITE (MY),
+ BFD_JUMP_TABLE_LINK (MY),
+ BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
+
+ & aout_mips_little_vec,
+
+ (PTR) MY_backend_data
+ };
diff --git a/contrib/binutils/bfd/pe-mips.c b/contrib/binutils/bfd/pe-mips.c
new file mode 100644
index 0000000..3066aaa
--- /dev/null
+++ b/contrib/binutils/bfd/pe-mips.c
@@ -0,0 +1,1000 @@
+/* BFD back-end for MIPS PE COFF files.
+ Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+ Modified from coff-i386.c by DJ Delorie, dj@cygnus.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. */
+
+#define COFF_WITH_PE
+#define COFF_LONG_SECTION_NAMES
+#define PCRELOFFSET TRUE
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+
+#include "coff/mipspe.h"
+
+#include "coff/internal.h"
+
+#include "coff/pe.h"
+
+#include "libcoff.h"
+
+static bfd_reloc_status_type coff_mips_reloc
+ PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
+static reloc_howto_type *coff_mips_rtype_to_howto
+ PARAMS ((bfd *, asection *, struct internal_reloc *,
+ struct coff_link_hash_entry *, struct internal_syment *,
+ bfd_vma *));
+#if 0
+static void mips_ecoff_swap_reloc_in
+ PARAMS ((bfd *, PTR, struct internal_reloc *));
+static void mips_ecoff_swap_reloc_out
+ PARAMS ((bfd *, const struct internal_reloc *, PTR));
+static void mips_adjust_reloc_in
+ PARAMS ((bfd *, const struct internal_reloc *, arelent *));
+static void mips_adjust_reloc_out
+ PARAMS ((bfd *, const arelent *, struct internal_reloc *));
+#endif
+
+static bfd_boolean in_reloc_p
+ PARAMS ((bfd *, reloc_howto_type *));
+static reloc_howto_type * coff_mips_reloc_type_lookup
+ PARAMS ((bfd *, bfd_reloc_code_real_type));
+static void mips_swap_reloc_in
+ PARAMS ((bfd *, PTR, PTR));
+static unsigned int mips_swap_reloc_out
+ PARAMS ((bfd *, PTR, PTR));
+static bfd_boolean coff_pe_mips_relocate_section
+ PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
+ struct internal_reloc *, struct internal_syment *, asection **));
+
+#define COFF_DEFAULT_SECTION_ALIGNMENT_POWER (2)
+/* The page size is a guess based on ELF. */
+
+#define COFF_PAGE_SIZE 0x1000
+
+/* For some reason when using mips COFF the value stored in the .text
+ section for a reference to a common symbol is the value itself plus
+ any desired offset. Ian Taylor, Cygnus Support. */
+
+/* If we are producing relocatable output, we need to do some
+ adjustments to the object file that are not done by the
+ bfd_perform_relocation function. This function is called by every
+ reloc type to make any required adjustments. */
+
+static bfd_reloc_status_type
+coff_mips_reloc (abfd, reloc_entry, symbol, data, input_section, output_bfd,
+ error_message)
+ bfd *abfd;
+ arelent *reloc_entry;
+ asymbol *symbol;
+ PTR data;
+ asection *input_section ATTRIBUTE_UNUSED;
+ bfd *output_bfd;
+ char **error_message ATTRIBUTE_UNUSED;
+{
+ symvalue diff;
+
+ if (output_bfd == (bfd *) NULL)
+ return bfd_reloc_continue;
+
+ if (bfd_is_com_section (symbol->section))
+ {
+#ifndef COFF_WITH_PE
+ /* We are relocating a common symbol. The current value in the
+ object file is ORIG + OFFSET, where ORIG is the value of the
+ common symbol as seen by the object file when it was compiled
+ (this may be zero if the symbol was undefined) and OFFSET is
+ the offset into the common symbol (normally zero, but may be
+ non-zero when referring to a field in a common structure).
+ ORIG is the negative of reloc_entry->addend, which is set by
+ the CALC_ADDEND macro below. We want to replace the value in
+ the object file with NEW + OFFSET, where NEW is the value of
+ the common symbol which we are going to put in the final
+ object file. NEW is symbol->value. */
+ diff = symbol->value + reloc_entry->addend;
+#else
+ /* In PE mode, we do not offset the common symbol. */
+ diff = reloc_entry->addend;
+#endif
+ }
+ else
+ {
+ /* For some reason bfd_perform_relocation always effectively
+ ignores the addend for a COFF target when producing
+ relocatable output. This seems to be always wrong for 386
+ COFF, so we handle the addend here instead. */
+ diff = reloc_entry->addend;
+ }
+
+#ifdef COFF_WITH_PE
+#if 0
+ /* dj - handle it like any other reloc? */
+ /* FIXME: How should this case be handled? */
+ if (reloc_entry->howto->type == MIPS_R_RVA && diff != 0)
+ abort ();
+#endif
+#endif
+
+#define DOIT(x) \
+ x = ((x & ~howto->dst_mask) | (((x & howto->src_mask) + (diff >> howto->rightshift)) & howto->dst_mask))
+
+ if (diff != 0)
+ {
+ reloc_howto_type *howto = reloc_entry->howto;
+ unsigned char *addr = (unsigned char *) data + reloc_entry->address;
+
+ switch (howto->size)
+ {
+ case 0:
+ {
+ char x = bfd_get_8 (abfd, addr);
+ DOIT (x);
+ bfd_put_8 (abfd, x, addr);
+ }
+ break;
+
+ case 1:
+ {
+ short x = bfd_get_16 (abfd, addr);
+ DOIT (x);
+ bfd_put_16 (abfd, (bfd_vma) x, addr);
+ }
+ break;
+
+ case 2:
+ {
+ long x = bfd_get_32 (abfd, addr);
+ DOIT (x);
+ bfd_put_32 (abfd, (bfd_vma) x, addr);
+ }
+ break;
+
+ default:
+ abort ();
+ }
+ }
+
+ /* Now let bfd_perform_relocation finish everything up. */
+ return bfd_reloc_continue;
+}
+
+#ifdef COFF_WITH_PE
+/* Return TRUE if this relocation should
+ appear in the output .reloc section. */
+
+static bfd_boolean
+in_reloc_p (abfd, howto)
+ bfd * abfd ATTRIBUTE_UNUSED;
+ reloc_howto_type *howto;
+{
+ return ! howto->pc_relative && howto->type != MIPS_R_RVA;
+}
+#endif
+
+#ifndef PCRELOFFSET
+#define PCRELOFFSET FALSE
+#endif
+
+static reloc_howto_type howto_table[] =
+{
+ /* Reloc type 0 is ignored. The reloc reading code ensures that
+ this is a reference to the .abs section, which will cause
+ bfd_perform_relocation to do nothing. */
+ HOWTO (MIPS_R_ABSOLUTE, /* type */
+ 0, /* rightshift */
+ 0, /* size (0 = byte, 1 = short, 2 = long) */
+ 8, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ 0, /* special_function */
+ "IGNORE", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A 16 bit reference to a symbol, normally from a data section. */
+ HOWTO (MIPS_R_REFHALF, /* type */
+ 0, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ coff_mips_reloc, /* special_function */
+ "REFHALF", /* name */
+ TRUE, /* partial_inplace */
+ 0xffff, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A 32 bit reference to a symbol, normally from a data section. */
+ HOWTO (MIPS_R_REFWORD, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ coff_mips_reloc, /* special_function */
+ "REFWORD", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A 26 bit absolute jump address. */
+ HOWTO (MIPS_R_JMPADDR, /* type */
+ 2, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 26, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ /* This needs complex overflow
+ detection, because the upper four
+ bits must match the PC. */
+ coff_mips_reloc, /* special_function */
+ "JMPADDR", /* name */
+ TRUE, /* partial_inplace */
+ 0x3ffffff, /* src_mask */
+ 0x3ffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* The high 16 bits of a symbol value. Handled by the function
+ mips_refhi_reloc. */
+ HOWTO (MIPS_R_REFHI, /* type */
+ 16, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ coff_mips_reloc, /* special_function */
+ "REFHI", /* name */
+ TRUE, /* partial_inplace */
+ 0xffff, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* The low 16 bits of a symbol value. */
+ HOWTO (MIPS_R_REFLO, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ coff_mips_reloc, /* special_function */
+ "REFLO", /* name */
+ TRUE, /* partial_inplace */
+ 0xffff, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A reference to an offset from the gp register. Handled by the
+ function mips_gprel_reloc. */
+ HOWTO (MIPS_R_GPREL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ coff_mips_reloc, /* special_function */
+ "GPREL", /* name */
+ TRUE, /* partial_inplace */
+ 0xffff, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A reference to a literal using an offset from the gp register.
+ Handled by the function mips_gprel_reloc. */
+ HOWTO (MIPS_R_LITERAL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ coff_mips_reloc, /* special_function */
+ "LITERAL", /* name */
+ TRUE, /* partial_inplace */
+ 0xffff, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ EMPTY_HOWTO (8),
+ EMPTY_HOWTO (9),
+ EMPTY_HOWTO (10),
+ EMPTY_HOWTO (11),
+ EMPTY_HOWTO (12),
+ EMPTY_HOWTO (13),
+ EMPTY_HOWTO (14),
+ EMPTY_HOWTO (15),
+ EMPTY_HOWTO (16),
+ EMPTY_HOWTO (17),
+ EMPTY_HOWTO (18),
+ EMPTY_HOWTO (19),
+ EMPTY_HOWTO (20),
+ EMPTY_HOWTO (21),
+ EMPTY_HOWTO (22),
+ EMPTY_HOWTO (23),
+ EMPTY_HOWTO (24),
+ EMPTY_HOWTO (25),
+ EMPTY_HOWTO (26),
+ EMPTY_HOWTO (27),
+ EMPTY_HOWTO (28),
+ EMPTY_HOWTO (29),
+ EMPTY_HOWTO (30),
+ EMPTY_HOWTO (31),
+ EMPTY_HOWTO (32),
+ EMPTY_HOWTO (33),
+ HOWTO (MIPS_R_RVA, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ coff_mips_reloc, /* special_function */
+ "rva32", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+ EMPTY_HOWTO (35),
+ EMPTY_HOWTO (36),
+ HOWTO (MIPS_R_PAIR, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ coff_mips_reloc, /* special_function */
+ "PAIR", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+};
+
+/* Turn a howto into a reloc nunmber */
+
+#define SELECT_RELOC(x,howto) { x.r_type = howto->type; }
+#define BADMAG(x) MIPSBADMAG(x)
+#define MIPS 1 /* Customize coffcode.h */
+
+#define RTYPE2HOWTO(cache_ptr, dst) \
+ (cache_ptr)->howto = howto_table + (dst)->r_type;
+
+/* Compute the addend of a reloc. If the reloc is to a common symbol,
+ the object file contains the value of the common symbol. By the
+ time this is called, the linker may be using a different symbol
+ from a different object file with a different value. Therefore, we
+ hack wildly to locate the original symbol from this file so that we
+ can make the correct adjustment. This macro sets coffsym to the
+ symbol from the original file, and uses it to set the addend value
+ correctly. If this is not a common symbol, the usual addend
+ calculation is done, except that an additional tweak is needed for
+ PC relative relocs.
+ FIXME: This macro refers to symbols and asect; these are from the
+ calling function, not the macro arguments. */
+
+#define CALC_ADDEND(abfd, ptr, reloc, cache_ptr) \
+ { \
+ coff_symbol_type *coffsym = (coff_symbol_type *) NULL; \
+ if (ptr && bfd_asymbol_bfd (ptr) != abfd) \
+ coffsym = (obj_symbols (abfd) \
+ + (cache_ptr->sym_ptr_ptr - symbols)); \
+ else if (ptr) \
+ coffsym = coff_symbol_from (abfd, ptr); \
+ if (coffsym != (coff_symbol_type *) NULL \
+ && coffsym->native->u.syment.n_scnum == 0) \
+ cache_ptr->addend = - coffsym->native->u.syment.n_value; \
+ else if (ptr && bfd_asymbol_bfd (ptr) == abfd \
+ && ptr->section != (asection *) NULL) \
+ cache_ptr->addend = - (ptr->section->vma + ptr->value); \
+ else \
+ cache_ptr->addend = 0; \
+ if (ptr && howto_table[reloc.r_type].pc_relative) \
+ cache_ptr->addend += asect->vma; \
+ }
+
+/* Convert an rtype to howto for the COFF backend linker. */
+
+static reloc_howto_type *
+coff_mips_rtype_to_howto (abfd, sec, rel, h, sym, addendp)
+ bfd *abfd ATTRIBUTE_UNUSED;
+ asection *sec;
+ struct internal_reloc *rel;
+ struct coff_link_hash_entry *h;
+ struct internal_syment *sym;
+ bfd_vma *addendp;
+{
+
+ reloc_howto_type *howto;
+
+ howto = howto_table + rel->r_type;
+
+#ifdef COFF_WITH_PE
+ *addendp = 0;
+#endif
+
+ if (howto->pc_relative)
+ *addendp += sec->vma;
+
+ if (sym != NULL && sym->n_scnum == 0 && sym->n_value != 0)
+ {
+ /* This is a common symbol. The section contents include the
+ size (sym->n_value) as an addend. The relocate_section
+ function will be adding in the final value of the symbol. We
+ need to subtract out the current size in order to get the
+ correct result. */
+
+ BFD_ASSERT (h != NULL);
+
+#ifndef COFF_WITH_PE
+ /* I think we *do* want to bypass this. If we don't, I have
+ seen some data parameters get the wrong relocation address.
+ If I link two versions with and without this section bypassed
+ and then do a binary comparison, the addresses which are
+ different can be looked up in the map. The case in which
+ this section has been bypassed has addresses which correspond
+ to values I can find in the map. */
+ *addendp -= sym->n_value;
+#endif
+ }
+
+#ifndef COFF_WITH_PE
+ /* If the output symbol is common (in which case this must be a
+ relocatable link), we need to add in the final size of the
+ common symbol. */
+ if (h != NULL && h->root.type == bfd_link_hash_common)
+ *addendp += h->root.u.c.size;
+#endif
+
+#ifdef COFF_WITH_PE
+ if (howto->pc_relative)
+ {
+ *addendp -= 4;
+
+ /* If the symbol is defined, then the generic code is going to
+ add back the symbol value in order to cancel out an
+ adjustment it made to the addend. However, we set the addend
+ to 0 at the start of this function. We need to adjust here,
+ to avoid the adjustment the generic code will make. FIXME:
+ This is getting a bit hackish. */
+ if (sym != NULL && sym->n_scnum != 0)
+ *addendp -= sym->n_value;
+ }
+
+ if (rel->r_type == MIPS_R_RVA)
+ {
+ *addendp -= pe_data(sec->output_section->owner)->pe_opthdr.ImageBase;
+ }
+#endif
+
+ return howto;
+}
+
+#define coff_rtype_to_howto coff_mips_rtype_to_howto
+
+#define coff_bfd_reloc_type_lookup coff_mips_reloc_type_lookup
+
+/* Get the howto structure for a generic reloc type. */
+
+static reloc_howto_type *
+coff_mips_reloc_type_lookup (abfd, code)
+ bfd *abfd ATTRIBUTE_UNUSED;
+ bfd_reloc_code_real_type code;
+{
+ int mips_type;
+
+ switch (code)
+ {
+ case BFD_RELOC_16:
+ mips_type = MIPS_R_REFHALF;
+ break;
+ case BFD_RELOC_32:
+ case BFD_RELOC_CTOR:
+ mips_type = MIPS_R_REFWORD;
+ break;
+ case BFD_RELOC_MIPS_JMP:
+ mips_type = MIPS_R_JMPADDR;
+ break;
+ case BFD_RELOC_HI16_S:
+ mips_type = MIPS_R_REFHI;
+ break;
+ case BFD_RELOC_LO16:
+ mips_type = MIPS_R_REFLO;
+ break;
+ case BFD_RELOC_GPREL16:
+ mips_type = MIPS_R_GPREL;
+ break;
+ case BFD_RELOC_MIPS_LITERAL:
+ mips_type = MIPS_R_LITERAL;
+ break;
+/* FIXME?
+ case BFD_RELOC_16_PCREL_S2:
+ mips_type = MIPS_R_PCREL16;
+ break;
+ case BFD_RELOC_PCREL_HI16_S:
+ mips_type = MIPS_R_RELHI;
+ break;
+ case BFD_RELOC_PCREL_LO16:
+ mips_type = MIPS_R_RELLO;
+ break;
+ case BFD_RELOC_GPREL32:
+ mips_type = MIPS_R_SWITCH;
+ break;
+*/
+ case BFD_RELOC_RVA:
+ mips_type = MIPS_R_RVA;
+ break;
+ default:
+ return (reloc_howto_type *) NULL;
+ }
+
+ return &howto_table[mips_type];
+}
+
+static void
+mips_swap_reloc_in (abfd, src, dst)
+ bfd *abfd;
+ PTR src;
+ PTR dst;
+{
+ static struct internal_reloc pair_prev;
+ RELOC *reloc_src = (RELOC *) src;
+ struct internal_reloc *reloc_dst = (struct internal_reloc *) dst;
+
+ reloc_dst->r_vaddr = H_GET_32 (abfd, reloc_src->r_vaddr);
+ reloc_dst->r_symndx = H_GET_S32 (abfd, reloc_src->r_symndx);
+ reloc_dst->r_type = H_GET_16 (abfd, reloc_src->r_type);
+ reloc_dst->r_size = 0;
+ reloc_dst->r_extern = 0;
+ reloc_dst->r_offset = 0;
+
+ switch (reloc_dst->r_type)
+ {
+ case MIPS_R_REFHI:
+ pair_prev = *reloc_dst;
+ break;
+ case MIPS_R_PAIR:
+ reloc_dst->r_offset = reloc_dst->r_symndx;
+ if (reloc_dst->r_offset & 0x8000)
+ reloc_dst->r_offset -= 0x10000;
+ /*printf ("dj: pair offset is %08x\n", reloc_dst->r_offset);*/
+ reloc_dst->r_symndx = pair_prev.r_symndx;
+ break;
+ }
+}
+
+static unsigned int
+mips_swap_reloc_out (abfd, src, dst)
+ bfd *abfd;
+ PTR src;
+ PTR dst;
+{
+ static int prev_offset = 1;
+ static bfd_vma prev_addr = 0;
+ struct internal_reloc *reloc_src = (struct internal_reloc *)src;
+ struct external_reloc *reloc_dst = (struct external_reloc *)dst;
+
+ switch (reloc_src->r_type)
+ {
+ case MIPS_R_REFHI:
+ prev_addr = reloc_src->r_vaddr;
+ prev_offset = reloc_src->r_offset;
+ break;
+ case MIPS_R_REFLO:
+ if (reloc_src->r_vaddr == prev_addr)
+ {
+ /* FIXME: only slightly hackish. If we see a REFLO pointing to
+ the same address as a REFHI, we assume this is the matching
+ PAIR reloc and output it accordingly. The symndx is really
+ the low 16 bits of the addend */
+ H_PUT_32 (abfd, reloc_src->r_vaddr, reloc_dst->r_vaddr);
+ H_PUT_32 (abfd, reloc_src->r_symndx, reloc_dst->r_symndx);
+ H_PUT_16 (abfd, MIPS_R_PAIR, reloc_dst->r_type);
+ return RELSZ;
+ }
+ break;
+ }
+
+ H_PUT_32 (abfd, reloc_src->r_vaddr, reloc_dst->r_vaddr);
+ H_PUT_32 (abfd, reloc_src->r_symndx, reloc_dst->r_symndx);
+
+ H_PUT_16 (abfd, reloc_src->r_type, reloc_dst->r_type);
+ return RELSZ;
+}
+
+#define coff_swap_reloc_in mips_swap_reloc_in
+#define coff_swap_reloc_out mips_swap_reloc_out
+#define NO_COFF_RELOCS
+
+static bfd_boolean
+coff_pe_mips_relocate_section (output_bfd, info, input_bfd,
+ input_section, contents, relocs, syms,
+ sections)
+ bfd *output_bfd;
+ struct bfd_link_info *info;
+ bfd *input_bfd;
+ asection *input_section;
+ bfd_byte *contents;
+ struct internal_reloc *relocs;
+ struct internal_syment *syms;
+ asection **sections;
+{
+ bfd_vma gp;
+ bfd_boolean gp_undefined;
+ size_t adjust;
+ struct internal_reloc *rel;
+ struct internal_reloc *rel_end;
+ unsigned int i;
+ bfd_boolean got_lo;
+
+ if (info->relocatable)
+ {
+ (*_bfd_error_handler) (_("\
+%s: `ld -r' not supported with PE MIPS objects\n"),
+ bfd_archive_filename (input_bfd));
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+
+ BFD_ASSERT (input_bfd->xvec->byteorder
+ == output_bfd->xvec->byteorder);
+
+#if 0
+ printf ("dj: relocate %s(%s) %08x\n",
+ input_bfd->filename, input_section->name,
+ input_section->output_section->vma + input_section->output_offset);
+#endif
+
+ gp = _bfd_get_gp_value (output_bfd);
+ if (gp == 0)
+ gp_undefined = TRUE;
+ else
+ gp_undefined = FALSE;
+
+ got_lo = FALSE;
+
+ adjust = 0;
+
+ rel = relocs;
+ rel_end = rel + input_section->reloc_count;
+ for (i = 0; rel < rel_end; rel++, i++)
+ {
+ long symndx;
+ struct coff_link_hash_entry *h;
+ struct internal_syment *sym;
+ bfd_vma addend = 0;
+ bfd_vma val, tmp, targ, src, low;
+ reloc_howto_type *howto;
+ unsigned char *mem = contents + rel->r_vaddr;
+
+ symndx = rel->r_symndx;
+
+ if (symndx == -1)
+ {
+ h = NULL;
+ sym = NULL;
+ }
+ else
+ {
+ h = obj_coff_sym_hashes (input_bfd)[symndx];
+ sym = syms + symndx;
+ }
+
+ /* COFF treats common symbols in one of two ways. Either the
+ size of the symbol is included in the section contents, or it
+ is not. We assume that the size is not included, and force
+ the rtype_to_howto function to adjust the addend as needed. */
+
+ if (sym != NULL && sym->n_scnum != 0)
+ addend = - sym->n_value;
+ else
+ addend = 0;
+
+ howto = bfd_coff_rtype_to_howto (input_bfd, input_section, rel, h,
+ sym, &addend);
+ if (howto == NULL)
+ return FALSE;
+
+ /* If we are doing a relocatable link, then we can just ignore
+ a PC relative reloc that is pcrel_offset. It will already
+ have the correct value. If this is not a relocatable link,
+ then we should ignore the symbol value. */
+ if (howto->pc_relative && howto->pcrel_offset)
+ {
+ if (info->relocatable)
+ continue;
+ if (sym != NULL && sym->n_scnum != 0)
+ addend += sym->n_value;
+ }
+
+ val = 0;
+
+ if (h == NULL)
+ {
+ asection *sec;
+
+ if (symndx == -1)
+ {
+ sec = bfd_abs_section_ptr;
+ val = 0;
+ }
+ else
+ {
+ sec = sections[symndx];
+ val = (sec->output_section->vma
+ + sec->output_offset
+ + sym->n_value);
+ if (! obj_pe (input_bfd))
+ val -= sec->vma;
+ }
+ }
+ else
+ {
+ if (h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ {
+ asection *sec;
+
+ sec = h->root.u.def.section;
+ val = (h->root.u.def.value
+ + sec->output_section->vma
+ + sec->output_offset);
+ }
+
+ else if (! info->relocatable)
+ {
+ if (! ((*info->callbacks->undefined_symbol)
+ (info, h->root.root.string, input_bfd, input_section,
+ rel->r_vaddr - input_section->vma, TRUE)))
+ return FALSE;
+ }
+ }
+
+ src = rel->r_vaddr + input_section->output_section->vma
+ + input_section->output_offset;
+#if 0
+ printf ("dj: reloc %02x %-8s a=%08x/%08x(%08x) v=%08x+%08x %s\n",
+ rel->r_type, howto_table[rel->r_type].name,
+ src, rel->r_vaddr, *(unsigned long *)mem, val, rel->r_offset,
+ h?h->root.root.string:"(none)");
+#endif
+
+ /* OK, at this point the following variables are set up:
+ src = VMA of the memory we're fixing up
+ mem = pointer to memory we're fixing up
+ val = VMA of what we need to refer to
+ */
+
+#define UI(x) (*_bfd_error_handler) (_("%s: unimplemented %s\n"), \
+ bfd_archive_filename (input_bfd), x); \
+ bfd_set_error (bfd_error_bad_value);
+
+ switch (rel->r_type)
+ {
+ case MIPS_R_ABSOLUTE:
+ /* ignore these */
+ break;
+
+ case MIPS_R_REFHALF:
+ UI("refhalf");
+ break;
+
+ case MIPS_R_REFWORD:
+ tmp = bfd_get_32(input_bfd, mem);
+ /* printf ("refword: src=%08x targ=%08x+%08x\n", src, tmp, val); */
+ tmp += val;
+ bfd_put_32(input_bfd, tmp, mem);
+ break;
+
+ case MIPS_R_JMPADDR:
+ tmp = bfd_get_32(input_bfd, mem);
+ targ = val + (tmp&0x03ffffff)*4;
+ if ((src & 0xf0000000) != (targ & 0xf0000000))
+ {
+ (*_bfd_error_handler) (_("%s: jump too far away\n"),
+ bfd_archive_filename (input_bfd));
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ tmp &= 0xfc000000;
+ tmp |= (targ/4) & 0x3ffffff;
+ bfd_put_32(input_bfd, tmp, mem);
+ break;
+
+ case MIPS_R_REFHI:
+ tmp = bfd_get_32(input_bfd, mem);
+ switch (rel[1].r_type)
+ {
+ case MIPS_R_PAIR:
+ /* MS PE object */
+ targ = val + rel[1].r_offset + ((tmp & 0xffff) << 16);
+ break;
+ case MIPS_R_REFLO:
+ /* GNU COFF object */
+ low = bfd_get_32(input_bfd, contents + rel[1].r_vaddr);
+ low &= 0xffff;
+ if (low & 0x8000)
+ low -= 0x10000;
+ targ = val + low + ((tmp & 0xffff) << 16);
+ break;
+ default:
+ (*_bfd_error_handler) (_("%s: bad pair/reflo after refhi\n"),
+ bfd_archive_filename (input_bfd));
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ tmp &= 0xffff0000;
+ tmp |= (targ >> 16) & 0xffff;
+ bfd_put_32(input_bfd, tmp, mem);
+ break;
+
+ case MIPS_R_REFLO:
+ tmp = bfd_get_32(input_bfd, mem);
+ targ = val + (tmp & 0xffff);
+ /* printf ("refword: src=%08x targ=%08x\n", src, targ); */
+ tmp &= 0xffff0000;
+ tmp |= targ & 0xffff;
+ bfd_put_32(input_bfd, tmp, mem);
+ break;
+
+ case MIPS_R_GPREL:
+ case MIPS_R_LITERAL:
+ UI("gprel");
+ break;
+
+ case MIPS_R_SECTION:
+ UI("section");
+ break;
+
+ case MIPS_R_SECREL:
+ UI("secrel");
+ break;
+
+ case MIPS_R_SECRELLO:
+ UI("secrello");
+ break;
+
+ case MIPS_R_SECRELHI:
+ UI("secrelhi");
+ break;
+
+ case MIPS_R_RVA:
+ tmp = bfd_get_32 (input_bfd, mem);
+ /* printf ("rva: src=%08x targ=%08x+%08x\n", src, tmp, val); */
+ tmp += val
+ - pe_data (input_section->output_section->owner)->pe_opthdr.ImageBase;
+ bfd_put_32 (input_bfd, tmp, mem);
+ break;
+
+ case MIPS_R_PAIR:
+ /* ignore these */
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+#define coff_relocate_section coff_pe_mips_relocate_section
+
+#ifdef TARGET_UNDERSCORE
+
+/* If mips gcc uses underscores for symbol names, then it does not use
+ a leading dot for local labels, so if TARGET_UNDERSCORE is defined
+ we treat all symbols starting with L as local. */
+
+static bfd_boolean coff_mips_is_local_label_name
+ PARAMS ((bfd *, const char *));
+
+static bfd_boolean
+coff_mips_is_local_label_name (abfd, name)
+ bfd *abfd;
+ const char *name;
+{
+ if (name[0] == 'L')
+ return TRUE;
+
+ return _bfd_coff_is_local_label_name (abfd, name);
+}
+
+#define coff_bfd_is_local_label_name coff_mips_is_local_label_name
+
+#endif /* TARGET_UNDERSCORE */
+
+#define COFF_NO_HACK_SCNHDR_SIZE
+
+#include "coffcode.h"
+
+const bfd_target
+#ifdef TARGET_SYM
+ TARGET_SYM =
+#else
+ mipslpe_vec =
+#endif
+{
+#ifdef TARGET_NAME
+ TARGET_NAME,
+#else
+ "pe-mips", /* name */
+#endif
+ bfd_target_coff_flavour,
+ BFD_ENDIAN_LITTLE, /* data byte order is little */
+ BFD_ENDIAN_LITTLE, /* header byte order is little */
+
+ (HAS_RELOC | EXEC_P | /* object flags */
+ HAS_LINENO | HAS_DEBUG |
+ HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED),
+
+#ifndef COFF_WITH_PE
+ (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC /* section flags */
+ | SEC_CODE | SEC_DATA),
+#else
+ (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC /* section flags */
+ | SEC_CODE | SEC_DATA
+ | SEC_LINK_ONCE | SEC_LINK_DUPLICATES),
+#endif
+
+#ifdef TARGET_UNDERSCORE
+ TARGET_UNDERSCORE, /* leading underscore */
+#else
+ 0, /* leading underscore */
+#endif
+ '/', /* ar_pad_char */
+ 15, /* ar_max_namelen */
+
+ bfd_getl64, bfd_getl_signed_64, bfd_putl64,
+ bfd_getl32, bfd_getl_signed_32, bfd_putl32,
+ bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* data */
+ bfd_getl64, bfd_getl_signed_64, bfd_putl64,
+ bfd_getl32, bfd_getl_signed_32, bfd_putl32,
+ bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* hdrs */
+
+/* Note that we allow an object file to be treated as a core file as well. */
+ {_bfd_dummy_target, coff_object_p, /* bfd_check_format */
+ bfd_generic_archive_p, coff_object_p},
+ {bfd_false, coff_mkobject, _bfd_generic_mkarchive, /* bfd_set_format */
+ bfd_false},
+ {bfd_false, coff_write_object_contents, /* bfd_write_contents */
+ _bfd_write_archive_contents, bfd_false},
+
+ BFD_JUMP_TABLE_GENERIC (coff),
+ BFD_JUMP_TABLE_COPY (coff),
+ BFD_JUMP_TABLE_CORE (_bfd_nocore),
+ BFD_JUMP_TABLE_ARCHIVE (_bfd_archive_coff),
+ BFD_JUMP_TABLE_SYMBOLS (coff),
+ BFD_JUMP_TABLE_RELOCS (coff),
+ BFD_JUMP_TABLE_WRITE (coff),
+ BFD_JUMP_TABLE_LINK (coff),
+ BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
+
+ NULL,
+
+ COFF_SWAP_TABLE
+};
diff --git a/contrib/binutils/bfd/pei-mips.c b/contrib/binutils/bfd/pei-mips.c
new file mode 100644
index 0000000..976b2f9
--- /dev/null
+++ b/contrib/binutils/bfd/pei-mips.c
@@ -0,0 +1,30 @@
+/* BFD back-end for MIPS PE IMAGE COFF files.
+ Copyright 1995, 2000, 2001, 2002 Free Software Foundation, 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. */
+
+#include "bfd.h"
+#include "sysdep.h"
+
+#define TARGET_SYM mipslpei_vec
+#define TARGET_NAME "pei-mips"
+#define COFF_IMAGE_WITH_PE
+#define PCRELOFFSET TRUE
+#define COFF_LONG_SECTION_NAMES
+
+#include "pe-mips.c"
+
diff --git a/contrib/binutils/gas/config/e-mipsecoff.c b/contrib/binutils/gas/config/e-mipsecoff.c
new file mode 100644
index 0000000..be2f71b
--- /dev/null
+++ b/contrib/binutils/gas/config/e-mipsecoff.c
@@ -0,0 +1,37 @@
+#include "as.h"
+#include "emul.h"
+
+static const char *mipsecoff_bfd_name PARAMS ((void));
+
+static const char *
+mipsecoff_bfd_name ()
+{
+ abort ();
+ return NULL;
+}
+
+#define emul_bfd_name mipsecoff_bfd_name
+#define emul_format &ecoff_format_ops
+
+#define emul_name "mipsbecoff"
+#define emul_struct_name mipsbecoff
+#define emul_default_endian 1
+#include "emul-target.h"
+
+#undef emul_name
+#undef emul_struct_name
+#undef emul_default_endian
+
+#define emul_name "mipslecoff"
+#define emul_struct_name mipslecoff
+#define emul_default_endian 0
+#include "emul-target.h"
+
+#undef emul_name
+#undef emul_struct_name
+#undef emul_default_endian
+
+#define emul_name "mipsecoff"
+#define emul_struct_name mipsecoff
+#define emul_default_endian 2
+#include "emul-target.h"
diff --git a/contrib/binutils/gas/config/e-mipself.c b/contrib/binutils/gas/config/e-mipself.c
new file mode 100644
index 0000000..eea72f5
--- /dev/null
+++ b/contrib/binutils/gas/config/e-mipself.c
@@ -0,0 +1,37 @@
+#include "as.h"
+#include "emul.h"
+
+static const char *mipself_bfd_name PARAMS ((void));
+
+static const char *
+mipself_bfd_name ()
+{
+ abort ();
+ return NULL;
+}
+
+#define emul_bfd_name mipself_bfd_name
+#define emul_format &elf_format_ops
+
+#define emul_name "mipsbelf"
+#define emul_struct_name mipsbelf
+#define emul_default_endian 1
+#include "emul-target.h"
+
+#undef emul_name
+#undef emul_struct_name
+#undef emul_default_endian
+
+#define emul_name "mipslelf"
+#define emul_struct_name mipslelf
+#define emul_default_endian 0
+#include "emul-target.h"
+
+#undef emul_name
+#undef emul_struct_name
+#undef emul_default_endian
+
+#define emul_name "mipself"
+#define emul_struct_name mipself
+#define emul_default_endian 2
+#include "emul-target.h"
diff --git a/contrib/binutils/gas/config/itbl-mips.h b/contrib/binutils/gas/config/itbl-mips.h
new file mode 100644
index 0000000..8ecb9ec
--- /dev/null
+++ b/contrib/binutils/gas/config/itbl-mips.h
@@ -0,0 +1,47 @@
+
+/* itbl-mips.h
+
+ Copyright 1997 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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. */
+
+/* Defines for Mips itbl cop support */
+
+#include "opcode/mips.h"
+
+/* Values for processors will be from 0 to NUMBER_OF_PROCESSORS-1 */
+#define NUMBER_OF_PROCESSORS 4
+#define MAX_BITPOS 31
+
+/* Mips specifics */
+#define MIPS_OPCODE_COP0 (0x21) /* COPz+CO, bits 31-25: 0100zz1 */
+#define MIPS_ENCODE_COP_NUM(z) ((MIPS_OPCODE_COP0|z<<1)<<25)
+#define MIPS_IS_COP_INSN(insn) ((MIPS_OPCODE_COP0&(insn>>25)) \
+ == MIPS_OPCODE_COP0)
+#define MIPS_DECODE_COP_NUM(insn) ((~MIPS_OPCODE_COP0&(insn>>25))>>1)
+#define MIPS_DECODE_COP_COFUN(insn) ((~MIPS_ENCODE_COP_NUM(3))&(insn))
+
+/* definitions required by generic code */
+#define ITBL_IS_INSN(insn) MIPS_IS_COP_INSN(insn)
+#define ITBL_DECODE_PNUM(insn) MIPS_DECODE_COP_NUM(insn)
+#define ITBL_ENCODE_PNUM(pnum) MIPS_ENCODE_COP_NUM(pnum)
+
+#define ITBL_OPCODE_STRUCT mips_opcode
+#define ITBL_OPCODES mips_opcodes
+#define ITBL_NUM_OPCODES NUMOPCODES
+#define ITBL_NUM_MACROS M_NUM_MACROS
diff --git a/contrib/binutils/gas/config/tc-mips.c b/contrib/binutils/gas/config/tc-mips.c
new file mode 100644
index 0000000..7b6cee8
--- /dev/null
+++ b/contrib/binutils/gas/config/tc-mips.c
@@ -0,0 +1,14431 @@
+/* tc-mips.c -- assemble code for a MIPS chip.
+ Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+ 2003, 2004 Free Software Foundation, Inc.
+ Contributed by the OSF and Ralph Campbell.
+ Written by Keith Knowles and Ralph Campbell, working independently.
+ Modified for ECOFF and R4000 support by Ian Lance Taylor of Cygnus
+ Support.
+
+ 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 "as.h"
+#include "config.h"
+#include "subsegs.h"
+#include "safe-ctype.h"
+
+#include <stdarg.h>
+
+#include "opcode/mips.h"
+#include "itbl-ops.h"
+#include "dwarf2dbg.h"
+
+#ifdef DEBUG
+#define DBG(x) printf x
+#else
+#define DBG(x)
+#endif
+
+#ifdef OBJ_MAYBE_ELF
+/* Clean up namespace so we can include obj-elf.h too. */
+static int mips_output_flavor (void);
+static int mips_output_flavor (void) { return OUTPUT_FLAVOR; }
+#undef OBJ_PROCESS_STAB
+#undef OUTPUT_FLAVOR
+#undef S_GET_ALIGN
+#undef S_GET_SIZE
+#undef S_SET_ALIGN
+#undef S_SET_SIZE
+#undef obj_frob_file
+#undef obj_frob_file_after_relocs
+#undef obj_frob_symbol
+#undef obj_pop_insert
+#undef obj_sec_sym_ok_for_reloc
+#undef OBJ_COPY_SYMBOL_ATTRIBUTES
+
+#include "obj-elf.h"
+/* Fix any of them that we actually care about. */
+#undef OUTPUT_FLAVOR
+#define OUTPUT_FLAVOR mips_output_flavor()
+#endif
+
+#if defined (OBJ_ELF)
+#include "elf/mips.h"
+#endif
+
+#ifndef ECOFF_DEBUGGING
+#define NO_ECOFF_DEBUGGING
+#define ECOFF_DEBUGGING 0
+#endif
+
+int mips_flag_mdebug = -1;
+
+/* Control generation of .pdr sections. Off by default on IRIX: the native
+ linker doesn't know about and discards them, but relocations against them
+ remain, leading to rld crashes. */
+#ifdef TE_IRIX
+int mips_flag_pdr = FALSE;
+#else
+int mips_flag_pdr = TRUE;
+#endif
+
+#include "ecoff.h"
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+static char *mips_regmask_frag;
+#endif
+
+#define ZERO 0
+#define AT 1
+#define TREG 24
+#define PIC_CALL_REG 25
+#define KT0 26
+#define KT1 27
+#define GP 28
+#define SP 29
+#define FP 30
+#define RA 31
+
+#define ILLEGAL_REG (32)
+
+/* Allow override of standard little-endian ECOFF format. */
+
+#ifndef ECOFF_LITTLE_FORMAT
+#define ECOFF_LITTLE_FORMAT "ecoff-littlemips"
+#endif
+
+extern int target_big_endian;
+
+/* The name of the readonly data section. */
+#define RDATA_SECTION_NAME (OUTPUT_FLAVOR == bfd_target_aout_flavour \
+ ? ".data" \
+ : OUTPUT_FLAVOR == bfd_target_ecoff_flavour \
+ ? ".rdata" \
+ : OUTPUT_FLAVOR == bfd_target_coff_flavour \
+ ? ".rdata" \
+ : OUTPUT_FLAVOR == bfd_target_elf_flavour \
+ ? ".rodata" \
+ : (abort (), ""))
+
+/* The ABI to use. */
+enum mips_abi_level
+{
+ NO_ABI = 0,
+ O32_ABI,
+ O64_ABI,
+ N32_ABI,
+ N64_ABI,
+ EABI_ABI
+};
+
+/* MIPS ABI we are using for this output file. */
+static enum mips_abi_level mips_abi = NO_ABI;
+
+/* Whether or not we have code that can call pic code. */
+int mips_abicalls = FALSE;
+
+/* This is the set of options which may be modified by the .set
+ pseudo-op. We use a struct so that .set push and .set pop are more
+ reliable. */
+
+struct mips_set_options
+{
+ /* MIPS ISA (Instruction Set Architecture) level. This is set to -1
+ if it has not been initialized. Changed by `.set mipsN', and the
+ -mipsN command line option, and the default CPU. */
+ int isa;
+ /* Enabled Application Specific Extensions (ASEs). These are set to -1
+ if they have not been initialized. Changed by `.set <asename>', by
+ command line options, and based on the default architecture. */
+ int ase_mips3d;
+ int ase_mdmx;
+ /* Whether we are assembling for the mips16 processor. 0 if we are
+ not, 1 if we are, and -1 if the value has not been initialized.
+ Changed by `.set mips16' and `.set nomips16', and the -mips16 and
+ -nomips16 command line options, and the default CPU. */
+ int mips16;
+ /* Non-zero if we should not reorder instructions. Changed by `.set
+ reorder' and `.set noreorder'. */
+ int noreorder;
+ /* Non-zero if we should not permit the $at ($1) register to be used
+ in instructions. Changed by `.set at' and `.set noat'. */
+ int noat;
+ /* Non-zero if we should warn when a macro instruction expands into
+ more than one machine instruction. Changed by `.set nomacro' and
+ `.set macro'. */
+ int warn_about_macros;
+ /* Non-zero if we should not move instructions. Changed by `.set
+ move', `.set volatile', `.set nomove', and `.set novolatile'. */
+ int nomove;
+ /* Non-zero if we should not optimize branches by moving the target
+ of the branch into the delay slot. Actually, we don't perform
+ this optimization anyhow. Changed by `.set bopt' and `.set
+ nobopt'. */
+ int nobopt;
+ /* Non-zero if we should not autoextend mips16 instructions.
+ Changed by `.set autoextend' and `.set noautoextend'. */
+ int noautoextend;
+ /* Restrict general purpose registers and floating point registers
+ to 32 bit. This is initially determined when -mgp32 or -mfp32
+ is passed but can changed if the assembler code uses .set mipsN. */
+ int gp32;
+ int fp32;
+ /* MIPS architecture (CPU) type. Changed by .set arch=FOO, the -march
+ command line option, and the default CPU. */
+ int arch;
+};
+
+/* True if -mgp32 was passed. */
+static int file_mips_gp32 = -1;
+
+/* True if -mfp32 was passed. */
+static int file_mips_fp32 = -1;
+
+/* This is the struct we use to hold the current set of options. Note
+ that we must set the isa field to ISA_UNKNOWN and the ASE fields to
+ -1 to indicate that they have not been initialized. */
+
+static struct mips_set_options mips_opts =
+{
+ ISA_UNKNOWN, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, CPU_UNKNOWN
+};
+
+/* These variables are filled in with the masks of registers used.
+ The object format code reads them and puts them in the appropriate
+ place. */
+unsigned long mips_gprmask;
+unsigned long mips_cprmask[4];
+
+/* MIPS ISA we are using for this output file. */
+static int file_mips_isa = ISA_UNKNOWN;
+
+/* True if -mips16 was passed or implied by arguments passed on the
+ command line (e.g., by -march). */
+static int file_ase_mips16;
+
+/* True if -mips3d was passed or implied by arguments passed on the
+ command line (e.g., by -march). */
+static int file_ase_mips3d;
+
+/* True if -mdmx was passed or implied by arguments passed on the
+ command line (e.g., by -march). */
+static int file_ase_mdmx;
+
+/* The argument of the -march= flag. The architecture we are assembling. */
+static int file_mips_arch = CPU_UNKNOWN;
+static const char *mips_arch_string;
+
+/* The argument of the -mtune= flag. The architecture for which we
+ are optimizing. */
+static int mips_tune = CPU_UNKNOWN;
+static const char *mips_tune_string;
+
+/* True when generating 32-bit code for a 64-bit processor. */
+static int mips_32bitmode = 0;
+
+/* True if the given ABI requires 32-bit registers. */
+#define ABI_NEEDS_32BIT_REGS(ABI) ((ABI) == O32_ABI)
+
+/* Likewise 64-bit registers. */
+#define ABI_NEEDS_64BIT_REGS(ABI) \
+ ((ABI) == N32_ABI \
+ || (ABI) == N64_ABI \
+ || (ABI) == O64_ABI)
+
+/* Return true if ISA supports 64 bit gp register instructions. */
+#define ISA_HAS_64BIT_REGS(ISA) ( \
+ (ISA) == ISA_MIPS3 \
+ || (ISA) == ISA_MIPS4 \
+ || (ISA) == ISA_MIPS5 \
+ || (ISA) == ISA_MIPS64 \
+ || (ISA) == ISA_MIPS64R2 \
+ )
+
+/* Return true if ISA supports 64-bit right rotate (dror et al.)
+ instructions. */
+#define ISA_HAS_DROR(ISA) ( \
+ (ISA) == ISA_MIPS64R2 \
+ )
+
+/* Return true if ISA supports 32-bit right rotate (ror et al.)
+ instructions. */
+#define ISA_HAS_ROR(ISA) ( \
+ (ISA) == ISA_MIPS32R2 \
+ || (ISA) == ISA_MIPS64R2 \
+ )
+
+#define HAVE_32BIT_GPRS \
+ (mips_opts.gp32 || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
+
+#define HAVE_32BIT_FPRS \
+ (mips_opts.fp32 || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
+
+#define HAVE_64BIT_GPRS (! HAVE_32BIT_GPRS)
+#define HAVE_64BIT_FPRS (! HAVE_32BIT_FPRS)
+
+#define HAVE_NEWABI (mips_abi == N32_ABI || mips_abi == N64_ABI)
+
+#define HAVE_64BIT_OBJECTS (mips_abi == N64_ABI)
+
+/* We can only have 64bit addresses if the object file format
+ supports it. */
+#define HAVE_32BIT_ADDRESSES \
+ (HAVE_32BIT_GPRS \
+ || ((bfd_arch_bits_per_address (stdoutput) == 32 \
+ || ! HAVE_64BIT_OBJECTS) \
+ && mips_pic != EMBEDDED_PIC))
+
+#define HAVE_64BIT_ADDRESSES (! HAVE_32BIT_ADDRESSES)
+
+/* Addresses are loaded in different ways, depending on the address size
+ in use. The n32 ABI Documentation also mandates the use of additions
+ with overflow checking, but existing implementations don't follow it. */
+#define ADDRESS_ADD_INSN \
+ (HAVE_32BIT_ADDRESSES ? "addu" : "daddu")
+
+#define ADDRESS_ADDI_INSN \
+ (HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu")
+
+#define ADDRESS_LOAD_INSN \
+ (HAVE_32BIT_ADDRESSES ? "lw" : "ld")
+
+#define ADDRESS_STORE_INSN \
+ (HAVE_32BIT_ADDRESSES ? "sw" : "sd")
+
+/* Return true if the given CPU supports the MIPS16 ASE. */
+#define CPU_HAS_MIPS16(cpu) \
+ (strncmp (TARGET_CPU, "mips16", sizeof ("mips16") - 1) == 0 \
+ || strncmp (TARGET_CANONICAL, "mips-lsi-elf", sizeof ("mips-lsi-elf") - 1) == 0)
+
+/* Return true if the given CPU supports the MIPS3D ASE. */
+#define CPU_HAS_MIPS3D(cpu) ((cpu) == CPU_SB1 \
+ )
+
+/* Return true if the given CPU supports the MDMX ASE. */
+#define CPU_HAS_MDMX(cpu) (FALSE \
+ )
+
+/* True if CPU has a dror instruction. */
+#define CPU_HAS_DROR(CPU) ((CPU) == CPU_VR5400 || (CPU) == CPU_VR5500)
+
+/* True if CPU has a ror instruction. */
+#define CPU_HAS_ROR(CPU) CPU_HAS_DROR (CPU)
+
+/* True if mflo and mfhi can be immediately followed by instructions
+ which write to the HI and LO registers.
+
+ According to MIPS specifications, MIPS ISAs I, II, and III need
+ (at least) two instructions between the reads of HI/LO and
+ instructions which write them, and later ISAs do not. Contradicting
+ the MIPS specifications, some MIPS IV processor user manuals (e.g.
+ the UM for the NEC Vr5000) document needing the instructions between
+ HI/LO reads and writes, as well. Therefore, we declare only MIPS32,
+ MIPS64 and later ISAs to have the interlocks, plus any specific
+ earlier-ISA CPUs for which CPU documentation declares that the
+ instructions are really interlocked. */
+#define hilo_interlocks \
+ (mips_opts.isa == ISA_MIPS32 \
+ || mips_opts.isa == ISA_MIPS32R2 \
+ || mips_opts.isa == ISA_MIPS64 \
+ || mips_opts.isa == ISA_MIPS64R2 \
+ || mips_opts.arch == CPU_R4010 \
+ || mips_opts.arch == CPU_R10000 \
+ || mips_opts.arch == CPU_R12000 \
+ || mips_opts.arch == CPU_RM7000 \
+ || mips_opts.arch == CPU_SB1 \
+ || mips_opts.arch == CPU_VR5500 \
+ )
+
+/* Whether the processor uses hardware interlocks to protect reads
+ from the GPRs after they are loaded from memory, and thus does not
+ require nops to be inserted. This applies to instructions marked
+ INSN_LOAD_MEMORY_DELAY. These nops are only required at MIPS ISA
+ level I. */
+#define gpr_interlocks \
+ (mips_opts.isa != ISA_MIPS1 \
+ || mips_opts.arch == CPU_VR5400 \
+ || mips_opts.arch == CPU_VR5500 \
+ || mips_opts.arch == CPU_R3900)
+
+/* Whether the processor uses hardware interlocks to avoid delays
+ required by coprocessor instructions, and thus does not require
+ nops to be inserted. This applies to instructions marked
+ INSN_LOAD_COPROC_DELAY, INSN_COPROC_MOVE_DELAY, and to delays
+ between instructions marked INSN_WRITE_COND_CODE and ones marked
+ INSN_READ_COND_CODE. These nops are only required at MIPS ISA
+ levels I, II, and III. */
+/* Itbl support may require additional care here. */
+#define cop_interlocks \
+ ((mips_opts.isa != ISA_MIPS1 \
+ && mips_opts.isa != ISA_MIPS2 \
+ && mips_opts.isa != ISA_MIPS3) \
+ || mips_opts.arch == CPU_R4300 \
+ || mips_opts.arch == CPU_VR5400 \
+ || mips_opts.arch == CPU_VR5500 \
+ || mips_opts.arch == CPU_SB1 \
+ )
+
+/* Whether the processor uses hardware interlocks to protect reads
+ from coprocessor registers after they are loaded from memory, and
+ thus does not require nops to be inserted. This applies to
+ instructions marked INSN_COPROC_MEMORY_DELAY. These nops are only
+ requires at MIPS ISA level I. */
+#define cop_mem_interlocks (mips_opts.isa != ISA_MIPS1)
+
+/* Is this a mfhi or mflo instruction? */
+#define MF_HILO_INSN(PINFO) \
+ ((PINFO & INSN_READ_HI) || (PINFO & INSN_READ_LO))
+
+/* MIPS PIC level. */
+
+enum mips_pic_level mips_pic;
+
+/* 1 if we should generate 32 bit offsets from the $gp register in
+ SVR4_PIC mode. Currently has no meaning in other modes. */
+static int mips_big_got = 0;
+
+/* 1 if trap instructions should used for overflow rather than break
+ instructions. */
+static int mips_trap = 0;
+
+/* 1 if double width floating point constants should not be constructed
+ by assembling two single width halves into two single width floating
+ point registers which just happen to alias the double width destination
+ register. On some architectures this aliasing can be disabled by a bit
+ in the status register, and the setting of this bit cannot be determined
+ automatically at assemble time. */
+static int mips_disable_float_construction;
+
+/* Non-zero if any .set noreorder directives were used. */
+
+static int mips_any_noreorder;
+
+/* Non-zero if nops should be inserted when the register referenced in
+ an mfhi/mflo instruction is read in the next two instructions. */
+static int mips_7000_hilo_fix;
+
+/* The size of the small data section. */
+static unsigned int g_switch_value = 8;
+/* Whether the -G option was used. */
+static int g_switch_seen = 0;
+
+#define N_RMASK 0xc4
+#define N_VFP 0xd4
+
+/* If we can determine in advance that GP optimization won't be
+ possible, we can skip the relaxation stuff that tries to produce
+ GP-relative references. This makes delay slot optimization work
+ better.
+
+ This function can only provide a guess, but it seems to work for
+ gcc output. It needs to guess right for gcc, otherwise gcc
+ will put what it thinks is a GP-relative instruction in a branch
+ delay slot.
+
+ I don't know if a fix is needed for the SVR4_PIC mode. I've only
+ fixed it for the non-PIC mode. KR 95/04/07 */
+static int nopic_need_relax (symbolS *, int);
+
+/* handle of the OPCODE hash table */
+static struct hash_control *op_hash = NULL;
+
+/* The opcode hash table we use for the mips16. */
+static struct hash_control *mips16_op_hash = NULL;
+
+/* 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.
+ */
+
+static char *insn_error;
+
+static int auto_align = 1;
+
+/* When outputting SVR4 PIC code, the assembler needs to know the
+ offset in the stack frame from which to restore the $gp register.
+ This is set by the .cprestore pseudo-op, and saved in this
+ variable. */
+static offsetT mips_cprestore_offset = -1;
+
+/* Similar for NewABI PIC code, where $gp is callee-saved. NewABI has some
+ more optimizations, it can use a register value instead of a memory-saved
+ offset and even an other register than $gp as global pointer. */
+static offsetT mips_cpreturn_offset = -1;
+static int mips_cpreturn_register = -1;
+static int mips_gp_register = GP;
+static int mips_gprel_offset = 0;
+
+/* Whether mips_cprestore_offset has been set in the current function
+ (or whether it has already been warned about, if not). */
+static int mips_cprestore_valid = 0;
+
+/* This is the register which holds the stack frame, as set by the
+ .frame pseudo-op. This is needed to implement .cprestore. */
+static int mips_frame_reg = SP;
+
+/* Whether mips_frame_reg has been set in the current function
+ (or whether it has already been warned about, if not). */
+static int mips_frame_reg_valid = 0;
+
+/* To output NOP instructions correctly, we need to keep information
+ about the previous two instructions. */
+
+/* Whether we are optimizing. The default value of 2 means to remove
+ unneeded NOPs and swap branch instructions when possible. A value
+ of 1 means to not swap branches. A value of 0 means to always
+ insert NOPs. */
+static int mips_optimize = 2;
+
+/* Debugging level. -g sets this to 2. -gN sets this to N. -g0 is
+ equivalent to seeing no -g option at all. */
+static int mips_debug = 0;
+
+/* The previous instruction. */
+static struct mips_cl_insn prev_insn;
+
+/* The instruction before prev_insn. */
+static struct mips_cl_insn prev_prev_insn;
+
+/* If we don't want information for prev_insn or prev_prev_insn, we
+ point the insn_mo field at this dummy integer. */
+static const struct mips_opcode dummy_opcode = { NULL, NULL, 0, 0, 0, 0 };
+
+/* Non-zero if prev_insn is valid. */
+static int prev_insn_valid;
+
+/* The frag for the previous instruction. */
+static struct frag *prev_insn_frag;
+
+/* The offset into prev_insn_frag for the previous instruction. */
+static long prev_insn_where;
+
+/* The reloc type for the previous instruction, if any. */
+static bfd_reloc_code_real_type prev_insn_reloc_type[3];
+
+/* The reloc for the previous instruction, if any. */
+static fixS *prev_insn_fixp[3];
+
+/* Non-zero if the previous instruction was in a delay slot. */
+static int prev_insn_is_delay_slot;
+
+/* Non-zero if the previous instruction was in a .set noreorder. */
+static int prev_insn_unreordered;
+
+/* Non-zero if the previous instruction uses an extend opcode (if
+ mips16). */
+static int prev_insn_extended;
+
+/* Non-zero if the previous previous instruction was in a .set
+ noreorder. */
+static int prev_prev_insn_unreordered;
+
+/* If this is set, it points to a frag holding nop instructions which
+ were inserted before the start of a noreorder section. If those
+ nops turn out to be unnecessary, the size of the frag can be
+ decreased. */
+static fragS *prev_nop_frag;
+
+/* The number of nop instructions we created in prev_nop_frag. */
+static int prev_nop_frag_holds;
+
+/* The number of nop instructions that we know we need in
+ prev_nop_frag. */
+static int prev_nop_frag_required;
+
+/* The number of instructions we've seen since prev_nop_frag. */
+static int prev_nop_frag_since;
+
+/* For ECOFF and ELF, relocations against symbols are done in two
+ parts, with a HI relocation and a LO relocation. Each relocation
+ has only 16 bits of space to store an addend. This means that in
+ order for the linker to handle carries correctly, it must be able
+ to locate both the HI and the LO relocation. This means that the
+ relocations must appear in order in the relocation table.
+
+ In order to implement this, we keep track of each unmatched HI
+ relocation. We then sort them so that they immediately precede the
+ corresponding LO relocation. */
+
+struct mips_hi_fixup
+{
+ /* Next HI fixup. */
+ struct mips_hi_fixup *next;
+ /* This fixup. */
+ fixS *fixp;
+ /* The section this fixup is in. */
+ segT seg;
+};
+
+/* The list of unmatched HI relocs. */
+
+static struct mips_hi_fixup *mips_hi_fixup_list;
+
+/* The frag containing the last explicit relocation operator.
+ Null if explicit relocations have not been used. */
+
+static fragS *prev_reloc_op_frag;
+
+/* Map normal MIPS register numbers to mips16 register numbers. */
+
+#define X ILLEGAL_REG
+static const int mips32_to_16_reg_map[] =
+{
+ X, X, 2, 3, 4, 5, 6, 7,
+ X, X, X, X, X, X, X, X,
+ 0, 1, X, X, X, X, X, X,
+ X, X, X, X, X, X, X, X
+};
+#undef X
+
+/* Map mips16 register numbers to normal MIPS register numbers. */
+
+static const unsigned int mips16_to_32_reg_map[] =
+{
+ 16, 17, 2, 3, 4, 5, 6, 7
+};
+
+static int mips_fix_vr4120;
+
+/* We don't relax branches by default, since this causes us to expand
+ `la .l2 - .l1' if there's a branch between .l1 and .l2, because we
+ fail to compute the offset before expanding the macro to the most
+ efficient expansion. */
+
+static int mips_relax_branch;
+
+/* The expansion of many macros depends on the type of symbol that
+ they refer to. For example, when generating position-dependent code,
+ a macro that refers to a symbol may have two different expansions,
+ one which uses GP-relative addresses and one which uses absolute
+ addresses. When generating SVR4-style PIC, a macro may have
+ different expansions for local and global symbols.
+
+ We handle these situations by generating both sequences and putting
+ them in variant frags. In position-dependent code, the first sequence
+ will be the GP-relative one and the second sequence will be the
+ absolute one. In SVR4 PIC, the first sequence will be for global
+ symbols and the second will be for local symbols.
+
+ The frag's "subtype" is RELAX_ENCODE (FIRST, SECOND), where FIRST and
+ SECOND are the lengths of the two sequences in bytes. These fields
+ can be extracted using RELAX_FIRST() and RELAX_SECOND(). In addition,
+ the subtype has the following flags:
+
+ RELAX_USE_SECOND
+ Set if it has been decided that we should use the second
+ sequence instead of the first.
+
+ RELAX_SECOND_LONGER
+ Set in the first variant frag if the macro's second implementation
+ is longer than its first. This refers to the macro as a whole,
+ not an individual relaxation.
+
+ RELAX_NOMACRO
+ Set in the first variant frag if the macro appeared in a .set nomacro
+ block and if one alternative requires a warning but the other does not.
+
+ RELAX_DELAY_SLOT
+ Like RELAX_NOMACRO, but indicates that the macro appears in a branch
+ delay slot.
+
+ The frag's "opcode" points to the first fixup for relaxable code.
+
+ Relaxable macros are generated using a sequence such as:
+
+ relax_start (SYMBOL);
+ ... generate first expansion ...
+ relax_switch ();
+ ... generate second expansion ...
+ relax_end ();
+
+ The code and fixups for the unwanted alternative are discarded
+ by md_convert_frag. */
+#define RELAX_ENCODE(FIRST, SECOND) (((FIRST) << 8) | (SECOND))
+
+#define RELAX_FIRST(X) (((X) >> 8) & 0xff)
+#define RELAX_SECOND(X) ((X) & 0xff)
+#define RELAX_USE_SECOND 0x10000
+#define RELAX_SECOND_LONGER 0x20000
+#define RELAX_NOMACRO 0x40000
+#define RELAX_DELAY_SLOT 0x80000
+
+/* Branch without likely bit. If label is out of range, we turn:
+
+ beq reg1, reg2, label
+ delay slot
+
+ into
+
+ bne reg1, reg2, 0f
+ nop
+ j label
+ 0: delay slot
+
+ with the following opcode replacements:
+
+ beq <-> bne
+ blez <-> bgtz
+ bltz <-> bgez
+ bc1f <-> bc1t
+
+ bltzal <-> bgezal (with jal label instead of j label)
+
+ Even though keeping the delay slot instruction in the delay slot of
+ the branch would be more efficient, it would be very tricky to do
+ correctly, because we'd have to introduce a variable frag *after*
+ the delay slot instruction, and expand that instead. Let's do it
+ the easy way for now, even if the branch-not-taken case now costs
+ one additional instruction. Out-of-range branches are not supposed
+ to be common, anyway.
+
+ Branch likely. If label is out of range, we turn:
+
+ beql reg1, reg2, label
+ delay slot (annulled if branch not taken)
+
+ into
+
+ beql reg1, reg2, 1f
+ nop
+ beql $0, $0, 2f
+ nop
+ 1: j[al] label
+ delay slot (executed only if branch taken)
+ 2:
+
+ It would be possible to generate a shorter sequence by losing the
+ likely bit, generating something like:
+
+ bne reg1, reg2, 0f
+ nop
+ j[al] label
+ delay slot (executed only if branch taken)
+ 0:
+
+ beql -> bne
+ bnel -> beq
+ blezl -> bgtz
+ bgtzl -> blez
+ bltzl -> bgez
+ bgezl -> bltz
+ bc1fl -> bc1t
+ bc1tl -> bc1f
+
+ bltzall -> bgezal (with jal label instead of j label)
+ bgezall -> bltzal (ditto)
+
+
+ but it's not clear that it would actually improve performance. */
+#define RELAX_BRANCH_ENCODE(uncond, likely, link, toofar) \
+ ((relax_substateT) \
+ (0xc0000000 \
+ | ((toofar) ? 1 : 0) \
+ | ((link) ? 2 : 0) \
+ | ((likely) ? 4 : 0) \
+ | ((uncond) ? 8 : 0)))
+#define RELAX_BRANCH_P(i) (((i) & 0xf0000000) == 0xc0000000)
+#define RELAX_BRANCH_UNCOND(i) (((i) & 8) != 0)
+#define RELAX_BRANCH_LIKELY(i) (((i) & 4) != 0)
+#define RELAX_BRANCH_LINK(i) (((i) & 2) != 0)
+#define RELAX_BRANCH_TOOFAR(i) (((i) & 1) != 0)
+
+/* For mips16 code, we use an entirely different form of relaxation.
+ mips16 supports two versions of most instructions which take
+ immediate values: a small one which takes some small value, and a
+ larger one which takes a 16 bit value. Since branches also follow
+ this pattern, relaxing these values is required.
+
+ We can assemble both mips16 and normal MIPS code in a single
+ object. Therefore, we need to support this type of relaxation at
+ the same time that we support the relaxation described above. We
+ use the high bit of the subtype field to distinguish these cases.
+
+ The information we store for this type of relaxation is the
+ argument code found in the opcode file for this relocation, whether
+ the user explicitly requested a small or extended form, and whether
+ the relocation is in a jump or jal delay slot. That tells us the
+ size of the value, and how it should be stored. We also store
+ whether the fragment is considered to be extended or not. We also
+ store whether this is known to be a branch to a different section,
+ whether we have tried to relax this frag yet, and whether we have
+ ever extended a PC relative fragment because of a shift count. */
+#define RELAX_MIPS16_ENCODE(type, small, ext, dslot, jal_dslot) \
+ (0x80000000 \
+ | ((type) & 0xff) \
+ | ((small) ? 0x100 : 0) \
+ | ((ext) ? 0x200 : 0) \
+ | ((dslot) ? 0x400 : 0) \
+ | ((jal_dslot) ? 0x800 : 0))
+#define RELAX_MIPS16_P(i) (((i) & 0xc0000000) == 0x80000000)
+#define RELAX_MIPS16_TYPE(i) ((i) & 0xff)
+#define RELAX_MIPS16_USER_SMALL(i) (((i) & 0x100) != 0)
+#define RELAX_MIPS16_USER_EXT(i) (((i) & 0x200) != 0)
+#define RELAX_MIPS16_DSLOT(i) (((i) & 0x400) != 0)
+#define RELAX_MIPS16_JAL_DSLOT(i) (((i) & 0x800) != 0)
+#define RELAX_MIPS16_EXTENDED(i) (((i) & 0x1000) != 0)
+#define RELAX_MIPS16_MARK_EXTENDED(i) ((i) | 0x1000)
+#define RELAX_MIPS16_CLEAR_EXTENDED(i) ((i) &~ 0x1000)
+#define RELAX_MIPS16_LONG_BRANCH(i) (((i) & 0x2000) != 0)
+#define RELAX_MIPS16_MARK_LONG_BRANCH(i) ((i) | 0x2000)
+#define RELAX_MIPS16_CLEAR_LONG_BRANCH(i) ((i) &~ 0x2000)
+
+/* Is the given value a sign-extended 32-bit value? */
+#define IS_SEXT_32BIT_NUM(x) \
+ (((x) &~ (offsetT) 0x7fffffff) == 0 \
+ || (((x) &~ (offsetT) 0x7fffffff) == ~ (offsetT) 0x7fffffff))
+
+/* Is the given value a sign-extended 16-bit value? */
+#define IS_SEXT_16BIT_NUM(x) \
+ (((x) &~ (offsetT) 0x7fff) == 0 \
+ || (((x) &~ (offsetT) 0x7fff) == ~ (offsetT) 0x7fff))
+
+
+/* Global variables used when generating relaxable macros. See the
+ comment above RELAX_ENCODE for more details about how relaxation
+ is used. */
+static struct {
+ /* 0 if we're not emitting a relaxable macro.
+ 1 if we're emitting the first of the two relaxation alternatives.
+ 2 if we're emitting the second alternative. */
+ int sequence;
+
+ /* The first relaxable fixup in the current frag. (In other words,
+ the first fixup that refers to relaxable code.) */
+ fixS *first_fixup;
+
+ /* sizes[0] says how many bytes of the first alternative are stored in
+ the current frag. Likewise sizes[1] for the second alternative. */
+ unsigned int sizes[2];
+
+ /* The symbol on which the choice of sequence depends. */
+ symbolS *symbol;
+} mips_relax;
+
+/* Global variables used to decide whether a macro needs a warning. */
+static struct {
+ /* True if the macro is in a branch delay slot. */
+ bfd_boolean delay_slot_p;
+
+ /* For relaxable macros, sizes[0] is the length of the first alternative
+ in bytes and sizes[1] is the length of the second alternative.
+ For non-relaxable macros, both elements give the length of the
+ macro in bytes. */
+ unsigned int sizes[2];
+
+ /* The first variant frag for this macro. */
+ fragS *first_frag;
+} mips_macro_warning;
+
+/* Prototypes for static functions. */
+
+#define internalError() \
+ as_fatal (_("internal Error, line %d, %s"), __LINE__, __FILE__)
+
+enum mips_regclass { MIPS_GR_REG, MIPS_FP_REG, MIPS16_REG };
+
+static void append_insn
+ (struct mips_cl_insn *ip, expressionS *p, bfd_reloc_code_real_type *r);
+static void mips_no_prev_insn (int);
+static void mips16_macro_build
+ (expressionS *, const char *, const char *, va_list);
+static void load_register (int, expressionS *, int);
+static void macro_start (void);
+static void macro_end (void);
+static void macro (struct mips_cl_insn * ip);
+static void mips16_macro (struct mips_cl_insn * ip);
+#ifdef LOSING_COMPILER
+static void macro2 (struct mips_cl_insn * ip);
+#endif
+static void mips_ip (char *str, struct mips_cl_insn * ip);
+static void mips16_ip (char *str, struct mips_cl_insn * ip);
+static void mips16_immed
+ (char *, unsigned int, int, offsetT, bfd_boolean, bfd_boolean, bfd_boolean,
+ unsigned long *, bfd_boolean *, unsigned short *);
+static size_t my_getSmallExpression
+ (expressionS *, bfd_reloc_code_real_type *, char *);
+static void my_getExpression (expressionS *, char *);
+static void s_align (int);
+static void s_change_sec (int);
+static void s_change_section (int);
+static void s_cons (int);
+static void s_float_cons (int);
+static void s_mips_globl (int);
+static void s_option (int);
+static void s_mipsset (int);
+static void s_abicalls (int);
+static void s_cpload (int);
+static void s_cpsetup (int);
+static void s_cplocal (int);
+static void s_cprestore (int);
+static void s_cpreturn (int);
+static void s_gpvalue (int);
+static void s_gpword (int);
+static void s_gpdword (int);
+static void s_cpadd (int);
+static void s_insn (int);
+static void md_obj_begin (void);
+static void md_obj_end (void);
+static void s_mips_ent (int);
+static void s_mips_end (int);
+static void s_mips_frame (int);
+static void s_mips_mask (int reg_type);
+static void s_mips_stab (int);
+static void s_mips_weakext (int);
+static void s_mips_file (int);
+static void s_mips_loc (int);
+static bfd_boolean pic_need_relax (symbolS *, asection *);
+static int relaxed_branch_length (fragS *, asection *, int);
+static int validate_mips_insn (const struct mips_opcode *);
+
+/* Table and functions used to map between CPU/ISA names, and
+ ISA levels, and CPU numbers. */
+
+struct mips_cpu_info
+{
+ const char *name; /* CPU or ISA name. */
+ int is_isa; /* Is this an ISA? (If 0, a CPU.) */
+ int isa; /* ISA level. */
+ int cpu; /* CPU number (default CPU if ISA). */
+};
+
+static const struct mips_cpu_info *mips_parse_cpu (const char *, const char *);
+static const struct mips_cpu_info *mips_cpu_info_from_isa (int);
+static const struct mips_cpu_info *mips_cpu_info_from_arch (int);
+
+/* Pseudo-op table.
+
+ The following pseudo-ops from the Kane and Heinrich MIPS book
+ should be defined here, but are currently unsupported: .alias,
+ .galive, .gjaldef, .gjrlive, .livereg, .noalias.
+
+ The following pseudo-ops from the Kane and Heinrich MIPS book are
+ specific to the type of debugging information being generated, and
+ should be defined by the object format: .aent, .begin, .bend,
+ .bgnb, .end, .endb, .ent, .fmask, .frame, .loc, .mask, .verstamp,
+ .vreg.
+
+ The following pseudo-ops from the Kane and Heinrich MIPS book are
+ not MIPS CPU specific, but are also not specific to the object file
+ format. This file is probably the best place to define them, but
+ they are not currently supported: .asm0, .endr, .lab, .repeat,
+ .struct. */
+
+static const pseudo_typeS mips_pseudo_table[] =
+{
+ /* MIPS specific pseudo-ops. */
+ {"option", s_option, 0},
+ {"set", s_mipsset, 0},
+ {"rdata", s_change_sec, 'r'},
+ {"sdata", s_change_sec, 's'},
+ {"livereg", s_ignore, 0},
+ {"abicalls", s_abicalls, 0},
+ {"cpload", s_cpload, 0},
+ {"cpsetup", s_cpsetup, 0},
+ {"cplocal", s_cplocal, 0},
+ {"cprestore", s_cprestore, 0},
+ {"cpreturn", s_cpreturn, 0},
+ {"gpvalue", s_gpvalue, 0},
+ {"gpword", s_gpword, 0},
+ {"gpdword", s_gpdword, 0},
+ {"cpadd", s_cpadd, 0},
+ {"insn", s_insn, 0},
+
+ /* Relatively generic pseudo-ops that happen to be used on MIPS
+ chips. */
+ {"asciiz", stringer, 1},
+ {"bss", s_change_sec, 'b'},
+ {"err", s_err, 0},
+ {"half", s_cons, 1},
+ {"dword", s_cons, 3},
+ {"weakext", s_mips_weakext, 0},
+
+ /* These pseudo-ops are defined in read.c, but must be overridden
+ here for one reason or another. */
+ {"align", s_align, 0},
+ {"byte", s_cons, 0},
+ {"data", s_change_sec, 'd'},
+ {"double", s_float_cons, 'd'},
+ {"float", s_float_cons, 'f'},
+ {"globl", s_mips_globl, 0},
+ {"global", s_mips_globl, 0},
+ {"hword", s_cons, 1},
+ {"int", s_cons, 2},
+ {"long", s_cons, 2},
+ {"octa", s_cons, 4},
+ {"quad", s_cons, 3},
+ {"section", s_change_section, 0},
+ {"short", s_cons, 1},
+ {"single", s_float_cons, 'f'},
+ {"stabn", s_mips_stab, 'n'},
+ {"text", s_change_sec, 't'},
+ {"word", s_cons, 2},
+
+ { "extern", ecoff_directive_extern, 0},
+
+ { NULL, NULL, 0 },
+};
+
+static const pseudo_typeS mips_nonecoff_pseudo_table[] =
+{
+ /* These pseudo-ops should be defined by the object file format.
+ However, a.out doesn't support them, so we have versions here. */
+ {"aent", s_mips_ent, 1},
+ {"bgnb", s_ignore, 0},
+ {"end", s_mips_end, 0},
+ {"endb", s_ignore, 0},
+ {"ent", s_mips_ent, 0},
+ {"file", s_mips_file, 0},
+ {"fmask", s_mips_mask, 'F'},
+ {"frame", s_mips_frame, 0},
+ {"loc", s_mips_loc, 0},
+ {"mask", s_mips_mask, 'R'},
+ {"verstamp", s_ignore, 0},
+ { NULL, NULL, 0 },
+};
+
+extern void pop_insert (const pseudo_typeS *);
+
+void
+mips_pop_insert (void)
+{
+ pop_insert (mips_pseudo_table);
+ if (! ECOFF_DEBUGGING)
+ pop_insert (mips_nonecoff_pseudo_table);
+}
+
+/* Symbols labelling the current insn. */
+
+struct insn_label_list
+{
+ struct insn_label_list *next;
+ symbolS *label;
+};
+
+static struct insn_label_list *insn_labels;
+static struct insn_label_list *free_insn_labels;
+
+static void mips_clear_insn_labels (void);
+
+static inline void
+mips_clear_insn_labels (void)
+{
+ register struct insn_label_list **pl;
+
+ for (pl = &free_insn_labels; *pl != NULL; pl = &(*pl)->next)
+ ;
+ *pl = insn_labels;
+ insn_labels = NULL;
+}
+
+static char *expr_end;
+
+/* Expressions which appear in instructions. These are set by
+ mips_ip. */
+
+static expressionS imm_expr;
+static expressionS imm2_expr;
+static expressionS offset_expr;
+
+/* Relocs associated with imm_expr and offset_expr. */
+
+static bfd_reloc_code_real_type imm_reloc[3]
+ = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+static bfd_reloc_code_real_type offset_reloc[3]
+ = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+
+/* These are set by mips16_ip if an explicit extension is used. */
+
+static bfd_boolean mips16_small, mips16_ext;
+
+#ifdef OBJ_ELF
+/* The pdr segment for per procedure frame/regmask info. Not used for
+ ECOFF debugging. */
+
+static segT pdr_seg;
+#endif
+
+/* The default target format to use. */
+
+const char *
+mips_target_format (void)
+{
+ switch (OUTPUT_FLAVOR)
+ {
+ case bfd_target_aout_flavour:
+ return target_big_endian ? "a.out-mips-big" : "a.out-mips-little";
+ case bfd_target_ecoff_flavour:
+ return target_big_endian ? "ecoff-bigmips" : ECOFF_LITTLE_FORMAT;
+ case bfd_target_coff_flavour:
+ return "pe-mips";
+ case bfd_target_elf_flavour:
+#ifdef TE_TMIPS
+ /* This is traditional mips. */
+ return (target_big_endian
+ ? (HAVE_64BIT_OBJECTS
+ ? "elf64-tradbigmips"
+ : (HAVE_NEWABI
+ ? "elf32-ntradbigmips" : "elf32-tradbigmips"))
+ : (HAVE_64BIT_OBJECTS
+ ? "elf64-tradlittlemips"
+ : (HAVE_NEWABI
+ ? "elf32-ntradlittlemips" : "elf32-tradlittlemips")));
+#else
+ return (target_big_endian
+ ? (HAVE_64BIT_OBJECTS
+ ? "elf64-bigmips"
+ : (HAVE_NEWABI
+ ? "elf32-nbigmips" : "elf32-bigmips"))
+ : (HAVE_64BIT_OBJECTS
+ ? "elf64-littlemips"
+ : (HAVE_NEWABI
+ ? "elf32-nlittlemips" : "elf32-littlemips")));
+#endif
+ default:
+ abort ();
+ return NULL;
+ }
+}
+
+/* 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)
+{
+ register const char *retval = NULL;
+ int i = 0;
+ int broken = 0;
+
+ if (! bfd_set_arch_mach (stdoutput, bfd_arch_mips, file_mips_arch))
+ as_warn (_("Could not set architecture and machine"));
+
+ op_hash = hash_new ();
+
+ for (i = 0; i < NUMOPCODES;)
+ {
+ const char *name = mips_opcodes[i].name;
+
+ retval = hash_insert (op_hash, name, (void *) &mips_opcodes[i]);
+ if (retval != NULL)
+ {
+ fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
+ mips_opcodes[i].name, retval);
+ /* Probably a memory allocation problem? Give up now. */
+ as_fatal (_("Broken assembler. No assembly attempted."));
+ }
+ do
+ {
+ if (mips_opcodes[i].pinfo != INSN_MACRO)
+ {
+ if (!validate_mips_insn (&mips_opcodes[i]))
+ broken = 1;
+ }
+ ++i;
+ }
+ while ((i < NUMOPCODES) && !strcmp (mips_opcodes[i].name, name));
+ }
+
+ mips16_op_hash = hash_new ();
+
+ i = 0;
+ while (i < bfd_mips16_num_opcodes)
+ {
+ const char *name = mips16_opcodes[i].name;
+
+ retval = hash_insert (mips16_op_hash, name, (void *) &mips16_opcodes[i]);
+ if (retval != NULL)
+ as_fatal (_("internal: can't hash `%s': %s"),
+ mips16_opcodes[i].name, retval);
+ do
+ {
+ if (mips16_opcodes[i].pinfo != INSN_MACRO
+ && ((mips16_opcodes[i].match & mips16_opcodes[i].mask)
+ != mips16_opcodes[i].match))
+ {
+ fprintf (stderr, _("internal error: bad mips16 opcode: %s %s\n"),
+ mips16_opcodes[i].name, mips16_opcodes[i].args);
+ broken = 1;
+ }
+ ++i;
+ }
+ while (i < bfd_mips16_num_opcodes
+ && strcmp (mips16_opcodes[i].name, name) == 0);
+ }
+
+ if (broken)
+ as_fatal (_("Broken assembler. No assembly attempted."));
+
+ /* We add all the general register names to the symbol table. This
+ helps us detect invalid uses of them. */
+ for (i = 0; i < 32; i++)
+ {
+ char buf[5];
+
+ sprintf (buf, "$%d", i);
+ symbol_table_insert (symbol_new (buf, reg_section, i,
+ &zero_address_frag));
+ }
+ symbol_table_insert (symbol_new ("$ra", reg_section, RA,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("$fp", reg_section, FP,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("$sp", reg_section, SP,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("$gp", reg_section, GP,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("$at", reg_section, AT,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("$kt0", reg_section, KT0,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("$kt1", reg_section, KT1,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("$zero", reg_section, ZERO,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("$pc", reg_section, -1,
+ &zero_address_frag));
+
+ /* If we don't add these register names to the symbol table, they
+ may end up being added as regular symbols by operand(), and then
+ make it to the object file as undefined in case they're not
+ regarded as local symbols. They're local in o32, since `$' is a
+ local symbol prefix, but not in n32 or n64. */
+ for (i = 0; i < 8; i++)
+ {
+ char buf[6];
+
+ sprintf (buf, "$fcc%i", i);
+ symbol_table_insert (symbol_new (buf, reg_section, -1,
+ &zero_address_frag));
+ }
+
+ mips_no_prev_insn (FALSE);
+
+ mips_gprmask = 0;
+ mips_cprmask[0] = 0;
+ mips_cprmask[1] = 0;
+ mips_cprmask[2] = 0;
+ mips_cprmask[3] = 0;
+
+ /* set the default alignment for the text section (2**2) */
+ record_alignment (text_section, 2);
+
+ if (USE_GLOBAL_POINTER_OPT)
+ bfd_set_gp_size (stdoutput, g_switch_value);
+
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ {
+ /* On a native system, sections must be aligned to 16 byte
+ boundaries. When configured for an embedded ELF target, we
+ don't bother. */
+ if (strcmp (TARGET_OS, "elf") != 0)
+ {
+ (void) bfd_set_section_alignment (stdoutput, text_section, 4);
+ (void) bfd_set_section_alignment (stdoutput, data_section, 4);
+ (void) bfd_set_section_alignment (stdoutput, bss_section, 4);
+ }
+
+ /* Create a .reginfo section for register masks and a .mdebug
+ section for debugging information. */
+ {
+ segT seg;
+ subsegT subseg;
+ flagword flags;
+ segT sec;
+
+ seg = now_seg;
+ subseg = now_subseg;
+
+ /* The ABI says this section should be loaded so that the
+ running program can access it. However, we don't load it
+ if we are configured for an embedded target */
+ flags = SEC_READONLY | SEC_DATA;
+ if (strcmp (TARGET_OS, "elf") != 0)
+ flags |= SEC_ALLOC | SEC_LOAD;
+
+ if (mips_abi != N64_ABI)
+ {
+ sec = subseg_new (".reginfo", (subsegT) 0);
+
+ bfd_set_section_flags (stdoutput, sec, flags);
+ bfd_set_section_alignment (stdoutput, sec, HAVE_NEWABI ? 3 : 2);
+
+#ifdef OBJ_ELF
+ mips_regmask_frag = frag_more (sizeof (Elf32_External_RegInfo));
+#endif
+ }
+ else
+ {
+ /* The 64-bit ABI uses a .MIPS.options section rather than
+ .reginfo section. */
+ sec = subseg_new (".MIPS.options", (subsegT) 0);
+ bfd_set_section_flags (stdoutput, sec, flags);
+ bfd_set_section_alignment (stdoutput, sec, 3);
+
+#ifdef OBJ_ELF
+ /* Set up the option header. */
+ {
+ Elf_Internal_Options opthdr;
+ char *f;
+
+ opthdr.kind = ODK_REGINFO;
+ opthdr.size = (sizeof (Elf_External_Options)
+ + sizeof (Elf64_External_RegInfo));
+ opthdr.section = 0;
+ opthdr.info = 0;
+ f = frag_more (sizeof (Elf_External_Options));
+ bfd_mips_elf_swap_options_out (stdoutput, &opthdr,
+ (Elf_External_Options *) f);
+
+ mips_regmask_frag = frag_more (sizeof (Elf64_External_RegInfo));
+ }
+#endif
+ }
+
+ if (ECOFF_DEBUGGING)
+ {
+ sec = subseg_new (".mdebug", (subsegT) 0);
+ (void) bfd_set_section_flags (stdoutput, sec,
+ SEC_HAS_CONTENTS | SEC_READONLY);
+ (void) bfd_set_section_alignment (stdoutput, sec, 2);
+ }
+#ifdef OBJ_ELF
+ else if (OUTPUT_FLAVOR == bfd_target_elf_flavour && mips_flag_pdr)
+ {
+ pdr_seg = subseg_new (".pdr", (subsegT) 0);
+ (void) bfd_set_section_flags (stdoutput, pdr_seg,
+ SEC_READONLY | SEC_RELOC
+ | SEC_DEBUGGING);
+ (void) bfd_set_section_alignment (stdoutput, pdr_seg, 2);
+ }
+#endif
+
+ subseg_set (seg, subseg);
+ }
+ }
+
+ if (! ECOFF_DEBUGGING)
+ md_obj_begin ();
+}
+
+void
+md_mips_end (void)
+{
+ if (! ECOFF_DEBUGGING)
+ md_obj_end ();
+}
+
+void
+md_assemble (char *str)
+{
+ struct mips_cl_insn insn;
+ bfd_reloc_code_real_type unused_reloc[3]
+ = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+
+ imm_expr.X_op = O_absent;
+ imm2_expr.X_op = O_absent;
+ offset_expr.X_op = O_absent;
+ imm_reloc[0] = BFD_RELOC_UNUSED;
+ imm_reloc[1] = BFD_RELOC_UNUSED;
+ imm_reloc[2] = BFD_RELOC_UNUSED;
+ offset_reloc[0] = BFD_RELOC_UNUSED;
+ offset_reloc[1] = BFD_RELOC_UNUSED;
+ offset_reloc[2] = BFD_RELOC_UNUSED;
+
+ if (mips_opts.mips16)
+ mips16_ip (str, &insn);
+ else
+ {
+ mips_ip (str, &insn);
+ DBG ((_("returned from mips_ip(%s) insn_opcode = 0x%x\n"),
+ str, insn.insn_opcode));
+ }
+
+ if (insn_error)
+ {
+ as_bad ("%s `%s'", insn_error, str);
+ return;
+ }
+
+ if (insn.insn_mo->pinfo == INSN_MACRO)
+ {
+ macro_start ();
+ if (mips_opts.mips16)
+ mips16_macro (&insn);
+ else
+ macro (&insn);
+ macro_end ();
+ }
+ else
+ {
+ if (imm_expr.X_op != O_absent)
+ append_insn (&insn, &imm_expr, imm_reloc);
+ else if (offset_expr.X_op != O_absent)
+ append_insn (&insn, &offset_expr, offset_reloc);
+ else
+ append_insn (&insn, NULL, unused_reloc);
+ }
+}
+
+/* Return true if the given relocation might need a matching %lo().
+ Note that R_MIPS_GOT16 relocations only need a matching %lo() when
+ applied to local symbols. */
+
+static inline bfd_boolean
+reloc_needs_lo_p (bfd_reloc_code_real_type reloc)
+{
+ return (reloc == BFD_RELOC_HI16_S
+ || reloc == BFD_RELOC_MIPS_GOT16);
+}
+
+/* Return true if the given fixup is followed by a matching R_MIPS_LO16
+ relocation. */
+
+static inline bfd_boolean
+fixup_has_matching_lo_p (fixS *fixp)
+{
+ return (fixp->fx_next != NULL
+ && fixp->fx_next->fx_r_type == BFD_RELOC_LO16
+ && fixp->fx_addsy == fixp->fx_next->fx_addsy
+ && fixp->fx_offset == fixp->fx_next->fx_offset);
+}
+
+/* See whether instruction IP reads register REG. CLASS is the type
+ of register. */
+
+static int
+insn_uses_reg (struct mips_cl_insn *ip, unsigned int reg,
+ enum mips_regclass class)
+{
+ if (class == MIPS16_REG)
+ {
+ assert (mips_opts.mips16);
+ reg = mips16_to_32_reg_map[reg];
+ class = MIPS_GR_REG;
+ }
+
+ /* Don't report on general register ZERO, since it never changes. */
+ if (class == MIPS_GR_REG && reg == ZERO)
+ return 0;
+
+ if (class == MIPS_FP_REG)
+ {
+ assert (! mips_opts.mips16);
+ /* If we are called with either $f0 or $f1, we must check $f0.
+ This is not optimal, because it will introduce an unnecessary
+ NOP between "lwc1 $f0" and "swc1 $f1". To fix this we would
+ need to distinguish reading both $f0 and $f1 or just one of
+ them. Note that we don't have to check the other way,
+ because there is no instruction that sets both $f0 and $f1
+ and requires a delay. */
+ if ((ip->insn_mo->pinfo & INSN_READ_FPR_S)
+ && ((((ip->insn_opcode >> OP_SH_FS) & OP_MASK_FS) &~(unsigned)1)
+ == (reg &~ (unsigned) 1)))
+ return 1;
+ if ((ip->insn_mo->pinfo & INSN_READ_FPR_T)
+ && ((((ip->insn_opcode >> OP_SH_FT) & OP_MASK_FT) &~(unsigned)1)
+ == (reg &~ (unsigned) 1)))
+ return 1;
+ }
+ else if (! mips_opts.mips16)
+ {
+ if ((ip->insn_mo->pinfo & INSN_READ_GPR_S)
+ && ((ip->insn_opcode >> OP_SH_RS) & OP_MASK_RS) == reg)
+ return 1;
+ if ((ip->insn_mo->pinfo & INSN_READ_GPR_T)
+ && ((ip->insn_opcode >> OP_SH_RT) & OP_MASK_RT) == reg)
+ return 1;
+ }
+ else
+ {
+ if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_X)
+ && (mips16_to_32_reg_map[((ip->insn_opcode >> MIPS16OP_SH_RX)
+ & MIPS16OP_MASK_RX)]
+ == reg))
+ return 1;
+ if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Y)
+ && (mips16_to_32_reg_map[((ip->insn_opcode >> MIPS16OP_SH_RY)
+ & MIPS16OP_MASK_RY)]
+ == reg))
+ return 1;
+ if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Z)
+ && (mips16_to_32_reg_map[((ip->insn_opcode >> MIPS16OP_SH_MOVE32Z)
+ & MIPS16OP_MASK_MOVE32Z)]
+ == reg))
+ return 1;
+ if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_T) && reg == TREG)
+ return 1;
+ if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_SP) && reg == SP)
+ return 1;
+ if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_31) && reg == RA)
+ return 1;
+ if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_GPR_X)
+ && ((ip->insn_opcode >> MIPS16OP_SH_REGR32)
+ & MIPS16OP_MASK_REGR32) == reg)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* This function returns true if modifying a register requires a
+ delay. */
+
+static int
+reg_needs_delay (unsigned int reg)
+{
+ unsigned long prev_pinfo;
+
+ prev_pinfo = prev_insn.insn_mo->pinfo;
+ if (! mips_opts.noreorder
+ && (((prev_pinfo & INSN_LOAD_MEMORY_DELAY)
+ && ! gpr_interlocks)
+ || ((prev_pinfo & INSN_LOAD_COPROC_DELAY)
+ && ! cop_interlocks)))
+ {
+ /* A load from a coprocessor or from memory. All load delays
+ delay the use of general register rt for one instruction. */
+ /* Itbl support may require additional care here. */
+ know (prev_pinfo & INSN_WRITE_GPR_T);
+ if (reg == ((prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Mark instruction labels in mips16 mode. This permits the linker to
+ handle them specially, such as generating jalx instructions when
+ needed. We also make them odd for the duration of the assembly, in
+ order to generate the right sort of code. We will make them even
+ in the adjust_symtab routine, while leaving them marked. This is
+ convenient for the debugger and the disassembler. The linker knows
+ to make them odd again. */
+
+static void
+mips16_mark_labels (void)
+{
+ if (mips_opts.mips16)
+ {
+ struct insn_label_list *l;
+ valueT val;
+
+ for (l = insn_labels; l != NULL; l = l->next)
+ {
+#ifdef OBJ_ELF
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ S_SET_OTHER (l->label, STO_MIPS16);
+#endif
+ val = S_GET_VALUE (l->label);
+ if ((val & 1) == 0)
+ S_SET_VALUE (l->label, val + 1);
+ }
+ }
+}
+
+/* End the current frag. Make it a variant frag and record the
+ relaxation info. */
+
+static void
+relax_close_frag (void)
+{
+ mips_macro_warning.first_frag = frag_now;
+ frag_var (rs_machine_dependent, 0, 0,
+ RELAX_ENCODE (mips_relax.sizes[0], mips_relax.sizes[1]),
+ mips_relax.symbol, 0, (char *) mips_relax.first_fixup);
+
+ memset (&mips_relax.sizes, 0, sizeof (mips_relax.sizes));
+ mips_relax.first_fixup = 0;
+}
+
+/* Start a new relaxation sequence whose expansion depends on SYMBOL.
+ See the comment above RELAX_ENCODE for more details. */
+
+static void
+relax_start (symbolS *symbol)
+{
+ assert (mips_relax.sequence == 0);
+ mips_relax.sequence = 1;
+ mips_relax.symbol = symbol;
+}
+
+/* Start generating the second version of a relaxable sequence.
+ See the comment above RELAX_ENCODE for more details. */
+
+static void
+relax_switch (void)
+{
+ assert (mips_relax.sequence == 1);
+ mips_relax.sequence = 2;
+}
+
+/* End the current relaxable sequence. */
+
+static void
+relax_end (void)
+{
+ assert (mips_relax.sequence == 2);
+ relax_close_frag ();
+ mips_relax.sequence = 0;
+}
+
+/* Output an instruction. IP is the instruction information.
+ ADDRESS_EXPR is an operand of the instruction to be used with
+ RELOC_TYPE. */
+
+static void
+append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
+ bfd_reloc_code_real_type *reloc_type)
+{
+ register unsigned long prev_pinfo, pinfo;
+ char *f;
+ fixS *fixp[3];
+ int nops = 0;
+ relax_stateT prev_insn_frag_type = 0;
+ bfd_boolean relaxed_branch = FALSE;
+ bfd_boolean force_new_frag = FALSE;
+
+ /* Mark instruction labels in mips16 mode. */
+ mips16_mark_labels ();
+
+ prev_pinfo = prev_insn.insn_mo->pinfo;
+ pinfo = ip->insn_mo->pinfo;
+
+ if (mips_relax.sequence != 2
+ && (!mips_opts.noreorder || prev_nop_frag != NULL))
+ {
+ int prev_prev_nop;
+
+ /* If the previous insn required any delay slots, see if we need
+ to insert a NOP or two. There are eight kinds of possible
+ hazards, of which an instruction can have at most one type.
+ (1) a load from memory delay
+ (2) a load from a coprocessor delay
+ (3) an unconditional branch delay
+ (4) a conditional branch delay
+ (5) a move to coprocessor register delay
+ (6) a load coprocessor register from memory delay
+ (7) a coprocessor condition code delay
+ (8) a HI/LO special register delay
+
+ There are a lot of optimizations we could do that we don't.
+ In particular, we do not, in general, reorder instructions.
+ If you use gcc with optimization, it will reorder
+ instructions and generally do much more optimization then we
+ do here; repeating all that work in the assembler would only
+ benefit hand written assembly code, and does not seem worth
+ it. */
+
+ /* This is how a NOP is emitted. */
+#define emit_nop() \
+ (mips_opts.mips16 \
+ ? md_number_to_chars (frag_more (2), 0x6500, 2) \
+ : md_number_to_chars (frag_more (4), 0, 4))
+
+ /* The previous insn might require a delay slot, depending upon
+ the contents of the current insn. */
+ if (! mips_opts.mips16
+ && (((prev_pinfo & INSN_LOAD_MEMORY_DELAY)
+ && ! gpr_interlocks)
+ || ((prev_pinfo & INSN_LOAD_COPROC_DELAY)
+ && ! cop_interlocks)))
+ {
+ /* A load from a coprocessor or from memory. All load
+ delays delay the use of general register rt for one
+ instruction. */
+ /* Itbl support may require additional care here. */
+ know (prev_pinfo & INSN_WRITE_GPR_T);
+ if (mips_optimize == 0
+ || insn_uses_reg (ip,
+ ((prev_insn.insn_opcode >> OP_SH_RT)
+ & OP_MASK_RT),
+ MIPS_GR_REG))
+ ++nops;
+ }
+ else if (! mips_opts.mips16
+ && (((prev_pinfo & INSN_COPROC_MOVE_DELAY)
+ && ! cop_interlocks)
+ || ((prev_pinfo & INSN_COPROC_MEMORY_DELAY)
+ && ! cop_mem_interlocks)))
+ {
+ /* A generic coprocessor delay. The previous instruction
+ modified a coprocessor general or control register. If
+ it modified a control register, we need to avoid any
+ coprocessor instruction (this is probably not always
+ required, but it sometimes is). If it modified a general
+ register, we avoid using that register.
+
+ This case is not handled very well. There is no special
+ knowledge of CP0 handling, and the coprocessors other
+ than the floating point unit are not distinguished at
+ all. */
+ /* Itbl support may require additional care here. FIXME!
+ Need to modify this to include knowledge about
+ user specified delays! */
+ if (prev_pinfo & INSN_WRITE_FPR_T)
+ {
+ if (mips_optimize == 0
+ || insn_uses_reg (ip,
+ ((prev_insn.insn_opcode >> OP_SH_FT)
+ & OP_MASK_FT),
+ MIPS_FP_REG))
+ ++nops;
+ }
+ else if (prev_pinfo & INSN_WRITE_FPR_S)
+ {
+ if (mips_optimize == 0
+ || insn_uses_reg (ip,
+ ((prev_insn.insn_opcode >> OP_SH_FS)
+ & OP_MASK_FS),
+ MIPS_FP_REG))
+ ++nops;
+ }
+ else
+ {
+ /* We don't know exactly what the previous instruction
+ does. If the current instruction uses a coprocessor
+ register, we must insert a NOP. If previous
+ instruction may set the condition codes, and the
+ current instruction uses them, we must insert two
+ NOPS. */
+ /* Itbl support may require additional care here. */
+ if (mips_optimize == 0
+ || ((prev_pinfo & INSN_WRITE_COND_CODE)
+ && (pinfo & INSN_READ_COND_CODE)))
+ nops += 2;
+ else if (pinfo & INSN_COP)
+ ++nops;
+ }
+ }
+ else if (! mips_opts.mips16
+ && (prev_pinfo & INSN_WRITE_COND_CODE)
+ && ! cop_interlocks)
+ {
+ /* The previous instruction sets the coprocessor condition
+ codes, but does not require a general coprocessor delay
+ (this means it is a floating point comparison
+ instruction). If this instruction uses the condition
+ codes, we need to insert a single NOP. */
+ /* Itbl support may require additional care here. */
+ if (mips_optimize == 0
+ || (pinfo & INSN_READ_COND_CODE))
+ ++nops;
+ }
+
+ /* If we're fixing up mfhi/mflo for the r7000 and the
+ previous insn was an mfhi/mflo and the current insn
+ reads the register that the mfhi/mflo wrote to, then
+ insert two nops. */
+
+ else if (mips_7000_hilo_fix
+ && MF_HILO_INSN (prev_pinfo)
+ && insn_uses_reg (ip, ((prev_insn.insn_opcode >> OP_SH_RD)
+ & OP_MASK_RD),
+ MIPS_GR_REG))
+ {
+ nops += 2;
+ }
+
+ /* If we're fixing up mfhi/mflo for the r7000 and the
+ 2nd previous insn was an mfhi/mflo and the current insn
+ reads the register that the mfhi/mflo wrote to, then
+ insert one nop. */
+
+ else if (mips_7000_hilo_fix
+ && MF_HILO_INSN (prev_prev_insn.insn_opcode)
+ && insn_uses_reg (ip, ((prev_prev_insn.insn_opcode >> OP_SH_RD)
+ & OP_MASK_RD),
+ MIPS_GR_REG))
+
+ {
+ ++nops;
+ }
+
+ else if (prev_pinfo & INSN_READ_LO)
+ {
+ /* The previous instruction reads the LO register; if the
+ current instruction writes to the LO register, we must
+ insert two NOPS. Some newer processors have interlocks.
+ Also the tx39's multiply instructions can be executed
+ immediately after a read from HI/LO (without the delay),
+ though the tx39's divide insns still do require the
+ delay. */
+ if (! (hilo_interlocks
+ || (mips_opts.arch == CPU_R3900 && (pinfo & INSN_MULT)))
+ && (mips_optimize == 0
+ || (pinfo & INSN_WRITE_LO)))
+ nops += 2;
+ /* Most mips16 branch insns don't have a delay slot.
+ If a read from LO is immediately followed by a branch
+ to a write to LO we have a read followed by a write
+ less than 2 insns away. We assume the target of
+ a branch might be a write to LO, and insert a nop
+ between a read and an immediately following branch. */
+ else if (mips_opts.mips16
+ && (mips_optimize == 0
+ || (pinfo & MIPS16_INSN_BRANCH)))
+ ++nops;
+ }
+ else if (prev_insn.insn_mo->pinfo & INSN_READ_HI)
+ {
+ /* The previous instruction reads the HI register; if the
+ current instruction writes to the HI register, we must
+ insert a NOP. Some newer processors have interlocks.
+ Also the note tx39's multiply above. */
+ if (! (hilo_interlocks
+ || (mips_opts.arch == CPU_R3900 && (pinfo & INSN_MULT)))
+ && (mips_optimize == 0
+ || (pinfo & INSN_WRITE_HI)))
+ nops += 2;
+ /* Most mips16 branch insns don't have a delay slot.
+ If a read from HI is immediately followed by a branch
+ to a write to HI we have a read followed by a write
+ less than 2 insns away. We assume the target of
+ a branch might be a write to HI, and insert a nop
+ between a read and an immediately following branch. */
+ else if (mips_opts.mips16
+ && (mips_optimize == 0
+ || (pinfo & MIPS16_INSN_BRANCH)))
+ ++nops;
+ }
+
+ /* If the previous instruction was in a noreorder section, then
+ we don't want to insert the nop after all. */
+ /* Itbl support may require additional care here. */
+ if (prev_insn_unreordered)
+ nops = 0;
+
+ /* There are two cases which require two intervening
+ instructions: 1) setting the condition codes using a move to
+ coprocessor instruction which requires a general coprocessor
+ delay and then reading the condition codes 2) reading the HI
+ or LO register and then writing to it (except on processors
+ which have interlocks). If we are not already emitting a NOP
+ instruction, we must check for these cases compared to the
+ instruction previous to the previous instruction. */
+ if ((! mips_opts.mips16
+ && (prev_prev_insn.insn_mo->pinfo & INSN_COPROC_MOVE_DELAY)
+ && (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
+ && (pinfo & INSN_READ_COND_CODE)
+ && ! cop_interlocks)
+ || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)
+ && (pinfo & INSN_WRITE_LO)
+ && ! (hilo_interlocks
+ || (mips_opts.arch == CPU_R3900 && (pinfo & INSN_MULT))))
+ || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
+ && (pinfo & INSN_WRITE_HI)
+ && ! (hilo_interlocks
+ || (mips_opts.arch == CPU_R3900 && (pinfo & INSN_MULT)))))
+ prev_prev_nop = 1;
+ else
+ prev_prev_nop = 0;
+
+ if (prev_prev_insn_unreordered)
+ prev_prev_nop = 0;
+
+ if (prev_prev_nop && nops == 0)
+ ++nops;
+
+ if (mips_fix_vr4120 && prev_insn.insn_mo->name)
+ {
+ /* We're out of bits in pinfo, so we must resort to string
+ ops here. Shortcuts are selected based on opcodes being
+ limited to the VR4120 instruction set. */
+ int min_nops = 0;
+ const char *pn = prev_insn.insn_mo->name;
+ const char *tn = ip->insn_mo->name;
+ if (strncmp(pn, "macc", 4) == 0
+ || strncmp(pn, "dmacc", 5) == 0)
+ {
+ /* Errata 21 - [D]DIV[U] after [D]MACC */
+ if (strstr (tn, "div"))
+ {
+ min_nops = 1;
+ }
+
+ /* Errata 23 - Continuous DMULT[U]/DMACC instructions */
+ if (pn[0] == 'd' /* dmacc */
+ && (strncmp(tn, "dmult", 5) == 0
+ || strncmp(tn, "dmacc", 5) == 0))
+ {
+ min_nops = 1;
+ }
+
+ /* Errata 24 - MT{LO,HI} after [D]MACC */
+ if (strcmp (tn, "mtlo") == 0
+ || strcmp (tn, "mthi") == 0)
+ {
+ min_nops = 1;
+ }
+
+ }
+ else if (strncmp(pn, "dmult", 5) == 0
+ && (strncmp(tn, "dmult", 5) == 0
+ || strncmp(tn, "dmacc", 5) == 0))
+ {
+ /* Here is the rest of errata 23. */
+ min_nops = 1;
+ }
+ if (nops < min_nops)
+ nops = min_nops;
+ }
+
+ /* If we are being given a nop instruction, don't bother with
+ one of the nops we would otherwise output. This will only
+ happen when a nop instruction is used with mips_optimize set
+ to 0. */
+ if (nops > 0
+ && ! mips_opts.noreorder
+ && ip->insn_opcode == (unsigned) (mips_opts.mips16 ? 0x6500 : 0))
+ --nops;
+
+ /* Now emit the right number of NOP instructions. */
+ if (nops > 0 && ! mips_opts.noreorder)
+ {
+ fragS *old_frag;
+ unsigned long old_frag_offset;
+ int i;
+ struct insn_label_list *l;
+
+ old_frag = frag_now;
+ old_frag_offset = frag_now_fix ();
+
+ for (i = 0; i < nops; i++)
+ emit_nop ();
+
+ if (listing)
+ {
+ listing_prev_line ();
+ /* We may be at the start of a variant frag. In case we
+ are, make sure there is enough space for the frag
+ after the frags created by listing_prev_line. The
+ argument to frag_grow here must be at least as large
+ as the argument to all other calls to frag_grow in
+ this file. We don't have to worry about being in the
+ middle of a variant frag, because the variants insert
+ all needed nop instructions themselves. */
+ frag_grow (40);
+ }
+
+ for (l = insn_labels; l != NULL; l = l->next)
+ {
+ valueT val;
+
+ assert (S_GET_SEGMENT (l->label) == now_seg);
+ symbol_set_frag (l->label, frag_now);
+ val = (valueT) frag_now_fix ();
+ /* mips16 text labels are stored as odd. */
+ if (mips_opts.mips16)
+ ++val;
+ S_SET_VALUE (l->label, val);
+ }
+
+#ifndef NO_ECOFF_DEBUGGING
+ if (ECOFF_DEBUGGING)
+ ecoff_fix_loc (old_frag, old_frag_offset);
+#endif
+ }
+ else if (prev_nop_frag != NULL)
+ {
+ /* We have a frag holding nops we may be able to remove. If
+ we don't need any nops, we can decrease the size of
+ prev_nop_frag by the size of one instruction. If we do
+ need some nops, we count them in prev_nops_required. */
+ if (prev_nop_frag_since == 0)
+ {
+ if (nops == 0)
+ {
+ prev_nop_frag->fr_fix -= mips_opts.mips16 ? 2 : 4;
+ --prev_nop_frag_holds;
+ }
+ else
+ prev_nop_frag_required += nops;
+ }
+ else
+ {
+ if (prev_prev_nop == 0)
+ {
+ prev_nop_frag->fr_fix -= mips_opts.mips16 ? 2 : 4;
+ --prev_nop_frag_holds;
+ }
+ else
+ ++prev_nop_frag_required;
+ }
+
+ if (prev_nop_frag_holds <= prev_nop_frag_required)
+ prev_nop_frag = NULL;
+
+ ++prev_nop_frag_since;
+
+ /* Sanity check: by the time we reach the second instruction
+ after prev_nop_frag, we should have used up all the nops
+ one way or another. */
+ assert (prev_nop_frag_since <= 1 || prev_nop_frag == NULL);
+ }
+ }
+
+ /* Record the frag type before frag_var. */
+ if (prev_insn_frag)
+ prev_insn_frag_type = prev_insn_frag->fr_type;
+
+ if (address_expr
+ && *reloc_type == BFD_RELOC_16_PCREL_S2
+ && (pinfo & INSN_UNCOND_BRANCH_DELAY || pinfo & INSN_COND_BRANCH_DELAY
+ || pinfo & INSN_COND_BRANCH_LIKELY)
+ && mips_relax_branch
+ /* Don't try branch relaxation within .set nomacro, or within
+ .set noat if we use $at for PIC computations. If it turns
+ out that the branch was out-of-range, we'll get an error. */
+ && !mips_opts.warn_about_macros
+ && !(mips_opts.noat && mips_pic != NO_PIC)
+ && !mips_opts.mips16)
+ {
+ relaxed_branch = TRUE;
+ f = frag_var (rs_machine_dependent,
+ relaxed_branch_length
+ (NULL, NULL,
+ (pinfo & INSN_UNCOND_BRANCH_DELAY) ? -1
+ : (pinfo & INSN_COND_BRANCH_LIKELY) ? 1 : 0), 4,
+ RELAX_BRANCH_ENCODE
+ (pinfo & INSN_UNCOND_BRANCH_DELAY,
+ pinfo & INSN_COND_BRANCH_LIKELY,
+ pinfo & INSN_WRITE_GPR_31,
+ 0),
+ address_expr->X_add_symbol,
+ address_expr->X_add_number,
+ 0);
+ *reloc_type = BFD_RELOC_UNUSED;
+ }
+ else if (*reloc_type > BFD_RELOC_UNUSED)
+ {
+ /* We need to set up a variant frag. */
+ assert (mips_opts.mips16 && address_expr != NULL);
+ f = frag_var (rs_machine_dependent, 4, 0,
+ RELAX_MIPS16_ENCODE (*reloc_type - BFD_RELOC_UNUSED,
+ mips16_small, mips16_ext,
+ (prev_pinfo
+ & INSN_UNCOND_BRANCH_DELAY),
+ (*prev_insn_reloc_type
+ == BFD_RELOC_MIPS16_JMP)),
+ make_expr_symbol (address_expr), 0, NULL);
+ }
+ else if (mips_opts.mips16
+ && ! ip->use_extend
+ && *reloc_type != BFD_RELOC_MIPS16_JMP)
+ {
+ /* Make sure there is enough room to swap this instruction with
+ a following jump instruction. */
+ frag_grow (6);
+ f = frag_more (2);
+ }
+ else
+ {
+ if (mips_opts.mips16
+ && mips_opts.noreorder
+ && (prev_pinfo & INSN_UNCOND_BRANCH_DELAY) != 0)
+ as_warn (_("extended instruction in delay slot"));
+
+ if (mips_relax.sequence)
+ {
+ /* If we've reached the end of this frag, turn it into a variant
+ frag and record the information for the instructions we've
+ written so far. */
+ if (frag_room () < 4)
+ relax_close_frag ();
+ mips_relax.sizes[mips_relax.sequence - 1] += 4;
+ }
+
+ if (mips_relax.sequence != 2)
+ mips_macro_warning.sizes[0] += 4;
+ if (mips_relax.sequence != 1)
+ mips_macro_warning.sizes[1] += 4;
+
+ f = frag_more (4);
+ }
+
+ fixp[0] = fixp[1] = fixp[2] = NULL;
+ if (address_expr != NULL && *reloc_type < BFD_RELOC_UNUSED)
+ {
+ if (address_expr->X_op == O_constant)
+ {
+ valueT tmp;
+
+ switch (*reloc_type)
+ {
+ case BFD_RELOC_32:
+ ip->insn_opcode |= address_expr->X_add_number;
+ break;
+
+ case BFD_RELOC_MIPS_HIGHEST:
+ tmp = (address_expr->X_add_number
+ + ((valueT) 0x8000 << 32) + 0x80008000) >> 16;
+ tmp >>= 16;
+ ip->insn_opcode |= (tmp >> 16) & 0xffff;
+ break;
+
+ case BFD_RELOC_MIPS_HIGHER:
+ tmp = (address_expr->X_add_number + 0x80008000) >> 16;
+ ip->insn_opcode |= (tmp >> 16) & 0xffff;
+ break;
+
+ case BFD_RELOC_HI16_S:
+ ip->insn_opcode |= ((address_expr->X_add_number + 0x8000)
+ >> 16) & 0xffff;
+ break;
+
+ case BFD_RELOC_HI16:
+ ip->insn_opcode |= (address_expr->X_add_number >> 16) & 0xffff;
+ break;
+
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_MIPS_GOT_DISP:
+ ip->insn_opcode |= address_expr->X_add_number & 0xffff;
+ break;
+
+ case BFD_RELOC_MIPS_JMP:
+ if ((address_expr->X_add_number & 3) != 0)
+ as_bad (_("jump to misaligned address (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ if (address_expr->X_add_number & ~0xfffffff)
+ as_bad (_("jump address range overflow (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ ip->insn_opcode |= (address_expr->X_add_number >> 2) & 0x3ffffff;
+ break;
+
+ case BFD_RELOC_MIPS16_JMP:
+ if ((address_expr->X_add_number & 3) != 0)
+ as_bad (_("jump to misaligned address (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ if (address_expr->X_add_number & ~0xfffffff)
+ as_bad (_("jump address range overflow (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ ip->insn_opcode |=
+ (((address_expr->X_add_number & 0x7c0000) << 3)
+ | ((address_expr->X_add_number & 0xf800000) >> 7)
+ | ((address_expr->X_add_number & 0x3fffc) >> 2));
+ break;
+
+ case BFD_RELOC_16_PCREL_S2:
+ goto need_reloc;
+
+ default:
+ internalError ();
+ }
+ }
+ else
+ need_reloc:
+ {
+ reloc_howto_type *howto;
+ int i;
+
+ /* In a compound relocation, it is the final (outermost)
+ operator that determines the relocated field. */
+ for (i = 1; i < 3; i++)
+ if (reloc_type[i] == BFD_RELOC_UNUSED)
+ break;
+
+ howto = bfd_reloc_type_lookup (stdoutput, reloc_type[i - 1]);
+ fixp[0] = fix_new_exp (frag_now, f - frag_now->fr_literal,
+ bfd_get_reloc_size(howto),
+ address_expr,
+ reloc_type[0] == BFD_RELOC_16_PCREL_S2,
+ reloc_type[0]);
+
+ /* These relocations can have an addend that won't fit in
+ 4 octets for 64bit assembly. */
+ if (HAVE_64BIT_GPRS
+ && ! howto->partial_inplace
+ && (reloc_type[0] == BFD_RELOC_16
+ || reloc_type[0] == BFD_RELOC_32
+ || reloc_type[0] == BFD_RELOC_MIPS_JMP
+ || reloc_type[0] == BFD_RELOC_HI16_S
+ || reloc_type[0] == BFD_RELOC_LO16
+ || reloc_type[0] == BFD_RELOC_GPREL16
+ || reloc_type[0] == BFD_RELOC_MIPS_LITERAL
+ || reloc_type[0] == BFD_RELOC_GPREL32
+ || reloc_type[0] == BFD_RELOC_64
+ || reloc_type[0] == BFD_RELOC_CTOR
+ || reloc_type[0] == BFD_RELOC_MIPS_SUB
+ || reloc_type[0] == BFD_RELOC_MIPS_HIGHEST
+ || reloc_type[0] == BFD_RELOC_MIPS_HIGHER
+ || reloc_type[0] == BFD_RELOC_MIPS_SCN_DISP
+ || reloc_type[0] == BFD_RELOC_MIPS_REL16
+ || reloc_type[0] == BFD_RELOC_MIPS_RELGOT))
+ fixp[0]->fx_no_overflow = 1;
+
+ if (mips_relax.sequence)
+ {
+ if (mips_relax.first_fixup == 0)
+ mips_relax.first_fixup = fixp[0];
+ }
+ else if (reloc_needs_lo_p (*reloc_type))
+ {
+ struct mips_hi_fixup *hi_fixup;
+
+ /* Reuse the last entry if it already has a matching %lo. */
+ hi_fixup = mips_hi_fixup_list;
+ if (hi_fixup == 0
+ || !fixup_has_matching_lo_p (hi_fixup->fixp))
+ {
+ hi_fixup = ((struct mips_hi_fixup *)
+ xmalloc (sizeof (struct mips_hi_fixup)));
+ hi_fixup->next = mips_hi_fixup_list;
+ mips_hi_fixup_list = hi_fixup;
+ }
+ hi_fixup->fixp = fixp[0];
+ hi_fixup->seg = now_seg;
+ }
+
+ /* Add fixups for the second and third relocations, if given.
+ Note that the ABI allows the second relocation to be
+ against RSS_UNDEF, RSS_GP, RSS_GP0 or RSS_LOC. At the
+ moment we only use RSS_UNDEF, but we could add support
+ for the others if it ever becomes necessary. */
+ for (i = 1; i < 3; i++)
+ if (reloc_type[i] != BFD_RELOC_UNUSED)
+ {
+ address_expr->X_op = O_absent;
+ address_expr->X_add_symbol = 0;
+ address_expr->X_add_number = 0;
+
+ fixp[i] = fix_new_exp (frag_now, fixp[0]->fx_where,
+ fixp[0]->fx_size, address_expr,
+ FALSE, reloc_type[i]);
+ }
+ }
+ }
+
+ if (! mips_opts.mips16)
+ {
+ md_number_to_chars (f, ip->insn_opcode, 4);
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (4);
+#endif
+ }
+ else if (*reloc_type == BFD_RELOC_MIPS16_JMP)
+ {
+ md_number_to_chars (f, ip->insn_opcode >> 16, 2);
+ md_number_to_chars (f + 2, ip->insn_opcode & 0xffff, 2);
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (4);
+#endif
+ }
+ else
+ {
+ if (ip->use_extend)
+ {
+ md_number_to_chars (f, 0xf000 | ip->extend, 2);
+ f += 2;
+ }
+ md_number_to_chars (f, ip->insn_opcode, 2);
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (ip->use_extend ? 4 : 2);
+#endif
+ }
+
+ /* Update the register mask information. */
+ if (! mips_opts.mips16)
+ {
+ if (pinfo & INSN_WRITE_GPR_D)
+ mips_gprmask |= 1 << ((ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD);
+ if ((pinfo & (INSN_WRITE_GPR_T | INSN_READ_GPR_T)) != 0)
+ mips_gprmask |= 1 << ((ip->insn_opcode >> OP_SH_RT) & OP_MASK_RT);
+ if (pinfo & INSN_READ_GPR_S)
+ mips_gprmask |= 1 << ((ip->insn_opcode >> OP_SH_RS) & OP_MASK_RS);
+ if (pinfo & INSN_WRITE_GPR_31)
+ mips_gprmask |= 1 << RA;
+ if (pinfo & INSN_WRITE_FPR_D)
+ mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FD) & OP_MASK_FD);
+ if ((pinfo & (INSN_WRITE_FPR_S | INSN_READ_FPR_S)) != 0)
+ mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FS) & OP_MASK_FS);
+ if ((pinfo & (INSN_WRITE_FPR_T | INSN_READ_FPR_T)) != 0)
+ mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FT) & OP_MASK_FT);
+ if ((pinfo & INSN_READ_FPR_R) != 0)
+ mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FR) & OP_MASK_FR);
+ if (pinfo & INSN_COP)
+ {
+ /* We don't keep enough information to sort these cases out.
+ The itbl support does keep this information however, although
+ we currently don't support itbl fprmats as part of the cop
+ instruction. May want to add this support in the future. */
+ }
+ /* Never set the bit for $0, which is always zero. */
+ mips_gprmask &= ~1 << 0;
+ }
+ else
+ {
+ if (pinfo & (MIPS16_INSN_WRITE_X | MIPS16_INSN_READ_X))
+ mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_RX)
+ & MIPS16OP_MASK_RX);
+ if (pinfo & (MIPS16_INSN_WRITE_Y | MIPS16_INSN_READ_Y))
+ mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_RY)
+ & MIPS16OP_MASK_RY);
+ if (pinfo & MIPS16_INSN_WRITE_Z)
+ mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_RZ)
+ & MIPS16OP_MASK_RZ);
+ if (pinfo & (MIPS16_INSN_WRITE_T | MIPS16_INSN_READ_T))
+ mips_gprmask |= 1 << TREG;
+ if (pinfo & (MIPS16_INSN_WRITE_SP | MIPS16_INSN_READ_SP))
+ mips_gprmask |= 1 << SP;
+ if (pinfo & (MIPS16_INSN_WRITE_31 | MIPS16_INSN_READ_31))
+ mips_gprmask |= 1 << RA;
+ if (pinfo & MIPS16_INSN_WRITE_GPR_Y)
+ mips_gprmask |= 1 << MIPS16OP_EXTRACT_REG32R (ip->insn_opcode);
+ if (pinfo & MIPS16_INSN_READ_Z)
+ mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_MOVE32Z)
+ & MIPS16OP_MASK_MOVE32Z);
+ if (pinfo & MIPS16_INSN_READ_GPR_X)
+ mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_REGR32)
+ & MIPS16OP_MASK_REGR32);
+ }
+
+ if (mips_relax.sequence != 2 && !mips_opts.noreorder)
+ {
+ /* Filling the branch delay slot is more complex. We try to
+ switch the branch with the previous instruction, which we can
+ do if the previous instruction does not set up a condition
+ that the branch tests and if the branch is not itself the
+ target of any branch. */
+ if ((pinfo & INSN_UNCOND_BRANCH_DELAY)
+ || (pinfo & INSN_COND_BRANCH_DELAY))
+ {
+ if (mips_optimize < 2
+ /* If we have seen .set volatile or .set nomove, don't
+ optimize. */
+ || mips_opts.nomove != 0
+ /* If we had to emit any NOP instructions, then we
+ already know we can not swap. */
+ || nops != 0
+ /* If we don't even know the previous insn, we can not
+ swap. */
+ || ! prev_insn_valid
+ /* If the previous insn is already in a branch delay
+ slot, then we can not swap. */
+ || prev_insn_is_delay_slot
+ /* If the previous previous insn was in a .set
+ noreorder, we can't swap. Actually, the MIPS
+ assembler will swap in this situation. However, gcc
+ configured -with-gnu-as will generate code like
+ .set noreorder
+ lw $4,XXX
+ .set reorder
+ INSN
+ bne $4,$0,foo
+ in which we can not swap the bne and INSN. If gcc is
+ not configured -with-gnu-as, it does not output the
+ .set pseudo-ops. We don't have to check
+ prev_insn_unreordered, because prev_insn_valid will
+ be 0 in that case. We don't want to use
+ prev_prev_insn_valid, because we do want to be able
+ to swap at the start of a function. */
+ || prev_prev_insn_unreordered
+ /* If the branch is itself the target of a branch, we
+ can not swap. We cheat on this; all we check for is
+ whether there is a label on this instruction. If
+ there are any branches to anything other than a
+ label, users must use .set noreorder. */
+ || insn_labels != NULL
+ /* If the previous instruction is in a variant frag
+ other than this branch's one, we cannot do the swap.
+ This does not apply to the mips16, which uses variant
+ frags for different purposes. */
+ || (! mips_opts.mips16
+ && prev_insn_frag_type == rs_machine_dependent)
+ /* If the branch reads the condition codes, we don't
+ even try to swap, because in the sequence
+ ctc1 $X,$31
+ INSN
+ INSN
+ bc1t LABEL
+ we can not swap, and I don't feel like handling that
+ case. */
+ || (! mips_opts.mips16
+ && (pinfo & INSN_READ_COND_CODE)
+ && ! cop_interlocks)
+ /* We can not swap with an instruction that requires a
+ delay slot, because the target of the branch might
+ interfere with that instruction. */
+ || (! mips_opts.mips16
+ && (prev_pinfo
+ /* Itbl support may require additional care here. */
+ & (INSN_LOAD_COPROC_DELAY
+ | INSN_COPROC_MOVE_DELAY
+ | INSN_WRITE_COND_CODE))
+ && ! cop_interlocks)
+ || (! (hilo_interlocks
+ || (mips_opts.arch == CPU_R3900 && (pinfo & INSN_MULT)))
+ && (prev_pinfo
+ & (INSN_READ_LO
+ | INSN_READ_HI)))
+ || (! mips_opts.mips16
+ && (prev_pinfo & INSN_LOAD_MEMORY_DELAY)
+ && ! gpr_interlocks)
+ || (! mips_opts.mips16
+ /* Itbl support may require additional care here. */
+ && (prev_pinfo & INSN_COPROC_MEMORY_DELAY)
+ && ! cop_mem_interlocks)
+ /* We can not swap with a branch instruction. */
+ || (prev_pinfo
+ & (INSN_UNCOND_BRANCH_DELAY
+ | INSN_COND_BRANCH_DELAY
+ | INSN_COND_BRANCH_LIKELY))
+ /* We do not swap with a trap instruction, since it
+ complicates trap handlers to have the trap
+ instruction be in a delay slot. */
+ || (prev_pinfo & INSN_TRAP)
+ /* If the branch reads a register that the previous
+ instruction sets, we can not swap. */
+ || (! mips_opts.mips16
+ && (prev_pinfo & INSN_WRITE_GPR_T)
+ && insn_uses_reg (ip,
+ ((prev_insn.insn_opcode >> OP_SH_RT)
+ & OP_MASK_RT),
+ MIPS_GR_REG))
+ || (! mips_opts.mips16
+ && (prev_pinfo & INSN_WRITE_GPR_D)
+ && insn_uses_reg (ip,
+ ((prev_insn.insn_opcode >> OP_SH_RD)
+ & OP_MASK_RD),
+ MIPS_GR_REG))
+ || (mips_opts.mips16
+ && (((prev_pinfo & MIPS16_INSN_WRITE_X)
+ && insn_uses_reg (ip,
+ ((prev_insn.insn_opcode
+ >> MIPS16OP_SH_RX)
+ & MIPS16OP_MASK_RX),
+ MIPS16_REG))
+ || ((prev_pinfo & MIPS16_INSN_WRITE_Y)
+ && insn_uses_reg (ip,
+ ((prev_insn.insn_opcode
+ >> MIPS16OP_SH_RY)
+ & MIPS16OP_MASK_RY),
+ MIPS16_REG))
+ || ((prev_pinfo & MIPS16_INSN_WRITE_Z)
+ && insn_uses_reg (ip,
+ ((prev_insn.insn_opcode
+ >> MIPS16OP_SH_RZ)
+ & MIPS16OP_MASK_RZ),
+ MIPS16_REG))
+ || ((prev_pinfo & MIPS16_INSN_WRITE_T)
+ && insn_uses_reg (ip, TREG, MIPS_GR_REG))
+ || ((prev_pinfo & MIPS16_INSN_WRITE_31)
+ && insn_uses_reg (ip, RA, MIPS_GR_REG))
+ || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y)
+ && insn_uses_reg (ip,
+ MIPS16OP_EXTRACT_REG32R (prev_insn.
+ insn_opcode),
+ MIPS_GR_REG))))
+ /* If the branch writes a register that the previous
+ instruction sets, we can not swap (we know that
+ branches write only to RD or to $31). */
+ || (! mips_opts.mips16
+ && (prev_pinfo & INSN_WRITE_GPR_T)
+ && (((pinfo & INSN_WRITE_GPR_D)
+ && (((prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT)
+ == ((ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD)))
+ || ((pinfo & INSN_WRITE_GPR_31)
+ && (((prev_insn.insn_opcode >> OP_SH_RT)
+ & OP_MASK_RT)
+ == RA))))
+ || (! mips_opts.mips16
+ && (prev_pinfo & INSN_WRITE_GPR_D)
+ && (((pinfo & INSN_WRITE_GPR_D)
+ && (((prev_insn.insn_opcode >> OP_SH_RD) & OP_MASK_RD)
+ == ((ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD)))
+ || ((pinfo & INSN_WRITE_GPR_31)
+ && (((prev_insn.insn_opcode >> OP_SH_RD)
+ & OP_MASK_RD)
+ == RA))))
+ || (mips_opts.mips16
+ && (pinfo & MIPS16_INSN_WRITE_31)
+ && ((prev_pinfo & MIPS16_INSN_WRITE_31)
+ || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y)
+ && (MIPS16OP_EXTRACT_REG32R (prev_insn.insn_opcode)
+ == RA))))
+ /* If the branch writes a register that the previous
+ instruction reads, we can not swap (we know that
+ branches only write to RD or to $31). */
+ || (! mips_opts.mips16
+ && (pinfo & INSN_WRITE_GPR_D)
+ && insn_uses_reg (&prev_insn,
+ ((ip->insn_opcode >> OP_SH_RD)
+ & OP_MASK_RD),
+ MIPS_GR_REG))
+ || (! mips_opts.mips16
+ && (pinfo & INSN_WRITE_GPR_31)
+ && insn_uses_reg (&prev_insn, RA, MIPS_GR_REG))
+ || (mips_opts.mips16
+ && (pinfo & MIPS16_INSN_WRITE_31)
+ && insn_uses_reg (&prev_insn, RA, MIPS_GR_REG))
+ /* If we are generating embedded PIC code, the branch
+ might be expanded into a sequence which uses $at, so
+ we can't swap with an instruction which reads it. */
+ || (mips_pic == EMBEDDED_PIC
+ && insn_uses_reg (&prev_insn, AT, MIPS_GR_REG))
+ /* If the previous previous instruction has a load
+ delay, and sets a register that the branch reads, we
+ can not swap. */
+ || (! mips_opts.mips16
+ /* Itbl support may require additional care here. */
+ && (((prev_prev_insn.insn_mo->pinfo & INSN_LOAD_COPROC_DELAY)
+ && ! cop_interlocks)
+ || ((prev_prev_insn.insn_mo->pinfo
+ & INSN_LOAD_MEMORY_DELAY)
+ && ! gpr_interlocks))
+ && insn_uses_reg (ip,
+ ((prev_prev_insn.insn_opcode >> OP_SH_RT)
+ & OP_MASK_RT),
+ MIPS_GR_REG))
+ /* If one instruction sets a condition code and the
+ other one uses a condition code, we can not swap. */
+ || ((pinfo & INSN_READ_COND_CODE)
+ && (prev_pinfo & INSN_WRITE_COND_CODE))
+ || ((pinfo & INSN_WRITE_COND_CODE)
+ && (prev_pinfo & INSN_READ_COND_CODE))
+ /* If the previous instruction uses the PC, we can not
+ swap. */
+ || (mips_opts.mips16
+ && (prev_pinfo & MIPS16_INSN_READ_PC))
+ /* If the previous instruction was extended, we can not
+ swap. */
+ || (mips_opts.mips16 && prev_insn_extended)
+ /* If the previous instruction had a fixup in mips16
+ mode, we can not swap. This normally means that the
+ previous instruction was a 4 byte branch anyhow. */
+ || (mips_opts.mips16 && prev_insn_fixp[0])
+ /* If the previous instruction is a sync, sync.l, or
+ sync.p, we can not swap. */
+ || (prev_pinfo & INSN_SYNC))
+ {
+ /* We could do even better for unconditional branches to
+ portions of this object file; we could pick up the
+ instruction at the destination, put it in the delay
+ slot, and bump the destination address. */
+ emit_nop ();
+ /* Update the previous insn information. */
+ prev_prev_insn = *ip;
+ prev_insn.insn_mo = &dummy_opcode;
+ }
+ else
+ {
+ /* It looks like we can actually do the swap. */
+ if (! mips_opts.mips16)
+ {
+ char *prev_f;
+ char temp[4];
+
+ prev_f = prev_insn_frag->fr_literal + prev_insn_where;
+ if (!relaxed_branch)
+ {
+ /* If this is not a relaxed branch, then just
+ swap the instructions. */
+ memcpy (temp, prev_f, 4);
+ memcpy (prev_f, f, 4);
+ memcpy (f, temp, 4);
+ }
+ else
+ {
+ /* If this is a relaxed branch, then we move the
+ instruction to be placed in the delay slot to
+ the current frag, shrinking the fixed part of
+ the originating frag. If the branch occupies
+ the tail of the latter, we move it backwards,
+ into the space freed by the moved instruction. */
+ f = frag_more (4);
+ memcpy (f, prev_f, 4);
+ prev_insn_frag->fr_fix -= 4;
+ if (prev_insn_frag->fr_type == rs_machine_dependent)
+ memmove (prev_f, prev_f + 4, prev_insn_frag->fr_var);
+ }
+
+ if (prev_insn_fixp[0])
+ {
+ prev_insn_fixp[0]->fx_frag = frag_now;
+ prev_insn_fixp[0]->fx_where = f - frag_now->fr_literal;
+ }
+ if (prev_insn_fixp[1])
+ {
+ prev_insn_fixp[1]->fx_frag = frag_now;
+ prev_insn_fixp[1]->fx_where = f - frag_now->fr_literal;
+ }
+ if (prev_insn_fixp[2])
+ {
+ prev_insn_fixp[2]->fx_frag = frag_now;
+ prev_insn_fixp[2]->fx_where = f - frag_now->fr_literal;
+ }
+ if (prev_insn_fixp[0] && HAVE_NEWABI
+ && prev_insn_frag != frag_now
+ && (prev_insn_fixp[0]->fx_r_type
+ == BFD_RELOC_MIPS_GOT_DISP
+ || (prev_insn_fixp[0]->fx_r_type
+ == BFD_RELOC_MIPS_CALL16)))
+ {
+ /* To avoid confusion in tc_gen_reloc, we must
+ ensure that this does not become a variant
+ frag. */
+ force_new_frag = TRUE;
+ }
+
+ if (!relaxed_branch)
+ {
+ if (fixp[0])
+ {
+ fixp[0]->fx_frag = prev_insn_frag;
+ fixp[0]->fx_where = prev_insn_where;
+ }
+ if (fixp[1])
+ {
+ fixp[1]->fx_frag = prev_insn_frag;
+ fixp[1]->fx_where = prev_insn_where;
+ }
+ if (fixp[2])
+ {
+ fixp[2]->fx_frag = prev_insn_frag;
+ fixp[2]->fx_where = prev_insn_where;
+ }
+ }
+ else if (prev_insn_frag->fr_type == rs_machine_dependent)
+ {
+ if (fixp[0])
+ fixp[0]->fx_where -= 4;
+ if (fixp[1])
+ fixp[1]->fx_where -= 4;
+ if (fixp[2])
+ fixp[2]->fx_where -= 4;
+ }
+ }
+ else
+ {
+ char *prev_f;
+ char temp[2];
+
+ assert (prev_insn_fixp[0] == NULL);
+ assert (prev_insn_fixp[1] == NULL);
+ assert (prev_insn_fixp[2] == NULL);
+ prev_f = prev_insn_frag->fr_literal + prev_insn_where;
+ memcpy (temp, prev_f, 2);
+ memcpy (prev_f, f, 2);
+ if (*reloc_type != BFD_RELOC_MIPS16_JMP)
+ {
+ assert (*reloc_type == BFD_RELOC_UNUSED);
+ memcpy (f, temp, 2);
+ }
+ else
+ {
+ memcpy (f, f + 2, 2);
+ memcpy (f + 2, temp, 2);
+ }
+ if (fixp[0])
+ {
+ fixp[0]->fx_frag = prev_insn_frag;
+ fixp[0]->fx_where = prev_insn_where;
+ }
+ if (fixp[1])
+ {
+ fixp[1]->fx_frag = prev_insn_frag;
+ fixp[1]->fx_where = prev_insn_where;
+ }
+ if (fixp[2])
+ {
+ fixp[2]->fx_frag = prev_insn_frag;
+ fixp[2]->fx_where = prev_insn_where;
+ }
+ }
+
+ /* Update the previous insn information; leave prev_insn
+ unchanged. */
+ prev_prev_insn = *ip;
+ }
+ prev_insn_is_delay_slot = 1;
+
+ /* If that was an unconditional branch, forget the previous
+ insn information. */
+ if (pinfo & INSN_UNCOND_BRANCH_DELAY)
+ {
+ prev_prev_insn.insn_mo = &dummy_opcode;
+ prev_insn.insn_mo = &dummy_opcode;
+ }
+
+ prev_insn_fixp[0] = NULL;
+ prev_insn_fixp[1] = NULL;
+ prev_insn_fixp[2] = NULL;
+ prev_insn_reloc_type[0] = BFD_RELOC_UNUSED;
+ prev_insn_reloc_type[1] = BFD_RELOC_UNUSED;
+ prev_insn_reloc_type[2] = BFD_RELOC_UNUSED;
+ prev_insn_extended = 0;
+ }
+ else if (pinfo & INSN_COND_BRANCH_LIKELY)
+ {
+ /* We don't yet optimize a branch likely. What we should do
+ is look at the target, copy the instruction found there
+ into the delay slot, and increment the branch to jump to
+ the next instruction. */
+ emit_nop ();
+ /* Update the previous insn information. */
+ prev_prev_insn = *ip;
+ prev_insn.insn_mo = &dummy_opcode;
+ prev_insn_fixp[0] = NULL;
+ prev_insn_fixp[1] = NULL;
+ prev_insn_fixp[2] = NULL;
+ prev_insn_reloc_type[0] = BFD_RELOC_UNUSED;
+ prev_insn_reloc_type[1] = BFD_RELOC_UNUSED;
+ prev_insn_reloc_type[2] = BFD_RELOC_UNUSED;
+ prev_insn_extended = 0;
+ }
+ else
+ {
+ /* Update the previous insn information. */
+ if (nops > 0)
+ prev_prev_insn.insn_mo = &dummy_opcode;
+ else
+ prev_prev_insn = prev_insn;
+ prev_insn = *ip;
+
+ /* Any time we see a branch, we always fill the delay slot
+ immediately; since this insn is not a branch, we know it
+ is not in a delay slot. */
+ prev_insn_is_delay_slot = 0;
+
+ prev_insn_fixp[0] = fixp[0];
+ prev_insn_fixp[1] = fixp[1];
+ prev_insn_fixp[2] = fixp[2];
+ prev_insn_reloc_type[0] = reloc_type[0];
+ prev_insn_reloc_type[1] = reloc_type[1];
+ prev_insn_reloc_type[2] = reloc_type[2];
+ if (mips_opts.mips16)
+ prev_insn_extended = (ip->use_extend
+ || *reloc_type > BFD_RELOC_UNUSED);
+ }
+
+ prev_prev_insn_unreordered = prev_insn_unreordered;
+ prev_insn_unreordered = 0;
+ prev_insn_frag = frag_now;
+ prev_insn_where = f - frag_now->fr_literal;
+ prev_insn_valid = 1;
+ }
+ else if (mips_relax.sequence != 2)
+ {
+ /* We need to record a bit of information even when we are not
+ reordering, in order to determine the base address for mips16
+ PC relative relocs. */
+ prev_prev_insn = prev_insn;
+ prev_insn = *ip;
+ prev_insn_reloc_type[0] = reloc_type[0];
+ prev_insn_reloc_type[1] = reloc_type[1];
+ prev_insn_reloc_type[2] = reloc_type[2];
+ prev_prev_insn_unreordered = prev_insn_unreordered;
+ prev_insn_unreordered = 1;
+ }
+
+ /* We just output an insn, so the next one doesn't have a label. */
+ mips_clear_insn_labels ();
+}
+
+/* This function forgets that there was any previous instruction or
+ label. If PRESERVE is non-zero, it remembers enough information to
+ know whether nops are needed before a noreorder section. */
+
+static void
+mips_no_prev_insn (int preserve)
+{
+ if (! preserve)
+ {
+ prev_insn.insn_mo = &dummy_opcode;
+ prev_prev_insn.insn_mo = &dummy_opcode;
+ prev_nop_frag = NULL;
+ prev_nop_frag_holds = 0;
+ prev_nop_frag_required = 0;
+ prev_nop_frag_since = 0;
+ }
+ prev_insn_valid = 0;
+ prev_insn_is_delay_slot = 0;
+ prev_insn_unreordered = 0;
+ prev_insn_extended = 0;
+ prev_insn_reloc_type[0] = BFD_RELOC_UNUSED;
+ prev_insn_reloc_type[1] = BFD_RELOC_UNUSED;
+ prev_insn_reloc_type[2] = BFD_RELOC_UNUSED;
+ prev_prev_insn_unreordered = 0;
+ mips_clear_insn_labels ();
+}
+
+/* This function must be called whenever we turn on noreorder or emit
+ something other than instructions. It inserts any NOPS which might
+ be needed by the previous instruction, and clears the information
+ kept for the previous instructions. The INSNS parameter is true if
+ instructions are to follow. */
+
+static void
+mips_emit_delays (bfd_boolean insns)
+{
+ if (! mips_opts.noreorder)
+ {
+ int nops;
+
+ nops = 0;
+ if ((! mips_opts.mips16
+ && ((prev_insn.insn_mo->pinfo
+ & (INSN_LOAD_COPROC_DELAY
+ | INSN_COPROC_MOVE_DELAY
+ | INSN_WRITE_COND_CODE))
+ && ! cop_interlocks))
+ || (! hilo_interlocks
+ && (prev_insn.insn_mo->pinfo
+ & (INSN_READ_LO
+ | INSN_READ_HI)))
+ || (! mips_opts.mips16
+ && (prev_insn.insn_mo->pinfo & INSN_LOAD_MEMORY_DELAY)
+ && ! gpr_interlocks)
+ || (! mips_opts.mips16
+ && (prev_insn.insn_mo->pinfo & INSN_COPROC_MEMORY_DELAY)
+ && ! cop_mem_interlocks))
+ {
+ /* Itbl support may require additional care here. */
+ ++nops;
+ if ((! mips_opts.mips16
+ && ((prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
+ && ! cop_interlocks))
+ || (! hilo_interlocks
+ && ((prev_insn.insn_mo->pinfo & INSN_READ_HI)
+ || (prev_insn.insn_mo->pinfo & INSN_READ_LO))))
+ ++nops;
+
+ if (prev_insn_unreordered)
+ nops = 0;
+ }
+ else if ((! mips_opts.mips16
+ && ((prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
+ && ! cop_interlocks))
+ || (! hilo_interlocks
+ && ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
+ || (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO))))
+ {
+ /* Itbl support may require additional care here. */
+ if (! prev_prev_insn_unreordered)
+ ++nops;
+ }
+
+ if (mips_fix_vr4120 && prev_insn.insn_mo->name)
+ {
+ int min_nops = 0;
+ const char *pn = prev_insn.insn_mo->name;
+ if (strncmp(pn, "macc", 4) == 0
+ || strncmp(pn, "dmacc", 5) == 0
+ || strncmp(pn, "dmult", 5) == 0)
+ {
+ min_nops = 1;
+ }
+ if (nops < min_nops)
+ nops = min_nops;
+ }
+
+ if (nops > 0)
+ {
+ struct insn_label_list *l;
+
+ if (insns)
+ {
+ /* Record the frag which holds the nop instructions, so
+ that we can remove them if we don't need them. */
+ frag_grow (mips_opts.mips16 ? nops * 2 : nops * 4);
+ prev_nop_frag = frag_now;
+ prev_nop_frag_holds = nops;
+ prev_nop_frag_required = 0;
+ prev_nop_frag_since = 0;
+ }
+
+ for (; nops > 0; --nops)
+ emit_nop ();
+
+ if (insns)
+ {
+ /* Move on to a new frag, so that it is safe to simply
+ decrease the size of prev_nop_frag. */
+ frag_wane (frag_now);
+ frag_new (0);
+ }
+
+ for (l = insn_labels; l != NULL; l = l->next)
+ {
+ valueT val;
+
+ assert (S_GET_SEGMENT (l->label) == now_seg);
+ symbol_set_frag (l->label, frag_now);
+ val = (valueT) frag_now_fix ();
+ /* mips16 text labels are stored as odd. */
+ if (mips_opts.mips16)
+ ++val;
+ S_SET_VALUE (l->label, val);
+ }
+ }
+ }
+
+ /* Mark instruction labels in mips16 mode. */
+ if (insns)
+ mips16_mark_labels ();
+
+ mips_no_prev_insn (insns);
+}
+
+/* Set up global variables for the start of a new macro. */
+
+static void
+macro_start (void)
+{
+ memset (&mips_macro_warning.sizes, 0, sizeof (mips_macro_warning.sizes));
+ mips_macro_warning.delay_slot_p = (mips_opts.noreorder
+ && (prev_insn.insn_mo->pinfo
+ & (INSN_UNCOND_BRANCH_DELAY
+ | INSN_COND_BRANCH_DELAY
+ | INSN_COND_BRANCH_LIKELY)) != 0);
+}
+
+/* Given that a macro is longer than 4 bytes, return the appropriate warning
+ for it. Return null if no warning is needed. SUBTYPE is a bitmask of
+ RELAX_DELAY_SLOT and RELAX_NOMACRO. */
+
+static const char *
+macro_warning (relax_substateT subtype)
+{
+ if (subtype & RELAX_DELAY_SLOT)
+ return _("Macro instruction expanded into multiple instructions"
+ " in a branch delay slot");
+ else if (subtype & RELAX_NOMACRO)
+ return _("Macro instruction expanded into multiple instructions");
+ else
+ return 0;
+}
+
+/* Finish up a macro. Emit warnings as appropriate. */
+
+static void
+macro_end (void)
+{
+ if (mips_macro_warning.sizes[0] > 4 || mips_macro_warning.sizes[1] > 4)
+ {
+ relax_substateT subtype;
+
+ /* Set up the relaxation warning flags. */
+ subtype = 0;
+ if (mips_macro_warning.sizes[1] > mips_macro_warning.sizes[0])
+ subtype |= RELAX_SECOND_LONGER;
+ if (mips_opts.warn_about_macros)
+ subtype |= RELAX_NOMACRO;
+ if (mips_macro_warning.delay_slot_p)
+ subtype |= RELAX_DELAY_SLOT;
+
+ if (mips_macro_warning.sizes[0] > 4 && mips_macro_warning.sizes[1] > 4)
+ {
+ /* Either the macro has a single implementation or both
+ implementations are longer than 4 bytes. Emit the
+ warning now. */
+ const char *msg = macro_warning (subtype);
+ if (msg != 0)
+ as_warn (msg);
+ }
+ else
+ {
+ /* One implementation might need a warning but the other
+ definitely doesn't. */
+ mips_macro_warning.first_frag->fr_subtype |= subtype;
+ }
+ }
+}
+
+/* Build an instruction created by a macro expansion. This is passed
+ a pointer to the count of instructions created so far, an
+ expression, the name of the instruction to build, an operand format
+ string, and corresponding arguments. */
+
+static void
+macro_build (expressionS *ep, const char *name, const char *fmt, ...)
+{
+ struct mips_cl_insn insn;
+ bfd_reloc_code_real_type r[3];
+ va_list args;
+
+ va_start (args, fmt);
+
+ if (mips_opts.mips16)
+ {
+ mips16_macro_build (ep, name, fmt, args);
+ va_end (args);
+ return;
+ }
+
+ r[0] = BFD_RELOC_UNUSED;
+ r[1] = BFD_RELOC_UNUSED;
+ r[2] = BFD_RELOC_UNUSED;
+ insn.insn_mo = (struct mips_opcode *) hash_find (op_hash, name);
+ assert (insn.insn_mo);
+ assert (strcmp (name, insn.insn_mo->name) == 0);
+
+ /* Search until we get a match for NAME. */
+ while (1)
+ {
+ /* It is assumed here that macros will never generate
+ MDMX or MIPS-3D instructions. */
+ if (strcmp (fmt, insn.insn_mo->args) == 0
+ && insn.insn_mo->pinfo != INSN_MACRO
+ && OPCODE_IS_MEMBER (insn.insn_mo,
+ (mips_opts.isa
+ | (file_ase_mips16 ? INSN_MIPS16 : 0)),
+ mips_opts.arch)
+ && (mips_opts.arch != CPU_R4650 || (insn.insn_mo->pinfo & FP_D) == 0))
+ break;
+
+ ++insn.insn_mo;
+ assert (insn.insn_mo->name);
+ assert (strcmp (name, insn.insn_mo->name) == 0);
+ }
+
+ insn.insn_opcode = insn.insn_mo->match;
+ for (;;)
+ {
+ switch (*fmt++)
+ {
+ case '\0':
+ break;
+
+ case ',':
+ case '(':
+ case ')':
+ continue;
+
+ case '+':
+ switch (*fmt++)
+ {
+ case 'A':
+ case 'E':
+ insn.insn_opcode |= (va_arg (args, int)
+ & OP_MASK_SHAMT) << OP_SH_SHAMT;
+ continue;
+
+ case 'B':
+ case 'F':
+ /* Note that in the macro case, these arguments are already
+ in MSB form. (When handling the instruction in the
+ non-macro case, these arguments are sizes from which
+ MSB values must be calculated.) */
+ insn.insn_opcode |= (va_arg (args, int)
+ & OP_MASK_INSMSB) << OP_SH_INSMSB;
+ continue;
+
+ case 'C':
+ case 'G':
+ case 'H':
+ /* Note that in the macro case, these arguments are already
+ in MSBD form. (When handling the instruction in the
+ non-macro case, these arguments are sizes from which
+ MSBD values must be calculated.) */
+ insn.insn_opcode |= (va_arg (args, int)
+ & OP_MASK_EXTMSBD) << OP_SH_EXTMSBD;
+ continue;
+
+ default:
+ internalError ();
+ }
+ continue;
+
+ case 't':
+ case 'w':
+ case 'E':
+ insn.insn_opcode |= va_arg (args, int) << OP_SH_RT;
+ continue;
+
+ case 'c':
+ insn.insn_opcode |= va_arg (args, int) << OP_SH_CODE;
+ continue;
+
+ case 'T':
+ case 'W':
+ insn.insn_opcode |= va_arg (args, int) << OP_SH_FT;
+ continue;
+
+ case 'd':
+ case 'G':
+ case 'K':
+ insn.insn_opcode |= va_arg (args, int) << OP_SH_RD;
+ continue;
+
+ case 'U':
+ {
+ int tmp = va_arg (args, int);
+
+ insn.insn_opcode |= tmp << OP_SH_RT;
+ insn.insn_opcode |= tmp << OP_SH_RD;
+ continue;
+ }
+
+ case 'V':
+ case 'S':
+ insn.insn_opcode |= va_arg (args, int) << OP_SH_FS;
+ continue;
+
+ case 'z':
+ continue;
+
+ case '<':
+ insn.insn_opcode |= va_arg (args, int) << OP_SH_SHAMT;
+ continue;
+
+ case 'D':
+ insn.insn_opcode |= va_arg (args, int) << OP_SH_FD;
+ continue;
+
+ case 'B':
+ insn.insn_opcode |= va_arg (args, int) << OP_SH_CODE20;
+ continue;
+
+ case 'J':
+ insn.insn_opcode |= va_arg (args, int) << OP_SH_CODE19;
+ continue;
+
+ case 'q':
+ insn.insn_opcode |= va_arg (args, int) << OP_SH_CODE2;
+ continue;
+
+ case 'b':
+ case 's':
+ case 'r':
+ case 'v':
+ insn.insn_opcode |= va_arg (args, int) << OP_SH_RS;
+ continue;
+
+ case 'i':
+ case 'j':
+ case 'o':
+ *r = (bfd_reloc_code_real_type) va_arg (args, int);
+ assert (*r == BFD_RELOC_GPREL16
+ || *r == BFD_RELOC_MIPS_LITERAL
+ || *r == BFD_RELOC_MIPS_HIGHER
+ || *r == BFD_RELOC_HI16_S
+ || *r == BFD_RELOC_LO16
+ || *r == BFD_RELOC_MIPS_GOT16
+ || *r == BFD_RELOC_MIPS_CALL16
+ || *r == BFD_RELOC_MIPS_GOT_DISP
+ || *r == BFD_RELOC_MIPS_GOT_PAGE
+ || *r == BFD_RELOC_MIPS_GOT_OFST
+ || *r == BFD_RELOC_MIPS_GOT_LO16
+ || *r == BFD_RELOC_MIPS_CALL_LO16
+ || (ep->X_op == O_subtract
+ && *r == BFD_RELOC_PCREL_LO16));
+ continue;
+
+ case 'u':
+ *r = (bfd_reloc_code_real_type) va_arg (args, int);
+ assert (ep != NULL
+ && (ep->X_op == O_constant
+ || (ep->X_op == O_symbol
+ && (*r == BFD_RELOC_MIPS_HIGHEST
+ || *r == BFD_RELOC_HI16_S
+ || *r == BFD_RELOC_HI16
+ || *r == BFD_RELOC_GPREL16
+ || *r == BFD_RELOC_MIPS_GOT_HI16
+ || *r == BFD_RELOC_MIPS_CALL_HI16))
+ || (ep->X_op == O_subtract
+ && *r == BFD_RELOC_PCREL_HI16_S)));
+ continue;
+
+ case 'p':
+ assert (ep != NULL);
+ /*
+ * This allows macro() to pass an immediate expression for
+ * creating short branches without creating a symbol.
+ * Note that the expression still might come from the assembly
+ * input, in which case the value is not checked for range nor
+ * is a relocation entry generated (yuck).
+ */
+ if (ep->X_op == O_constant)
+ {
+ insn.insn_opcode |= (ep->X_add_number >> 2) & 0xffff;
+ ep = NULL;
+ }
+ else
+ *r = BFD_RELOC_16_PCREL_S2;
+ continue;
+
+ case 'a':
+ assert (ep != NULL);
+ *r = BFD_RELOC_MIPS_JMP;
+ continue;
+
+ case 'C':
+ insn.insn_opcode |= va_arg (args, unsigned long);
+ continue;
+
+ default:
+ internalError ();
+ }
+ break;
+ }
+ va_end (args);
+ assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
+
+ append_insn (&insn, ep, r);
+}
+
+static void
+mips16_macro_build (expressionS *ep, const char *name, const char *fmt,
+ va_list args)
+{
+ struct mips_cl_insn insn;
+ bfd_reloc_code_real_type r[3]
+ = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+
+ insn.insn_mo = (struct mips_opcode *) hash_find (mips16_op_hash, name);
+ assert (insn.insn_mo);
+ assert (strcmp (name, insn.insn_mo->name) == 0);
+
+ while (strcmp (fmt, insn.insn_mo->args) != 0
+ || insn.insn_mo->pinfo == INSN_MACRO)
+ {
+ ++insn.insn_mo;
+ assert (insn.insn_mo->name);
+ assert (strcmp (name, insn.insn_mo->name) == 0);
+ }
+
+ insn.insn_opcode = insn.insn_mo->match;
+ insn.use_extend = FALSE;
+
+ for (;;)
+ {
+ int c;
+
+ c = *fmt++;
+ switch (c)
+ {
+ case '\0':
+ break;
+
+ case ',':
+ case '(':
+ case ')':
+ continue;
+
+ case 'y':
+ case 'w':
+ insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_RY;
+ continue;
+
+ case 'x':
+ case 'v':
+ insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_RX;
+ continue;
+
+ case 'z':
+ insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_RZ;
+ continue;
+
+ case 'Z':
+ insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_MOVE32Z;
+ continue;
+
+ case '0':
+ case 'S':
+ case 'P':
+ case 'R':
+ continue;
+
+ case 'X':
+ insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_REGR32;
+ continue;
+
+ case 'Y':
+ {
+ int regno;
+
+ regno = va_arg (args, int);
+ regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3);
+ insn.insn_opcode |= regno << MIPS16OP_SH_REG32R;
+ }
+ continue;
+
+ case '<':
+ case '>':
+ case '4':
+ case '5':
+ case 'H':
+ case 'W':
+ case 'D':
+ case 'j':
+ case '8':
+ case 'V':
+ case 'C':
+ case 'U':
+ case 'k':
+ case 'K':
+ case 'p':
+ case 'q':
+ {
+ assert (ep != NULL);
+
+ if (ep->X_op != O_constant)
+ *r = (int) BFD_RELOC_UNUSED + c;
+ else
+ {
+ mips16_immed (NULL, 0, c, ep->X_add_number, FALSE, FALSE,
+ FALSE, &insn.insn_opcode, &insn.use_extend,
+ &insn.extend);
+ ep = NULL;
+ *r = BFD_RELOC_UNUSED;
+ }
+ }
+ continue;
+
+ case '6':
+ insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_IMM6;
+ continue;
+ }
+
+ break;
+ }
+
+ assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
+
+ append_insn (&insn, ep, r);
+}
+
+/*
+ * Generate a "jalr" instruction with a relocation hint to the called
+ * function. This occurs in NewABI PIC code.
+ */
+static void
+macro_build_jalr (expressionS *ep)
+{
+ char *f = NULL;
+
+ if (HAVE_NEWABI)
+ {
+ frag_grow (8);
+ f = frag_more (0);
+ }
+ macro_build (NULL, "jalr", "d,s", RA, PIC_CALL_REG);
+ if (HAVE_NEWABI)
+ fix_new_exp (frag_now, f - frag_now->fr_literal,
+ 4, ep, FALSE, BFD_RELOC_MIPS_JALR);
+}
+
+/*
+ * Generate a "lui" instruction.
+ */
+static void
+macro_build_lui (expressionS *ep, int regnum)
+{
+ expressionS high_expr;
+ struct mips_cl_insn insn;
+ bfd_reloc_code_real_type r[3]
+ = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+ const char *name = "lui";
+ const char *fmt = "t,u";
+
+ assert (! mips_opts.mips16);
+
+ high_expr = *ep;
+
+ if (high_expr.X_op == O_constant)
+ {
+ /* we can compute the instruction now without a relocation entry */
+ high_expr.X_add_number = ((high_expr.X_add_number + 0x8000)
+ >> 16) & 0xffff;
+ *r = BFD_RELOC_UNUSED;
+ }
+ else
+ {
+ assert (ep->X_op == O_symbol);
+ /* _gp_disp is a special case, used from s_cpload. */
+ assert (mips_pic == NO_PIC
+ || (! HAVE_NEWABI
+ && strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0));
+ *r = BFD_RELOC_HI16_S;
+ }
+
+ insn.insn_mo = (struct mips_opcode *) hash_find (op_hash, name);
+ assert (insn.insn_mo);
+ assert (strcmp (name, insn.insn_mo->name) == 0);
+ assert (strcmp (fmt, insn.insn_mo->args) == 0);
+
+ insn.insn_opcode = insn.insn_mo->match | (regnum << OP_SH_RT);
+ if (*r == BFD_RELOC_UNUSED)
+ {
+ insn.insn_opcode |= high_expr.X_add_number;
+ append_insn (&insn, NULL, r);
+ }
+ else
+ append_insn (&insn, &high_expr, r);
+}
+
+/* Generate a sequence of instructions to do a load or store from a constant
+ offset off of a base register (breg) into/from a target register (treg),
+ using AT if necessary. */
+static void
+macro_build_ldst_constoffset (expressionS *ep, const char *op,
+ int treg, int breg, int dbl)
+{
+ assert (ep->X_op == O_constant);
+
+ /* Sign-extending 32-bit constants makes their handling easier. */
+ if (! dbl && ! ((ep->X_add_number & ~((bfd_vma) 0x7fffffff))
+ == ~((bfd_vma) 0x7fffffff)))
+ {
+ if (ep->X_add_number & ~((bfd_vma) 0xffffffff))
+ as_bad (_("constant too large"));
+
+ ep->X_add_number = (((ep->X_add_number & 0xffffffff) ^ 0x80000000)
+ - 0x80000000);
+ }
+
+ /* Right now, this routine can only handle signed 32-bit constants. */
+ if (! IS_SEXT_32BIT_NUM(ep->X_add_number + 0x8000))
+ as_warn (_("operand overflow"));
+
+ if (IS_SEXT_16BIT_NUM(ep->X_add_number))
+ {
+ /* Signed 16-bit offset will fit in the op. Easy! */
+ macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, breg);
+ }
+ else
+ {
+ /* 32-bit offset, need multiple instructions and AT, like:
+ lui $tempreg,const_hi (BFD_RELOC_HI16_S)
+ addu $tempreg,$tempreg,$breg
+ <op> $treg,const_lo($tempreg) (BFD_RELOC_LO16)
+ to handle the complete offset. */
+ macro_build_lui (ep, AT);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
+ macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, AT);
+
+ if (mips_opts.noat)
+ as_warn (_("Macro used $at after \".set noat\""));
+ }
+}
+
+/* set_at()
+ * Generates code to set the $at register to true (one)
+ * if reg is less than the immediate expression.
+ */
+static void
+set_at (int reg, int unsignedp)
+{
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ macro_build (&imm_expr, unsignedp ? "sltiu" : "slti", "t,r,j",
+ AT, reg, BFD_RELOC_LO16);
+ else
+ {
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, unsignedp ? "sltu" : "slt", "d,v,t", AT, reg, AT);
+ }
+}
+
+static void
+normalize_constant_expr (expressionS *ex)
+{
+ if (ex->X_op == O_constant && HAVE_32BIT_GPRS)
+ ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
+ - 0x80000000);
+}
+
+/* Warn if an expression is not a constant. */
+
+static void
+check_absolute_expr (struct mips_cl_insn *ip, expressionS *ex)
+{
+ if (ex->X_op == O_big)
+ as_bad (_("unsupported large constant"));
+ else if (ex->X_op != O_constant)
+ as_bad (_("Instruction %s requires absolute expression"), ip->insn_mo->name);
+
+ normalize_constant_expr (ex);
+}
+
+/* Count the leading zeroes by performing a binary chop. This is a
+ bulky bit of source, but performance is a LOT better for the
+ majority of values than a simple loop to count the bits:
+ for (lcnt = 0; (lcnt < 32); lcnt++)
+ if ((v) & (1 << (31 - lcnt)))
+ break;
+ However it is not code size friendly, and the gain will drop a bit
+ on certain cached systems.
+*/
+#define COUNT_TOP_ZEROES(v) \
+ (((v) & ~0xffff) == 0 \
+ ? ((v) & ~0xff) == 0 \
+ ? ((v) & ~0xf) == 0 \
+ ? ((v) & ~0x3) == 0 \
+ ? ((v) & ~0x1) == 0 \
+ ? !(v) \
+ ? 32 \
+ : 31 \
+ : 30 \
+ : ((v) & ~0x7) == 0 \
+ ? 29 \
+ : 28 \
+ : ((v) & ~0x3f) == 0 \
+ ? ((v) & ~0x1f) == 0 \
+ ? 27 \
+ : 26 \
+ : ((v) & ~0x7f) == 0 \
+ ? 25 \
+ : 24 \
+ : ((v) & ~0xfff) == 0 \
+ ? ((v) & ~0x3ff) == 0 \
+ ? ((v) & ~0x1ff) == 0 \
+ ? 23 \
+ : 22 \
+ : ((v) & ~0x7ff) == 0 \
+ ? 21 \
+ : 20 \
+ : ((v) & ~0x3fff) == 0 \
+ ? ((v) & ~0x1fff) == 0 \
+ ? 19 \
+ : 18 \
+ : ((v) & ~0x7fff) == 0 \
+ ? 17 \
+ : 16 \
+ : ((v) & ~0xffffff) == 0 \
+ ? ((v) & ~0xfffff) == 0 \
+ ? ((v) & ~0x3ffff) == 0 \
+ ? ((v) & ~0x1ffff) == 0 \
+ ? 15 \
+ : 14 \
+ : ((v) & ~0x7ffff) == 0 \
+ ? 13 \
+ : 12 \
+ : ((v) & ~0x3fffff) == 0 \
+ ? ((v) & ~0x1fffff) == 0 \
+ ? 11 \
+ : 10 \
+ : ((v) & ~0x7fffff) == 0 \
+ ? 9 \
+ : 8 \
+ : ((v) & ~0xfffffff) == 0 \
+ ? ((v) & ~0x3ffffff) == 0 \
+ ? ((v) & ~0x1ffffff) == 0 \
+ ? 7 \
+ : 6 \
+ : ((v) & ~0x7ffffff) == 0 \
+ ? 5 \
+ : 4 \
+ : ((v) & ~0x3fffffff) == 0 \
+ ? ((v) & ~0x1fffffff) == 0 \
+ ? 3 \
+ : 2 \
+ : ((v) & ~0x7fffffff) == 0 \
+ ? 1 \
+ : 0)
+
+/* load_register()
+ * This routine generates the least number of instructions necessary to load
+ * an absolute expression value into a register.
+ */
+static void
+load_register (int reg, expressionS *ep, int dbl)
+{
+ int freg;
+ expressionS hi32, lo32;
+
+ if (ep->X_op != O_big)
+ {
+ assert (ep->X_op == O_constant);
+
+ /* Sign-extending 32-bit constants makes their handling easier. */
+ if (! dbl && ! ((ep->X_add_number & ~((bfd_vma) 0x7fffffff))
+ == ~((bfd_vma) 0x7fffffff)))
+ {
+ if (ep->X_add_number & ~((bfd_vma) 0xffffffff))
+ as_bad (_("constant too large"));
+
+ ep->X_add_number = (((ep->X_add_number & 0xffffffff) ^ 0x80000000)
+ - 0x80000000);
+ }
+
+ if (IS_SEXT_16BIT_NUM (ep->X_add_number))
+ {
+ /* We can handle 16 bit signed values with an addiu to
+ $zero. No need to ever use daddiu here, since $zero and
+ the result are always correct in 32 bit mode. */
+ macro_build (ep, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
+ return;
+ }
+ else if (ep->X_add_number >= 0 && ep->X_add_number < 0x10000)
+ {
+ /* We can handle 16 bit unsigned values with an ori to
+ $zero. */
+ macro_build (ep, "ori", "t,r,i", reg, 0, BFD_RELOC_LO16);
+ return;
+ }
+ else if ((IS_SEXT_32BIT_NUM (ep->X_add_number)))
+ {
+ /* 32 bit values require an lui. */
+ macro_build (ep, "lui", "t,u", reg, BFD_RELOC_HI16);
+ if ((ep->X_add_number & 0xffff) != 0)
+ macro_build (ep, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16);
+ return;
+ }
+ }
+
+ /* The value is larger than 32 bits. */
+
+ if (HAVE_32BIT_GPRS)
+ {
+ as_bad (_("Number (0x%lx) larger than 32 bits"),
+ (unsigned long) ep->X_add_number);
+ macro_build (ep, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
+ return;
+ }
+
+ if (ep->X_op != O_big)
+ {
+ hi32 = *ep;
+ hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
+ hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
+ hi32.X_add_number &= 0xffffffff;
+ lo32 = *ep;
+ lo32.X_add_number &= 0xffffffff;
+ }
+ else
+ {
+ assert (ep->X_add_number > 2);
+ if (ep->X_add_number == 3)
+ generic_bignum[3] = 0;
+ else if (ep->X_add_number > 4)
+ as_bad (_("Number larger than 64 bits"));
+ lo32.X_op = O_constant;
+ lo32.X_add_number = generic_bignum[0] + (generic_bignum[1] << 16);
+ hi32.X_op = O_constant;
+ hi32.X_add_number = generic_bignum[2] + (generic_bignum[3] << 16);
+ }
+
+ if (hi32.X_add_number == 0)
+ freg = 0;
+ else
+ {
+ int shift, bit;
+ unsigned long hi, lo;
+
+ if (hi32.X_add_number == (offsetT) 0xffffffff)
+ {
+ if ((lo32.X_add_number & 0xffff8000) == 0xffff8000)
+ {
+ macro_build (&lo32, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
+ return;
+ }
+ if (lo32.X_add_number & 0x80000000)
+ {
+ macro_build (&lo32, "lui", "t,u", reg, BFD_RELOC_HI16);
+ if (lo32.X_add_number & 0xffff)
+ macro_build (&lo32, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16);
+ return;
+ }
+ }
+
+ /* Check for 16bit shifted constant. We know that hi32 is
+ non-zero, so start the mask on the first bit of the hi32
+ value. */
+ shift = 17;
+ do
+ {
+ unsigned long himask, lomask;
+
+ if (shift < 32)
+ {
+ himask = 0xffff >> (32 - shift);
+ lomask = (0xffff << shift) & 0xffffffff;
+ }
+ else
+ {
+ himask = 0xffff << (shift - 32);
+ lomask = 0;
+ }
+ if ((hi32.X_add_number & ~(offsetT) himask) == 0
+ && (lo32.X_add_number & ~(offsetT) lomask) == 0)
+ {
+ expressionS tmp;
+
+ tmp.X_op = O_constant;
+ if (shift < 32)
+ tmp.X_add_number = ((hi32.X_add_number << (32 - shift))
+ | (lo32.X_add_number >> shift));
+ else
+ tmp.X_add_number = hi32.X_add_number >> (shift - 32);
+ macro_build (&tmp, "ori", "t,r,i", reg, 0, BFD_RELOC_LO16);
+ macro_build (NULL, (shift >= 32) ? "dsll32" : "dsll", "d,w,<",
+ reg, reg, (shift >= 32) ? shift - 32 : shift);
+ return;
+ }
+ ++shift;
+ }
+ while (shift <= (64 - 16));
+
+ /* Find the bit number of the lowest one bit, and store the
+ shifted value in hi/lo. */
+ hi = (unsigned long) (hi32.X_add_number & 0xffffffff);
+ lo = (unsigned long) (lo32.X_add_number & 0xffffffff);
+ if (lo != 0)
+ {
+ bit = 0;
+ while ((lo & 1) == 0)
+ {
+ lo >>= 1;
+ ++bit;
+ }
+ lo |= (hi & (((unsigned long) 1 << bit) - 1)) << (32 - bit);
+ hi >>= bit;
+ }
+ else
+ {
+ bit = 32;
+ while ((hi & 1) == 0)
+ {
+ hi >>= 1;
+ ++bit;
+ }
+ lo = hi;
+ hi = 0;
+ }
+
+ /* Optimize if the shifted value is a (power of 2) - 1. */
+ if ((hi == 0 && ((lo + 1) & lo) == 0)
+ || (lo == 0xffffffff && ((hi + 1) & hi) == 0))
+ {
+ shift = COUNT_TOP_ZEROES ((unsigned int) hi32.X_add_number);
+ if (shift != 0)
+ {
+ expressionS tmp;
+
+ /* This instruction will set the register to be all
+ ones. */
+ tmp.X_op = O_constant;
+ tmp.X_add_number = (offsetT) -1;
+ macro_build (&tmp, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
+ if (bit != 0)
+ {
+ bit += shift;
+ macro_build (NULL, (bit >= 32) ? "dsll32" : "dsll", "d,w,<",
+ reg, reg, (bit >= 32) ? bit - 32 : bit);
+ }
+ macro_build (NULL, (shift >= 32) ? "dsrl32" : "dsrl", "d,w,<",
+ reg, reg, (shift >= 32) ? shift - 32 : shift);
+ return;
+ }
+ }
+
+ /* Sign extend hi32 before calling load_register, because we can
+ generally get better code when we load a sign extended value. */
+ if ((hi32.X_add_number & 0x80000000) != 0)
+ hi32.X_add_number |= ~(offsetT) 0xffffffff;
+ load_register (reg, &hi32, 0);
+ freg = reg;
+ }
+ if ((lo32.X_add_number & 0xffff0000) == 0)
+ {
+ if (freg != 0)
+ {
+ macro_build (NULL, "dsll32", "d,w,<", reg, freg, 0);
+ freg = reg;
+ }
+ }
+ else
+ {
+ expressionS mid16;
+
+ if ((freg == 0) && (lo32.X_add_number == (offsetT) 0xffffffff))
+ {
+ macro_build (&lo32, "lui", "t,u", reg, BFD_RELOC_HI16);
+ macro_build (NULL, "dsrl32", "d,w,<", reg, reg, 0);
+ return;
+ }
+
+ if (freg != 0)
+ {
+ macro_build (NULL, "dsll", "d,w,<", reg, freg, 16);
+ freg = reg;
+ }
+ mid16 = lo32;
+ mid16.X_add_number >>= 16;
+ macro_build (&mid16, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
+ macro_build (NULL, "dsll", "d,w,<", reg, reg, 16);
+ freg = reg;
+ }
+ if ((lo32.X_add_number & 0xffff) != 0)
+ macro_build (&lo32, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
+}
+
+/* Load an address into a register. */
+
+static void
+load_address (int reg, expressionS *ep, int *used_at)
+{
+ if (ep->X_op != O_constant
+ && ep->X_op != O_symbol)
+ {
+ as_bad (_("expression too complex"));
+ ep->X_op = O_constant;
+ }
+
+ if (ep->X_op == O_constant)
+ {
+ load_register (reg, ep, HAVE_64BIT_ADDRESSES);
+ return;
+ }
+
+ if (mips_pic == NO_PIC)
+ {
+ /* If this is a reference to a GP relative symbol, we want
+ addiu $reg,$gp,<sym> (BFD_RELOC_GPREL16)
+ Otherwise we want
+ lui $reg,<sym> (BFD_RELOC_HI16_S)
+ addiu $reg,$reg,<sym> (BFD_RELOC_LO16)
+ If we have an addend, we always use the latter form.
+
+ With 64bit address space and a usable $at we want
+ lui $reg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ lui $at,<sym> (BFD_RELOC_HI16_S)
+ daddiu $reg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ daddiu $at,<sym> (BFD_RELOC_LO16)
+ dsll32 $reg,0
+ daddu $reg,$reg,$at
+
+ If $at is already in use, we use a path which is suboptimal
+ on superscalar processors.
+ lui $reg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ daddiu $reg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ dsll $reg,16
+ daddiu $reg,<sym> (BFD_RELOC_HI16_S)
+ dsll $reg,16
+ daddiu $reg,<sym> (BFD_RELOC_LO16)
+ */
+ if (HAVE_64BIT_ADDRESSES)
+ {
+ /* ??? We don't provide a GP-relative alternative for these macros.
+ It used not to be possible with the original relaxation code,
+ but it could be done now. */
+
+ if (*used_at == 0 && ! mips_opts.noat)
+ {
+ macro_build (ep, "lui", "t,u", reg, BFD_RELOC_MIPS_HIGHEST);
+ macro_build (ep, "lui", "t,u", AT, BFD_RELOC_HI16_S);
+ macro_build (ep, "daddiu", "t,r,j", reg, reg,
+ BFD_RELOC_MIPS_HIGHER);
+ macro_build (ep, "daddiu", "t,r,j", AT, AT, BFD_RELOC_LO16);
+ macro_build (NULL, "dsll32", "d,w,<", reg, reg, 0);
+ macro_build (NULL, "daddu", "d,v,t", reg, reg, AT);
+ *used_at = 1;
+ }
+ else
+ {
+ macro_build (ep, "lui", "t,u", reg, BFD_RELOC_MIPS_HIGHEST);
+ macro_build (ep, "daddiu", "t,r,j", reg, reg,
+ BFD_RELOC_MIPS_HIGHER);
+ macro_build (NULL, "dsll", "d,w,<", reg, reg, 16);
+ macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_HI16_S);
+ macro_build (NULL, "dsll", "d,w,<", reg, reg, 16);
+ macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_LO16);
+ }
+ }
+ else
+ {
+ if ((valueT) ep->X_add_number <= MAX_GPREL_OFFSET
+ && ! nopic_need_relax (ep->X_add_symbol, 1))
+ {
+ relax_start (ep->X_add_symbol);
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg,
+ mips_gp_register, BFD_RELOC_GPREL16);
+ relax_switch ();
+ }
+ macro_build_lui (ep, reg);
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j",
+ reg, reg, BFD_RELOC_LO16);
+ if (mips_relax.sequence)
+ relax_end ();
+ }
+ }
+ else if (mips_pic == SVR4_PIC && ! mips_big_got)
+ {
+ expressionS ex;
+
+ /* If this is a reference to an external symbol, we want
+ lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ Otherwise we want
+ lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $reg,$reg,<sym> (BFD_RELOC_LO16)
+ If there is a constant, it must be added in after.
+
+ If we have NewABI, we want
+ lw $reg,<sym+cst>($gp) (BFD_RELOC_MIPS_GOT_DISP)
+ unless we're referencing a global symbol with a non-zero
+ offset, in which case cst must be added separately. */
+ if (HAVE_NEWABI)
+ {
+ if (ep->X_add_number)
+ {
+ ex.X_add_number = ep->X_add_number;
+ ep->X_add_number = 0;
+ relax_start (ep->X_add_symbol);
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+ BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+ if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ ex.X_op = O_constant;
+ macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j",
+ reg, reg, BFD_RELOC_LO16);
+ ep->X_add_number = ex.X_add_number;
+ relax_switch ();
+ }
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+ BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+ if (mips_relax.sequence)
+ relax_end ();
+ }
+ else
+ {
+ ex.X_add_number = ep->X_add_number;
+ ep->X_add_number = 0;
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ macro_build (NULL, "nop", "");
+ relax_start (ep->X_add_symbol);
+ relax_switch ();
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+ BFD_RELOC_LO16);
+ relax_end ();
+
+ if (ex.X_add_number != 0)
+ {
+ if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ ex.X_op = O_constant;
+ macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j",
+ reg, reg, BFD_RELOC_LO16);
+ }
+ }
+ }
+ else if (mips_pic == SVR4_PIC)
+ {
+ expressionS ex;
+
+ /* This is the large GOT case. If this is a reference to an
+ external symbol, we want
+ lui $reg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $reg,$reg,$gp
+ lw $reg,<sym>($reg) (BFD_RELOC_MIPS_GOT_LO16)
+
+ Otherwise, for a reference to a local symbol in old ABI, we want
+ lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $reg,$reg,<sym> (BFD_RELOC_LO16)
+ If there is a constant, it must be added in after.
+
+ In the NewABI, for local symbols, with or without offsets, we want:
+ lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT_PAGE)
+ addiu $reg,$reg,<sym> (BFD_RELOC_MIPS_GOT_OFST)
+ */
+ if (HAVE_NEWABI)
+ {
+ ex.X_add_number = ep->X_add_number;
+ ep->X_add_number = 0;
+ relax_start (ep->X_add_symbol);
+ macro_build (ep, "lui", "t,u", reg, BFD_RELOC_MIPS_GOT_HI16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ reg, reg, mips_gp_register);
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)",
+ reg, BFD_RELOC_MIPS_GOT_LO16, reg);
+ if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ else if (ex.X_add_number)
+ {
+ ex.X_op = O_constant;
+ macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+ BFD_RELOC_LO16);
+ }
+
+ ep->X_add_number = ex.X_add_number;
+ relax_switch ();
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+ BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+ BFD_RELOC_MIPS_GOT_OFST);
+ relax_end ();
+ }
+ else
+ {
+ ex.X_add_number = ep->X_add_number;
+ ep->X_add_number = 0;
+ relax_start (ep->X_add_symbol);
+ macro_build (ep, "lui", "t,u", reg, BFD_RELOC_MIPS_GOT_HI16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ reg, reg, mips_gp_register);
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)",
+ reg, BFD_RELOC_MIPS_GOT_LO16, reg);
+ relax_switch ();
+ if (reg_needs_delay (mips_gp_register))
+ {
+ /* We need a nop before loading from $gp. This special
+ check is required because the lui which starts the main
+ instruction stream does not refer to $gp, and so will not
+ insert the nop which may be required. */
+ macro_build (NULL, "nop", "");
+ }
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ macro_build (NULL, "nop", "");
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+ BFD_RELOC_LO16);
+ relax_end ();
+
+ if (ex.X_add_number != 0)
+ {
+ if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ ex.X_op = O_constant;
+ macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+ BFD_RELOC_LO16);
+ }
+ }
+ }
+ else if (mips_pic == EMBEDDED_PIC)
+ {
+ /* We always do
+ addiu $reg,$gp,<sym> (BFD_RELOC_GPREL16)
+ */
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j",
+ reg, mips_gp_register, BFD_RELOC_GPREL16);
+ }
+ else
+ abort ();
+}
+
+/* Move the contents of register SOURCE into register DEST. */
+
+static void
+move_register (int dest, int source)
+{
+ macro_build (NULL, HAVE_32BIT_GPRS ? "addu" : "daddu", "d,v,t",
+ dest, source, 0);
+}
+
+/* Emit an SVR4 PIC sequence to load address LOCAL into DEST, where
+ LOCAL is the sum of a symbol and a 16-bit or 32-bit displacement.
+ The two alternatives are:
+
+ Global symbol Local sybmol
+ ------------- ------------
+ lw DEST,%got(SYMBOL) lw DEST,%got(SYMBOL + OFFSET)
+ ... ...
+ addiu DEST,DEST,OFFSET addiu DEST,DEST,%lo(SYMBOL + OFFSET)
+
+ load_got_offset emits the first instruction and add_got_offset
+ emits the second for a 16-bit offset or add_got_offset_hilo emits
+ a sequence to add a 32-bit offset using a scratch register. */
+
+static void
+load_got_offset (int dest, expressionS *local)
+{
+ expressionS global;
+
+ global = *local;
+ global.X_add_number = 0;
+
+ relax_start (local->X_add_symbol);
+ macro_build (&global, ADDRESS_LOAD_INSN, "t,o(b)", dest,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ relax_switch ();
+ macro_build (local, ADDRESS_LOAD_INSN, "t,o(b)", dest,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ relax_end ();
+}
+
+static void
+add_got_offset (int dest, expressionS *local)
+{
+ expressionS global;
+
+ global.X_op = O_constant;
+ global.X_op_symbol = NULL;
+ global.X_add_symbol = NULL;
+ global.X_add_number = local->X_add_number;
+
+ relax_start (local->X_add_symbol);
+ macro_build (&global, ADDRESS_ADDI_INSN, "t,r,j",
+ dest, dest, BFD_RELOC_LO16);
+ relax_switch ();
+ macro_build (local, ADDRESS_ADDI_INSN, "t,r,j", dest, dest, BFD_RELOC_LO16);
+ relax_end ();
+}
+
+static void
+add_got_offset_hilo (int dest, expressionS *local, int tmp)
+{
+ expressionS global;
+ int hold_mips_optimize;
+
+ global.X_op = O_constant;
+ global.X_op_symbol = NULL;
+ global.X_add_symbol = NULL;
+ global.X_add_number = local->X_add_number;
+
+ relax_start (local->X_add_symbol);
+ load_register (tmp, &global, HAVE_64BIT_ADDRESSES);
+ relax_switch ();
+ /* Set mips_optimize around the lui instruction to avoid
+ inserting an unnecessary nop after the lw. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
+ macro_build_lui (&global, tmp);
+ mips_optimize = hold_mips_optimize;
+ macro_build (local, ADDRESS_ADDI_INSN, "t,r,j", tmp, tmp, BFD_RELOC_LO16);
+ relax_end ();
+
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dest, dest, tmp);
+}
+
+/*
+ * Build macros
+ * This routine implements the seemingly endless macro or synthesized
+ * instructions and addressing modes in the mips assembly language. Many
+ * of these macros are simple and are similar to each other. These could
+ * probably be handled by some kind of table or grammar approach instead of
+ * this verbose method. Others are not simple macros but are more like
+ * optimizing code generation.
+ * One interesting optimization is when several store macros appear
+ * consecutively that would load AT with the upper half of the same address.
+ * The ensuing load upper instructions are ommited. This implies some kind
+ * of global optimization. We currently only optimize within a single macro.
+ * For many of the load and store macros if the address is specified as a
+ * constant expression in the first 64k of memory (ie ld $2,0x4000c) we
+ * first load register 'at' with zero and use it as the base register. The
+ * mips assembler simply uses register $zero. Just one tiny optimization
+ * we're missing.
+ */
+static void
+macro (struct mips_cl_insn *ip)
+{
+ register int treg, sreg, dreg, breg;
+ int tempreg;
+ int mask;
+ int used_at = 0;
+ expressionS expr1;
+ const char *s;
+ const char *s2;
+ const char *fmt;
+ int likely = 0;
+ int dbl = 0;
+ int coproc = 0;
+ int lr = 0;
+ int imm = 0;
+ int call = 0;
+ int off;
+ offsetT maxnum;
+ bfd_reloc_code_real_type r;
+ int hold_mips_optimize;
+
+ assert (! mips_opts.mips16);
+
+ treg = (ip->insn_opcode >> 16) & 0x1f;
+ dreg = (ip->insn_opcode >> 11) & 0x1f;
+ sreg = breg = (ip->insn_opcode >> 21) & 0x1f;
+ mask = ip->insn_mo->mask;
+
+ expr1.X_op = O_constant;
+ expr1.X_op_symbol = NULL;
+ expr1.X_add_symbol = NULL;
+ expr1.X_add_number = 1;
+
+ switch (mask)
+ {
+ case M_DABS:
+ dbl = 1;
+ case M_ABS:
+ /* bgez $a0,.+12
+ move v0,$a0
+ sub v0,$zero,$a0
+ */
+
+ mips_emit_delays (TRUE);
+ ++mips_opts.noreorder;
+ mips_any_noreorder = 1;
+
+ expr1.X_add_number = 8;
+ macro_build (&expr1, "bgez", "s,p", sreg);
+ if (dreg == sreg)
+ macro_build (NULL, "nop", "", 0);
+ else
+ move_register (dreg, sreg);
+ macro_build (NULL, dbl ? "dsub" : "sub", "d,v,t", dreg, 0, sreg);
+
+ --mips_opts.noreorder;
+ return;
+
+ case M_ADD_I:
+ s = "addi";
+ s2 = "add";
+ goto do_addi;
+ case M_ADDU_I:
+ s = "addiu";
+ s2 = "addu";
+ goto do_addi;
+ case M_DADD_I:
+ dbl = 1;
+ s = "daddi";
+ s2 = "dadd";
+ goto do_addi;
+ case M_DADDU_I:
+ dbl = 1;
+ s = "daddiu";
+ s2 = "daddu";
+ do_addi:
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ {
+ macro_build (&imm_expr, s, "t,r,j", treg, sreg, BFD_RELOC_LO16);
+ return;
+ }
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
+ break;
+
+ case M_AND_I:
+ s = "andi";
+ s2 = "and";
+ goto do_bit;
+ case M_OR_I:
+ s = "ori";
+ s2 = "or";
+ goto do_bit;
+ case M_NOR_I:
+ s = "";
+ s2 = "nor";
+ goto do_bit;
+ case M_XOR_I:
+ s = "xori";
+ s2 = "xor";
+ do_bit:
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= 0
+ && imm_expr.X_add_number < 0x10000)
+ {
+ if (mask != M_NOR_I)
+ macro_build (&imm_expr, s, "t,r,i", treg, sreg, BFD_RELOC_LO16);
+ else
+ {
+ macro_build (&imm_expr, "ori", "t,r,i",
+ treg, sreg, BFD_RELOC_LO16);
+ macro_build (NULL, "nor", "d,v,t", treg, treg, 0);
+ }
+ return;
+ }
+
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
+ break;
+
+ case M_BEQ_I:
+ s = "beq";
+ goto beq_i;
+ case M_BEQL_I:
+ s = "beql";
+ likely = 1;
+ goto beq_i;
+ case M_BNE_I:
+ s = "bne";
+ goto beq_i;
+ case M_BNEL_I:
+ s = "bnel";
+ likely = 1;
+ beq_i:
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ {
+ macro_build (&offset_expr, s, "s,t,p", sreg, 0);
+ return;
+ }
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (&offset_expr, s, "s,t,p", sreg, AT);
+ break;
+
+ case M_BGEL:
+ likely = 1;
+ case M_BGE:
+ if (treg == 0)
+ {
+ macro_build (&offset_expr, likely ? "bgezl" : "bgez", "s,p", sreg);
+ return;
+ }
+ if (sreg == 0)
+ {
+ macro_build (&offset_expr, likely ? "blezl" : "blez", "s,p", treg);
+ return;
+ }
+ macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
+ macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, 0);
+ break;
+
+ case M_BGTL_I:
+ likely = 1;
+ case M_BGT_I:
+ /* check for > max integer */
+ maxnum = 0x7fffffff;
+ if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4)
+ {
+ maxnum <<= 16;
+ maxnum |= 0xffff;
+ maxnum <<= 16;
+ maxnum |= 0xffff;
+ }
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= maxnum
+ && (HAVE_32BIT_GPRS || sizeof (maxnum) > 4))
+ {
+ do_false:
+ /* result is always false */
+ if (! likely)
+ macro_build (NULL, "nop", "", 0);
+ else
+ macro_build (&offset_expr, "bnel", "s,t,p", 0, 0);
+ return;
+ }
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ ++imm_expr.X_add_number;
+ /* FALLTHROUGH */
+ case M_BGE_I:
+ case M_BGEL_I:
+ if (mask == M_BGEL_I)
+ likely = 1;
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ {
+ macro_build (&offset_expr, likely ? "bgezl" : "bgez", "s,p", sreg);
+ return;
+ }
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+ {
+ macro_build (&offset_expr, likely ? "bgtzl" : "bgtz", "s,p", sreg);
+ return;
+ }
+ maxnum = 0x7fffffff;
+ if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4)
+ {
+ maxnum <<= 16;
+ maxnum |= 0xffff;
+ maxnum <<= 16;
+ maxnum |= 0xffff;
+ }
+ maxnum = - maxnum - 1;
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number <= maxnum
+ && (HAVE_32BIT_GPRS || sizeof (maxnum) > 4))
+ {
+ do_true:
+ /* result is always true */
+ as_warn (_("Branch %s is always true"), ip->insn_mo->name);
+ macro_build (&offset_expr, "b", "p");
+ return;
+ }
+ set_at (sreg, 0);
+ macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, 0);
+ break;
+
+ case M_BGEUL:
+ likely = 1;
+ case M_BGEU:
+ if (treg == 0)
+ goto do_true;
+ if (sreg == 0)
+ {
+ macro_build (&offset_expr, likely ? "beql" : "beq",
+ "s,t,p", 0, treg);
+ return;
+ }
+ macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
+ macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, 0);
+ break;
+
+ case M_BGTUL_I:
+ likely = 1;
+ case M_BGTU_I:
+ if (sreg == 0
+ || (HAVE_32BIT_GPRS
+ && imm_expr.X_op == O_constant
+ && imm_expr.X_add_number == (offsetT) 0xffffffff))
+ goto do_false;
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ ++imm_expr.X_add_number;
+ /* FALLTHROUGH */
+ case M_BGEU_I:
+ case M_BGEUL_I:
+ if (mask == M_BGEUL_I)
+ likely = 1;
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ goto do_true;
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+ {
+ macro_build (&offset_expr, likely ? "bnel" : "bne",
+ "s,t,p", sreg, 0);
+ return;
+ }
+ set_at (sreg, 1);
+ macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, 0);
+ break;
+
+ case M_BGTL:
+ likely = 1;
+ case M_BGT:
+ if (treg == 0)
+ {
+ macro_build (&offset_expr, likely ? "bgtzl" : "bgtz", "s,p", sreg);
+ return;
+ }
+ if (sreg == 0)
+ {
+ macro_build (&offset_expr, likely ? "bltzl" : "bltz", "s,p", treg);
+ return;
+ }
+ macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
+ macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, 0);
+ break;
+
+ case M_BGTUL:
+ likely = 1;
+ case M_BGTU:
+ if (treg == 0)
+ {
+ macro_build (&offset_expr, likely ? "bnel" : "bne",
+ "s,t,p", sreg, 0);
+ return;
+ }
+ if (sreg == 0)
+ goto do_false;
+ macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
+ macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, 0);
+ break;
+
+ case M_BLEL:
+ likely = 1;
+ case M_BLE:
+ if (treg == 0)
+ {
+ macro_build (&offset_expr, likely ? "blezl" : "blez", "s,p", sreg);
+ return;
+ }
+ if (sreg == 0)
+ {
+ macro_build (&offset_expr, likely ? "bgezl" : "bgez", "s,p", treg);
+ return;
+ }
+ macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
+ macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, 0);
+ break;
+
+ case M_BLEL_I:
+ likely = 1;
+ case M_BLE_I:
+ maxnum = 0x7fffffff;
+ if (HAVE_64BIT_GPRS && sizeof (maxnum) > 4)
+ {
+ maxnum <<= 16;
+ maxnum |= 0xffff;
+ maxnum <<= 16;
+ maxnum |= 0xffff;
+ }
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= maxnum
+ && (HAVE_32BIT_GPRS || sizeof (maxnum) > 4))
+ goto do_true;
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ ++imm_expr.X_add_number;
+ /* FALLTHROUGH */
+ case M_BLT_I:
+ case M_BLTL_I:
+ if (mask == M_BLTL_I)
+ likely = 1;
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ {
+ macro_build (&offset_expr, likely ? "bltzl" : "bltz", "s,p", sreg);
+ return;
+ }
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+ {
+ macro_build (&offset_expr, likely ? "blezl" : "blez", "s,p", sreg);
+ return;
+ }
+ set_at (sreg, 0);
+ macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, 0);
+ break;
+
+ case M_BLEUL:
+ likely = 1;
+ case M_BLEU:
+ if (treg == 0)
+ {
+ macro_build (&offset_expr, likely ? "beql" : "beq",
+ "s,t,p", sreg, 0);
+ return;
+ }
+ if (sreg == 0)
+ goto do_true;
+ macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
+ macro_build (&offset_expr, likely ? "beql" : "beq", "s,t,p", AT, 0);
+ break;
+
+ case M_BLEUL_I:
+ likely = 1;
+ case M_BLEU_I:
+ if (sreg == 0
+ || (HAVE_32BIT_GPRS
+ && imm_expr.X_op == O_constant
+ && imm_expr.X_add_number == (offsetT) 0xffffffff))
+ goto do_true;
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ ++imm_expr.X_add_number;
+ /* FALLTHROUGH */
+ case M_BLTU_I:
+ case M_BLTUL_I:
+ if (mask == M_BLTUL_I)
+ likely = 1;
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ goto do_false;
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+ {
+ macro_build (&offset_expr, likely ? "beql" : "beq",
+ "s,t,p", sreg, 0);
+ return;
+ }
+ set_at (sreg, 1);
+ macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, 0);
+ break;
+
+ case M_BLTL:
+ likely = 1;
+ case M_BLT:
+ if (treg == 0)
+ {
+ macro_build (&offset_expr, likely ? "bltzl" : "bltz", "s,p", sreg);
+ return;
+ }
+ if (sreg == 0)
+ {
+ macro_build (&offset_expr, likely ? "bgtzl" : "bgtz", "s,p", treg);
+ return;
+ }
+ macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
+ macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, 0);
+ break;
+
+ case M_BLTUL:
+ likely = 1;
+ case M_BLTU:
+ if (treg == 0)
+ goto do_false;
+ if (sreg == 0)
+ {
+ macro_build (&offset_expr, likely ? "bnel" : "bne",
+ "s,t,p", 0, treg);
+ return;
+ }
+ macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
+ macro_build (&offset_expr, likely ? "bnel" : "bne", "s,t,p", AT, 0);
+ break;
+
+ case M_DEXT:
+ {
+ unsigned long pos;
+ unsigned long size;
+
+ if (imm_expr.X_op != O_constant || imm2_expr.X_op != O_constant)
+ {
+ as_bad (_("Unsupported large constant"));
+ pos = size = 1;
+ }
+ else
+ {
+ pos = (unsigned long) imm_expr.X_add_number;
+ size = (unsigned long) imm2_expr.X_add_number;
+ }
+
+ if (pos > 63)
+ {
+ as_bad (_("Improper position (%lu)"), pos);
+ pos = 1;
+ }
+ if (size == 0 || size > 64
+ || (pos + size - 1) > 63)
+ {
+ as_bad (_("Improper extract size (%lu, position %lu)"),
+ size, pos);
+ size = 1;
+ }
+
+ if (size <= 32 && pos < 32)
+ {
+ s = "dext";
+ fmt = "t,r,+A,+C";
+ }
+ else if (size <= 32)
+ {
+ s = "dextu";
+ fmt = "t,r,+E,+H";
+ }
+ else
+ {
+ s = "dextm";
+ fmt = "t,r,+A,+G";
+ }
+ macro_build ((expressionS *) NULL, s, fmt, treg, sreg, pos, size - 1);
+ }
+ return;
+
+ case M_DINS:
+ {
+ unsigned long pos;
+ unsigned long size;
+
+ if (imm_expr.X_op != O_constant || imm2_expr.X_op != O_constant)
+ {
+ as_bad (_("Unsupported large constant"));
+ pos = size = 1;
+ }
+ else
+ {
+ pos = (unsigned long) imm_expr.X_add_number;
+ size = (unsigned long) imm2_expr.X_add_number;
+ }
+
+ if (pos > 63)
+ {
+ as_bad (_("Improper position (%lu)"), pos);
+ pos = 1;
+ }
+ if (size == 0 || size > 64
+ || (pos + size - 1) > 63)
+ {
+ as_bad (_("Improper insert size (%lu, position %lu)"),
+ size, pos);
+ size = 1;
+ }
+
+ if (pos < 32 && (pos + size - 1) < 32)
+ {
+ s = "dins";
+ fmt = "t,r,+A,+B";
+ }
+ else if (pos >= 32)
+ {
+ s = "dinsu";
+ fmt = "t,r,+E,+F";
+ }
+ else
+ {
+ s = "dinsm";
+ fmt = "t,r,+A,+F";
+ }
+ macro_build ((expressionS *) NULL, s, fmt, treg, sreg, pos,
+ pos + size - 1);
+ }
+ return;
+
+ case M_DDIV_3:
+ dbl = 1;
+ case M_DIV_3:
+ s = "mflo";
+ goto do_div3;
+ case M_DREM_3:
+ dbl = 1;
+ case M_REM_3:
+ s = "mfhi";
+ do_div3:
+ if (treg == 0)
+ {
+ as_warn (_("Divide by zero."));
+ if (mips_trap)
+ macro_build (NULL, "teq", "s,t,q", 0, 0, 7);
+ else
+ macro_build (NULL, "break", "c", 7);
+ return;
+ }
+
+ mips_emit_delays (TRUE);
+ ++mips_opts.noreorder;
+ mips_any_noreorder = 1;
+ if (mips_trap)
+ {
+ macro_build (NULL, "teq", "s,t,q", treg, 0, 7);
+ macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
+ }
+ else
+ {
+ expr1.X_add_number = 8;
+ macro_build (&expr1, "bne", "s,t,p", treg, 0);
+ macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
+ macro_build (NULL, "break", "c", 7);
+ }
+ expr1.X_add_number = -1;
+ load_register (AT, &expr1, dbl);
+ expr1.X_add_number = mips_trap ? (dbl ? 12 : 8) : (dbl ? 20 : 16);
+ macro_build (&expr1, "bne", "s,t,p", treg, AT);
+ if (dbl)
+ {
+ expr1.X_add_number = 1;
+ load_register (AT, &expr1, dbl);
+ macro_build (NULL, "dsll32", "d,w,<", AT, AT, 31);
+ }
+ else
+ {
+ expr1.X_add_number = 0x80000000;
+ macro_build (&expr1, "lui", "t,u", AT, BFD_RELOC_HI16);
+ }
+ if (mips_trap)
+ {
+ macro_build (NULL, "teq", "s,t,q", sreg, AT, 6);
+ /* We want to close the noreorder block as soon as possible, so
+ that later insns are available for delay slot filling. */
+ --mips_opts.noreorder;
+ }
+ else
+ {
+ expr1.X_add_number = 8;
+ macro_build (&expr1, "bne", "s,t,p", sreg, AT);
+ macro_build (NULL, "nop", "", 0);
+
+ /* We want to close the noreorder block as soon as possible, so
+ that later insns are available for delay slot filling. */
+ --mips_opts.noreorder;
+
+ macro_build (NULL, "break", "c", 6);
+ }
+ macro_build (NULL, s, "d", dreg);
+ break;
+
+ case M_DIV_3I:
+ s = "div";
+ s2 = "mflo";
+ goto do_divi;
+ case M_DIVU_3I:
+ s = "divu";
+ s2 = "mflo";
+ goto do_divi;
+ case M_REM_3I:
+ s = "div";
+ s2 = "mfhi";
+ goto do_divi;
+ case M_REMU_3I:
+ s = "divu";
+ s2 = "mfhi";
+ goto do_divi;
+ case M_DDIV_3I:
+ dbl = 1;
+ s = "ddiv";
+ s2 = "mflo";
+ goto do_divi;
+ case M_DDIVU_3I:
+ dbl = 1;
+ s = "ddivu";
+ s2 = "mflo";
+ goto do_divi;
+ case M_DREM_3I:
+ dbl = 1;
+ s = "ddiv";
+ s2 = "mfhi";
+ goto do_divi;
+ case M_DREMU_3I:
+ dbl = 1;
+ s = "ddivu";
+ s2 = "mfhi";
+ do_divi:
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ {
+ as_warn (_("Divide by zero."));
+ if (mips_trap)
+ macro_build (NULL, "teq", "s,t,q", 0, 0, 7);
+ else
+ macro_build (NULL, "break", "c", 7);
+ return;
+ }
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+ {
+ if (strcmp (s2, "mflo") == 0)
+ move_register (dreg, sreg);
+ else
+ move_register (dreg, 0);
+ return;
+ }
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number == -1
+ && s[strlen (s) - 1] != 'u')
+ {
+ if (strcmp (s2, "mflo") == 0)
+ {
+ macro_build (NULL, dbl ? "dneg" : "neg", "d,w", dreg, sreg);
+ }
+ else
+ move_register (dreg, 0);
+ return;
+ }
+
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, s, "z,s,t", sreg, AT);
+ macro_build (NULL, s2, "d", dreg);
+ break;
+
+ case M_DIVU_3:
+ s = "divu";
+ s2 = "mflo";
+ goto do_divu3;
+ case M_REMU_3:
+ s = "divu";
+ s2 = "mfhi";
+ goto do_divu3;
+ case M_DDIVU_3:
+ s = "ddivu";
+ s2 = "mflo";
+ goto do_divu3;
+ case M_DREMU_3:
+ s = "ddivu";
+ s2 = "mfhi";
+ do_divu3:
+ mips_emit_delays (TRUE);
+ ++mips_opts.noreorder;
+ mips_any_noreorder = 1;
+ if (mips_trap)
+ {
+ macro_build (NULL, "teq", "s,t,q", treg, 0, 7);
+ macro_build (NULL, s, "z,s,t", sreg, treg);
+ /* We want to close the noreorder block as soon as possible, so
+ that later insns are available for delay slot filling. */
+ --mips_opts.noreorder;
+ }
+ else
+ {
+ expr1.X_add_number = 8;
+ macro_build (&expr1, "bne", "s,t,p", treg, 0);
+ macro_build (NULL, s, "z,s,t", sreg, treg);
+
+ /* We want to close the noreorder block as soon as possible, so
+ that later insns are available for delay slot filling. */
+ --mips_opts.noreorder;
+ macro_build (NULL, "break", "c", 7);
+ }
+ macro_build (NULL, s2, "d", dreg);
+ return;
+
+ case M_DLCA_AB:
+ dbl = 1;
+ case M_LCA_AB:
+ call = 1;
+ goto do_la;
+ case M_DLA_AB:
+ dbl = 1;
+ case M_LA_AB:
+ do_la:
+ /* Load the address of a symbol into a register. If breg is not
+ zero, we then add a base register to it. */
+
+ if (dbl && HAVE_32BIT_GPRS)
+ as_warn (_("dla used to load 32-bit register"));
+
+ if (! dbl && HAVE_64BIT_OBJECTS)
+ as_warn (_("la used to load 64-bit address"));
+
+ if (offset_expr.X_op == O_constant
+ && offset_expr.X_add_number >= -0x8000
+ && offset_expr.X_add_number < 0x8000)
+ {
+ macro_build (&offset_expr,
+ (dbl || HAVE_64BIT_ADDRESSES) ? "daddiu" : "addiu",
+ "t,r,j", treg, sreg, BFD_RELOC_LO16);
+ return;
+ }
+
+ if (treg == breg)
+ {
+ tempreg = AT;
+ used_at = 1;
+ }
+ else
+ {
+ tempreg = treg;
+ used_at = 0;
+ }
+
+ /* When generating embedded PIC code, we permit expressions of
+ the form
+ la $treg,foo-bar
+ la $treg,foo-bar($breg)
+ where bar is an address in the current section. These are used
+ when getting the addresses of functions. We don't permit
+ X_add_number to be non-zero, because if the symbol is
+ external the relaxing code needs to know that any addend is
+ purely the offset to X_op_symbol. */
+ if (mips_pic == EMBEDDED_PIC
+ && offset_expr.X_op == O_subtract
+ && (symbol_constant_p (offset_expr.X_op_symbol)
+ ? S_GET_SEGMENT (offset_expr.X_op_symbol) == now_seg
+ : (symbol_equated_p (offset_expr.X_op_symbol)
+ && (S_GET_SEGMENT
+ (symbol_get_value_expression (offset_expr.X_op_symbol)
+ ->X_add_symbol)
+ == now_seg)))
+ && (offset_expr.X_add_number == 0
+ || OUTPUT_FLAVOR == bfd_target_elf_flavour))
+ {
+ if (breg == 0)
+ {
+ tempreg = treg;
+ used_at = 0;
+ macro_build (&offset_expr, "lui", "t,u",
+ tempreg, BFD_RELOC_PCREL_HI16_S);
+ }
+ else
+ {
+ macro_build (&offset_expr, "lui", "t,u",
+ tempreg, BFD_RELOC_PCREL_HI16_S);
+ macro_build (NULL,
+ (dbl || HAVE_64BIT_ADDRESSES) ? "daddu" : "addu",
+ "d,v,t", tempreg, tempreg, breg);
+ }
+ macro_build (&offset_expr,
+ (dbl || HAVE_64BIT_ADDRESSES) ? "daddiu" : "addiu",
+ "t,r,j", treg, tempreg, BFD_RELOC_PCREL_LO16);
+ if (! used_at)
+ return;
+ break;
+ }
+
+ if (offset_expr.X_op != O_symbol
+ && offset_expr.X_op != O_constant)
+ {
+ as_bad (_("expression too complex"));
+ offset_expr.X_op = O_constant;
+ }
+
+ if (offset_expr.X_op == O_constant)
+ load_register (tempreg, &offset_expr,
+ ((mips_pic == EMBEDDED_PIC || mips_pic == NO_PIC)
+ ? (dbl || HAVE_64BIT_ADDRESSES)
+ : HAVE_64BIT_ADDRESSES));
+ else if (mips_pic == NO_PIC)
+ {
+ /* If this is a reference to a GP relative symbol, we want
+ addiu $tempreg,$gp,<sym> (BFD_RELOC_GPREL16)
+ Otherwise we want
+ lui $tempreg,<sym> (BFD_RELOC_HI16_S)
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+ If we have a constant, we need two instructions anyhow,
+ so we may as well always use the latter form.
+
+ With 64bit address space and a usable $at we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ lui $at,<sym> (BFD_RELOC_HI16_S)
+ daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ daddiu $at,<sym> (BFD_RELOC_LO16)
+ dsll32 $tempreg,0
+ daddu $tempreg,$tempreg,$at
+
+ If $at is already in use, we use a path which is suboptimal
+ on superscalar processors.
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ dsll $tempreg,16
+ daddiu $tempreg,<sym> (BFD_RELOC_HI16_S)
+ dsll $tempreg,16
+ daddiu $tempreg,<sym> (BFD_RELOC_LO16)
+ */
+ if (HAVE_64BIT_ADDRESSES)
+ {
+ /* ??? We don't provide a GP-relative alternative for
+ these macros. It used not to be possible with the
+ original relaxation code, but it could be done now. */
+
+ if (used_at == 0 && ! mips_opts.noat)
+ {
+ macro_build (&offset_expr, "lui", "t,u",
+ tempreg, BFD_RELOC_MIPS_HIGHEST);
+ macro_build (&offset_expr, "lui", "t,u",
+ AT, BFD_RELOC_HI16_S);
+ macro_build (&offset_expr, "daddiu", "t,r,j",
+ tempreg, tempreg, BFD_RELOC_MIPS_HIGHER);
+ macro_build (&offset_expr, "daddiu", "t,r,j",
+ AT, AT, BFD_RELOC_LO16);
+ macro_build (NULL, "dsll32", "d,w,<", tempreg, tempreg, 0);
+ macro_build (NULL, "daddu", "d,v,t", tempreg, tempreg, AT);
+ used_at = 1;
+ }
+ else
+ {
+ macro_build (&offset_expr, "lui", "t,u",
+ tempreg, BFD_RELOC_MIPS_HIGHEST);
+ macro_build (&offset_expr, "daddiu", "t,r,j",
+ tempreg, tempreg, BFD_RELOC_MIPS_HIGHER);
+ macro_build (NULL, "dsll", "d,w,<", tempreg, tempreg, 16);
+ macro_build (&offset_expr, "daddiu", "t,r,j",
+ tempreg, tempreg, BFD_RELOC_HI16_S);
+ macro_build (NULL, "dsll", "d,w,<", tempreg, tempreg, 16);
+ macro_build (&offset_expr, "daddiu", "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
+ }
+ }
+ else
+ {
+ if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+ && ! nopic_need_relax (offset_expr.X_add_symbol, 1))
+ {
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, mips_gp_register, BFD_RELOC_GPREL16);
+ relax_switch ();
+ }
+ macro_build_lui (&offset_expr, tempreg);
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
+ if (mips_relax.sequence)
+ relax_end ();
+ }
+ }
+ else if (mips_pic == SVR4_PIC && ! mips_big_got && ! HAVE_NEWABI)
+ {
+ int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
+
+ /* If this is a reference to an external symbol, and there
+ is no constant, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ or for lca or if tempreg is PIC_CALL_REG
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_CALL16)
+ For a local symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+
+ If we have a small constant, and this is a reference to
+ an external symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<constant>
+ For a local symbol, we want the same instruction
+ sequence, but we output a BFD_RELOC_LO16 reloc on the
+ addiu instruction.
+
+ If we have a large constant, and this is a reference to
+ an external symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ lui $at,<hiconstant>
+ addiu $at,$at,<loconstant>
+ addu $tempreg,$tempreg,$at
+ For a local symbol, we want the same instruction
+ sequence, but we output a BFD_RELOC_LO16 reloc on the
+ addiu instruction.
+ */
+
+ if (offset_expr.X_add_number == 0)
+ {
+ if (breg == 0 && (call || tempreg == PIC_CALL_REG))
+ lw_reloc_type = (int) BFD_RELOC_MIPS_CALL16;
+
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ lw_reloc_type, mips_gp_register);
+ if (breg != 0)
+ {
+ /* We're going to put in an addu instruction using
+ tempreg, so we may as well insert the nop right
+ now. */
+ macro_build (NULL, "nop", "");
+ }
+ relax_switch ();
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ tempreg, BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ macro_build (NULL, "nop", "");
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
+ relax_end ();
+ /* FIXME: If breg == 0, and the next instruction uses
+ $tempreg, then if this variant case is used an extra
+ nop will be generated. */
+ }
+ else if (offset_expr.X_add_number >= -0x8000
+ && offset_expr.X_add_number < 0x8000)
+ {
+ load_got_offset (tempreg, &offset_expr);
+ macro_build (NULL, "nop", "");
+ add_got_offset (tempreg, &offset_expr);
+ }
+ else
+ {
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number =
+ ((offset_expr.X_add_number + 0x8000) & 0xffff) - 0x8000;
+ load_got_offset (tempreg, &offset_expr);
+ offset_expr.X_add_number = expr1.X_add_number;
+ /* If we are going to add in a base register, and the
+ target register and the base register are the same,
+ then we are using AT as a temporary register. Since
+ we want to load the constant into AT, we add our
+ current AT (from the global offset table) and the
+ register into the register now, and pretend we were
+ not using a base register. */
+ if (breg == treg)
+ {
+ macro_build (NULL, "nop", "");
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ treg, AT, breg);
+ breg = 0;
+ tempreg = treg;
+ }
+ add_got_offset_hilo (tempreg, &offset_expr, AT);
+ used_at = 1;
+ }
+ }
+ else if (mips_pic == SVR4_PIC && ! mips_big_got && HAVE_NEWABI)
+ {
+ int add_breg_early = 0;
+
+ /* If this is a reference to an external, and there is no
+ constant, or local symbol (*), with or without a
+ constant, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_DISP)
+ or for lca or if tempreg is PIC_CALL_REG
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_CALL16)
+
+ If we have a small constant, and this is a reference to
+ an external symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_DISP)
+ addiu $tempreg,$tempreg,<constant>
+
+ If we have a large constant, and this is a reference to
+ an external symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_DISP)
+ lui $at,<hiconstant>
+ addiu $at,$at,<loconstant>
+ addu $tempreg,$tempreg,$at
+
+ (*) Other assemblers seem to prefer GOT_PAGE/GOT_OFST for
+ local symbols, even though it introduces an additional
+ instruction. */
+
+ if (offset_expr.X_add_number)
+ {
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+
+ if (expr1.X_add_number >= -0x8000
+ && expr1.X_add_number < 0x8000)
+ {
+ macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
+ }
+ else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
+ {
+ int dreg;
+
+ /* If we are going to add in a base register, and the
+ target register and the base register are the same,
+ then we are using AT as a temporary register. Since
+ we want to load the constant into AT, we add our
+ current AT (from the global offset table) and the
+ register into the register now, and pretend we were
+ not using a base register. */
+ if (breg != treg)
+ dreg = tempreg;
+ else
+ {
+ assert (tempreg == AT);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ treg, AT, breg);
+ dreg = treg;
+ add_breg_early = 1;
+ }
+
+ load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ dreg, dreg, AT);
+
+ used_at = 1;
+ }
+ else
+ as_bad (_("PIC code offset overflow (max 32 signed bits)"));
+
+ relax_switch ();
+ offset_expr.X_add_number = expr1.X_add_number;
+
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+ if (add_breg_early)
+ {
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ treg, tempreg, breg);
+ breg = 0;
+ tempreg = treg;
+ }
+ relax_end ();
+ }
+ else if (breg == 0 && (call || tempreg == PIC_CALL_REG))
+ {
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_CALL16, mips_gp_register);
+ relax_switch ();
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+ relax_end ();
+ }
+ else
+ {
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+ }
+ }
+ else if (mips_pic == SVR4_PIC && ! HAVE_NEWABI)
+ {
+ int gpdelay;
+ int lui_reloc_type = (int) BFD_RELOC_MIPS_GOT_HI16;
+ int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT_LO16;
+ int local_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
+
+ /* This is the large GOT case. If this is a reference to an
+ external symbol, and there is no constant, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ or for lca or if tempreg is PIC_CALL_REG
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_CALL_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_CALL_LO16)
+ For a local symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+
+ If we have a small constant, and this is a reference to
+ an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ nop
+ addiu $tempreg,$tempreg,<constant>
+ For a local symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<constant> (BFD_RELOC_LO16)
+
+ If we have a large constant, and this is a reference to
+ an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ lui $at,<hiconstant>
+ addiu $at,$at,<loconstant>
+ addu $tempreg,$tempreg,$at
+ For a local symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ lui $at,<hiconstant>
+ addiu $at,$at,<loconstant> (BFD_RELOC_LO16)
+ addu $tempreg,$tempreg,$at
+ */
+
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ relax_start (offset_expr.X_add_symbol);
+ gpdelay = reg_needs_delay (mips_gp_register);
+ if (expr1.X_add_number == 0 && breg == 0
+ && (call || tempreg == PIC_CALL_REG))
+ {
+ lui_reloc_type = (int) BFD_RELOC_MIPS_CALL_HI16;
+ lw_reloc_type = (int) BFD_RELOC_MIPS_CALL_LO16;
+ }
+ macro_build (&offset_expr, "lui", "t,u", tempreg, lui_reloc_type);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ tempreg, lw_reloc_type, tempreg);
+ if (expr1.X_add_number == 0)
+ {
+ if (breg != 0)
+ {
+ /* We're going to put in an addu instruction using
+ tempreg, so we may as well insert the nop right
+ now. */
+ macro_build (NULL, "nop", "");
+ }
+ }
+ else if (expr1.X_add_number >= -0x8000
+ && expr1.X_add_number < 0x8000)
+ {
+ macro_build (NULL, "nop", "");
+ macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
+ }
+ else
+ {
+ int dreg;
+
+ /* If we are going to add in a base register, and the
+ target register and the base register are the same,
+ then we are using AT as a temporary register. Since
+ we want to load the constant into AT, we add our
+ current AT (from the global offset table) and the
+ register into the register now, and pretend we were
+ not using a base register. */
+ if (breg != treg)
+ dreg = tempreg;
+ else
+ {
+ assert (tempreg == AT);
+ macro_build (NULL, "nop", "");
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ treg, AT, breg);
+ dreg = treg;
+ }
+
+ load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
+
+ used_at = 1;
+ }
+ offset_expr.X_add_number =
+ ((expr1.X_add_number + 0x8000) & 0xffff) - 0x8000;
+ relax_switch ();
+
+ if (gpdelay)
+ {
+ /* This is needed because this instruction uses $gp, but
+ the first instruction on the main stream does not. */
+ macro_build (NULL, "nop", "");
+ }
+
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ local_reloc_type, mips_gp_register);
+ if (expr1.X_add_number >= -0x8000
+ && expr1.X_add_number < 0x8000)
+ {
+ macro_build (NULL, "nop", "");
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
+ /* FIXME: If add_number is 0, and there was no base
+ register, the external symbol case ended with a load,
+ so if the symbol turns out to not be external, and
+ the next instruction uses tempreg, an unnecessary nop
+ will be inserted. */
+ }
+ else
+ {
+ if (breg == treg)
+ {
+ /* We must add in the base register now, as in the
+ external symbol case. */
+ assert (tempreg == AT);
+ macro_build (NULL, "nop", "");
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ treg, AT, breg);
+ tempreg = treg;
+ /* We set breg to 0 because we have arranged to add
+ it in in both cases. */
+ breg = 0;
+ }
+
+ macro_build_lui (&expr1, AT);
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ AT, AT, BFD_RELOC_LO16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, AT);
+ }
+ relax_end ();
+ }
+ else if (mips_pic == SVR4_PIC && HAVE_NEWABI)
+ {
+ int lui_reloc_type = (int) BFD_RELOC_MIPS_GOT_HI16;
+ int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT_LO16;
+ int add_breg_early = 0;
+
+ /* This is the large GOT case. If this is a reference to an
+ external symbol, and there is no constant, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ add $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ or for lca or if tempreg is PIC_CALL_REG
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_CALL_HI16)
+ add $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_CALL_LO16)
+
+ If we have a small constant, and this is a reference to
+ an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ add $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ addi $tempreg,$tempreg,<constant>
+
+ If we have a large constant, and this is a reference to
+ an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ lui $at,<hiconstant>
+ addi $at,$at,<loconstant>
+ add $tempreg,$tempreg,$at
+
+ If we have NewABI, and we know it's a local symbol, we want
+ lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT_PAGE)
+ addiu $reg,$reg,<sym> (BFD_RELOC_MIPS_GOT_OFST)
+ otherwise we have to resort to GOT_HI16/GOT_LO16. */
+
+ relax_start (offset_expr.X_add_symbol);
+
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+
+ if (expr1.X_add_number == 0 && breg == 0
+ && (call || tempreg == PIC_CALL_REG))
+ {
+ lui_reloc_type = (int) BFD_RELOC_MIPS_CALL_HI16;
+ lw_reloc_type = (int) BFD_RELOC_MIPS_CALL_LO16;
+ }
+ macro_build (&offset_expr, "lui", "t,u", tempreg, lui_reloc_type);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ tempreg, lw_reloc_type, tempreg);
+
+ if (expr1.X_add_number == 0)
+ ;
+ else if (expr1.X_add_number >= -0x8000
+ && expr1.X_add_number < 0x8000)
+ {
+ macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
+ }
+ else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
+ {
+ int dreg;
+
+ /* If we are going to add in a base register, and the
+ target register and the base register are the same,
+ then we are using AT as a temporary register. Since
+ we want to load the constant into AT, we add our
+ current AT (from the global offset table) and the
+ register into the register now, and pretend we were
+ not using a base register. */
+ if (breg != treg)
+ dreg = tempreg;
+ else
+ {
+ assert (tempreg == AT);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ treg, AT, breg);
+ dreg = treg;
+ add_breg_early = 1;
+ }
+
+ load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
+
+ used_at = 1;
+ }
+ else
+ as_bad (_("PIC code offset overflow (max 32 signed bits)"));
+
+ relax_switch ();
+ offset_expr.X_add_number = expr1.X_add_number;
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
+ tempreg, BFD_RELOC_MIPS_GOT_OFST);
+ if (add_breg_early)
+ {
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ treg, tempreg, breg);
+ breg = 0;
+ tempreg = treg;
+ }
+ relax_end ();
+ }
+ else if (mips_pic == EMBEDDED_PIC)
+ {
+ /* We use
+ addiu $tempreg,$gp,<sym> (BFD_RELOC_GPREL16)
+ */
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
+ mips_gp_register, BFD_RELOC_GPREL16);
+ }
+ else
+ abort ();
+
+ if (breg != 0)
+ {
+ char *s;
+
+ if (mips_pic == EMBEDDED_PIC || mips_pic == NO_PIC)
+ s = (dbl || HAVE_64BIT_ADDRESSES) ? "daddu" : "addu";
+ else
+ s = ADDRESS_ADD_INSN;
+
+ macro_build (NULL, s, "d,v,t", treg, tempreg, breg);
+ }
+
+ if (! used_at)
+ return;
+
+ break;
+
+ case M_J_A:
+ /* The j instruction may not be used in PIC code, since it
+ requires an absolute address. We convert it to a b
+ instruction. */
+ if (mips_pic == NO_PIC)
+ macro_build (&offset_expr, "j", "a");
+ else
+ macro_build (&offset_expr, "b", "p");
+ return;
+
+ /* The jal instructions must be handled as macros because when
+ generating PIC code they expand to multi-instruction
+ sequences. Normally they are simple instructions. */
+ case M_JAL_1:
+ dreg = RA;
+ /* Fall through. */
+ case M_JAL_2:
+ if (mips_pic == NO_PIC
+ || mips_pic == EMBEDDED_PIC)
+ macro_build (NULL, "jalr", "d,s", dreg, sreg);
+ else if (mips_pic == SVR4_PIC)
+ {
+ if (sreg != PIC_CALL_REG)
+ as_warn (_("MIPS PIC call to register other than $25"));
+
+ macro_build (NULL, "jalr", "d,s", dreg, sreg);
+ if (! HAVE_NEWABI)
+ {
+ if (mips_cprestore_offset < 0)
+ as_warn (_("No .cprestore pseudo-op used in PIC code"));
+ else
+ {
+ if (! mips_frame_reg_valid)
+ {
+ as_warn (_("No .frame pseudo-op used in PIC code"));
+ /* Quiet this warning. */
+ mips_frame_reg_valid = 1;
+ }
+ if (! mips_cprestore_valid)
+ {
+ as_warn (_("No .cprestore pseudo-op used in PIC code"));
+ /* Quiet this warning. */
+ mips_cprestore_valid = 1;
+ }
+ expr1.X_add_number = mips_cprestore_offset;
+ macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
+ mips_gp_register,
+ mips_frame_reg,
+ HAVE_64BIT_ADDRESSES);
+ }
+ }
+ }
+ else
+ abort ();
+
+ return;
+
+ case M_JAL_A:
+ if (mips_pic == NO_PIC)
+ macro_build (&offset_expr, "jal", "a");
+ else if (mips_pic == SVR4_PIC)
+ {
+ /* If this is a reference to an external symbol, and we are
+ using a small GOT, we want
+ lw $25,<sym>($gp) (BFD_RELOC_MIPS_CALL16)
+ nop
+ jalr $ra,$25
+ nop
+ lw $gp,cprestore($sp)
+ The cprestore value is set using the .cprestore
+ pseudo-op. If we are using a big GOT, we want
+ lui $25,<sym> (BFD_RELOC_MIPS_CALL_HI16)
+ addu $25,$25,$gp
+ lw $25,<sym>($25) (BFD_RELOC_MIPS_CALL_LO16)
+ nop
+ jalr $ra,$25
+ nop
+ lw $gp,cprestore($sp)
+ If the symbol is not external, we want
+ lw $25,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $25,$25,<sym> (BFD_RELOC_LO16)
+ jalr $ra,$25
+ nop
+ lw $gp,cprestore($sp)
+
+ For NewABI, we use the same CALL16 or CALL_HI16/CALL_LO16
+ sequences above, minus nops, unless the symbol is local,
+ which enables us to use GOT_PAGE/GOT_OFST (big got) or
+ GOT_DISP. */
+ if (HAVE_NEWABI)
+ {
+ if (! mips_big_got)
+ {
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ PIC_CALL_REG, BFD_RELOC_MIPS_CALL16,
+ mips_gp_register);
+ relax_switch ();
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ PIC_CALL_REG, BFD_RELOC_MIPS_GOT_DISP,
+ mips_gp_register);
+ relax_end ();
+ }
+ else
+ {
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, "lui", "t,u", PIC_CALL_REG,
+ BFD_RELOC_MIPS_CALL_HI16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG,
+ PIC_CALL_REG, mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ PIC_CALL_REG, BFD_RELOC_MIPS_CALL_LO16,
+ PIC_CALL_REG);
+ relax_switch ();
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ PIC_CALL_REG, BFD_RELOC_MIPS_GOT_PAGE,
+ mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ PIC_CALL_REG, PIC_CALL_REG,
+ BFD_RELOC_MIPS_GOT_OFST);
+ relax_end ();
+ }
+
+ macro_build_jalr (&offset_expr);
+ }
+ else
+ {
+ relax_start (offset_expr.X_add_symbol);
+ if (! mips_big_got)
+ {
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ PIC_CALL_REG, BFD_RELOC_MIPS_CALL16,
+ mips_gp_register);
+ macro_build (NULL, "nop", "");
+ relax_switch ();
+ }
+ else
+ {
+ int gpdelay;
+
+ gpdelay = reg_needs_delay (mips_gp_register);
+ macro_build (&offset_expr, "lui", "t,u", PIC_CALL_REG,
+ BFD_RELOC_MIPS_CALL_HI16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG,
+ PIC_CALL_REG, mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ PIC_CALL_REG, BFD_RELOC_MIPS_CALL_LO16,
+ PIC_CALL_REG);
+ macro_build (NULL, "nop", "");
+ relax_switch ();
+ if (gpdelay)
+ macro_build (NULL, "nop", "");
+ }
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ PIC_CALL_REG, BFD_RELOC_MIPS_GOT16,
+ mips_gp_register);
+ macro_build (NULL, "nop", "");
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ PIC_CALL_REG, PIC_CALL_REG, BFD_RELOC_LO16);
+ relax_end ();
+ macro_build_jalr (&offset_expr);
+
+ if (mips_cprestore_offset < 0)
+ as_warn (_("No .cprestore pseudo-op used in PIC code"));
+ else
+ {
+ if (! mips_frame_reg_valid)
+ {
+ as_warn (_("No .frame pseudo-op used in PIC code"));
+ /* Quiet this warning. */
+ mips_frame_reg_valid = 1;
+ }
+ if (! mips_cprestore_valid)
+ {
+ as_warn (_("No .cprestore pseudo-op used in PIC code"));
+ /* Quiet this warning. */
+ mips_cprestore_valid = 1;
+ }
+ if (mips_opts.noreorder)
+ macro_build (NULL, "nop", "");
+ expr1.X_add_number = mips_cprestore_offset;
+ macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
+ mips_gp_register,
+ mips_frame_reg,
+ HAVE_64BIT_ADDRESSES);
+ }
+ }
+ }
+ else if (mips_pic == EMBEDDED_PIC)
+ {
+ macro_build (&offset_expr, "bal", "p");
+ /* The linker may expand the call to a longer sequence which
+ uses $at, so we must break rather than return. */
+ break;
+ }
+ else
+ abort ();
+
+ return;
+
+ case M_LB_AB:
+ s = "lb";
+ goto ld;
+ case M_LBU_AB:
+ s = "lbu";
+ goto ld;
+ case M_LH_AB:
+ s = "lh";
+ goto ld;
+ case M_LHU_AB:
+ s = "lhu";
+ goto ld;
+ case M_LW_AB:
+ s = "lw";
+ goto ld;
+ case M_LWC0_AB:
+ s = "lwc0";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld;
+ case M_LWC1_AB:
+ s = "lwc1";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld;
+ case M_LWC2_AB:
+ s = "lwc2";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld;
+ case M_LWC3_AB:
+ s = "lwc3";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld;
+ case M_LWL_AB:
+ s = "lwl";
+ lr = 1;
+ goto ld;
+ case M_LWR_AB:
+ s = "lwr";
+ lr = 1;
+ goto ld;
+ case M_LDC1_AB:
+ if (mips_opts.arch == CPU_R4650)
+ {
+ as_bad (_("opcode not supported on this processor"));
+ return;
+ }
+ s = "ldc1";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld;
+ case M_LDC2_AB:
+ s = "ldc2";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld;
+ case M_LDC3_AB:
+ s = "ldc3";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld;
+ case M_LDL_AB:
+ s = "ldl";
+ lr = 1;
+ goto ld;
+ case M_LDR_AB:
+ s = "ldr";
+ lr = 1;
+ goto ld;
+ case M_LL_AB:
+ s = "ll";
+ goto ld;
+ case M_LLD_AB:
+ s = "lld";
+ goto ld;
+ case M_LWU_AB:
+ s = "lwu";
+ ld:
+ if (breg == treg || coproc || lr)
+ {
+ tempreg = AT;
+ used_at = 1;
+ }
+ else
+ {
+ tempreg = treg;
+ used_at = 0;
+ }
+ goto ld_st;
+ case M_SB_AB:
+ s = "sb";
+ goto st;
+ case M_SH_AB:
+ s = "sh";
+ goto st;
+ case M_SW_AB:
+ s = "sw";
+ goto st;
+ case M_SWC0_AB:
+ s = "swc0";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto st;
+ case M_SWC1_AB:
+ s = "swc1";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto st;
+ case M_SWC2_AB:
+ s = "swc2";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto st;
+ case M_SWC3_AB:
+ s = "swc3";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto st;
+ case M_SWL_AB:
+ s = "swl";
+ goto st;
+ case M_SWR_AB:
+ s = "swr";
+ goto st;
+ case M_SC_AB:
+ s = "sc";
+ goto st;
+ case M_SCD_AB:
+ s = "scd";
+ goto st;
+ case M_SDC1_AB:
+ if (mips_opts.arch == CPU_R4650)
+ {
+ as_bad (_("opcode not supported on this processor"));
+ return;
+ }
+ s = "sdc1";
+ coproc = 1;
+ /* Itbl support may require additional care here. */
+ goto st;
+ case M_SDC2_AB:
+ s = "sdc2";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto st;
+ case M_SDC3_AB:
+ s = "sdc3";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto st;
+ case M_SDL_AB:
+ s = "sdl";
+ goto st;
+ case M_SDR_AB:
+ s = "sdr";
+ st:
+ tempreg = AT;
+ used_at = 1;
+ ld_st:
+ /* Itbl support may require additional care here. */
+ if (mask == M_LWC1_AB
+ || mask == M_SWC1_AB
+ || mask == M_LDC1_AB
+ || mask == M_SDC1_AB
+ || mask == M_L_DAB
+ || mask == M_S_DAB)
+ fmt = "T,o(b)";
+ else if (coproc)
+ fmt = "E,o(b)";
+ else
+ fmt = "t,o(b)";
+
+ /* Sign-extending 32-bit constants makes their handling easier.
+ The HAVE_64BIT_GPRS... part is due to the linux kernel hack
+ described below. */
+ if ((! HAVE_64BIT_ADDRESSES
+ && (! HAVE_64BIT_GPRS && offset_expr.X_op == O_constant))
+ && (offset_expr.X_op == O_constant)
+ && ! ((offset_expr.X_add_number & ~((bfd_vma) 0x7fffffff))
+ == ~((bfd_vma) 0x7fffffff)))
+ {
+ if (offset_expr.X_add_number & ~((bfd_vma) 0xffffffff))
+ as_bad (_("constant too large"));
+
+ offset_expr.X_add_number = (((offset_expr.X_add_number & 0xffffffff)
+ ^ 0x80000000) - 0x80000000);
+ }
+
+ /* For embedded PIC, we allow loads where the offset is calculated
+ by subtracting a symbol in the current segment from an unknown
+ symbol, relative to a base register, e.g.:
+ <op> $treg, <sym>-<localsym>($breg)
+ This is used by the compiler for switch statements. */
+ if (mips_pic == EMBEDDED_PIC
+ && offset_expr.X_op == O_subtract
+ && (symbol_constant_p (offset_expr.X_op_symbol)
+ ? S_GET_SEGMENT (offset_expr.X_op_symbol) == now_seg
+ : (symbol_equated_p (offset_expr.X_op_symbol)
+ && (S_GET_SEGMENT
+ (symbol_get_value_expression (offset_expr.X_op_symbol)
+ ->X_add_symbol)
+ == now_seg)))
+ && breg != 0
+ && (offset_expr.X_add_number == 0
+ || OUTPUT_FLAVOR == bfd_target_elf_flavour))
+ {
+ /* For this case, we output the instructions:
+ lui $tempreg,<sym> (BFD_RELOC_PCREL_HI16_S)
+ addiu $tempreg,$tempreg,$breg
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_PCREL_LO16)
+ If the relocation would fit entirely in 16 bits, it would be
+ nice to emit:
+ <op> $treg,<sym>($breg) (BFD_RELOC_PCREL_LO16)
+ instead, but that seems quite difficult. */
+ macro_build (&offset_expr, "lui", "t,u", tempreg,
+ BFD_RELOC_PCREL_HI16_S);
+ macro_build (NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || ! ISA_HAS_64BIT_REGS (mips_opts.isa))
+ ? "addu" : "daddu"),
+ "d,v,t", tempreg, tempreg, breg);
+ macro_build (&offset_expr, s, fmt, treg,
+ BFD_RELOC_PCREL_LO16, tempreg);
+ if (! used_at)
+ return;
+ break;
+ }
+
+ if (offset_expr.X_op != O_constant
+ && offset_expr.X_op != O_symbol)
+ {
+ as_bad (_("expression too complex"));
+ offset_expr.X_op = O_constant;
+ }
+
+ /* A constant expression in PIC code can be handled just as it
+ is in non PIC code. */
+ if (mips_pic == NO_PIC
+ || offset_expr.X_op == O_constant)
+ {
+ /* If this is a reference to a GP relative symbol, and there
+ is no base register, we want
+ <op> $treg,<sym>($gp) (BFD_RELOC_GPREL16)
+ Otherwise, if there is no base register, we want
+ lui $tempreg,<sym> (BFD_RELOC_HI16_S)
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16)
+ If we have a constant, we need two instructions anyhow,
+ so we always use the latter form.
+
+ If we have a base register, and this is a reference to a
+ GP relative symbol, we want
+ addu $tempreg,$breg,$gp
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_GPREL16)
+ Otherwise we want
+ lui $tempreg,<sym> (BFD_RELOC_HI16_S)
+ addu $tempreg,$tempreg,$breg
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16)
+ With a constant we always use the latter case.
+
+ With 64bit address space and no base register and $at usable,
+ we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ lui $at,<sym> (BFD_RELOC_HI16_S)
+ daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ dsll32 $tempreg,0
+ daddu $tempreg,$at
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16)
+ If we have a base register, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ lui $at,<sym> (BFD_RELOC_HI16_S)
+ daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ daddu $at,$breg
+ dsll32 $tempreg,0
+ daddu $tempreg,$at
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16)
+
+ Without $at we can't generate the optimal path for superscalar
+ processors here since this would require two temporary registers.
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ dsll $tempreg,16
+ daddiu $tempreg,<sym> (BFD_RELOC_HI16_S)
+ dsll $tempreg,16
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16)
+ If we have a base register, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ dsll $tempreg,16
+ daddiu $tempreg,<sym> (BFD_RELOC_HI16_S)
+ dsll $tempreg,16
+ daddu $tempreg,$tempreg,$breg
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16)
+
+ If we have 64-bit addresses, as an optimization, for
+ addresses which are 32-bit constants (e.g. kseg0/kseg1
+ addresses) we fall back to the 32-bit address generation
+ mechanism since it is more efficient. Note that due to
+ the signed offset used by memory operations, the 32-bit
+ range is shifted down by 32768 here. This code should
+ probably attempt to generate 64-bit constants more
+ efficiently in general.
+
+ As an extension for architectures with 64-bit registers,
+ we don't truncate 64-bit addresses given as literal
+ constants down to 32 bits, to support existing practice
+ in the mips64 Linux (the kernel), that compiles source
+ files with -mabi=64, assembling them as o32 or n32 (with
+ -Wa,-32 or -Wa,-n32). This is not beautiful, but since
+ the whole kernel is loaded into a memory region that is
+ addressable with sign-extended 32-bit addresses, it is
+ wasteful to compute the upper 32 bits of every
+ non-literal address, that takes more space and time.
+ Some day this should probably be implemented as an
+ assembler option, such that the kernel doesn't have to
+ use such ugly hacks, even though it will still have to
+ end up converting the binary to ELF32 for a number of
+ platforms whose boot loaders don't support ELF64
+ binaries. */
+ if ((HAVE_64BIT_ADDRESSES
+ && ! (offset_expr.X_op == O_constant
+ && IS_SEXT_32BIT_NUM (offset_expr.X_add_number + 0x8000)))
+ || (HAVE_64BIT_GPRS
+ && offset_expr.X_op == O_constant
+ && ! IS_SEXT_32BIT_NUM (offset_expr.X_add_number + 0x8000)))
+ {
+ /* ??? We don't provide a GP-relative alternative for
+ these macros. It used not to be possible with the
+ original relaxation code, but it could be done now. */
+
+ if (used_at == 0 && ! mips_opts.noat)
+ {
+ macro_build (&offset_expr, "lui", "t,u", tempreg,
+ BFD_RELOC_MIPS_HIGHEST);
+ macro_build (&offset_expr, "lui", "t,u", AT,
+ BFD_RELOC_HI16_S);
+ macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
+ tempreg, BFD_RELOC_MIPS_HIGHER);
+ if (breg != 0)
+ macro_build (NULL, "daddu", "d,v,t", AT, AT, breg);
+ macro_build (NULL, "dsll32", "d,w,<", tempreg, tempreg, 0);
+ macro_build (NULL, "daddu", "d,v,t", tempreg, tempreg, AT);
+ macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16,
+ tempreg);
+ used_at = 1;
+ }
+ else
+ {
+ macro_build (&offset_expr, "lui", "t,u", tempreg,
+ BFD_RELOC_MIPS_HIGHEST);
+ macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
+ tempreg, BFD_RELOC_MIPS_HIGHER);
+ macro_build (NULL, "dsll", "d,w,<", tempreg, tempreg, 16);
+ macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
+ tempreg, BFD_RELOC_HI16_S);
+ macro_build (NULL, "dsll", "d,w,<", tempreg, tempreg, 16);
+ if (breg != 0)
+ macro_build (NULL, "daddu", "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&offset_expr, s, fmt, treg,
+ BFD_RELOC_LO16, tempreg);
+ }
+
+ return;
+ }
+
+ if (offset_expr.X_op == O_constant
+ && ! IS_SEXT_32BIT_NUM (offset_expr.X_add_number + 0x8000))
+ as_bad (_("load/store address overflow (max 32 bits)"));
+
+ if (breg == 0)
+ {
+ if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+ && ! nopic_need_relax (offset_expr.X_add_symbol, 1))
+ {
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_GPREL16,
+ mips_gp_register);
+ relax_switch ();
+ used_at = 0;
+ }
+ macro_build_lui (&offset_expr, tempreg);
+ macro_build (&offset_expr, s, fmt, treg,
+ BFD_RELOC_LO16, tempreg);
+ if (mips_relax.sequence)
+ relax_end ();
+ }
+ else
+ {
+ if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+ && ! nopic_need_relax (offset_expr.X_add_symbol, 1))
+ {
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, breg, mips_gp_register);
+ macro_build (&offset_expr, s, fmt, treg,
+ BFD_RELOC_GPREL16, tempreg);
+ relax_switch ();
+ }
+ macro_build_lui (&offset_expr, tempreg);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&offset_expr, s, fmt, treg,
+ BFD_RELOC_LO16, tempreg);
+ if (mips_relax.sequence)
+ relax_end ();
+ }
+ }
+ else if (mips_pic == SVR4_PIC && ! mips_big_got)
+ {
+ int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
+
+ /* If this is a reference to an external symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ <op> $treg,0($tempreg)
+ Otherwise we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+ <op> $treg,0($tempreg)
+
+ For NewABI, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_PAGE)
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_OFST)
+
+ If there is a base register, we add it to $tempreg before
+ the <op>. If there is a constant, we stick it in the
+ <op> instruction. We don't handle constants larger than
+ 16 bits, because we have no way to load the upper 16 bits
+ (actually, we could handle them for the subset of cases
+ in which we are not using $at). */
+ assert (offset_expr.X_op == O_symbol);
+ if (HAVE_NEWABI)
+ {
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&offset_expr, s, fmt, treg,
+ BFD_RELOC_MIPS_GOT_OFST, tempreg);
+
+ if (! used_at)
+ return;
+
+ break;
+ }
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ if (expr1.X_add_number < -0x8000
+ || expr1.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ lw_reloc_type, mips_gp_register);
+ macro_build (NULL, "nop", "");
+ relax_start (offset_expr.X_add_symbol);
+ relax_switch ();
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
+ tempreg, BFD_RELOC_LO16);
+ relax_end ();
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&expr1, s, fmt, treg, BFD_RELOC_LO16, tempreg);
+ }
+ else if (mips_pic == SVR4_PIC && ! HAVE_NEWABI)
+ {
+ int gpdelay;
+
+ /* If this is a reference to an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ <op> $treg,0($tempreg)
+ Otherwise we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+ <op> $treg,0($tempreg)
+ If there is a base register, we add it to $tempreg before
+ the <op>. If there is a constant, we stick it in the
+ <op> instruction. We don't handle constants larger than
+ 16 bits, because we have no way to load the upper 16 bits
+ (actually, we could handle them for the subset of cases
+ in which we are not using $at). */
+ assert (offset_expr.X_op == O_symbol);
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ if (expr1.X_add_number < -0x8000
+ || expr1.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ gpdelay = reg_needs_delay (mips_gp_register);
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, "lui", "t,u", tempreg,
+ BFD_RELOC_MIPS_GOT_HI16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg,
+ mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_LO16, tempreg);
+ relax_switch ();
+ if (gpdelay)
+ macro_build (NULL, "nop", "");
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ macro_build (NULL, "nop", "");
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
+ tempreg, BFD_RELOC_LO16);
+ relax_end ();
+
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&expr1, s, fmt, treg, BFD_RELOC_LO16, tempreg);
+ }
+ else if (mips_pic == SVR4_PIC && HAVE_NEWABI)
+ {
+ /* If this is a reference to an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ add $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ <op> $treg,<ofst>($tempreg)
+ Otherwise, for local symbols, we want:
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_PAGE)
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_OFST) */
+ assert (offset_expr.X_op == O_symbol);
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ if (expr1.X_add_number < -0x8000
+ || expr1.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, "lui", "t,u", tempreg,
+ BFD_RELOC_MIPS_GOT_HI16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg,
+ mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_LO16, tempreg);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&expr1, s, fmt, treg, BFD_RELOC_LO16, tempreg);
+
+ relax_switch ();
+ offset_expr.X_add_number = expr1.X_add_number;
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&offset_expr, s, fmt, treg,
+ BFD_RELOC_MIPS_GOT_OFST, tempreg);
+ relax_end ();
+ }
+ else if (mips_pic == EMBEDDED_PIC)
+ {
+ /* If there is no base register, we want
+ <op> $treg,<sym>($gp) (BFD_RELOC_GPREL16)
+ If there is a base register, we want
+ addu $tempreg,$breg,$gp
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_GPREL16)
+ */
+ assert (offset_expr.X_op == O_symbol);
+ if (breg == 0)
+ {
+ macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_GPREL16,
+ mips_gp_register);
+ used_at = 0;
+ }
+ else
+ {
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, breg, mips_gp_register);
+ macro_build (&offset_expr, s, fmt, treg,
+ BFD_RELOC_GPREL16, tempreg);
+ }
+ }
+ else
+ abort ();
+
+ if (! used_at)
+ return;
+
+ break;
+
+ case M_LI:
+ case M_LI_S:
+ load_register (treg, &imm_expr, 0);
+ return;
+
+ case M_DLI:
+ load_register (treg, &imm_expr, 1);
+ return;
+
+ case M_LI_SS:
+ if (imm_expr.X_op == O_constant)
+ {
+ load_register (AT, &imm_expr, 0);
+ macro_build (NULL, "mtc1", "t,G", AT, treg);
+ break;
+ }
+ else
+ {
+ assert (offset_expr.X_op == O_symbol
+ && strcmp (segment_name (S_GET_SEGMENT
+ (offset_expr.X_add_symbol)),
+ ".lit4") == 0
+ && offset_expr.X_add_number == 0);
+ macro_build (&offset_expr, "lwc1", "T,o(b)", treg,
+ BFD_RELOC_MIPS_LITERAL, mips_gp_register);
+ return;
+ }
+
+ case M_LI_D:
+ /* Check if we have a constant in IMM_EXPR. If the GPRs are 64 bits
+ wide, IMM_EXPR is the entire value. Otherwise IMM_EXPR is the high
+ order 32 bits of the value and the low order 32 bits are either
+ zero or in OFFSET_EXPR. */
+ if (imm_expr.X_op == O_constant || imm_expr.X_op == O_big)
+ {
+ if (HAVE_64BIT_GPRS)
+ load_register (treg, &imm_expr, 1);
+ else
+ {
+ int hreg, lreg;
+
+ if (target_big_endian)
+ {
+ hreg = treg;
+ lreg = treg + 1;
+ }
+ else
+ {
+ hreg = treg + 1;
+ lreg = treg;
+ }
+
+ if (hreg <= 31)
+ load_register (hreg, &imm_expr, 0);
+ if (lreg <= 31)
+ {
+ if (offset_expr.X_op == O_absent)
+ move_register (lreg, 0);
+ else
+ {
+ assert (offset_expr.X_op == O_constant);
+ load_register (lreg, &offset_expr, 0);
+ }
+ }
+ }
+ return;
+ }
+
+ /* We know that sym is in the .rdata section. First we get the
+ upper 16 bits of the address. */
+ if (mips_pic == NO_PIC)
+ {
+ macro_build_lui (&offset_expr, AT);
+ }
+ else if (mips_pic == SVR4_PIC)
+ {
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ }
+ else if (mips_pic == EMBEDDED_PIC)
+ {
+ /* For embedded PIC we pick up the entire address off $gp in
+ a single instruction. */
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", AT,
+ mips_gp_register, BFD_RELOC_GPREL16);
+ offset_expr.X_op = O_constant;
+ offset_expr.X_add_number = 0;
+ }
+ else
+ abort ();
+
+ /* Now we load the register(s). */
+ if (HAVE_64BIT_GPRS)
+ macro_build (&offset_expr, "ld", "t,o(b)", treg, BFD_RELOC_LO16, AT);
+ else
+ {
+ macro_build (&offset_expr, "lw", "t,o(b)", treg, BFD_RELOC_LO16, AT);
+ if (treg != RA)
+ {
+ /* FIXME: How in the world do we deal with the possible
+ overflow here? */
+ offset_expr.X_add_number += 4;
+ macro_build (&offset_expr, "lw", "t,o(b)",
+ treg + 1, BFD_RELOC_LO16, AT);
+ }
+ }
+ break;
+
+ case M_LI_DD:
+ /* Check if we have a constant in IMM_EXPR. If the FPRs are 64 bits
+ wide, IMM_EXPR is the entire value and the GPRs are known to be 64
+ bits wide as well. Otherwise IMM_EXPR is the high order 32 bits of
+ the value and the low order 32 bits are either zero or in
+ OFFSET_EXPR. */
+ if (imm_expr.X_op == O_constant || imm_expr.X_op == O_big)
+ {
+ load_register (AT, &imm_expr, HAVE_64BIT_FPRS);
+ if (HAVE_64BIT_FPRS)
+ {
+ assert (HAVE_64BIT_GPRS);
+ macro_build (NULL, "dmtc1", "t,S", AT, treg);
+ }
+ else
+ {
+ macro_build (NULL, "mtc1", "t,G", AT, treg + 1);
+ if (offset_expr.X_op == O_absent)
+ macro_build (NULL, "mtc1", "t,G", 0, treg);
+ else
+ {
+ assert (offset_expr.X_op == O_constant);
+ load_register (AT, &offset_expr, 0);
+ macro_build (NULL, "mtc1", "t,G", AT, treg);
+ }
+ }
+ break;
+ }
+
+ assert (offset_expr.X_op == O_symbol
+ && offset_expr.X_add_number == 0);
+ s = segment_name (S_GET_SEGMENT (offset_expr.X_add_symbol));
+ if (strcmp (s, ".lit8") == 0)
+ {
+ if (mips_opts.isa != ISA_MIPS1)
+ {
+ macro_build (&offset_expr, "ldc1", "T,o(b)", treg,
+ BFD_RELOC_MIPS_LITERAL, mips_gp_register);
+ return;
+ }
+ breg = mips_gp_register;
+ r = BFD_RELOC_MIPS_LITERAL;
+ goto dob;
+ }
+ else
+ {
+ assert (strcmp (s, RDATA_SECTION_NAME) == 0);
+ if (mips_pic == SVR4_PIC)
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ else
+ {
+ /* FIXME: This won't work for a 64 bit address. */
+ macro_build_lui (&offset_expr, AT);
+ }
+
+ if (mips_opts.isa != ISA_MIPS1)
+ {
+ macro_build (&offset_expr, "ldc1", "T,o(b)",
+ treg, BFD_RELOC_LO16, AT);
+ break;
+ }
+ breg = AT;
+ r = BFD_RELOC_LO16;
+ goto dob;
+ }
+
+ case M_L_DOB:
+ if (mips_opts.arch == CPU_R4650)
+ {
+ as_bad (_("opcode not supported on this processor"));
+ return;
+ }
+ /* Even on a big endian machine $fn comes before $fn+1. We have
+ to adjust when loading from memory. */
+ r = BFD_RELOC_LO16;
+ dob:
+ assert (mips_opts.isa == ISA_MIPS1);
+ macro_build (&offset_expr, "lwc1", "T,o(b)",
+ target_big_endian ? treg + 1 : treg, r, breg);
+ /* FIXME: A possible overflow which I don't know how to deal
+ with. */
+ offset_expr.X_add_number += 4;
+ macro_build (&offset_expr, "lwc1", "T,o(b)",
+ target_big_endian ? treg : treg + 1, r, breg);
+
+ if (breg != AT)
+ return;
+ break;
+
+ case M_L_DAB:
+ /*
+ * The MIPS assembler seems to check for X_add_number not
+ * being double aligned and generating:
+ * lui at,%hi(foo+1)
+ * addu at,at,v1
+ * addiu at,at,%lo(foo+1)
+ * lwc1 f2,0(at)
+ * lwc1 f3,4(at)
+ * But, the resulting address is the same after relocation so why
+ * generate the extra instruction?
+ */
+ if (mips_opts.arch == CPU_R4650)
+ {
+ as_bad (_("opcode not supported on this processor"));
+ return;
+ }
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ if (mips_opts.isa != ISA_MIPS1)
+ {
+ s = "ldc1";
+ goto ld;
+ }
+
+ s = "lwc1";
+ fmt = "T,o(b)";
+ goto ldd_std;
+
+ case M_S_DAB:
+ if (mips_opts.arch == CPU_R4650)
+ {
+ as_bad (_("opcode not supported on this processor"));
+ return;
+ }
+
+ if (mips_opts.isa != ISA_MIPS1)
+ {
+ s = "sdc1";
+ goto st;
+ }
+
+ s = "swc1";
+ fmt = "T,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ldd_std;
+
+ case M_LD_AB:
+ if (HAVE_64BIT_GPRS)
+ {
+ s = "ld";
+ goto ld;
+ }
+
+ s = "lw";
+ fmt = "t,o(b)";
+ goto ldd_std;
+
+ case M_SD_AB:
+ if (HAVE_64BIT_GPRS)
+ {
+ s = "sd";
+ goto st;
+ }
+
+ s = "sw";
+ fmt = "t,o(b)";
+
+ ldd_std:
+ /* We do _not_ bother to allow embedded PIC (symbol-local_symbol)
+ loads for the case of doing a pair of loads to simulate an 'ld'.
+ This is not currently done by the compiler, and assembly coders
+ writing embedded-pic code can cope. */
+
+ if (offset_expr.X_op != O_symbol
+ && offset_expr.X_op != O_constant)
+ {
+ as_bad (_("expression too complex"));
+ offset_expr.X_op = O_constant;
+ }
+
+ /* Even on a big endian machine $fn comes before $fn+1. We have
+ to adjust when loading from memory. We set coproc if we must
+ load $fn+1 first. */
+ /* Itbl support may require additional care here. */
+ if (! target_big_endian)
+ coproc = 0;
+
+ if (mips_pic == NO_PIC
+ || offset_expr.X_op == O_constant)
+ {
+ /* If this is a reference to a GP relative symbol, we want
+ <op> $treg,<sym>($gp) (BFD_RELOC_GPREL16)
+ <op> $treg+1,<sym>+4($gp) (BFD_RELOC_GPREL16)
+ If we have a base register, we use this
+ addu $at,$breg,$gp
+ <op> $treg,<sym>($at) (BFD_RELOC_GPREL16)
+ <op> $treg+1,<sym>+4($at) (BFD_RELOC_GPREL16)
+ If this is not a GP relative symbol, we want
+ lui $at,<sym> (BFD_RELOC_HI16_S)
+ <op> $treg,<sym>($at) (BFD_RELOC_LO16)
+ <op> $treg+1,<sym>+4($at) (BFD_RELOC_LO16)
+ If there is a base register, we add it to $at after the
+ lui instruction. If there is a constant, we always use
+ the last case. */
+ if ((valueT) offset_expr.X_add_number > MAX_GPREL_OFFSET
+ || nopic_need_relax (offset_expr.X_add_symbol, 1))
+ used_at = 1;
+ else
+ {
+ relax_start (offset_expr.X_add_symbol);
+ if (breg == 0)
+ {
+ tempreg = mips_gp_register;
+ used_at = 0;
+ }
+ else
+ {
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ AT, breg, mips_gp_register);
+ tempreg = AT;
+ used_at = 1;
+ }
+
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
+ BFD_RELOC_GPREL16, tempreg);
+ offset_expr.X_add_number += 4;
+
+ /* Set mips_optimize to 2 to avoid inserting an
+ undesired nop. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
+ BFD_RELOC_GPREL16, tempreg);
+ mips_optimize = hold_mips_optimize;
+
+ relax_switch ();
+
+ /* We just generated two relocs. When tc_gen_reloc
+ handles this case, it will skip the first reloc and
+ handle the second. The second reloc already has an
+ extra addend of 4, which we added above. We must
+ subtract it out, and then subtract another 4 to make
+ the first reloc come out right. The second reloc
+ will come out right because we are going to add 4 to
+ offset_expr when we build its instruction below.
+
+ If we have a symbol, then we don't want to include
+ the offset, because it will wind up being included
+ when we generate the reloc. */
+
+ if (offset_expr.X_op == O_constant)
+ offset_expr.X_add_number -= 8;
+ else
+ {
+ offset_expr.X_add_number = -4;
+ offset_expr.X_op = O_constant;
+ }
+ }
+ macro_build_lui (&offset_expr, AT);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
+ BFD_RELOC_LO16, AT);
+ /* FIXME: How do we handle overflow here? */
+ offset_expr.X_add_number += 4;
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
+ BFD_RELOC_LO16, AT);
+ if (mips_relax.sequence)
+ relax_end ();
+ }
+ else if (mips_pic == SVR4_PIC && ! mips_big_got)
+ {
+ /* If this is a reference to an external symbol, we want
+ lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ <op> $treg,0($at)
+ <op> $treg+1,4($at)
+ Otherwise we want
+ lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ <op> $treg,<sym>($at) (BFD_RELOC_LO16)
+ <op> $treg+1,<sym>+4($at) (BFD_RELOC_LO16)
+ If there is a base register we add it to $at before the
+ lwc1 instructions. If there is a constant we include it
+ in the lwc1 instructions. */
+ used_at = 1;
+ expr1.X_add_number = offset_expr.X_add_number;
+ if (expr1.X_add_number < -0x8000
+ || expr1.X_add_number >= 0x8000 - 4)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ load_got_offset (AT, &offset_expr);
+ macro_build (NULL, "nop", "");
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
+
+ /* Set mips_optimize to 2 to avoid inserting an undesired
+ nop. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
+
+ /* Itbl support may require additional care here. */
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&expr1, s, fmt, coproc ? treg + 1 : treg,
+ BFD_RELOC_LO16, AT);
+ expr1.X_add_number += 4;
+ macro_build (&expr1, s, fmt, coproc ? treg : treg + 1,
+ BFD_RELOC_LO16, AT);
+ relax_switch ();
+ macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
+ BFD_RELOC_LO16, AT);
+ offset_expr.X_add_number += 4;
+ macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
+ BFD_RELOC_LO16, AT);
+ relax_end ();
+
+ mips_optimize = hold_mips_optimize;
+ }
+ else if (mips_pic == SVR4_PIC)
+ {
+ int gpdelay;
+
+ /* If this is a reference to an external symbol, we want
+ lui $at,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $at,$at,$gp
+ lw $at,<sym>($at) (BFD_RELOC_MIPS_GOT_LO16)
+ nop
+ <op> $treg,0($at)
+ <op> $treg+1,4($at)
+ Otherwise we want
+ lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ <op> $treg,<sym>($at) (BFD_RELOC_LO16)
+ <op> $treg+1,<sym>+4($at) (BFD_RELOC_LO16)
+ If there is a base register we add it to $at before the
+ lwc1 instructions. If there is a constant we include it
+ in the lwc1 instructions. */
+ used_at = 1;
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ if (expr1.X_add_number < -0x8000
+ || expr1.X_add_number >= 0x8000 - 4)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ gpdelay = reg_needs_delay (mips_gp_register);
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, "lui", "t,u",
+ AT, BFD_RELOC_MIPS_GOT_HI16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ AT, AT, mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ AT, BFD_RELOC_MIPS_GOT_LO16, AT);
+ macro_build (NULL, "nop", "");
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
+ /* Itbl support may require additional care here. */
+ macro_build (&expr1, s, fmt, coproc ? treg + 1 : treg,
+ BFD_RELOC_LO16, AT);
+ expr1.X_add_number += 4;
+
+ /* Set mips_optimize to 2 to avoid inserting an undesired
+ nop. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
+ /* Itbl support may require additional care here. */
+ macro_build (&expr1, s, fmt, coproc ? treg : treg + 1,
+ BFD_RELOC_LO16, AT);
+ mips_optimize = hold_mips_optimize;
+ expr1.X_add_number -= 4;
+
+ relax_switch ();
+ offset_expr.X_add_number = expr1.X_add_number;
+ if (gpdelay)
+ macro_build (NULL, "nop", "");
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ macro_build (NULL, "nop", "");
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
+ BFD_RELOC_LO16, AT);
+ offset_expr.X_add_number += 4;
+
+ /* Set mips_optimize to 2 to avoid inserting an undesired
+ nop. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
+ BFD_RELOC_LO16, AT);
+ mips_optimize = hold_mips_optimize;
+ relax_end ();
+ }
+ else if (mips_pic == EMBEDDED_PIC)
+ {
+ /* If there is no base register, we use
+ <op> $treg,<sym>($gp) (BFD_RELOC_GPREL16)
+ <op> $treg+1,<sym>+4($gp) (BFD_RELOC_GPREL16)
+ If we have a base register, we use
+ addu $at,$breg,$gp
+ <op> $treg,<sym>($at) (BFD_RELOC_GPREL16)
+ <op> $treg+1,<sym>+4($at) (BFD_RELOC_GPREL16)
+ */
+ if (breg == 0)
+ {
+ tempreg = mips_gp_register;
+ used_at = 0;
+ }
+ else
+ {
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ AT, breg, mips_gp_register);
+ tempreg = AT;
+ used_at = 1;
+ }
+
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
+ BFD_RELOC_GPREL16, tempreg);
+ offset_expr.X_add_number += 4;
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
+ BFD_RELOC_GPREL16, tempreg);
+ }
+ else
+ abort ();
+
+ if (! used_at)
+ return;
+
+ break;
+
+ case M_LD_OB:
+ s = "lw";
+ goto sd_ob;
+ case M_SD_OB:
+ s = "sw";
+ sd_ob:
+ assert (HAVE_32BIT_ADDRESSES);
+ macro_build (&offset_expr, s, "t,o(b)", treg, BFD_RELOC_LO16, breg);
+ offset_expr.X_add_number += 4;
+ macro_build (&offset_expr, s, "t,o(b)", treg + 1, BFD_RELOC_LO16, breg);
+ return;
+
+ /* New code added to support COPZ instructions.
+ This code builds table entries out of the macros in mip_opcodes.
+ R4000 uses interlocks to handle coproc delays.
+ Other chips (like the R3000) require nops to be inserted for delays.
+
+ FIXME: Currently, we require that the user handle delays.
+ In order to fill delay slots for non-interlocked chips,
+ we must have a way to specify delays based on the coprocessor.
+ Eg. 4 cycles if load coproc reg from memory, 1 if in cache, etc.
+ What are the side-effects of the cop instruction?
+ What cache support might we have and what are its effects?
+ Both coprocessor & memory require delays. how long???
+ What registers are read/set/modified?
+
+ If an itbl is provided to interpret cop instructions,
+ this knowledge can be encoded in the itbl spec. */
+
+ case M_COP0:
+ s = "c0";
+ goto copz;
+ case M_COP1:
+ s = "c1";
+ goto copz;
+ case M_COP2:
+ s = "c2";
+ goto copz;
+ case M_COP3:
+ s = "c3";
+ copz:
+ /* For now we just do C (same as Cz). The parameter will be
+ stored in insn_opcode by mips_ip. */
+ macro_build (NULL, s, "C", ip->insn_opcode);
+ return;
+
+ case M_MOVE:
+ move_register (dreg, sreg);
+ return;
+
+#ifdef LOSING_COMPILER
+ default:
+ /* Try and see if this is a new itbl instruction.
+ This code builds table entries out of the macros in mip_opcodes.
+ FIXME: For now we just assemble the expression and pass it's
+ value along as a 32-bit immediate.
+ We may want to have the assembler assemble this value,
+ so that we gain the assembler's knowledge of delay slots,
+ symbols, etc.
+ Would it be more efficient to use mask (id) here? */
+ if (itbl_have_entries
+ && (immed_expr = itbl_assemble (ip->insn_mo->name, "")))
+ {
+ s = ip->insn_mo->name;
+ s2 = "cop3";
+ coproc = ITBL_DECODE_PNUM (immed_expr);;
+ macro_build (&immed_expr, s, "C");
+ return;
+ }
+ macro2 (ip);
+ return;
+ }
+ if (mips_opts.noat)
+ as_warn (_("Macro used $at after \".set noat\""));
+}
+
+static void
+macro2 (struct mips_cl_insn *ip)
+{
+ register int treg, sreg, dreg, breg;
+ int tempreg;
+ int mask;
+ int used_at;
+ expressionS expr1;
+ const char *s;
+ const char *s2;
+ const char *fmt;
+ int likely = 0;
+ int dbl = 0;
+ int coproc = 0;
+ int lr = 0;
+ int imm = 0;
+ int off;
+ offsetT maxnum;
+ bfd_reloc_code_real_type r;
+
+ treg = (ip->insn_opcode >> 16) & 0x1f;
+ dreg = (ip->insn_opcode >> 11) & 0x1f;
+ sreg = breg = (ip->insn_opcode >> 21) & 0x1f;
+ mask = ip->insn_mo->mask;
+
+ expr1.X_op = O_constant;
+ expr1.X_op_symbol = NULL;
+ expr1.X_add_symbol = NULL;
+ expr1.X_add_number = 1;
+
+ switch (mask)
+ {
+#endif /* LOSING_COMPILER */
+
+ case M_DMUL:
+ dbl = 1;
+ case M_MUL:
+ macro_build (NULL, dbl ? "dmultu" : "multu", "s,t", sreg, treg);
+ macro_build (NULL, "mflo", "d", dreg);
+ return;
+
+ case M_DMUL_I:
+ dbl = 1;
+ case M_MUL_I:
+ /* The MIPS assembler some times generates shifts and adds. I'm
+ not trying to be that fancy. GCC should do this for us
+ anyway. */
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, dbl ? "dmult" : "mult", "s,t", sreg, AT);
+ macro_build (NULL, "mflo", "d", dreg);
+ break;
+
+ case M_DMULO_I:
+ dbl = 1;
+ case M_MULO_I:
+ imm = 1;
+ goto do_mulo;
+
+ case M_DMULO:
+ dbl = 1;
+ case M_MULO:
+ do_mulo:
+ mips_emit_delays (TRUE);
+ ++mips_opts.noreorder;
+ mips_any_noreorder = 1;
+ if (imm)
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, dbl ? "dmult" : "mult", "s,t", sreg, imm ? AT : treg);
+ macro_build (NULL, "mflo", "d", dreg);
+ macro_build (NULL, dbl ? "dsra32" : "sra", "d,w,<", dreg, dreg, RA);
+ macro_build (NULL, "mfhi", "d", AT);
+ if (mips_trap)
+ macro_build (NULL, "tne", "s,t,q", dreg, AT, 6);
+ else
+ {
+ expr1.X_add_number = 8;
+ macro_build (&expr1, "beq", "s,t,p", dreg, AT);
+ macro_build (NULL, "nop", "", 0);
+ macro_build (NULL, "break", "c", 6);
+ }
+ --mips_opts.noreorder;
+ macro_build (NULL, "mflo", "d", dreg);
+ break;
+
+ case M_DMULOU_I:
+ dbl = 1;
+ case M_MULOU_I:
+ imm = 1;
+ goto do_mulou;
+
+ case M_DMULOU:
+ dbl = 1;
+ case M_MULOU:
+ do_mulou:
+ mips_emit_delays (TRUE);
+ ++mips_opts.noreorder;
+ mips_any_noreorder = 1;
+ if (imm)
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, dbl ? "dmultu" : "multu", "s,t",
+ sreg, imm ? AT : treg);
+ macro_build (NULL, "mfhi", "d", AT);
+ macro_build (NULL, "mflo", "d", dreg);
+ if (mips_trap)
+ macro_build (NULL, "tne", "s,t,q", AT, 0, 6);
+ else
+ {
+ expr1.X_add_number = 8;
+ macro_build (&expr1, "beq", "s,t,p", AT, 0);
+ macro_build (NULL, "nop", "", 0);
+ macro_build (NULL, "break", "c", 6);
+ }
+ --mips_opts.noreorder;
+ break;
+
+ case M_DROL:
+ if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+ {
+ if (dreg == sreg)
+ {
+ tempreg = AT;
+ used_at = 1;
+ }
+ else
+ {
+ tempreg = dreg;
+ used_at = 0;
+ }
+ macro_build (NULL, "dnegu", "d,w", tempreg, treg);
+ macro_build (NULL, "drorv", "d,t,s", dreg, sreg, tempreg);
+ if (used_at)
+ break;
+ return;
+ }
+ macro_build (NULL, "dsubu", "d,v,t", AT, 0, treg);
+ macro_build (NULL, "dsrlv", "d,t,s", AT, sreg, AT);
+ macro_build (NULL, "dsllv", "d,t,s", dreg, sreg, treg);
+ macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+ break;
+
+ case M_ROL:
+ if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+ {
+ if (dreg == sreg)
+ {
+ tempreg = AT;
+ used_at = 1;
+ }
+ else
+ {
+ tempreg = dreg;
+ used_at = 0;
+ }
+ macro_build (NULL, "negu", "d,w", tempreg, treg);
+ macro_build (NULL, "rorv", "d,t,s", dreg, sreg, tempreg);
+ if (used_at)
+ break;
+ return;
+ }
+ macro_build (NULL, "subu", "d,v,t", AT, 0, treg);
+ macro_build (NULL, "srlv", "d,t,s", AT, sreg, AT);
+ macro_build (NULL, "sllv", "d,t,s", dreg, sreg, treg);
+ macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+ break;
+
+ case M_DROL_I:
+ {
+ unsigned int rot;
+ char *l, *r;
+
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Improper rotate count"));
+ rot = imm_expr.X_add_number & 0x3f;
+ if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+ {
+ rot = (64 - rot) & 0x3f;
+ if (rot >= 32)
+ macro_build (NULL, "dror32", "d,w,<", dreg, sreg, rot - 32);
+ else
+ macro_build (NULL, "dror", "d,w,<", dreg, sreg, rot);
+ return;
+ }
+ if (rot == 0)
+ {
+ macro_build (NULL, "dsrl", "d,w,<", dreg, sreg, 0);
+ return;
+ }
+ l = (rot < 0x20) ? "dsll" : "dsll32";
+ r = ((0x40 - rot) < 0x20) ? "dsrl" : "dsrl32";
+ rot &= 0x1f;
+ macro_build (NULL, l, "d,w,<", AT, sreg, rot);
+ macro_build (NULL, r, "d,w,<", dreg, sreg, (0x20 - rot) & 0x1f);
+ macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+ }
+ break;
+
+ case M_ROL_I:
+ {
+ unsigned int rot;
+
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Improper rotate count"));
+ rot = imm_expr.X_add_number & 0x1f;
+ if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+ {
+ macro_build (NULL, "ror", "d,w,<", dreg, sreg, (32 - rot) & 0x1f);
+ return;
+ }
+ if (rot == 0)
+ {
+ macro_build (NULL, "srl", "d,w,<", dreg, sreg, 0);
+ return;
+ }
+ macro_build (NULL, "sll", "d,w,<", AT, sreg, rot);
+ macro_build (NULL, "srl", "d,w,<", dreg, sreg, (0x20 - rot) & 0x1f);
+ macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+ }
+ break;
+
+ case M_DROR:
+ if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+ {
+ macro_build (NULL, "drorv", "d,t,s", dreg, sreg, treg);
+ return;
+ }
+ macro_build (NULL, "dsubu", "d,v,t", AT, 0, treg);
+ macro_build (NULL, "dsllv", "d,t,s", AT, sreg, AT);
+ macro_build (NULL, "dsrlv", "d,t,s", dreg, sreg, treg);
+ macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+ break;
+
+ case M_ROR:
+ if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+ {
+ macro_build (NULL, "rorv", "d,t,s", dreg, sreg, treg);
+ return;
+ }
+ macro_build (NULL, "subu", "d,v,t", AT, 0, treg);
+ macro_build (NULL, "sllv", "d,t,s", AT, sreg, AT);
+ macro_build (NULL, "srlv", "d,t,s", dreg, sreg, treg);
+ macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+ break;
+
+ case M_DROR_I:
+ {
+ unsigned int rot;
+ char *l, *r;
+
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Improper rotate count"));
+ rot = imm_expr.X_add_number & 0x3f;
+ if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+ {
+ if (rot >= 32)
+ macro_build (NULL, "dror32", "d,w,<", dreg, sreg, rot - 32);
+ else
+ macro_build (NULL, "dror", "d,w,<", dreg, sreg, rot);
+ return;
+ }
+ if (rot == 0)
+ {
+ macro_build (NULL, "dsrl", "d,w,<", dreg, sreg, 0);
+ return;
+ }
+ r = (rot < 0x20) ? "dsrl" : "dsrl32";
+ l = ((0x40 - rot) < 0x20) ? "dsll" : "dsll32";
+ rot &= 0x1f;
+ macro_build (NULL, r, "d,w,<", AT, sreg, rot);
+ macro_build (NULL, l, "d,w,<", dreg, sreg, (0x20 - rot) & 0x1f);
+ macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+ }
+ break;
+
+ case M_ROR_I:
+ {
+ unsigned int rot;
+
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Improper rotate count"));
+ rot = imm_expr.X_add_number & 0x1f;
+ if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+ {
+ macro_build (NULL, "ror", "d,w,<", dreg, sreg, rot);
+ return;
+ }
+ if (rot == 0)
+ {
+ macro_build (NULL, "srl", "d,w,<", dreg, sreg, 0);
+ return;
+ }
+ macro_build (NULL, "srl", "d,w,<", AT, sreg, rot);
+ macro_build (NULL, "sll", "d,w,<", dreg, sreg, (0x20 - rot) & 0x1f);
+ macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+ }
+ break;
+
+ case M_S_DOB:
+ if (mips_opts.arch == CPU_R4650)
+ {
+ as_bad (_("opcode not supported on this processor"));
+ return;
+ }
+ assert (mips_opts.isa == ISA_MIPS1);
+ /* Even on a big endian machine $fn comes before $fn+1. We have
+ to adjust when storing to memory. */
+ macro_build (&offset_expr, "swc1", "T,o(b)",
+ target_big_endian ? treg + 1 : treg, BFD_RELOC_LO16, breg);
+ offset_expr.X_add_number += 4;
+ macro_build (&offset_expr, "swc1", "T,o(b)",
+ target_big_endian ? treg : treg + 1, BFD_RELOC_LO16, breg);
+ return;
+
+ case M_SEQ:
+ if (sreg == 0)
+ macro_build (&expr1, "sltiu", "t,r,j", dreg, treg, BFD_RELOC_LO16);
+ else if (treg == 0)
+ macro_build (&expr1, "sltiu", "t,r,j", dreg, sreg, BFD_RELOC_LO16);
+ else
+ {
+ macro_build (NULL, "xor", "d,v,t", dreg, sreg, treg);
+ macro_build (&expr1, "sltiu", "t,r,j", dreg, dreg, BFD_RELOC_LO16);
+ }
+ return;
+
+ case M_SEQ_I:
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ {
+ macro_build (&expr1, "sltiu", "t,r,j", dreg, sreg, BFD_RELOC_LO16);
+ return;
+ }
+ if (sreg == 0)
+ {
+ as_warn (_("Instruction %s: result is always false"),
+ ip->insn_mo->name);
+ move_register (dreg, 0);
+ return;
+ }
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= 0
+ && imm_expr.X_add_number < 0x10000)
+ {
+ macro_build (&imm_expr, "xori", "t,r,i", dreg, sreg, BFD_RELOC_LO16);
+ used_at = 0;
+ }
+ else if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number > -0x8000
+ && imm_expr.X_add_number < 0)
+ {
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, HAVE_32BIT_GPRS ? "addiu" : "daddiu",
+ "t,r,j", dreg, sreg, BFD_RELOC_LO16);
+ used_at = 0;
+ }
+ else
+ {
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, "xor", "d,v,t", dreg, sreg, AT);
+ used_at = 1;
+ }
+ macro_build (&expr1, "sltiu", "t,r,j", dreg, dreg, BFD_RELOC_LO16);
+ if (used_at)
+ break;
+ return;
+
+ case M_SGE: /* sreg >= treg <==> not (sreg < treg) */
+ s = "slt";
+ goto sge;
+ case M_SGEU:
+ s = "sltu";
+ sge:
+ macro_build (NULL, s, "d,v,t", dreg, sreg, treg);
+ macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
+ return;
+
+ case M_SGE_I: /* sreg >= I <==> not (sreg < I) */
+ case M_SGEU_I:
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ {
+ macro_build (&imm_expr, mask == M_SGE_I ? "slti" : "sltiu", "t,r,j",
+ dreg, sreg, BFD_RELOC_LO16);
+ used_at = 0;
+ }
+ else
+ {
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, mask == M_SGE_I ? "slt" : "sltu", "d,v,t",
+ dreg, sreg, AT);
+ used_at = 1;
+ }
+ macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
+ if (used_at)
+ break;
+ return;
+
+ case M_SGT: /* sreg > treg <==> treg < sreg */
+ s = "slt";
+ goto sgt;
+ case M_SGTU:
+ s = "sltu";
+ sgt:
+ macro_build (NULL, s, "d,v,t", dreg, treg, sreg);
+ return;
+
+ case M_SGT_I: /* sreg > I <==> I < sreg */
+ s = "slt";
+ goto sgti;
+ case M_SGTU_I:
+ s = "sltu";
+ sgti:
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, s, "d,v,t", dreg, AT, sreg);
+ break;
+
+ case M_SLE: /* sreg <= treg <==> treg >= sreg <==> not (treg < sreg) */
+ s = "slt";
+ goto sle;
+ case M_SLEU:
+ s = "sltu";
+ sle:
+ macro_build (NULL, s, "d,v,t", dreg, treg, sreg);
+ macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
+ return;
+
+ case M_SLE_I: /* sreg <= I <==> I >= sreg <==> not (I < sreg) */
+ s = "slt";
+ goto slei;
+ case M_SLEU_I:
+ s = "sltu";
+ slei:
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, s, "d,v,t", dreg, AT, sreg);
+ macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
+ break;
+
+ case M_SLT_I:
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ {
+ macro_build (&imm_expr, "slti", "t,r,j", dreg, sreg, BFD_RELOC_LO16);
+ return;
+ }
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, "slt", "d,v,t", dreg, sreg, AT);
+ break;
+
+ case M_SLTU_I:
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ {
+ macro_build (&imm_expr, "sltiu", "t,r,j", dreg, sreg,
+ BFD_RELOC_LO16);
+ return;
+ }
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, "sltu", "d,v,t", dreg, sreg, AT);
+ break;
+
+ case M_SNE:
+ if (sreg == 0)
+ macro_build (NULL, "sltu", "d,v,t", dreg, 0, treg);
+ else if (treg == 0)
+ macro_build (NULL, "sltu", "d,v,t", dreg, 0, sreg);
+ else
+ {
+ macro_build (NULL, "xor", "d,v,t", dreg, sreg, treg);
+ macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
+ }
+ return;
+
+ case M_SNE_I:
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ {
+ macro_build (NULL, "sltu", "d,v,t", dreg, 0, sreg);
+ return;
+ }
+ if (sreg == 0)
+ {
+ as_warn (_("Instruction %s: result is always true"),
+ ip->insn_mo->name);
+ macro_build (&expr1, HAVE_32BIT_GPRS ? "addiu" : "daddiu", "t,r,j",
+ dreg, 0, BFD_RELOC_LO16);
+ return;
+ }
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= 0
+ && imm_expr.X_add_number < 0x10000)
+ {
+ macro_build (&imm_expr, "xori", "t,r,i", dreg, sreg, BFD_RELOC_LO16);
+ used_at = 0;
+ }
+ else if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number > -0x8000
+ && imm_expr.X_add_number < 0)
+ {
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, HAVE_32BIT_GPRS ? "addiu" : "daddiu",
+ "t,r,j", dreg, sreg, BFD_RELOC_LO16);
+ used_at = 0;
+ }
+ else
+ {
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, "xor", "d,v,t", dreg, sreg, AT);
+ used_at = 1;
+ }
+ macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
+ if (used_at)
+ break;
+ return;
+
+ case M_DSUB_I:
+ dbl = 1;
+ case M_SUB_I:
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number > -0x8000
+ && imm_expr.X_add_number <= 0x8000)
+ {
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, dbl ? "daddi" : "addi", "t,r,j",
+ dreg, sreg, BFD_RELOC_LO16);
+ return;
+ }
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, dbl ? "dsub" : "sub", "d,v,t", dreg, sreg, AT);
+ break;
+
+ case M_DSUBU_I:
+ dbl = 1;
+ case M_SUBU_I:
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number > -0x8000
+ && imm_expr.X_add_number <= 0x8000)
+ {
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, dbl ? "daddiu" : "addiu", "t,r,j",
+ dreg, sreg, BFD_RELOC_LO16);
+ return;
+ }
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, dbl ? "dsubu" : "subu", "d,v,t", dreg, sreg, AT);
+ break;
+
+ case M_TEQ_I:
+ s = "teq";
+ goto trap;
+ case M_TGE_I:
+ s = "tge";
+ goto trap;
+ case M_TGEU_I:
+ s = "tgeu";
+ goto trap;
+ case M_TLT_I:
+ s = "tlt";
+ goto trap;
+ case M_TLTU_I:
+ s = "tltu";
+ goto trap;
+ case M_TNE_I:
+ s = "tne";
+ trap:
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, s, "s,t", sreg, AT);
+ break;
+
+ case M_TRUNCWS:
+ case M_TRUNCWD:
+ assert (mips_opts.isa == ISA_MIPS1);
+ sreg = (ip->insn_opcode >> 11) & 0x1f; /* floating reg */
+ dreg = (ip->insn_opcode >> 06) & 0x1f; /* floating reg */
+
+ /*
+ * Is the double cfc1 instruction a bug in the mips assembler;
+ * or is there a reason for it?
+ */
+ mips_emit_delays (TRUE);
+ ++mips_opts.noreorder;
+ mips_any_noreorder = 1;
+ macro_build (NULL, "cfc1", "t,G", treg, RA);
+ macro_build (NULL, "cfc1", "t,G", treg, RA);
+ macro_build (NULL, "nop", "");
+ expr1.X_add_number = 3;
+ macro_build (&expr1, "ori", "t,r,i", AT, treg, BFD_RELOC_LO16);
+ expr1.X_add_number = 2;
+ macro_build (&expr1, "xori", "t,r,i", AT, AT, BFD_RELOC_LO16);
+ macro_build (NULL, "ctc1", "t,G", AT, RA);
+ macro_build (NULL, "nop", "");
+ macro_build (NULL, mask == M_TRUNCWD ? "cvt.w.d" : "cvt.w.s", "D,S",
+ dreg, sreg);
+ macro_build (NULL, "ctc1", "t,G", treg, RA);
+ macro_build (NULL, "nop", "");
+ --mips_opts.noreorder;
+ break;
+
+ case M_ULH:
+ s = "lb";
+ goto ulh;
+ case M_ULHU:
+ s = "lbu";
+ ulh:
+ if (offset_expr.X_add_number >= 0x7fff)
+ as_bad (_("operand overflow"));
+ if (! target_big_endian)
+ ++offset_expr.X_add_number;
+ macro_build (&offset_expr, s, "t,o(b)", AT, BFD_RELOC_LO16, breg);
+ if (! target_big_endian)
+ --offset_expr.X_add_number;
+ else
+ ++offset_expr.X_add_number;
+ macro_build (&offset_expr, "lbu", "t,o(b)", treg, BFD_RELOC_LO16, breg);
+ macro_build (NULL, "sll", "d,w,<", AT, AT, 8);
+ macro_build (NULL, "or", "d,v,t", treg, treg, AT);
+ break;
+
+ case M_ULD:
+ s = "ldl";
+ s2 = "ldr";
+ off = 7;
+ goto ulw;
+ case M_ULW:
+ s = "lwl";
+ s2 = "lwr";
+ off = 3;
+ ulw:
+ if (offset_expr.X_add_number >= 0x8000 - off)
+ as_bad (_("operand overflow"));
+ if (treg != breg)
+ tempreg = treg;
+ else
+ tempreg = AT;
+ if (! target_big_endian)
+ offset_expr.X_add_number += off;
+ macro_build (&offset_expr, s, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
+ if (! target_big_endian)
+ offset_expr.X_add_number -= off;
+ else
+ offset_expr.X_add_number += off;
+ macro_build (&offset_expr, s2, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
+
+ /* If necessary, move the result in tempreg the final destination. */
+ if (treg == tempreg)
+ return;
+ /* Protect second load's delay slot. */
+ if (!gpr_interlocks)
+ macro_build (NULL, "nop", "");
+ move_register (treg, tempreg);
+ break;
+
+ case M_ULD_A:
+ s = "ldl";
+ s2 = "ldr";
+ off = 7;
+ goto ulwa;
+ case M_ULW_A:
+ s = "lwl";
+ s2 = "lwr";
+ off = 3;
+ ulwa:
+ used_at = 1;
+ load_address (AT, &offset_expr, &used_at);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
+ if (! target_big_endian)
+ expr1.X_add_number = off;
+ else
+ expr1.X_add_number = 0;
+ macro_build (&expr1, s, "t,o(b)", treg, BFD_RELOC_LO16, AT);
+ if (! target_big_endian)
+ expr1.X_add_number = 0;
+ else
+ expr1.X_add_number = off;
+ macro_build (&expr1, s2, "t,o(b)", treg, BFD_RELOC_LO16, AT);
+ break;
+
+ case M_ULH_A:
+ case M_ULHU_A:
+ used_at = 1;
+ load_address (AT, &offset_expr, &used_at);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
+ if (target_big_endian)
+ expr1.X_add_number = 0;
+ macro_build (&expr1, mask == M_ULH_A ? "lb" : "lbu", "t,o(b)",
+ treg, BFD_RELOC_LO16, AT);
+ if (target_big_endian)
+ expr1.X_add_number = 1;
+ else
+ expr1.X_add_number = 0;
+ macro_build (&expr1, "lbu", "t,o(b)", AT, BFD_RELOC_LO16, AT);
+ macro_build (NULL, "sll", "d,w,<", treg, treg, 8);
+ macro_build (NULL, "or", "d,v,t", treg, treg, AT);
+ break;
+
+ case M_USH:
+ if (offset_expr.X_add_number >= 0x7fff)
+ as_bad (_("operand overflow"));
+ if (target_big_endian)
+ ++offset_expr.X_add_number;
+ macro_build (&offset_expr, "sb", "t,o(b)", treg, BFD_RELOC_LO16, breg);
+ macro_build (NULL, "srl", "d,w,<", AT, treg, 8);
+ if (target_big_endian)
+ --offset_expr.X_add_number;
+ else
+ ++offset_expr.X_add_number;
+ macro_build (&offset_expr, "sb", "t,o(b)", AT, BFD_RELOC_LO16, breg);
+ break;
+
+ case M_USD:
+ s = "sdl";
+ s2 = "sdr";
+ off = 7;
+ goto usw;
+ case M_USW:
+ s = "swl";
+ s2 = "swr";
+ off = 3;
+ usw:
+ if (offset_expr.X_add_number >= 0x8000 - off)
+ as_bad (_("operand overflow"));
+ if (! target_big_endian)
+ offset_expr.X_add_number += off;
+ macro_build (&offset_expr, s, "t,o(b)", treg, BFD_RELOC_LO16, breg);
+ if (! target_big_endian)
+ offset_expr.X_add_number -= off;
+ else
+ offset_expr.X_add_number += off;
+ macro_build (&offset_expr, s2, "t,o(b)", treg, BFD_RELOC_LO16, breg);
+ return;
+
+ case M_USD_A:
+ s = "sdl";
+ s2 = "sdr";
+ off = 7;
+ goto uswa;
+ case M_USW_A:
+ s = "swl";
+ s2 = "swr";
+ off = 3;
+ uswa:
+ used_at = 1;
+ load_address (AT, &offset_expr, &used_at);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
+ if (! target_big_endian)
+ expr1.X_add_number = off;
+ else
+ expr1.X_add_number = 0;
+ macro_build (&expr1, s, "t,o(b)", treg, BFD_RELOC_LO16, AT);
+ if (! target_big_endian)
+ expr1.X_add_number = 0;
+ else
+ expr1.X_add_number = off;
+ macro_build (&expr1, s2, "t,o(b)", treg, BFD_RELOC_LO16, AT);
+ break;
+
+ case M_USH_A:
+ used_at = 1;
+ load_address (AT, &offset_expr, &used_at);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
+ if (! target_big_endian)
+ expr1.X_add_number = 0;
+ macro_build (&expr1, "sb", "t,o(b)", treg, BFD_RELOC_LO16, AT);
+ macro_build (NULL, "srl", "d,w,<", treg, treg, 8);
+ if (! target_big_endian)
+ expr1.X_add_number = 1;
+ else
+ expr1.X_add_number = 0;
+ macro_build (&expr1, "sb", "t,o(b)", treg, BFD_RELOC_LO16, AT);
+ if (! target_big_endian)
+ expr1.X_add_number = 0;
+ else
+ expr1.X_add_number = 1;
+ macro_build (&expr1, "lbu", "t,o(b)", AT, BFD_RELOC_LO16, AT);
+ macro_build (NULL, "sll", "d,w,<", treg, treg, 8);
+ macro_build (NULL, "or", "d,v,t", treg, treg, AT);
+ break;
+
+ default:
+ /* FIXME: Check if this is one of the itbl macros, since they
+ are added dynamically. */
+ as_bad (_("Macro %s not implemented yet"), ip->insn_mo->name);
+ break;
+ }
+ if (mips_opts.noat)
+ as_warn (_("Macro used $at after \".set noat\""));
+}
+
+/* Implement macros in mips16 mode. */
+
+static void
+mips16_macro (struct mips_cl_insn *ip)
+{
+ int mask;
+ int xreg, yreg, zreg, tmp;
+ expressionS expr1;
+ int dbl;
+ const char *s, *s2, *s3;
+
+ mask = ip->insn_mo->mask;
+
+ xreg = (ip->insn_opcode >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX;
+ yreg = (ip->insn_opcode >> MIPS16OP_SH_RY) & MIPS16OP_MASK_RY;
+ zreg = (ip->insn_opcode >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ;
+
+ expr1.X_op = O_constant;
+ expr1.X_op_symbol = NULL;
+ expr1.X_add_symbol = NULL;
+ expr1.X_add_number = 1;
+
+ dbl = 0;
+
+ switch (mask)
+ {
+ default:
+ internalError ();
+
+ case M_DDIV_3:
+ dbl = 1;
+ case M_DIV_3:
+ s = "mflo";
+ goto do_div3;
+ case M_DREM_3:
+ dbl = 1;
+ case M_REM_3:
+ s = "mfhi";
+ do_div3:
+ mips_emit_delays (TRUE);
+ ++mips_opts.noreorder;
+ mips_any_noreorder = 1;
+ macro_build (NULL, dbl ? "ddiv" : "div", "0,x,y", xreg, yreg);
+ expr1.X_add_number = 2;
+ macro_build (&expr1, "bnez", "x,p", yreg);
+ macro_build (NULL, "break", "6", 7);
+
+ /* FIXME: The normal code checks for of -1 / -0x80000000 here,
+ since that causes an overflow. We should do that as well,
+ but I don't see how to do the comparisons without a temporary
+ register. */
+ --mips_opts.noreorder;
+ macro_build (NULL, s, "x", zreg);
+ break;
+
+ case M_DIVU_3:
+ s = "divu";
+ s2 = "mflo";
+ goto do_divu3;
+ case M_REMU_3:
+ s = "divu";
+ s2 = "mfhi";
+ goto do_divu3;
+ case M_DDIVU_3:
+ s = "ddivu";
+ s2 = "mflo";
+ goto do_divu3;
+ case M_DREMU_3:
+ s = "ddivu";
+ s2 = "mfhi";
+ do_divu3:
+ mips_emit_delays (TRUE);
+ ++mips_opts.noreorder;
+ mips_any_noreorder = 1;
+ macro_build (NULL, s, "0,x,y", xreg, yreg);
+ expr1.X_add_number = 2;
+ macro_build (&expr1, "bnez", "x,p", yreg);
+ macro_build (NULL, "break", "6", 7);
+ --mips_opts.noreorder;
+ macro_build (NULL, s2, "x", zreg);
+ break;
+
+ case M_DMUL:
+ dbl = 1;
+ case M_MUL:
+ macro_build (NULL, dbl ? "dmultu" : "multu", "x,y", xreg, yreg);
+ macro_build (NULL, "mflo", "x", zreg);
+ return;
+
+ case M_DSUBU_I:
+ dbl = 1;
+ goto do_subu;
+ case M_SUBU_I:
+ do_subu:
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, dbl ? "daddiu" : "addiu", "y,x,4", yreg, xreg);
+ break;
+
+ case M_SUBU_I_2:
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, "addiu", "x,k", xreg);
+ break;
+
+ case M_DSUBU_I_2:
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, "daddiu", "y,j", yreg);
+ break;
+
+ case M_BEQ:
+ s = "cmp";
+ s2 = "bteqz";
+ goto do_branch;
+ case M_BNE:
+ s = "cmp";
+ s2 = "btnez";
+ goto do_branch;
+ case M_BLT:
+ s = "slt";
+ s2 = "btnez";
+ goto do_branch;
+ case M_BLTU:
+ s = "sltu";
+ s2 = "btnez";
+ goto do_branch;
+ case M_BLE:
+ s = "slt";
+ s2 = "bteqz";
+ goto do_reverse_branch;
+ case M_BLEU:
+ s = "sltu";
+ s2 = "bteqz";
+ goto do_reverse_branch;
+ case M_BGE:
+ s = "slt";
+ s2 = "bteqz";
+ goto do_branch;
+ case M_BGEU:
+ s = "sltu";
+ s2 = "bteqz";
+ goto do_branch;
+ case M_BGT:
+ s = "slt";
+ s2 = "btnez";
+ goto do_reverse_branch;
+ case M_BGTU:
+ s = "sltu";
+ s2 = "btnez";
+
+ do_reverse_branch:
+ tmp = xreg;
+ xreg = yreg;
+ yreg = tmp;
+
+ do_branch:
+ macro_build (NULL, s, "x,y", xreg, yreg);
+ macro_build (&offset_expr, s2, "p");
+ break;
+
+ case M_BEQ_I:
+ s = "cmpi";
+ s2 = "bteqz";
+ s3 = "x,U";
+ goto do_branch_i;
+ case M_BNE_I:
+ s = "cmpi";
+ s2 = "btnez";
+ s3 = "x,U";
+ goto do_branch_i;
+ case M_BLT_I:
+ s = "slti";
+ s2 = "btnez";
+ s3 = "x,8";
+ goto do_branch_i;
+ case M_BLTU_I:
+ s = "sltiu";
+ s2 = "btnez";
+ s3 = "x,8";
+ goto do_branch_i;
+ case M_BLE_I:
+ s = "slti";
+ s2 = "btnez";
+ s3 = "x,8";
+ goto do_addone_branch_i;
+ case M_BLEU_I:
+ s = "sltiu";
+ s2 = "btnez";
+ s3 = "x,8";
+ goto do_addone_branch_i;
+ case M_BGE_I:
+ s = "slti";
+ s2 = "bteqz";
+ s3 = "x,8";
+ goto do_branch_i;
+ case M_BGEU_I:
+ s = "sltiu";
+ s2 = "bteqz";
+ s3 = "x,8";
+ goto do_branch_i;
+ case M_BGT_I:
+ s = "slti";
+ s2 = "bteqz";
+ s3 = "x,8";
+ goto do_addone_branch_i;
+ case M_BGTU_I:
+ s = "sltiu";
+ s2 = "bteqz";
+ s3 = "x,8";
+
+ do_addone_branch_i:
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ ++imm_expr.X_add_number;
+
+ do_branch_i:
+ macro_build (&imm_expr, s, s3, xreg);
+ macro_build (&offset_expr, s2, "p");
+ break;
+
+ case M_ABS:
+ expr1.X_add_number = 0;
+ macro_build (&expr1, "slti", "x,8", yreg);
+ if (xreg != yreg)
+ move_register (xreg, yreg);
+ expr1.X_add_number = 2;
+ macro_build (&expr1, "bteqz", "p");
+ macro_build (NULL, "neg", "x,w", xreg, xreg);
+ }
+}
+
+/* For consistency checking, verify that all bits are specified either
+ by the match/mask part of the instruction definition, or by the
+ operand list. */
+static int
+validate_mips_insn (const struct mips_opcode *opc)
+{
+ const char *p = opc->args;
+ char c;
+ unsigned long used_bits = opc->mask;
+
+ if ((used_bits & opc->match) != opc->match)
+ {
+ as_bad (_("internal: bad mips opcode (mask error): %s %s"),
+ opc->name, opc->args);
+ return 0;
+ }
+#define USE_BITS(mask,shift) (used_bits |= ((mask) << (shift)))
+ while (*p)
+ switch (c = *p++)
+ {
+ case ',': break;
+ case '(': break;
+ case ')': break;
+ case '+':
+ switch (c = *p++)
+ {
+ case 'A': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
+ case 'B': USE_BITS (OP_MASK_INSMSB, OP_SH_INSMSB); break;
+ case 'C': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break;
+ case 'D': USE_BITS (OP_MASK_RD, OP_SH_RD);
+ USE_BITS (OP_MASK_SEL, OP_SH_SEL); break;
+ case 'E': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
+ case 'F': USE_BITS (OP_MASK_INSMSB, OP_SH_INSMSB); break;
+ case 'G': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break;
+ case 'H': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break;
+ case 'I': break;
+ default:
+ as_bad (_("internal: bad mips opcode (unknown extension operand type `+%c'): %s %s"),
+ c, opc->name, opc->args);
+ return 0;
+ }
+ break;
+ case '<': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
+ case '>': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
+ case 'A': break;
+ case 'B': USE_BITS (OP_MASK_CODE20, OP_SH_CODE20); break;
+ case 'C': USE_BITS (OP_MASK_COPZ, OP_SH_COPZ); break;
+ case 'D': USE_BITS (OP_MASK_FD, OP_SH_FD); break;
+ case 'E': USE_BITS (OP_MASK_RT, OP_SH_RT); break;
+ case 'F': break;
+ case 'G': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
+ case 'H': USE_BITS (OP_MASK_SEL, OP_SH_SEL); break;
+ case 'I': break;
+ case 'J': USE_BITS (OP_MASK_CODE19, OP_SH_CODE19); break;
+ case 'K': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
+ case 'L': break;
+ case 'M': USE_BITS (OP_MASK_CCC, OP_SH_CCC); break;
+ case 'N': USE_BITS (OP_MASK_BCC, OP_SH_BCC); break;
+ case 'O': USE_BITS (OP_MASK_ALN, OP_SH_ALN); break;
+ case 'Q': USE_BITS (OP_MASK_VSEL, OP_SH_VSEL);
+ USE_BITS (OP_MASK_FT, OP_SH_FT); break;
+ case 'R': USE_BITS (OP_MASK_FR, OP_SH_FR); break;
+ case 'S': USE_BITS (OP_MASK_FS, OP_SH_FS); break;
+ case 'T': USE_BITS (OP_MASK_FT, OP_SH_FT); break;
+ case 'V': USE_BITS (OP_MASK_FS, OP_SH_FS); break;
+ case 'W': USE_BITS (OP_MASK_FT, OP_SH_FT); break;
+ case 'X': USE_BITS (OP_MASK_FD, OP_SH_FD); break;
+ case 'Y': USE_BITS (OP_MASK_FS, OP_SH_FS); break;
+ case 'Z': USE_BITS (OP_MASK_FT, OP_SH_FT); break;
+ case 'a': USE_BITS (OP_MASK_TARGET, OP_SH_TARGET); break;
+ case 'b': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
+ case 'c': USE_BITS (OP_MASK_CODE, OP_SH_CODE); break;
+ case 'd': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
+ case 'f': break;
+ case 'h': USE_BITS (OP_MASK_PREFX, OP_SH_PREFX); break;
+ case 'i': USE_BITS (OP_MASK_IMMEDIATE, OP_SH_IMMEDIATE); break;
+ case 'j': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break;
+ case 'k': USE_BITS (OP_MASK_CACHE, OP_SH_CACHE); break;
+ case 'l': break;
+ case 'o': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break;
+ case 'p': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break;
+ case 'q': USE_BITS (OP_MASK_CODE2, OP_SH_CODE2); break;
+ case 'r': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
+ case 's': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
+ case 't': USE_BITS (OP_MASK_RT, OP_SH_RT); break;
+ case 'u': USE_BITS (OP_MASK_IMMEDIATE, OP_SH_IMMEDIATE); break;
+ case 'v': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
+ case 'w': USE_BITS (OP_MASK_RT, OP_SH_RT); break;
+ case 'x': break;
+ case 'z': break;
+ case 'P': USE_BITS (OP_MASK_PERFREG, OP_SH_PERFREG); break;
+ case 'U': USE_BITS (OP_MASK_RD, OP_SH_RD);
+ USE_BITS (OP_MASK_RT, OP_SH_RT); break;
+ case 'e': USE_BITS (OP_MASK_VECBYTE, OP_SH_VECBYTE); break;
+ case '%': USE_BITS (OP_MASK_VECALIGN, OP_SH_VECALIGN); break;
+ case '[': break;
+ case ']': break;
+ default:
+ as_bad (_("internal: bad mips opcode (unknown operand type `%c'): %s %s"),
+ c, opc->name, opc->args);
+ return 0;
+ }
+#undef USE_BITS
+ if (used_bits != 0xffffffff)
+ {
+ as_bad (_("internal: bad mips opcode (bits 0x%lx undefined): %s %s"),
+ ~used_bits & 0xffffffff, opc->name, opc->args);
+ return 0;
+ }
+ return 1;
+}
+
+/* This routine assembles an instruction into its binary format. As a
+ side effect, it sets one of the global variables imm_reloc or
+ offset_reloc to the type of relocation to do if one of the operands
+ is an address expression. */
+
+static void
+mips_ip (char *str, struct mips_cl_insn *ip)
+{
+ char *s;
+ const char *args;
+ char c = 0;
+ struct mips_opcode *insn;
+ char *argsStart;
+ unsigned int regno;
+ unsigned int lastregno = 0;
+ unsigned int lastpos = 0;
+ unsigned int limlo, limhi;
+ char *s_reset;
+ char save_c = 0;
+
+ insn_error = NULL;
+
+ /* If the instruction contains a '.', we first try to match an instruction
+ including the '.'. Then we try again without the '.'. */
+ insn = NULL;
+ for (s = str; *s != '\0' && !ISSPACE (*s); ++s)
+ continue;
+
+ /* If we stopped on whitespace, then replace the whitespace with null for
+ the call to hash_find. Save the character we replaced just in case we
+ have to re-parse the instruction. */
+ if (ISSPACE (*s))
+ {
+ save_c = *s;
+ *s++ = '\0';
+ }
+
+ insn = (struct mips_opcode *) hash_find (op_hash, str);
+
+ /* If we didn't find the instruction in the opcode table, try again, but
+ this time with just the instruction up to, but not including the
+ first '.'. */
+ if (insn == NULL)
+ {
+ /* Restore the character we overwrite above (if any). */
+ if (save_c)
+ *(--s) = save_c;
+
+ /* Scan up to the first '.' or whitespace. */
+ for (s = str;
+ *s != '\0' && *s != '.' && !ISSPACE (*s);
+ ++s)
+ continue;
+
+ /* If we did not find a '.', then we can quit now. */
+ if (*s != '.')
+ {
+ insn_error = "unrecognized opcode";
+ return;
+ }
+
+ /* Lookup the instruction in the hash table. */
+ *s++ = '\0';
+ if ((insn = (struct mips_opcode *) hash_find (op_hash, str)) == NULL)
+ {
+ insn_error = "unrecognized opcode";
+ return;
+ }
+ }
+
+ argsStart = s;
+ for (;;)
+ {
+ bfd_boolean ok;
+
+ assert (strcmp (insn->name, str) == 0);
+
+ if (OPCODE_IS_MEMBER (insn,
+ (mips_opts.isa
+ | (file_ase_mips16 ? INSN_MIPS16 : 0)
+ | (mips_opts.ase_mdmx ? INSN_MDMX : 0)
+ | (mips_opts.ase_mips3d ? INSN_MIPS3D : 0)),
+ mips_opts.arch))
+ ok = TRUE;
+ else
+ ok = FALSE;
+
+ if (insn->pinfo != INSN_MACRO)
+ {
+ if (mips_opts.arch == CPU_R4650 && (insn->pinfo & FP_D) != 0)
+ ok = FALSE;
+ }
+
+ if (! ok)
+ {
+ if (insn + 1 < &mips_opcodes[NUMOPCODES]
+ && strcmp (insn->name, insn[1].name) == 0)
+ {
+ ++insn;
+ continue;
+ }
+ else
+ {
+ if (!insn_error)
+ {
+ static char buf[100];
+ sprintf (buf,
+ _("opcode not supported on this processor: %s (%s)"),
+ mips_cpu_info_from_arch (mips_opts.arch)->name,
+ mips_cpu_info_from_isa (mips_opts.isa)->name);
+ insn_error = buf;
+ }
+ if (save_c)
+ *(--s) = save_c;
+ return;
+ }
+ }
+
+ ip->insn_mo = insn;
+ ip->insn_opcode = insn->match;
+ insn_error = NULL;
+ for (args = insn->args;; ++args)
+ {
+ int is_mdmx;
+
+ s += strspn (s, " \t");
+ is_mdmx = 0;
+ switch (*args)
+ {
+ case '\0': /* end of args */
+ if (*s == '\0')
+ return;
+ break;
+
+ case ',':
+ if (*s++ == *args)
+ continue;
+ s--;
+ switch (*++args)
+ {
+ case 'r':
+ case 'v':
+ ip->insn_opcode |= lastregno << OP_SH_RS;
+ continue;
+
+ case 'w':
+ ip->insn_opcode |= lastregno << OP_SH_RT;
+ continue;
+
+ case 'W':
+ ip->insn_opcode |= lastregno << OP_SH_FT;
+ continue;
+
+ case 'V':
+ ip->insn_opcode |= lastregno << OP_SH_FS;
+ continue;
+ }
+ break;
+
+ case '(':
+ /* Handle optional base register.
+ Either the base register is omitted or
+ we must have a left paren. */
+ /* This is dependent on the next operand specifier
+ is a base register specification. */
+ assert (args[1] == 'b' || args[1] == '5'
+ || args[1] == '-' || args[1] == '4');
+ if (*s == '\0')
+ return;
+
+ case ')': /* these must match exactly */
+ case '[':
+ case ']':
+ if (*s++ == *args)
+ continue;
+ break;
+
+ case '+': /* Opcode extension character. */
+ switch (*++args)
+ {
+ case 'A': /* ins/ext position, becomes LSB. */
+ limlo = 0;
+ limhi = 31;
+ goto do_lsb;
+ case 'E':
+ limlo = 32;
+ limhi = 63;
+ goto do_lsb;
+do_lsb:
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number < limlo
+ || (unsigned long) imm_expr.X_add_number > limhi)
+ {
+ as_bad (_("Improper position (%lu)"),
+ (unsigned long) imm_expr.X_add_number);
+ imm_expr.X_add_number = limlo;
+ }
+ lastpos = imm_expr.X_add_number;
+ ip->insn_opcode |= (imm_expr.X_add_number
+ & OP_MASK_SHAMT) << OP_SH_SHAMT;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'B': /* ins size, becomes MSB. */
+ limlo = 1;
+ limhi = 32;
+ goto do_msb;
+ case 'F':
+ limlo = 33;
+ limhi = 64;
+ goto do_msb;
+do_msb:
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ /* Check for negative input so that small negative numbers
+ will not succeed incorrectly. The checks against
+ (pos+size) transitively check "size" itself,
+ assuming that "pos" is reasonable. */
+ if ((long) imm_expr.X_add_number < 0
+ || ((unsigned long) imm_expr.X_add_number
+ + lastpos) < limlo
+ || ((unsigned long) imm_expr.X_add_number
+ + lastpos) > limhi)
+ {
+ as_bad (_("Improper insert size (%lu, position %lu)"),
+ (unsigned long) imm_expr.X_add_number,
+ (unsigned long) lastpos);
+ imm_expr.X_add_number = limlo - lastpos;
+ }
+ ip->insn_opcode |= ((lastpos + imm_expr.X_add_number - 1)
+ & OP_MASK_INSMSB) << OP_SH_INSMSB;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'C': /* ext size, becomes MSBD. */
+ limlo = 1;
+ limhi = 32;
+ goto do_msbd;
+ case 'G':
+ limlo = 33;
+ limhi = 64;
+ goto do_msbd;
+ case 'H':
+ limlo = 33;
+ limhi = 64;
+ goto do_msbd;
+do_msbd:
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ /* Check for negative input so that small negative numbers
+ will not succeed incorrectly. The checks against
+ (pos+size) transitively check "size" itself,
+ assuming that "pos" is reasonable. */
+ if ((long) imm_expr.X_add_number < 0
+ || ((unsigned long) imm_expr.X_add_number
+ + lastpos) < limlo
+ || ((unsigned long) imm_expr.X_add_number
+ + lastpos) > limhi)
+ {
+ as_bad (_("Improper extract size (%lu, position %lu)"),
+ (unsigned long) imm_expr.X_add_number,
+ (unsigned long) lastpos);
+ imm_expr.X_add_number = limlo - lastpos;
+ }
+ ip->insn_opcode |= ((imm_expr.X_add_number - 1)
+ & OP_MASK_EXTMSBD) << OP_SH_EXTMSBD;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'D':
+ /* +D is for disassembly only; never match. */
+ break;
+
+ case 'I':
+ /* "+I" is like "I", except that imm2_expr is used. */
+ my_getExpression (&imm2_expr, s);
+ if (imm2_expr.X_op != O_big
+ && imm2_expr.X_op != O_constant)
+ insn_error = _("absolute expression required");
+ normalize_constant_expr (&imm2_expr);
+ s = expr_end;
+ continue;
+
+ default:
+ as_bad (_("internal: bad mips opcode (unknown extension operand type `+%c'): %s %s"),
+ *args, insn->name, insn->args);
+ /* Further processing is fruitless. */
+ return;
+ }
+ break;
+
+ case '<': /* must be at least one digit */
+ /*
+ * According to the manual, if the shift amount is greater
+ * than 31 or less than 0, then the shift amount should be
+ * mod 32. In reality the mips assembler issues an error.
+ * We issue a warning and mask out all but the low 5 bits.
+ */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number > 31)
+ {
+ as_warn (_("Improper shift amount (%lu)"),
+ (unsigned long) imm_expr.X_add_number);
+ imm_expr.X_add_number &= OP_MASK_SHAMT;
+ }
+ ip->insn_opcode |= imm_expr.X_add_number << OP_SH_SHAMT;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case '>': /* shift amount minus 32 */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number < 32
+ || (unsigned long) imm_expr.X_add_number > 63)
+ break;
+ ip->insn_opcode |= (imm_expr.X_add_number - 32) << OP_SH_SHAMT;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'k': /* cache code */
+ case 'h': /* prefx code */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number > 31)
+ {
+ as_warn (_("Invalid value for `%s' (%lu)"),
+ ip->insn_mo->name,
+ (unsigned long) imm_expr.X_add_number);
+ imm_expr.X_add_number &= 0x1f;
+ }
+ if (*args == 'k')
+ ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CACHE;
+ else
+ ip->insn_opcode |= imm_expr.X_add_number << OP_SH_PREFX;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'c': /* break code */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number > 1023)
+ {
+ as_warn (_("Illegal break code (%lu)"),
+ (unsigned long) imm_expr.X_add_number);
+ imm_expr.X_add_number &= OP_MASK_CODE;
+ }
+ ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CODE;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'q': /* lower break code */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number > 1023)
+ {
+ as_warn (_("Illegal lower break code (%lu)"),
+ (unsigned long) imm_expr.X_add_number);
+ imm_expr.X_add_number &= OP_MASK_CODE2;
+ }
+ ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CODE2;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'B': /* 20-bit syscall/break code. */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE20)
+ as_warn (_("Illegal 20-bit code (%lu)"),
+ (unsigned long) imm_expr.X_add_number);
+ ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CODE20;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'C': /* Coprocessor code */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number >= (1 << 25))
+ {
+ as_warn (_("Coproccesor code > 25 bits (%lu)"),
+ (unsigned long) imm_expr.X_add_number);
+ imm_expr.X_add_number &= ((1 << 25) - 1);
+ }
+ ip->insn_opcode |= imm_expr.X_add_number;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'J': /* 19-bit wait code. */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE19)
+ as_warn (_("Illegal 19-bit code (%lu)"),
+ (unsigned long) imm_expr.X_add_number);
+ ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CODE19;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'P': /* Performance register */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if (imm_expr.X_add_number != 0 && imm_expr.X_add_number != 1)
+ {
+ as_warn (_("Invalid performance register (%lu)"),
+ (unsigned long) imm_expr.X_add_number);
+ imm_expr.X_add_number &= OP_MASK_PERFREG;
+ }
+ ip->insn_opcode |= (imm_expr.X_add_number << OP_SH_PERFREG);
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'b': /* base register */
+ case 'd': /* destination register */
+ case 's': /* source register */
+ case 't': /* target register */
+ case 'r': /* both target and source */
+ case 'v': /* both dest and source */
+ case 'w': /* both dest and target */
+ case 'E': /* coprocessor target register */
+ case 'G': /* coprocessor destination register */
+ case 'K': /* 'rdhwr' destination register */
+ case 'x': /* ignore register name */
+ case 'z': /* must be zero register */
+ case 'U': /* destination register (clo/clz). */
+ s_reset = s;
+ if (s[0] == '$')
+ {
+
+ if (ISDIGIT (s[1]))
+ {
+ ++s;
+ regno = 0;
+ do
+ {
+ regno *= 10;
+ regno += *s - '0';
+ ++s;
+ }
+ while (ISDIGIT (*s));
+ if (regno > 31)
+ as_bad (_("Invalid register number (%d)"), regno);
+ }
+ else if (*args == 'E' || *args == 'G' || *args == 'K')
+ goto notreg;
+ else
+ {
+ if (s[1] == 'r' && s[2] == 'a')
+ {
+ s += 3;
+ regno = RA;
+ }
+ else if (s[1] == 'f' && s[2] == 'p')
+ {
+ s += 3;
+ regno = FP;
+ }
+ else if (s[1] == 's' && s[2] == 'p')
+ {
+ s += 3;
+ regno = SP;
+ }
+ else if (s[1] == 'g' && s[2] == 'p')
+ {
+ s += 3;
+ regno = GP;
+ }
+ else if (s[1] == 'a' && s[2] == 't')
+ {
+ s += 3;
+ regno = AT;
+ }
+ else if (s[1] == 'k' && s[2] == 't' && s[3] == '0')
+ {
+ s += 4;
+ regno = KT0;
+ }
+ else if (s[1] == 'k' && s[2] == 't' && s[3] == '1')
+ {
+ s += 4;
+ regno = KT1;
+ }
+ else if (s[1] == 'z' && s[2] == 'e' && s[3] == 'r' && s[4] == 'o')
+ {
+ s += 5;
+ regno = ZERO;
+ }
+ else if (itbl_have_entries)
+ {
+ char *p, *n;
+ unsigned long r;
+
+ p = s + 1; /* advance past '$' */
+ n = itbl_get_field (&p); /* n is name */
+
+ /* See if this is a register defined in an
+ itbl entry. */
+ if (itbl_get_reg_val (n, &r))
+ {
+ /* Get_field advances to the start of
+ the next field, so we need to back
+ rack to the end of the last field. */
+ if (p)
+ s = p - 1;
+ else
+ s = strchr (s, '\0');
+ regno = r;
+ }
+ else
+ goto notreg;
+ }
+ else
+ goto notreg;
+ }
+ if (regno == AT
+ && ! mips_opts.noat
+ && *args != 'E'
+ && *args != 'G'
+ && *args != 'K')
+ as_warn (_("Used $at without \".set noat\""));
+ c = *args;
+ if (*s == ' ')
+ ++s;
+ if (args[1] != *s)
+ {
+ if (c == 'r' || c == 'v' || c == 'w')
+ {
+ regno = lastregno;
+ s = s_reset;
+ ++args;
+ }
+ }
+ /* 'z' only matches $0. */
+ if (c == 'z' && regno != 0)
+ break;
+
+ /* Now that we have assembled one operand, we use the args string
+ * to figure out where it goes in the instruction. */
+ switch (c)
+ {
+ case 'r':
+ case 's':
+ case 'v':
+ case 'b':
+ ip->insn_opcode |= regno << OP_SH_RS;
+ break;
+ case 'd':
+ case 'G':
+ case 'K':
+ ip->insn_opcode |= regno << OP_SH_RD;
+ break;
+ case 'U':
+ ip->insn_opcode |= regno << OP_SH_RD;
+ ip->insn_opcode |= regno << OP_SH_RT;
+ break;
+ case 'w':
+ case 't':
+ case 'E':
+ ip->insn_opcode |= regno << OP_SH_RT;
+ break;
+ case 'x':
+ /* This case exists because on the r3000 trunc
+ expands into a macro which requires a gp
+ register. On the r6000 or r4000 it is
+ assembled into a single instruction which
+ ignores the register. Thus the insn version
+ is MIPS_ISA2 and uses 'x', and the macro
+ version is MIPS_ISA1 and uses 't'. */
+ break;
+ case 'z':
+ /* This case is for the div instruction, which
+ acts differently if the destination argument
+ is $0. This only matches $0, and is checked
+ outside the switch. */
+ break;
+ case 'D':
+ /* Itbl operand; not yet implemented. FIXME ?? */
+ break;
+ /* What about all other operands like 'i', which
+ can be specified in the opcode table? */
+ }
+ lastregno = regno;
+ continue;
+ }
+ notreg:
+ switch (*args++)
+ {
+ case 'r':
+ case 'v':
+ ip->insn_opcode |= lastregno << OP_SH_RS;
+ continue;
+ case 'w':
+ ip->insn_opcode |= lastregno << OP_SH_RT;
+ continue;
+ }
+ break;
+
+ case 'O': /* MDMX alignment immediate constant. */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number > OP_MASK_ALN)
+ {
+ as_warn ("Improper align amount (%ld), using low bits",
+ (long) imm_expr.X_add_number);
+ imm_expr.X_add_number &= OP_MASK_ALN;
+ }
+ ip->insn_opcode |= imm_expr.X_add_number << OP_SH_ALN;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'Q': /* MDMX vector, element sel, or const. */
+ if (s[0] != '$')
+ {
+ /* MDMX Immediate. */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number > OP_MASK_FT)
+ {
+ as_warn (_("Invalid MDMX Immediate (%ld)"),
+ (long) imm_expr.X_add_number);
+ imm_expr.X_add_number &= OP_MASK_FT;
+ }
+ imm_expr.X_add_number &= OP_MASK_FT;
+ if (ip->insn_opcode & (OP_MASK_VSEL << OP_SH_VSEL))
+ ip->insn_opcode |= MDMX_FMTSEL_IMM_QH << OP_SH_VSEL;
+ else
+ ip->insn_opcode |= MDMX_FMTSEL_IMM_OB << OP_SH_VSEL;
+ ip->insn_opcode |= imm_expr.X_add_number << OP_SH_FT;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+ }
+ /* Not MDMX Immediate. Fall through. */
+ case 'X': /* MDMX destination register. */
+ case 'Y': /* MDMX source register. */
+ case 'Z': /* MDMX target register. */
+ is_mdmx = 1;
+ case 'D': /* floating point destination register */
+ case 'S': /* floating point source register */
+ case 'T': /* floating point target register */
+ case 'R': /* floating point source register */
+ case 'V':
+ case 'W':
+ s_reset = s;
+ /* Accept $fN for FP and MDMX register numbers, and in
+ addition accept $vN for MDMX register numbers. */
+ if ((s[0] == '$' && s[1] == 'f' && ISDIGIT (s[2]))
+ || (is_mdmx != 0 && s[0] == '$' && s[1] == 'v'
+ && ISDIGIT (s[2])))
+ {
+ s += 2;
+ regno = 0;
+ do
+ {
+ regno *= 10;
+ regno += *s - '0';
+ ++s;
+ }
+ while (ISDIGIT (*s));
+
+ if (regno > 31)
+ as_bad (_("Invalid float register number (%d)"), regno);
+
+ if ((regno & 1) != 0
+ && HAVE_32BIT_FPRS
+ && ! (strcmp (str, "mtc1") == 0
+ || strcmp (str, "mfc1") == 0
+ || strcmp (str, "lwc1") == 0
+ || strcmp (str, "swc1") == 0
+ || strcmp (str, "l.s") == 0
+ || strcmp (str, "s.s") == 0))
+ as_warn (_("Float register should be even, was %d"),
+ regno);
+
+ c = *args;
+ if (*s == ' ')
+ ++s;
+ if (args[1] != *s)
+ {
+ if (c == 'V' || c == 'W')
+ {
+ regno = lastregno;
+ s = s_reset;
+ ++args;
+ }
+ }
+ switch (c)
+ {
+ case 'D':
+ case 'X':
+ ip->insn_opcode |= regno << OP_SH_FD;
+ break;
+ case 'V':
+ case 'S':
+ case 'Y':
+ ip->insn_opcode |= regno << OP_SH_FS;
+ break;
+ case 'Q':
+ /* This is like 'Z', but also needs to fix the MDMX
+ vector/scalar select bits. Note that the
+ scalar immediate case is handled above. */
+ if (*s == '[')
+ {
+ int is_qh = (ip->insn_opcode & (1 << OP_SH_VSEL));
+ int max_el = (is_qh ? 3 : 7);
+ s++;
+ my_getExpression(&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ s = expr_end;
+ if (imm_expr.X_add_number > max_el)
+ as_bad(_("Bad element selector %ld"),
+ (long) imm_expr.X_add_number);
+ imm_expr.X_add_number &= max_el;
+ ip->insn_opcode |= (imm_expr.X_add_number
+ << (OP_SH_VSEL +
+ (is_qh ? 2 : 1)));
+ if (*s != ']')
+ as_warn(_("Expecting ']' found '%s'"), s);
+ else
+ s++;
+ }
+ else
+ {
+ if (ip->insn_opcode & (OP_MASK_VSEL << OP_SH_VSEL))
+ ip->insn_opcode |= (MDMX_FMTSEL_VEC_QH
+ << OP_SH_VSEL);
+ else
+ ip->insn_opcode |= (MDMX_FMTSEL_VEC_OB <<
+ OP_SH_VSEL);
+ }
+ /* Fall through */
+ case 'W':
+ case 'T':
+ case 'Z':
+ ip->insn_opcode |= regno << OP_SH_FT;
+ break;
+ case 'R':
+ ip->insn_opcode |= regno << OP_SH_FR;
+ break;
+ }
+ lastregno = regno;
+ continue;
+ }
+
+ switch (*args++)
+ {
+ case 'V':
+ ip->insn_opcode |= lastregno << OP_SH_FS;
+ continue;
+ case 'W':
+ ip->insn_opcode |= lastregno << OP_SH_FT;
+ continue;
+ }
+ break;
+
+ case 'I':
+ my_getExpression (&imm_expr, s);
+ if (imm_expr.X_op != O_big
+ && imm_expr.X_op != O_constant)
+ insn_error = _("absolute expression required");
+ normalize_constant_expr (&imm_expr);
+ s = expr_end;
+ continue;
+
+ case 'A':
+ my_getExpression (&offset_expr, s);
+ *imm_reloc = BFD_RELOC_32;
+ s = expr_end;
+ continue;
+
+ case 'F':
+ case 'L':
+ case 'f':
+ case 'l':
+ {
+ int f64;
+ int using_gprs;
+ char *save_in;
+ char *err;
+ unsigned char temp[8];
+ int len;
+ unsigned int length;
+ segT seg;
+ subsegT subseg;
+ char *p;
+
+ /* These only appear as the last operand in an
+ instruction, and every instruction that accepts
+ them in any variant accepts them in all variants.
+ This means we don't have to worry about backing out
+ any changes if the instruction does not match.
+
+ The difference between them is the size of the
+ floating point constant and where it goes. For 'F'
+ and 'L' the constant is 64 bits; for 'f' and 'l' it
+ is 32 bits. Where the constant is placed is based
+ on how the MIPS assembler does things:
+ F -- .rdata
+ L -- .lit8
+ f -- immediate value
+ l -- .lit4
+
+ The .lit4 and .lit8 sections are only used if
+ permitted by the -G argument.
+
+ When generating embedded PIC code, we use the
+ .lit8 section but not the .lit4 section (we can do
+ .lit4 inline easily; we need to put .lit8
+ somewhere in the data segment, and using .lit8
+ permits the linker to eventually combine identical
+ .lit8 entries).
+
+ The code below needs to know whether the target register
+ is 32 or 64 bits wide. It relies on the fact 'f' and
+ 'F' are used with GPR-based instructions and 'l' and
+ 'L' are used with FPR-based instructions. */
+
+ f64 = *args == 'F' || *args == 'L';
+ using_gprs = *args == 'F' || *args == 'f';
+
+ save_in = input_line_pointer;
+ input_line_pointer = s;
+ err = md_atof (f64 ? 'd' : 'f', (char *) temp, &len);
+ length = len;
+ s = input_line_pointer;
+ input_line_pointer = save_in;
+ if (err != NULL && *err != '\0')
+ {
+ as_bad (_("Bad floating point constant: %s"), err);
+ memset (temp, '\0', sizeof temp);
+ length = f64 ? 8 : 4;
+ }
+
+ assert (length == (unsigned) (f64 ? 8 : 4));
+
+ if (*args == 'f'
+ || (*args == 'l'
+ && (! USE_GLOBAL_POINTER_OPT
+ || mips_pic == EMBEDDED_PIC
+ || g_switch_value < 4
+ || (temp[0] == 0 && temp[1] == 0)
+ || (temp[2] == 0 && temp[3] == 0))))
+ {
+ imm_expr.X_op = O_constant;
+ if (! target_big_endian)
+ imm_expr.X_add_number = bfd_getl32 (temp);
+ else
+ imm_expr.X_add_number = bfd_getb32 (temp);
+ }
+ else if (length > 4
+ && ! mips_disable_float_construction
+ /* Constants can only be constructed in GPRs and
+ copied to FPRs if the GPRs are at least as wide
+ as the FPRs. Force the constant into memory if
+ we are using 64-bit FPRs but the GPRs are only
+ 32 bits wide. */
+ && (using_gprs
+ || ! (HAVE_64BIT_FPRS && HAVE_32BIT_GPRS))
+ && ((temp[0] == 0 && temp[1] == 0)
+ || (temp[2] == 0 && temp[3] == 0))
+ && ((temp[4] == 0 && temp[5] == 0)
+ || (temp[6] == 0 && temp[7] == 0)))
+ {
+ /* The value is simple enough to load with a couple of
+ instructions. If using 32-bit registers, set
+ imm_expr to the high order 32 bits and offset_expr to
+ the low order 32 bits. Otherwise, set imm_expr to
+ the entire 64 bit constant. */
+ if (using_gprs ? HAVE_32BIT_GPRS : HAVE_32BIT_FPRS)
+ {
+ imm_expr.X_op = O_constant;
+ offset_expr.X_op = O_constant;
+ if (! target_big_endian)
+ {
+ imm_expr.X_add_number = bfd_getl32 (temp + 4);
+ offset_expr.X_add_number = bfd_getl32 (temp);
+ }
+ else
+ {
+ imm_expr.X_add_number = bfd_getb32 (temp);
+ offset_expr.X_add_number = bfd_getb32 (temp + 4);
+ }
+ if (offset_expr.X_add_number == 0)
+ offset_expr.X_op = O_absent;
+ }
+ else if (sizeof (imm_expr.X_add_number) > 4)
+ {
+ imm_expr.X_op = O_constant;
+ if (! target_big_endian)
+ imm_expr.X_add_number = bfd_getl64 (temp);
+ else
+ imm_expr.X_add_number = bfd_getb64 (temp);
+ }
+ else
+ {
+ imm_expr.X_op = O_big;
+ imm_expr.X_add_number = 4;
+ if (! target_big_endian)
+ {
+ generic_bignum[0] = bfd_getl16 (temp);
+ generic_bignum[1] = bfd_getl16 (temp + 2);
+ generic_bignum[2] = bfd_getl16 (temp + 4);
+ generic_bignum[3] = bfd_getl16 (temp + 6);
+ }
+ else
+ {
+ generic_bignum[0] = bfd_getb16 (temp + 6);
+ generic_bignum[1] = bfd_getb16 (temp + 4);
+ generic_bignum[2] = bfd_getb16 (temp + 2);
+ generic_bignum[3] = bfd_getb16 (temp);
+ }
+ }
+ }
+ else
+ {
+ const char *newname;
+ segT new_seg;
+
+ /* Switch to the right section. */
+ seg = now_seg;
+ subseg = now_subseg;
+ switch (*args)
+ {
+ default: /* unused default case avoids warnings. */
+ case 'L':
+ newname = RDATA_SECTION_NAME;
+ if ((USE_GLOBAL_POINTER_OPT && g_switch_value >= 8)
+ || mips_pic == EMBEDDED_PIC)
+ newname = ".lit8";
+ break;
+ case 'F':
+ if (mips_pic == EMBEDDED_PIC)
+ newname = ".lit8";
+ else
+ newname = RDATA_SECTION_NAME;
+ break;
+ case 'l':
+ assert (!USE_GLOBAL_POINTER_OPT
+ || g_switch_value >= 4);
+ newname = ".lit4";
+ break;
+ }
+ new_seg = subseg_new (newname, (subsegT) 0);
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ bfd_set_section_flags (stdoutput, new_seg,
+ (SEC_ALLOC
+ | SEC_LOAD
+ | SEC_READONLY
+ | SEC_DATA));
+ frag_align (*args == 'l' ? 2 : 3, 0, 0);
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour
+ && strcmp (TARGET_OS, "elf") != 0)
+ record_alignment (new_seg, 4);
+ else
+ record_alignment (new_seg, *args == 'l' ? 2 : 3);
+ if (seg == now_seg)
+ as_bad (_("Can't use floating point insn in this section"));
+
+ /* Set the argument to the current address in the
+ section. */
+ offset_expr.X_op = O_symbol;
+ offset_expr.X_add_symbol =
+ symbol_new ("L0\001", now_seg,
+ (valueT) frag_now_fix (), frag_now);
+ offset_expr.X_add_number = 0;
+
+ /* Put the floating point number into the section. */
+ p = frag_more ((int) length);
+ memcpy (p, temp, length);
+
+ /* Switch back to the original section. */
+ subseg_set (seg, subseg);
+ }
+ }
+ continue;
+
+ case 'i': /* 16 bit unsigned immediate */
+ case 'j': /* 16 bit signed immediate */
+ *imm_reloc = BFD_RELOC_LO16;
+ if (my_getSmallExpression (&imm_expr, imm_reloc, s) == 0)
+ {
+ int more;
+ offsetT minval, maxval;
+
+ more = (insn + 1 < &mips_opcodes[NUMOPCODES]
+ && strcmp (insn->name, insn[1].name) == 0);
+
+ /* If the expression was written as an unsigned number,
+ only treat it as signed if there are no more
+ alternatives. */
+ if (more
+ && *args == 'j'
+ && sizeof (imm_expr.X_add_number) <= 4
+ && imm_expr.X_op == O_constant
+ && imm_expr.X_add_number < 0
+ && imm_expr.X_unsigned
+ && HAVE_64BIT_GPRS)
+ break;
+
+ /* For compatibility with older assemblers, we accept
+ 0x8000-0xffff as signed 16-bit numbers when only
+ signed numbers are allowed. */
+ if (*args == 'i')
+ minval = 0, maxval = 0xffff;
+ else if (more)
+ minval = -0x8000, maxval = 0x7fff;
+ else
+ minval = -0x8000, maxval = 0xffff;
+
+ if (imm_expr.X_op != O_constant
+ || imm_expr.X_add_number < minval
+ || imm_expr.X_add_number > maxval)
+ {
+ if (more)
+ break;
+ if (imm_expr.X_op == O_constant
+ || imm_expr.X_op == O_big)
+ as_bad (_("expression out of range"));
+ }
+ }
+ s = expr_end;
+ continue;
+
+ case 'o': /* 16 bit offset */
+ /* Check whether there is only a single bracketed expression
+ left. If so, it must be the base register and the
+ constant must be zero. */
+ if (*s == '(' && strchr (s + 1, '(') == 0)
+ {
+ offset_expr.X_op = O_constant;
+ offset_expr.X_add_number = 0;
+ continue;
+ }
+
+ /* If this value won't fit into a 16 bit offset, then go
+ find a macro that will generate the 32 bit offset
+ code pattern. */
+ if (my_getSmallExpression (&offset_expr, offset_reloc, s) == 0
+ && (offset_expr.X_op != O_constant
+ || offset_expr.X_add_number >= 0x8000
+ || offset_expr.X_add_number < -0x8000))
+ break;
+
+ s = expr_end;
+ continue;
+
+ case 'p': /* pc relative offset */
+ *offset_reloc = BFD_RELOC_16_PCREL_S2;
+ my_getExpression (&offset_expr, s);
+ s = expr_end;
+ continue;
+
+ case 'u': /* upper 16 bits */
+ if (my_getSmallExpression (&imm_expr, imm_reloc, s) == 0
+ && imm_expr.X_op == O_constant
+ && (imm_expr.X_add_number < 0
+ || imm_expr.X_add_number >= 0x10000))
+ as_bad (_("lui expression not in range 0..65535"));
+ s = expr_end;
+ continue;
+
+ case 'a': /* 26 bit address */
+ my_getExpression (&offset_expr, s);
+ s = expr_end;
+ *offset_reloc = BFD_RELOC_MIPS_JMP;
+ continue;
+
+ case 'N': /* 3 bit branch condition code */
+ case 'M': /* 3 bit compare condition code */
+ if (strncmp (s, "$fcc", 4) != 0)
+ break;
+ s += 4;
+ regno = 0;
+ do
+ {
+ regno *= 10;
+ regno += *s - '0';
+ ++s;
+ }
+ while (ISDIGIT (*s));
+ if (regno > 7)
+ as_bad (_("Invalid condition code register $fcc%d"), regno);
+ if ((strcmp(str + strlen(str) - 3, ".ps") == 0
+ || strcmp(str + strlen(str) - 5, "any2f") == 0
+ || strcmp(str + strlen(str) - 5, "any2t") == 0)
+ && (regno & 1) != 0)
+ as_warn(_("Condition code register should be even for %s, was %d"),
+ str, regno);
+ if ((strcmp(str + strlen(str) - 5, "any4f") == 0
+ || strcmp(str + strlen(str) - 5, "any4t") == 0)
+ && (regno & 3) != 0)
+ as_warn(_("Condition code register should be 0 or 4 for %s, was %d"),
+ str, regno);
+ if (*args == 'N')
+ ip->insn_opcode |= regno << OP_SH_BCC;
+ else
+ ip->insn_opcode |= regno << OP_SH_CCC;
+ continue;
+
+ case 'H':
+ if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
+ s += 2;
+ if (ISDIGIT (*s))
+ {
+ c = 0;
+ do
+ {
+ c *= 10;
+ c += *s - '0';
+ ++s;
+ }
+ while (ISDIGIT (*s));
+ }
+ else
+ c = 8; /* Invalid sel value. */
+
+ if (c > 7)
+ as_bad (_("invalid coprocessor sub-selection value (0-7)"));
+ ip->insn_opcode |= c;
+ continue;
+
+ case 'e':
+ /* Must be at least one digit. */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+
+ if ((unsigned long) imm_expr.X_add_number
+ > (unsigned long) OP_MASK_VECBYTE)
+ {
+ as_bad (_("bad byte vector index (%ld)"),
+ (long) imm_expr.X_add_number);
+ imm_expr.X_add_number = 0;
+ }
+
+ ip->insn_opcode |= imm_expr.X_add_number << OP_SH_VECBYTE;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case '%':
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+
+ if ((unsigned long) imm_expr.X_add_number
+ > (unsigned long) OP_MASK_VECALIGN)
+ {
+ as_bad (_("bad byte vector index (%ld)"),
+ (long) imm_expr.X_add_number);
+ imm_expr.X_add_number = 0;
+ }
+
+ ip->insn_opcode |= imm_expr.X_add_number << OP_SH_VECALIGN;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ default:
+ as_bad (_("bad char = '%c'\n"), *args);
+ internalError ();
+ }
+ break;
+ }
+ /* Args don't match. */
+ if (insn + 1 < &mips_opcodes[NUMOPCODES] &&
+ !strcmp (insn->name, insn[1].name))
+ {
+ ++insn;
+ s = argsStart;
+ insn_error = _("illegal operands");
+ continue;
+ }
+ if (save_c)
+ *(--s) = save_c;
+ insn_error = _("illegal operands");
+ return;
+ }
+}
+
+/* This routine assembles an instruction into its binary format when
+ assembling for the mips16. As a side effect, it sets one of the
+ global variables imm_reloc or offset_reloc to the type of
+ relocation to do if one of the operands is an address expression.
+ It also sets mips16_small and mips16_ext if the user explicitly
+ requested a small or extended instruction. */
+
+static void
+mips16_ip (char *str, struct mips_cl_insn *ip)
+{
+ char *s;
+ const char *args;
+ struct mips_opcode *insn;
+ char *argsstart;
+ unsigned int regno;
+ unsigned int lastregno = 0;
+ char *s_reset;
+
+ insn_error = NULL;
+
+ mips16_small = FALSE;
+ mips16_ext = FALSE;
+
+ for (s = str; ISLOWER (*s); ++s)
+ ;
+ switch (*s)
+ {
+ case '\0':
+ break;
+
+ case ' ':
+ *s++ = '\0';
+ break;
+
+ case '.':
+ if (s[1] == 't' && s[2] == ' ')
+ {
+ *s = '\0';
+ mips16_small = TRUE;
+ s += 3;
+ break;
+ }
+ else if (s[1] == 'e' && s[2] == ' ')
+ {
+ *s = '\0';
+ mips16_ext = TRUE;
+ s += 3;
+ break;
+ }
+ /* Fall through. */
+ default:
+ insn_error = _("unknown opcode");
+ return;
+ }
+
+ if (mips_opts.noautoextend && ! mips16_ext)
+ mips16_small = TRUE;
+
+ if ((insn = (struct mips_opcode *) hash_find (mips16_op_hash, str)) == NULL)
+ {
+ insn_error = _("unrecognized opcode");
+ return;
+ }
+
+ argsstart = s;
+ for (;;)
+ {
+ assert (strcmp (insn->name, str) == 0);
+
+ ip->insn_mo = insn;
+ ip->insn_opcode = insn->match;
+ ip->use_extend = FALSE;
+ imm_expr.X_op = O_absent;
+ imm_reloc[0] = BFD_RELOC_UNUSED;
+ imm_reloc[1] = BFD_RELOC_UNUSED;
+ imm_reloc[2] = BFD_RELOC_UNUSED;
+ imm2_expr.X_op = O_absent;
+ offset_expr.X_op = O_absent;
+ offset_reloc[0] = BFD_RELOC_UNUSED;
+ offset_reloc[1] = BFD_RELOC_UNUSED;
+ offset_reloc[2] = BFD_RELOC_UNUSED;
+ for (args = insn->args; 1; ++args)
+ {
+ int c;
+
+ if (*s == ' ')
+ ++s;
+
+ /* In this switch statement we call break if we did not find
+ a match, continue if we did find a match, or return if we
+ are done. */
+
+ c = *args;
+ switch (c)
+ {
+ case '\0':
+ if (*s == '\0')
+ {
+ /* Stuff the immediate value in now, if we can. */
+ if (imm_expr.X_op == O_constant
+ && *imm_reloc > BFD_RELOC_UNUSED
+ && insn->pinfo != INSN_MACRO)
+ {
+ mips16_immed (NULL, 0, *imm_reloc - BFD_RELOC_UNUSED,
+ imm_expr.X_add_number, TRUE, mips16_small,
+ mips16_ext, &ip->insn_opcode,
+ &ip->use_extend, &ip->extend);
+ imm_expr.X_op = O_absent;
+ *imm_reloc = BFD_RELOC_UNUSED;
+ }
+
+ return;
+ }
+ break;
+
+ case ',':
+ if (*s++ == c)
+ continue;
+ s--;
+ switch (*++args)
+ {
+ case 'v':
+ ip->insn_opcode |= lastregno << MIPS16OP_SH_RX;
+ continue;
+ case 'w':
+ ip->insn_opcode |= lastregno << MIPS16OP_SH_RY;
+ continue;
+ }
+ break;
+
+ case '(':
+ case ')':
+ if (*s++ == c)
+ continue;
+ break;
+
+ case 'v':
+ case 'w':
+ if (s[0] != '$')
+ {
+ if (c == 'v')
+ ip->insn_opcode |= lastregno << MIPS16OP_SH_RX;
+ else
+ ip->insn_opcode |= lastregno << MIPS16OP_SH_RY;
+ ++args;
+ continue;
+ }
+ /* Fall through. */
+ case 'x':
+ case 'y':
+ case 'z':
+ case 'Z':
+ case '0':
+ case 'S':
+ case 'R':
+ case 'X':
+ case 'Y':
+ if (s[0] != '$')
+ break;
+ s_reset = s;
+ if (ISDIGIT (s[1]))
+ {
+ ++s;
+ regno = 0;
+ do
+ {
+ regno *= 10;
+ regno += *s - '0';
+ ++s;
+ }
+ while (ISDIGIT (*s));
+ if (regno > 31)
+ {
+ as_bad (_("invalid register number (%d)"), regno);
+ regno = 2;
+ }
+ }
+ else
+ {
+ if (s[1] == 'r' && s[2] == 'a')
+ {
+ s += 3;
+ regno = RA;
+ }
+ else if (s[1] == 'f' && s[2] == 'p')
+ {
+ s += 3;
+ regno = FP;
+ }
+ else if (s[1] == 's' && s[2] == 'p')
+ {
+ s += 3;
+ regno = SP;
+ }
+ else if (s[1] == 'g' && s[2] == 'p')
+ {
+ s += 3;
+ regno = GP;
+ }
+ else if (s[1] == 'a' && s[2] == 't')
+ {
+ s += 3;
+ regno = AT;
+ }
+ else if (s[1] == 'k' && s[2] == 't' && s[3] == '0')
+ {
+ s += 4;
+ regno = KT0;
+ }
+ else if (s[1] == 'k' && s[2] == 't' && s[3] == '1')
+ {
+ s += 4;
+ regno = KT1;
+ }
+ else if (s[1] == 'z' && s[2] == 'e' && s[3] == 'r' && s[4] == 'o')
+ {
+ s += 5;
+ regno = ZERO;
+ }
+ else
+ break;
+ }
+
+ if (*s == ' ')
+ ++s;
+ if (args[1] != *s)
+ {
+ if (c == 'v' || c == 'w')
+ {
+ regno = mips16_to_32_reg_map[lastregno];
+ s = s_reset;
+ ++args;
+ }
+ }
+
+ switch (c)
+ {
+ case 'x':
+ case 'y':
+ case 'z':
+ case 'v':
+ case 'w':
+ case 'Z':
+ regno = mips32_to_16_reg_map[regno];
+ break;
+
+ case '0':
+ if (regno != 0)
+ regno = ILLEGAL_REG;
+ break;
+
+ case 'S':
+ if (regno != SP)
+ regno = ILLEGAL_REG;
+ break;
+
+ case 'R':
+ if (regno != RA)
+ regno = ILLEGAL_REG;
+ break;
+
+ case 'X':
+ case 'Y':
+ if (regno == AT && ! mips_opts.noat)
+ as_warn (_("used $at without \".set noat\""));
+ break;
+
+ default:
+ internalError ();
+ }
+
+ if (regno == ILLEGAL_REG)
+ break;
+
+ switch (c)
+ {
+ case 'x':
+ case 'v':
+ ip->insn_opcode |= regno << MIPS16OP_SH_RX;
+ break;
+ case 'y':
+ case 'w':
+ ip->insn_opcode |= regno << MIPS16OP_SH_RY;
+ break;
+ case 'z':
+ ip->insn_opcode |= regno << MIPS16OP_SH_RZ;
+ break;
+ case 'Z':
+ ip->insn_opcode |= regno << MIPS16OP_SH_MOVE32Z;
+ case '0':
+ case 'S':
+ case 'R':
+ break;
+ case 'X':
+ ip->insn_opcode |= regno << MIPS16OP_SH_REGR32;
+ break;
+ case 'Y':
+ regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3);
+ ip->insn_opcode |= regno << MIPS16OP_SH_REG32R;
+ break;
+ default:
+ internalError ();
+ }
+
+ lastregno = regno;
+ continue;
+
+ case 'P':
+ if (strncmp (s, "$pc", 3) == 0)
+ {
+ s += 3;
+ continue;
+ }
+ break;
+
+ case '<':
+ case '>':
+ case '[':
+ case ']':
+ case '4':
+ case '5':
+ case 'H':
+ case 'W':
+ case 'D':
+ case 'j':
+ case '8':
+ case 'V':
+ case 'C':
+ case 'U':
+ case 'k':
+ case 'K':
+ if (s[0] == '%'
+ && strncmp (s + 1, "gprel(", sizeof "gprel(" - 1) == 0)
+ {
+ /* This is %gprel(SYMBOL). We need to read SYMBOL,
+ and generate the appropriate reloc. If the text
+ inside %gprel is not a symbol name with an
+ optional offset, then we generate a normal reloc
+ and will probably fail later. */
+ my_getExpression (&imm_expr, s + sizeof "%gprel" - 1);
+ if (imm_expr.X_op == O_symbol)
+ {
+ mips16_ext = TRUE;
+ *imm_reloc = BFD_RELOC_MIPS16_GPREL;
+ s = expr_end;
+ ip->use_extend = TRUE;
+ ip->extend = 0;
+ continue;
+ }
+ }
+ else
+ {
+ /* Just pick up a normal expression. */
+ my_getExpression (&imm_expr, s);
+ }
+
+ if (imm_expr.X_op == O_register)
+ {
+ /* What we thought was an expression turned out to
+ be a register. */
+
+ if (s[0] == '(' && args[1] == '(')
+ {
+ /* It looks like the expression was omitted
+ before a register indirection, which means
+ that the expression is implicitly zero. We
+ still set up imm_expr, so that we handle
+ explicit extensions correctly. */
+ imm_expr.X_op = O_constant;
+ imm_expr.X_add_number = 0;
+ *imm_reloc = (int) BFD_RELOC_UNUSED + c;
+ continue;
+ }
+
+ break;
+ }
+
+ /* We need to relax this instruction. */
+ *imm_reloc = (int) BFD_RELOC_UNUSED + c;
+ s = expr_end;
+ continue;
+
+ case 'p':
+ case 'q':
+ case 'A':
+ case 'B':
+ case 'E':
+ /* We use offset_reloc rather than imm_reloc for the PC
+ relative operands. This lets macros with both
+ immediate and address operands work correctly. */
+ my_getExpression (&offset_expr, s);
+
+ if (offset_expr.X_op == O_register)
+ break;
+
+ /* We need to relax this instruction. */
+ *offset_reloc = (int) BFD_RELOC_UNUSED + c;
+ s = expr_end;
+ continue;
+
+ case '6': /* break code */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number > 63)
+ {
+ as_warn (_("Invalid value for `%s' (%lu)"),
+ ip->insn_mo->name,
+ (unsigned long) imm_expr.X_add_number);
+ imm_expr.X_add_number &= 0x3f;
+ }
+ ip->insn_opcode |= imm_expr.X_add_number << MIPS16OP_SH_IMM6;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'a': /* 26 bit address */
+ my_getExpression (&offset_expr, s);
+ s = expr_end;
+ *offset_reloc = BFD_RELOC_MIPS16_JMP;
+ ip->insn_opcode <<= 16;
+ continue;
+
+ case 'l': /* register list for entry macro */
+ case 'L': /* register list for exit macro */
+ {
+ int mask;
+
+ if (c == 'l')
+ mask = 0;
+ else
+ mask = 7 << 3;
+ while (*s != '\0')
+ {
+ int freg, reg1, reg2;
+
+ while (*s == ' ' || *s == ',')
+ ++s;
+ if (*s != '$')
+ {
+ as_bad (_("can't parse register list"));
+ break;
+ }
+ ++s;
+ if (*s != 'f')
+ freg = 0;
+ else
+ {
+ freg = 1;
+ ++s;
+ }
+ reg1 = 0;
+ while (ISDIGIT (*s))
+ {
+ reg1 *= 10;
+ reg1 += *s - '0';
+ ++s;
+ }
+ if (*s == ' ')
+ ++s;
+ if (*s != '-')
+ reg2 = reg1;
+ else
+ {
+ ++s;
+ if (*s != '$')
+ break;
+ ++s;
+ if (freg)
+ {
+ if (*s == 'f')
+ ++s;
+ else
+ {
+ as_bad (_("invalid register list"));
+ break;
+ }
+ }
+ reg2 = 0;
+ while (ISDIGIT (*s))
+ {
+ reg2 *= 10;
+ reg2 += *s - '0';
+ ++s;
+ }
+ }
+ if (freg && reg1 == 0 && reg2 == 0 && c == 'L')
+ {
+ mask &= ~ (7 << 3);
+ mask |= 5 << 3;
+ }
+ else if (freg && reg1 == 0 && reg2 == 1 && c == 'L')
+ {
+ mask &= ~ (7 << 3);
+ mask |= 6 << 3;
+ }
+ else if (reg1 == 4 && reg2 >= 4 && reg2 <= 7 && c != 'L')
+ mask |= (reg2 - 3) << 3;
+ else if (reg1 == 16 && reg2 >= 16 && reg2 <= 17)
+ mask |= (reg2 - 15) << 1;
+ else if (reg1 == RA && reg2 == RA)
+ mask |= 1;
+ else
+ {
+ as_bad (_("invalid register list"));
+ break;
+ }
+ }
+ /* The mask is filled in in the opcode table for the
+ benefit of the disassembler. We remove it before
+ applying the actual mask. */
+ ip->insn_opcode &= ~ ((7 << 3) << MIPS16OP_SH_IMM6);
+ ip->insn_opcode |= mask << MIPS16OP_SH_IMM6;
+ }
+ continue;
+
+ case 'e': /* extend code */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number > 0x7ff)
+ {
+ as_warn (_("Invalid value for `%s' (%lu)"),
+ ip->insn_mo->name,
+ (unsigned long) imm_expr.X_add_number);
+ imm_expr.X_add_number &= 0x7ff;
+ }
+ ip->insn_opcode |= imm_expr.X_add_number;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ default:
+ internalError ();
+ }
+ break;
+ }
+
+ /* Args don't match. */
+ if (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes] &&
+ strcmp (insn->name, insn[1].name) == 0)
+ {
+ ++insn;
+ s = argsstart;
+ continue;
+ }
+
+ insn_error = _("illegal operands");
+
+ return;
+ }
+}
+
+/* This structure holds information we know about a mips16 immediate
+ argument type. */
+
+struct mips16_immed_operand
+{
+ /* The type code used in the argument string in the opcode table. */
+ int type;
+ /* The number of bits in the short form of the opcode. */
+ int nbits;
+ /* The number of bits in the extended form of the opcode. */
+ int extbits;
+ /* The amount by which the short form is shifted when it is used;
+ for example, the sw instruction has a shift count of 2. */
+ int shift;
+ /* The amount by which the short form is shifted when it is stored
+ into the instruction code. */
+ int op_shift;
+ /* Non-zero if the short form is unsigned. */
+ int unsp;
+ /* Non-zero if the extended form is unsigned. */
+ int extu;
+ /* Non-zero if the value is PC relative. */
+ int pcrel;
+};
+
+/* The mips16 immediate operand types. */
+
+static const struct mips16_immed_operand mips16_immed_operands[] =
+{
+ { '<', 3, 5, 0, MIPS16OP_SH_RZ, 1, 1, 0 },
+ { '>', 3, 5, 0, MIPS16OP_SH_RX, 1, 1, 0 },
+ { '[', 3, 6, 0, MIPS16OP_SH_RZ, 1, 1, 0 },
+ { ']', 3, 6, 0, MIPS16OP_SH_RX, 1, 1, 0 },
+ { '4', 4, 15, 0, MIPS16OP_SH_IMM4, 0, 0, 0 },
+ { '5', 5, 16, 0, MIPS16OP_SH_IMM5, 1, 0, 0 },
+ { 'H', 5, 16, 1, MIPS16OP_SH_IMM5, 1, 0, 0 },
+ { 'W', 5, 16, 2, MIPS16OP_SH_IMM5, 1, 0, 0 },
+ { 'D', 5, 16, 3, MIPS16OP_SH_IMM5, 1, 0, 0 },
+ { 'j', 5, 16, 0, MIPS16OP_SH_IMM5, 0, 0, 0 },
+ { '8', 8, 16, 0, MIPS16OP_SH_IMM8, 1, 0, 0 },
+ { 'V', 8, 16, 2, MIPS16OP_SH_IMM8, 1, 0, 0 },
+ { 'C', 8, 16, 3, MIPS16OP_SH_IMM8, 1, 0, 0 },
+ { 'U', 8, 16, 0, MIPS16OP_SH_IMM8, 1, 1, 0 },
+ { 'k', 8, 16, 0, MIPS16OP_SH_IMM8, 0, 0, 0 },
+ { 'K', 8, 16, 3, MIPS16OP_SH_IMM8, 0, 0, 0 },
+ { 'p', 8, 16, 0, MIPS16OP_SH_IMM8, 0, 0, 1 },
+ { 'q', 11, 16, 0, MIPS16OP_SH_IMM8, 0, 0, 1 },
+ { 'A', 8, 16, 2, MIPS16OP_SH_IMM8, 1, 0, 1 },
+ { 'B', 5, 16, 3, MIPS16OP_SH_IMM5, 1, 0, 1 },
+ { 'E', 5, 16, 2, MIPS16OP_SH_IMM5, 1, 0, 1 }
+};
+
+#define MIPS16_NUM_IMMED \
+ (sizeof mips16_immed_operands / sizeof mips16_immed_operands[0])
+
+/* Handle a mips16 instruction with an immediate value. This or's the
+ small immediate value into *INSN. It sets *USE_EXTEND to indicate
+ whether an extended value is needed; if one is needed, it sets
+ *EXTEND to the value. The argument type is TYPE. The value is VAL.
+ If SMALL is true, an unextended opcode was explicitly requested.
+ If EXT is true, an extended opcode was explicitly requested. If
+ WARN is true, warn if EXT does not match reality. */
+
+static void
+mips16_immed (char *file, unsigned int line, int type, offsetT val,
+ bfd_boolean warn, bfd_boolean small, bfd_boolean ext,
+ unsigned long *insn, bfd_boolean *use_extend,
+ unsigned short *extend)
+{
+ register const struct mips16_immed_operand *op;
+ int mintiny, maxtiny;
+ bfd_boolean needext;
+
+ op = mips16_immed_operands;
+ while (op->type != type)
+ {
+ ++op;
+ assert (op < mips16_immed_operands + MIPS16_NUM_IMMED);
+ }
+
+ if (op->unsp)
+ {
+ if (type == '<' || type == '>' || type == '[' || type == ']')
+ {
+ mintiny = 1;
+ maxtiny = 1 << op->nbits;
+ }
+ else
+ {
+ mintiny = 0;
+ maxtiny = (1 << op->nbits) - 1;
+ }
+ }
+ else
+ {
+ mintiny = - (1 << (op->nbits - 1));
+ maxtiny = (1 << (op->nbits - 1)) - 1;
+ }
+
+ /* Branch offsets have an implicit 0 in the lowest bit. */
+ if (type == 'p' || type == 'q')
+ val /= 2;
+
+ if ((val & ((1 << op->shift) - 1)) != 0
+ || val < (mintiny << op->shift)
+ || val > (maxtiny << op->shift))
+ needext = TRUE;
+ else
+ needext = FALSE;
+
+ if (warn && ext && ! needext)
+ as_warn_where (file, line,
+ _("extended operand requested but not required"));
+ if (small && needext)
+ as_bad_where (file, line, _("invalid unextended operand value"));
+
+ if (small || (! ext && ! needext))
+ {
+ int insnval;
+
+ *use_extend = FALSE;
+ insnval = ((val >> op->shift) & ((1 << op->nbits) - 1));
+ insnval <<= op->op_shift;
+ *insn |= insnval;
+ }
+ else
+ {
+ long minext, maxext;
+ int extval;
+
+ if (op->extu)
+ {
+ minext = 0;
+ maxext = (1 << op->extbits) - 1;
+ }
+ else
+ {
+ minext = - (1 << (op->extbits - 1));
+ maxext = (1 << (op->extbits - 1)) - 1;
+ }
+ if (val < minext || val > maxext)
+ as_bad_where (file, line,
+ _("operand value out of range for instruction"));
+
+ *use_extend = TRUE;
+ if (op->extbits == 16)
+ {
+ extval = ((val >> 11) & 0x1f) | (val & 0x7e0);
+ val &= 0x1f;
+ }
+ else if (op->extbits == 15)
+ {
+ extval = ((val >> 11) & 0xf) | (val & 0x7f0);
+ val &= 0xf;
+ }
+ else
+ {
+ extval = ((val & 0x1f) << 6) | (val & 0x20);
+ val = 0;
+ }
+
+ *extend = (unsigned short) extval;
+ *insn |= val;
+ }
+}
+
+static const struct percent_op_match
+{
+ const char *str;
+ bfd_reloc_code_real_type reloc;
+} percent_op[] =
+{
+ {"%lo", BFD_RELOC_LO16},
+#ifdef OBJ_ELF
+ {"%call_hi", BFD_RELOC_MIPS_CALL_HI16},
+ {"%call_lo", BFD_RELOC_MIPS_CALL_LO16},
+ {"%call16", BFD_RELOC_MIPS_CALL16},
+ {"%got_disp", BFD_RELOC_MIPS_GOT_DISP},
+ {"%got_page", BFD_RELOC_MIPS_GOT_PAGE},
+ {"%got_ofst", BFD_RELOC_MIPS_GOT_OFST},
+ {"%got_hi", BFD_RELOC_MIPS_GOT_HI16},
+ {"%got_lo", BFD_RELOC_MIPS_GOT_LO16},
+ {"%got", BFD_RELOC_MIPS_GOT16},
+ {"%gp_rel", BFD_RELOC_GPREL16},
+ {"%half", BFD_RELOC_16},
+ {"%highest", BFD_RELOC_MIPS_HIGHEST},
+ {"%higher", BFD_RELOC_MIPS_HIGHER},
+ {"%neg", BFD_RELOC_MIPS_SUB},
+#endif
+ {"%hi", BFD_RELOC_HI16_S}
+};
+
+
+/* Return true if *STR points to a relocation operator. When returning true,
+ move *STR over the operator and store its relocation code in *RELOC.
+ Leave both *STR and *RELOC alone when returning false. */
+
+static bfd_boolean
+parse_relocation (char **str, bfd_reloc_code_real_type *reloc)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE (percent_op); i++)
+ if (strncasecmp (*str, percent_op[i].str, strlen (percent_op[i].str)) == 0)
+ {
+ *str += strlen (percent_op[i].str);
+ *reloc = percent_op[i].reloc;
+
+ /* Check whether the output BFD supports this relocation.
+ If not, issue an error and fall back on something safe. */
+ if (!bfd_reloc_type_lookup (stdoutput, percent_op[i].reloc))
+ {
+ as_bad ("relocation %s isn't supported by the current ABI",
+ percent_op[i].str);
+ *reloc = BFD_RELOC_LO16;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/* Parse string STR as a 16-bit relocatable operand. Store the
+ expression in *EP and the relocations in the array starting
+ at RELOC. Return the number of relocation operators used.
+
+ On exit, EXPR_END points to the first character after the expression.
+ If no relocation operators are used, RELOC[0] is set to BFD_RELOC_LO16. */
+
+static size_t
+my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc,
+ char *str)
+{
+ bfd_reloc_code_real_type reversed_reloc[3];
+ size_t reloc_index, i;
+ int crux_depth, str_depth;
+ char *crux;
+
+ /* Search for the start of the main expression, recoding relocations
+ in REVERSED_RELOC. End the loop with CRUX pointing to the start
+ of the main expression and with CRUX_DEPTH containing the number
+ of open brackets at that point. */
+ reloc_index = -1;
+ str_depth = 0;
+ do
+ {
+ reloc_index++;
+ crux = str;
+ crux_depth = str_depth;
+
+ /* Skip over whitespace and brackets, keeping count of the number
+ of brackets. */
+ while (*str == ' ' || *str == '\t' || *str == '(')
+ if (*str++ == '(')
+ str_depth++;
+ }
+ while (*str == '%'
+ && reloc_index < (HAVE_NEWABI ? 3 : 1)
+ && parse_relocation (&str, &reversed_reloc[reloc_index]));
+
+ my_getExpression (ep, crux);
+ str = expr_end;
+
+ /* Match every open bracket. */
+ while (crux_depth > 0 && (*str == ')' || *str == ' ' || *str == '\t'))
+ if (*str++ == ')')
+ crux_depth--;
+
+ if (crux_depth > 0)
+ as_bad ("unclosed '('");
+
+ expr_end = str;
+
+ if (reloc_index == 0)
+ reloc[0] = BFD_RELOC_LO16;
+ else
+ {
+ prev_reloc_op_frag = frag_now;
+ for (i = 0; i < reloc_index; i++)
+ reloc[i] = reversed_reloc[reloc_index - 1 - i];
+ }
+
+ return reloc_index;
+}
+
+static void
+my_getExpression (expressionS *ep, char *str)
+{
+ char *save_in;
+ valueT val;
+
+ save_in = input_line_pointer;
+ input_line_pointer = str;
+ expression (ep);
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+
+ /* If we are in mips16 mode, and this is an expression based on `.',
+ then we bump the value of the symbol by 1 since that is how other
+ text symbols are handled. We don't bother to handle complex
+ expressions, just `.' plus or minus a constant. */
+ if (mips_opts.mips16
+ && ep->X_op == O_symbol
+ && strcmp (S_GET_NAME (ep->X_add_symbol), FAKE_LABEL_NAME) == 0
+ && S_GET_SEGMENT (ep->X_add_symbol) == now_seg
+ && symbol_get_frag (ep->X_add_symbol) == frag_now
+ && symbol_constant_p (ep->X_add_symbol)
+ && (val = S_GET_VALUE (ep->X_add_symbol)) == frag_now_fix ())
+ S_SET_VALUE (ep->X_add_symbol, val + 1);
+}
+
+/* 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, words[i], 2);
+ litP += 2;
+ }
+ }
+ else
+ {
+ for (i = 0; i < prec; i++)
+ {
+ md_number_to_chars (litP, words[i], 2);
+ litP += 2;
+ }
+ }
+
+ return NULL;
+}
+
+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);
+}
+
+#ifdef OBJ_ELF
+static int support_64bit_objects(void)
+{
+ const char **list, **l;
+ int yes;
+
+ list = bfd_target_list ();
+ for (l = list; *l != NULL; l++)
+#ifdef TE_TMIPS
+ /* This is traditional mips */
+ if (strcmp (*l, "elf64-tradbigmips") == 0
+ || strcmp (*l, "elf64-tradlittlemips") == 0)
+#else
+ if (strcmp (*l, "elf64-bigmips") == 0
+ || strcmp (*l, "elf64-littlemips") == 0)
+#endif
+ break;
+ yes = (*l != NULL);
+ free (list);
+ return yes;
+}
+#endif /* OBJ_ELF */
+
+const char *md_shortopts = "O::g::G:";
+
+struct option md_longopts[] =
+{
+ /* Options which specify architecture. */
+#define OPTION_ARCH_BASE (OPTION_MD_BASE)
+#define OPTION_MARCH (OPTION_ARCH_BASE + 0)
+ {"march", required_argument, NULL, OPTION_MARCH},
+#define OPTION_MTUNE (OPTION_ARCH_BASE + 1)
+ {"mtune", required_argument, NULL, OPTION_MTUNE},
+#define OPTION_MIPS1 (OPTION_ARCH_BASE + 2)
+ {"mips0", no_argument, NULL, OPTION_MIPS1},
+ {"mips1", no_argument, NULL, OPTION_MIPS1},
+#define OPTION_MIPS2 (OPTION_ARCH_BASE + 3)
+ {"mips2", no_argument, NULL, OPTION_MIPS2},
+#define OPTION_MIPS3 (OPTION_ARCH_BASE + 4)
+ {"mips3", no_argument, NULL, OPTION_MIPS3},
+#define OPTION_MIPS4 (OPTION_ARCH_BASE + 5)
+ {"mips4", no_argument, NULL, OPTION_MIPS4},
+#define OPTION_MIPS5 (OPTION_ARCH_BASE + 6)
+ {"mips5", no_argument, NULL, OPTION_MIPS5},
+#define OPTION_MIPS32 (OPTION_ARCH_BASE + 7)
+ {"mips32", no_argument, NULL, OPTION_MIPS32},
+#define OPTION_MIPS64 (OPTION_ARCH_BASE + 8)
+ {"mips64", no_argument, NULL, OPTION_MIPS64},
+#define OPTION_MIPS32R2 (OPTION_ARCH_BASE + 9)
+ {"mips32r2", no_argument, NULL, OPTION_MIPS32R2},
+#define OPTION_MIPS64R2 (OPTION_ARCH_BASE + 10)
+ {"mips64r2", no_argument, NULL, OPTION_MIPS64R2},
+
+ /* Options which specify Application Specific Extensions (ASEs). */
+#define OPTION_ASE_BASE (OPTION_ARCH_BASE + 11)
+#define OPTION_MIPS16 (OPTION_ASE_BASE + 0)
+ {"mips16", no_argument, NULL, OPTION_MIPS16},
+#define OPTION_NO_MIPS16 (OPTION_ASE_BASE + 1)
+ {"no-mips16", no_argument, NULL, OPTION_NO_MIPS16},
+#define OPTION_MIPS3D (OPTION_ASE_BASE + 2)
+ {"mips3d", no_argument, NULL, OPTION_MIPS3D},
+#define OPTION_NO_MIPS3D (OPTION_ASE_BASE + 3)
+ {"no-mips3d", no_argument, NULL, OPTION_NO_MIPS3D},
+#define OPTION_MDMX (OPTION_ASE_BASE + 4)
+ {"mdmx", no_argument, NULL, OPTION_MDMX},
+#define OPTION_NO_MDMX (OPTION_ASE_BASE + 5)
+ {"no-mdmx", no_argument, NULL, OPTION_NO_MDMX},
+
+ /* Old-style architecture options. Don't add more of these. */
+#define OPTION_COMPAT_ARCH_BASE (OPTION_ASE_BASE + 6)
+#define OPTION_M4650 (OPTION_COMPAT_ARCH_BASE + 0)
+ {"m4650", no_argument, NULL, OPTION_M4650},
+#define OPTION_NO_M4650 (OPTION_COMPAT_ARCH_BASE + 1)
+ {"no-m4650", no_argument, NULL, OPTION_NO_M4650},
+#define OPTION_M4010 (OPTION_COMPAT_ARCH_BASE + 2)
+ {"m4010", no_argument, NULL, OPTION_M4010},
+#define OPTION_NO_M4010 (OPTION_COMPAT_ARCH_BASE + 3)
+ {"no-m4010", no_argument, NULL, OPTION_NO_M4010},
+#define OPTION_M4100 (OPTION_COMPAT_ARCH_BASE + 4)
+ {"m4100", no_argument, NULL, OPTION_M4100},
+#define OPTION_NO_M4100 (OPTION_COMPAT_ARCH_BASE + 5)
+ {"no-m4100", no_argument, NULL, OPTION_NO_M4100},
+#define OPTION_M3900 (OPTION_COMPAT_ARCH_BASE + 6)
+ {"m3900", no_argument, NULL, OPTION_M3900},
+#define OPTION_NO_M3900 (OPTION_COMPAT_ARCH_BASE + 7)
+ {"no-m3900", no_argument, NULL, OPTION_NO_M3900},
+
+ /* Options which enable bug fixes. */
+#define OPTION_FIX_BASE (OPTION_COMPAT_ARCH_BASE + 8)
+#define OPTION_M7000_HILO_FIX (OPTION_FIX_BASE + 0)
+ {"mfix7000", no_argument, NULL, OPTION_M7000_HILO_FIX},
+#define OPTION_MNO_7000_HILO_FIX (OPTION_FIX_BASE + 1)
+ {"no-fix-7000", no_argument, NULL, OPTION_MNO_7000_HILO_FIX},
+ {"mno-fix7000", no_argument, NULL, OPTION_MNO_7000_HILO_FIX},
+#define OPTION_FIX_VR4120 (OPTION_FIX_BASE + 2)
+#define OPTION_NO_FIX_VR4120 (OPTION_FIX_BASE + 3)
+ {"mfix-vr4120", no_argument, NULL, OPTION_FIX_VR4120},
+ {"mno-fix-vr4120", no_argument, NULL, OPTION_NO_FIX_VR4120},
+
+ /* Miscellaneous options. */
+#define OPTION_MISC_BASE (OPTION_FIX_BASE + 4)
+#define OPTION_MEMBEDDED_PIC (OPTION_MISC_BASE + 0)
+ {"membedded-pic", no_argument, NULL, OPTION_MEMBEDDED_PIC},
+#define OPTION_TRAP (OPTION_MISC_BASE + 1)
+ {"trap", no_argument, NULL, OPTION_TRAP},
+ {"no-break", no_argument, NULL, OPTION_TRAP},
+#define OPTION_BREAK (OPTION_MISC_BASE + 2)
+ {"break", no_argument, NULL, OPTION_BREAK},
+ {"no-trap", no_argument, NULL, OPTION_BREAK},
+#define OPTION_EB (OPTION_MISC_BASE + 3)
+ {"EB", no_argument, NULL, OPTION_EB},
+#define OPTION_EL (OPTION_MISC_BASE + 4)
+ {"EL", no_argument, NULL, OPTION_EL},
+#define OPTION_FP32 (OPTION_MISC_BASE + 5)
+ {"mfp32", no_argument, NULL, OPTION_FP32},
+#define OPTION_GP32 (OPTION_MISC_BASE + 6)
+ {"mgp32", no_argument, NULL, OPTION_GP32},
+#define OPTION_CONSTRUCT_FLOATS (OPTION_MISC_BASE + 7)
+ {"construct-floats", no_argument, NULL, OPTION_CONSTRUCT_FLOATS},
+#define OPTION_NO_CONSTRUCT_FLOATS (OPTION_MISC_BASE + 8)
+ {"no-construct-floats", no_argument, NULL, OPTION_NO_CONSTRUCT_FLOATS},
+#define OPTION_FP64 (OPTION_MISC_BASE + 9)
+ {"mfp64", no_argument, NULL, OPTION_FP64},
+#define OPTION_GP64 (OPTION_MISC_BASE + 10)
+ {"mgp64", no_argument, NULL, OPTION_GP64},
+#define OPTION_RELAX_BRANCH (OPTION_MISC_BASE + 11)
+#define OPTION_NO_RELAX_BRANCH (OPTION_MISC_BASE + 12)
+ {"relax-branch", no_argument, NULL, OPTION_RELAX_BRANCH},
+ {"no-relax-branch", no_argument, NULL, OPTION_NO_RELAX_BRANCH},
+
+ /* ELF-specific options. */
+#ifdef OBJ_ELF
+#define OPTION_ELF_BASE (OPTION_MISC_BASE + 13)
+#define OPTION_CALL_SHARED (OPTION_ELF_BASE + 0)
+ {"KPIC", no_argument, NULL, OPTION_CALL_SHARED},
+ {"call_shared", no_argument, NULL, OPTION_CALL_SHARED},
+#define OPTION_NON_SHARED (OPTION_ELF_BASE + 1)
+ {"non_shared", no_argument, NULL, OPTION_NON_SHARED},
+#define OPTION_XGOT (OPTION_ELF_BASE + 2)
+ {"xgot", no_argument, NULL, OPTION_XGOT},
+#define OPTION_MABI (OPTION_ELF_BASE + 3)
+ {"mabi", required_argument, NULL, OPTION_MABI},
+#define OPTION_32 (OPTION_ELF_BASE + 4)
+ {"32", no_argument, NULL, OPTION_32},
+#define OPTION_N32 (OPTION_ELF_BASE + 5)
+ {"n32", no_argument, NULL, OPTION_N32},
+#define OPTION_64 (OPTION_ELF_BASE + 6)
+ {"64", no_argument, NULL, OPTION_64},
+#define OPTION_MDEBUG (OPTION_ELF_BASE + 7)
+ {"mdebug", no_argument, NULL, OPTION_MDEBUG},
+#define OPTION_NO_MDEBUG (OPTION_ELF_BASE + 8)
+ {"no-mdebug", no_argument, NULL, OPTION_NO_MDEBUG},
+#define OPTION_PDR (OPTION_ELF_BASE + 9)
+ {"mpdr", no_argument, NULL, OPTION_PDR},
+#define OPTION_NO_PDR (OPTION_ELF_BASE + 10)
+ {"mno-pdr", no_argument, NULL, OPTION_NO_PDR},
+#endif /* OBJ_ELF */
+
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* Set STRING_PTR (either &mips_arch_string or &mips_tune_string) to
+ NEW_VALUE. Warn if another value was already specified. Note:
+ we have to defer parsing the -march and -mtune arguments in order
+ to handle 'from-abi' correctly, since the ABI might be specified
+ in a later argument. */
+
+static void
+mips_set_option_string (const char **string_ptr, const char *new_value)
+{
+ if (*string_ptr != 0 && strcasecmp (*string_ptr, new_value) != 0)
+ as_warn (_("A different %s was already specified, is now %s"),
+ string_ptr == &mips_arch_string ? "-march" : "-mtune",
+ new_value);
+
+ *string_ptr = new_value;
+}
+
+int
+md_parse_option (int c, char *arg)
+{
+ switch (c)
+ {
+ case OPTION_CONSTRUCT_FLOATS:
+ mips_disable_float_construction = 0;
+ break;
+
+ case OPTION_NO_CONSTRUCT_FLOATS:
+ mips_disable_float_construction = 1;
+ break;
+
+ case OPTION_TRAP:
+ mips_trap = 1;
+ break;
+
+ case OPTION_BREAK:
+ mips_trap = 0;
+ break;
+
+ case OPTION_EB:
+ target_big_endian = 1;
+ break;
+
+ case OPTION_EL:
+ target_big_endian = 0;
+ break;
+
+ case 'O':
+ if (arg && arg[1] == '0')
+ mips_optimize = 1;
+ else
+ mips_optimize = 2;
+ break;
+
+ case 'g':
+ if (arg == NULL)
+ mips_debug = 2;
+ else
+ mips_debug = atoi (arg);
+ /* When the MIPS assembler sees -g or -g2, it does not do
+ optimizations which limit full symbolic debugging. We take
+ that to be equivalent to -O0. */
+ if (mips_debug == 2)
+ mips_optimize = 1;
+ break;
+
+ case OPTION_MIPS1:
+ file_mips_isa = ISA_MIPS1;
+ break;
+
+ case OPTION_MIPS2:
+ file_mips_isa = ISA_MIPS2;
+ break;
+
+ case OPTION_MIPS3:
+ file_mips_isa = ISA_MIPS3;
+ break;
+
+ case OPTION_MIPS4:
+ file_mips_isa = ISA_MIPS4;
+ break;
+
+ case OPTION_MIPS5:
+ file_mips_isa = ISA_MIPS5;
+ break;
+
+ case OPTION_MIPS32:
+ file_mips_isa = ISA_MIPS32;
+ break;
+
+ case OPTION_MIPS32R2:
+ file_mips_isa = ISA_MIPS32R2;
+ break;
+
+ case OPTION_MIPS64R2:
+ file_mips_isa = ISA_MIPS64R2;
+ break;
+
+ case OPTION_MIPS64:
+ file_mips_isa = ISA_MIPS64;
+ break;
+
+ case OPTION_MTUNE:
+ mips_set_option_string (&mips_tune_string, arg);
+ break;
+
+ case OPTION_MARCH:
+ mips_set_option_string (&mips_arch_string, arg);
+ break;
+
+ case OPTION_M4650:
+ mips_set_option_string (&mips_arch_string, "4650");
+ mips_set_option_string (&mips_tune_string, "4650");
+ break;
+
+ case OPTION_NO_M4650:
+ break;
+
+ case OPTION_M4010:
+ mips_set_option_string (&mips_arch_string, "4010");
+ mips_set_option_string (&mips_tune_string, "4010");
+ break;
+
+ case OPTION_NO_M4010:
+ break;
+
+ case OPTION_M4100:
+ mips_set_option_string (&mips_arch_string, "4100");
+ mips_set_option_string (&mips_tune_string, "4100");
+ break;
+
+ case OPTION_NO_M4100:
+ break;
+
+ case OPTION_M3900:
+ mips_set_option_string (&mips_arch_string, "3900");
+ mips_set_option_string (&mips_tune_string, "3900");
+ break;
+
+ case OPTION_NO_M3900:
+ break;
+
+ case OPTION_MDMX:
+ mips_opts.ase_mdmx = 1;
+ break;
+
+ case OPTION_NO_MDMX:
+ mips_opts.ase_mdmx = 0;
+ break;
+
+ case OPTION_MIPS16:
+ mips_opts.mips16 = 1;
+ mips_no_prev_insn (FALSE);
+ break;
+
+ case OPTION_NO_MIPS16:
+ mips_opts.mips16 = 0;
+ mips_no_prev_insn (FALSE);
+ break;
+
+ case OPTION_MIPS3D:
+ mips_opts.ase_mips3d = 1;
+ break;
+
+ case OPTION_NO_MIPS3D:
+ mips_opts.ase_mips3d = 0;
+ break;
+
+ case OPTION_MEMBEDDED_PIC:
+ mips_pic = EMBEDDED_PIC;
+ if (USE_GLOBAL_POINTER_OPT && g_switch_seen)
+ {
+ as_bad (_("-G may not be used with embedded PIC code"));
+ return 0;
+ }
+ g_switch_value = 0x7fffffff;
+ break;
+
+ case OPTION_FIX_VR4120:
+ mips_fix_vr4120 = 1;
+ break;
+
+ case OPTION_NO_FIX_VR4120:
+ mips_fix_vr4120 = 0;
+ break;
+
+ case OPTION_RELAX_BRANCH:
+ mips_relax_branch = 1;
+ break;
+
+ case OPTION_NO_RELAX_BRANCH:
+ mips_relax_branch = 0;
+ break;
+
+#ifdef OBJ_ELF
+ /* When generating ELF code, we permit -KPIC and -call_shared to
+ select SVR4_PIC, and -non_shared to select no PIC. This is
+ intended to be compatible with Irix 5. */
+ case OPTION_CALL_SHARED:
+ if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
+ {
+ as_bad (_("-call_shared is supported only for ELF format"));
+ return 0;
+ }
+ mips_pic = SVR4_PIC;
+ mips_abicalls = TRUE;
+ if (g_switch_seen && g_switch_value != 0)
+ {
+ as_bad (_("-G may not be used with SVR4 PIC code"));
+ return 0;
+ }
+ g_switch_value = 0;
+ break;
+
+ case OPTION_NON_SHARED:
+ if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
+ {
+ as_bad (_("-non_shared is supported only for ELF format"));
+ return 0;
+ }
+ mips_pic = NO_PIC;
+ mips_abicalls = FALSE;
+ break;
+
+ /* The -xgot option tells the assembler to use 32 offsets when
+ accessing the got in SVR4_PIC mode. It is for Irix
+ compatibility. */
+ case OPTION_XGOT:
+ mips_big_got = 1;
+ break;
+#endif /* OBJ_ELF */
+
+ case 'G':
+ if (! USE_GLOBAL_POINTER_OPT)
+ {
+ as_bad (_("-G is not supported for this configuration"));
+ return 0;
+ }
+ else if (mips_pic == SVR4_PIC || mips_pic == EMBEDDED_PIC)
+ {
+ as_bad (_("-G may not be used with SVR4 or embedded PIC code"));
+ return 0;
+ }
+ else
+ g_switch_value = atoi (arg);
+ g_switch_seen = 1;
+ break;
+
+#ifdef OBJ_ELF
+ /* The -32, -n32 and -64 options are shortcuts for -mabi=32, -mabi=n32
+ and -mabi=64. */
+ case OPTION_32:
+ if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
+ {
+ as_bad (_("-32 is supported for ELF format only"));
+ return 0;
+ }
+ mips_abi = O32_ABI;
+ break;
+
+ case OPTION_N32:
+ if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
+ {
+ as_bad (_("-n32 is supported for ELF format only"));
+ return 0;
+ }
+ mips_abi = N32_ABI;
+ break;
+
+ case OPTION_64:
+ if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
+ {
+ as_bad (_("-64 is supported for ELF format only"));
+ return 0;
+ }
+ mips_abi = N64_ABI;
+ if (! support_64bit_objects())
+ as_fatal (_("No compiled in support for 64 bit object file format"));
+ break;
+#endif /* OBJ_ELF */
+
+ case OPTION_GP32:
+ file_mips_gp32 = 1;
+ break;
+
+ case OPTION_GP64:
+ file_mips_gp32 = 0;
+ break;
+
+ case OPTION_FP32:
+ file_mips_fp32 = 1;
+ break;
+
+ case OPTION_FP64:
+ file_mips_fp32 = 0;
+ break;
+
+#ifdef OBJ_ELF
+ case OPTION_MABI:
+ if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
+ {
+ as_bad (_("-mabi is supported for ELF format only"));
+ return 0;
+ }
+ if (strcmp (arg, "32") == 0)
+ mips_abi = O32_ABI;
+ else if (strcmp (arg, "o64") == 0)
+ mips_abi = O64_ABI;
+ else if (strcmp (arg, "n32") == 0)
+ mips_abi = N32_ABI;
+ else if (strcmp (arg, "64") == 0)
+ {
+ mips_abi = N64_ABI;
+ if (! support_64bit_objects())
+ as_fatal (_("No compiled in support for 64 bit object file "
+ "format"));
+ }
+ else if (strcmp (arg, "eabi") == 0)
+ mips_abi = EABI_ABI;
+ else
+ {
+ as_fatal (_("invalid abi -mabi=%s"), arg);
+ return 0;
+ }
+ break;
+#endif /* OBJ_ELF */
+
+ case OPTION_M7000_HILO_FIX:
+ mips_7000_hilo_fix = TRUE;
+ break;
+
+ case OPTION_MNO_7000_HILO_FIX:
+ mips_7000_hilo_fix = FALSE;
+ break;
+
+#ifdef OBJ_ELF
+ case OPTION_MDEBUG:
+ mips_flag_mdebug = TRUE;
+ break;
+
+ case OPTION_NO_MDEBUG:
+ mips_flag_mdebug = FALSE;
+ break;
+
+ case OPTION_PDR:
+ mips_flag_pdr = TRUE;
+ break;
+
+ case OPTION_NO_PDR:
+ mips_flag_pdr = FALSE;
+ break;
+#endif /* OBJ_ELF */
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Set up globals to generate code for the ISA or processor
+ described by INFO. */
+
+static void
+mips_set_architecture (const struct mips_cpu_info *info)
+{
+ if (info != 0)
+ {
+ file_mips_arch = info->cpu;
+ mips_opts.arch = info->cpu;
+ mips_opts.isa = info->isa;
+ }
+}
+
+
+/* Likewise for tuning. */
+
+static void
+mips_set_tune (const struct mips_cpu_info *info)
+{
+ if (info != 0)
+ mips_tune = info->cpu;
+}
+
+
+void
+mips_after_parse_args (void)
+{
+ const struct mips_cpu_info *arch_info = 0;
+ const struct mips_cpu_info *tune_info = 0;
+
+ /* GP relative stuff not working for PE */
+ if (strncmp (TARGET_OS, "pe", 2) == 0
+ && g_switch_value != 0)
+ {
+ if (g_switch_seen)
+ as_bad (_("-G not supported in this configuration."));
+ g_switch_value = 0;
+ }
+
+ if (mips_abi == NO_ABI)
+ mips_abi = MIPS_DEFAULT_ABI;
+
+ /* The following code determines the architecture and register size.
+ Similar code was added to GCC 3.3 (see override_options() in
+ config/mips/mips.c). The GAS and GCC code should be kept in sync
+ as much as possible. */
+
+ if (mips_arch_string != 0)
+ arch_info = mips_parse_cpu ("-march", mips_arch_string);
+
+ if (file_mips_isa != ISA_UNKNOWN)
+ {
+ /* Handle -mipsN. At this point, file_mips_isa contains the
+ ISA level specified by -mipsN, while arch_info->isa contains
+ the -march selection (if any). */
+ if (arch_info != 0)
+ {
+ /* -march takes precedence over -mipsN, since it is more descriptive.
+ There's no harm in specifying both as long as the ISA levels
+ are the same. */
+ if (file_mips_isa != arch_info->isa)
+ as_bad (_("-%s conflicts with the other architecture options, which imply -%s"),
+ mips_cpu_info_from_isa (file_mips_isa)->name,
+ mips_cpu_info_from_isa (arch_info->isa)->name);
+ }
+ else
+ arch_info = mips_cpu_info_from_isa (file_mips_isa);
+ }
+
+ if (arch_info == 0)
+ arch_info = mips_parse_cpu ("default CPU", MIPS_CPU_STRING_DEFAULT);
+
+ if (ABI_NEEDS_64BIT_REGS (mips_abi) && !ISA_HAS_64BIT_REGS (arch_info->isa))
+ as_bad ("-march=%s is not compatible with the selected ABI",
+ arch_info->name);
+
+ mips_set_architecture (arch_info);
+
+ /* Optimize for file_mips_arch, unless -mtune selects a different processor. */
+ if (mips_tune_string != 0)
+ tune_info = mips_parse_cpu ("-mtune", mips_tune_string);
+
+ if (tune_info == 0)
+ mips_set_tune (arch_info);
+ else
+ mips_set_tune (tune_info);
+
+ if (file_mips_gp32 >= 0)
+ {
+ /* The user specified the size of the integer registers. Make sure
+ it agrees with the ABI and ISA. */
+ if (file_mips_gp32 == 0 && !ISA_HAS_64BIT_REGS (mips_opts.isa))
+ as_bad (_("-mgp64 used with a 32-bit processor"));
+ else if (file_mips_gp32 == 1 && ABI_NEEDS_64BIT_REGS (mips_abi))
+ as_bad (_("-mgp32 used with a 64-bit ABI"));
+ else if (file_mips_gp32 == 0 && ABI_NEEDS_32BIT_REGS (mips_abi))
+ as_bad (_("-mgp64 used with a 32-bit ABI"));
+ }
+ else
+ {
+ /* Infer the integer register size from the ABI and processor.
+ Restrict ourselves to 32-bit registers if that's all the
+ processor has, or if the ABI cannot handle 64-bit registers. */
+ file_mips_gp32 = (ABI_NEEDS_32BIT_REGS (mips_abi)
+ || !ISA_HAS_64BIT_REGS (mips_opts.isa));
+ }
+
+ /* ??? GAS treats single-float processors as though they had 64-bit
+ float registers (although it complains when double-precision
+ instructions are used). As things stand, saying they have 32-bit
+ registers would lead to spurious "register must be even" messages.
+ So here we assume float registers are always the same size as
+ integer ones, unless the user says otherwise. */
+ if (file_mips_fp32 < 0)
+ file_mips_fp32 = file_mips_gp32;
+
+ /* End of GCC-shared inference code. */
+
+ /* This flag is set when we have a 64-bit capable CPU but use only
+ 32-bit wide registers. Note that EABI does not use it. */
+ if (ISA_HAS_64BIT_REGS (mips_opts.isa)
+ && ((mips_abi == NO_ABI && file_mips_gp32 == 1)
+ || mips_abi == O32_ABI))
+ mips_32bitmode = 1;
+
+ if (mips_opts.isa == ISA_MIPS1 && mips_trap)
+ as_bad (_("trap exception not supported at ISA 1"));
+
+ /* If the selected architecture includes support for ASEs, enable
+ generation of code for them. */
+ if (mips_opts.mips16 == -1)
+ mips_opts.mips16 = (CPU_HAS_MIPS16 (file_mips_arch)) ? 1 : 0;
+ if (mips_opts.ase_mips3d == -1)
+ mips_opts.ase_mips3d = (CPU_HAS_MIPS3D (file_mips_arch)) ? 1 : 0;
+ if (mips_opts.ase_mdmx == -1)
+ mips_opts.ase_mdmx = (CPU_HAS_MDMX (file_mips_arch)) ? 1 : 0;
+
+ file_mips_isa = mips_opts.isa;
+ file_ase_mips16 = mips_opts.mips16;
+ file_ase_mips3d = mips_opts.ase_mips3d;
+ file_ase_mdmx = mips_opts.ase_mdmx;
+ mips_opts.gp32 = file_mips_gp32;
+ mips_opts.fp32 = file_mips_fp32;
+
+ if (mips_flag_mdebug < 0)
+ {
+#ifdef OBJ_MAYBE_ECOFF
+ if (OUTPUT_FLAVOR == bfd_target_ecoff_flavour)
+ mips_flag_mdebug = 1;
+ else
+#endif /* OBJ_MAYBE_ECOFF */
+ mips_flag_mdebug = 0;
+ }
+}
+
+void
+mips_init_after_args (void)
+{
+ /* initialize opcodes */
+ bfd_mips_num_opcodes = bfd_mips_num_builtin_opcodes;
+ mips_opcodes = (struct mips_opcode *) mips_builtin_opcodes;
+}
+
+long
+md_pcrel_from (fixS *fixP)
+{
+ valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_16_PCREL_S2:
+ case BFD_RELOC_MIPS_JMP:
+ /* Return the address of the delay slot. */
+ return addr + 4;
+ default:
+ return addr;
+ }
+}
+
+/* This is called before the symbol table is processed. In order to
+ work with gcc when using mips-tfile, we must keep all local labels.
+ However, in other cases, we want to discard them. If we were
+ called with -g, but we didn't see any debugging information, it may
+ mean that gcc is smuggling debugging information through to
+ mips-tfile, in which case we must generate all local labels. */
+
+void
+mips_frob_file_before_adjust (void)
+{
+#ifndef NO_ECOFF_DEBUGGING
+ if (ECOFF_DEBUGGING
+ && mips_debug != 0
+ && ! ecoff_debugging_seen)
+ flag_keep_locals = 1;
+#endif
+}
+
+/* Sort any unmatched HI16_S relocs so that they immediately precede
+ the corresponding LO reloc. This is called before md_apply_fix3 and
+ tc_gen_reloc. Unmatched HI16_S relocs can only be generated by
+ explicit use of the %hi modifier. */
+
+void
+mips_frob_file (void)
+{
+ struct mips_hi_fixup *l;
+
+ for (l = mips_hi_fixup_list; l != NULL; l = l->next)
+ {
+ segment_info_type *seginfo;
+ int pass;
+
+ assert (reloc_needs_lo_p (l->fixp->fx_r_type));
+
+ /* If a GOT16 relocation turns out to be against a global symbol,
+ there isn't supposed to be a matching LO. */
+ if (l->fixp->fx_r_type == BFD_RELOC_MIPS_GOT16
+ && !pic_need_relax (l->fixp->fx_addsy, l->seg))
+ continue;
+
+ /* Check quickly whether the next fixup happens to be a matching %lo. */
+ if (fixup_has_matching_lo_p (l->fixp))
+ continue;
+
+ /* Look through the fixups for this segment for a matching %lo.
+ When we find one, move the %hi just in front of it. We do
+ this in two passes. In the first pass, we try to find a
+ unique %lo. In the second pass, we permit multiple %hi
+ relocs for a single %lo (this is a GNU extension). */
+ seginfo = seg_info (l->seg);
+ for (pass = 0; pass < 2; pass++)
+ {
+ fixS *f, *prev;
+
+ prev = NULL;
+ for (f = seginfo->fix_root; f != NULL; f = f->fx_next)
+ {
+ /* Check whether this is a %lo fixup which matches l->fixp. */
+ if (f->fx_r_type == BFD_RELOC_LO16
+ && f->fx_addsy == l->fixp->fx_addsy
+ && f->fx_offset == l->fixp->fx_offset
+ && (pass == 1
+ || prev == NULL
+ || !reloc_needs_lo_p (prev->fx_r_type)
+ || !fixup_has_matching_lo_p (prev)))
+ {
+ fixS **pf;
+
+ /* Move l->fixp before f. */
+ for (pf = &seginfo->fix_root;
+ *pf != l->fixp;
+ pf = &(*pf)->fx_next)
+ assert (*pf != NULL);
+
+ *pf = l->fixp->fx_next;
+
+ l->fixp->fx_next = f;
+ if (prev == NULL)
+ seginfo->fix_root = l->fixp;
+ else
+ prev->fx_next = l->fixp;
+
+ break;
+ }
+
+ prev = f;
+ }
+
+ if (f != NULL)
+ break;
+
+#if 0 /* GCC code motion plus incomplete dead code elimination
+ can leave a %hi without a %lo. */
+ if (pass == 1)
+ as_warn_where (l->fixp->fx_file, l->fixp->fx_line,
+ _("Unmatched %%hi reloc"));
+#endif
+ }
+ }
+}
+
+/* When generating embedded PIC code we need to use a special
+ relocation to represent the difference of two symbols in the .text
+ section (switch tables use a difference of this sort). See
+ include/coff/mips.h for details. This macro checks whether this
+ fixup requires the special reloc. */
+#define SWITCH_TABLE(fixp) \
+ ((fixp)->fx_r_type == BFD_RELOC_32 \
+ && OUTPUT_FLAVOR != bfd_target_elf_flavour \
+ && (fixp)->fx_addsy != NULL \
+ && (fixp)->fx_subsy != NULL \
+ && S_GET_SEGMENT ((fixp)->fx_addsy) == text_section \
+ && S_GET_SEGMENT ((fixp)->fx_subsy) == text_section)
+
+/* When generating embedded PIC code we must keep all PC relative
+ relocations, in case the linker has to relax a call. We also need
+ to keep relocations for switch table entries.
+
+ We may have combined relocations without symbols in the N32/N64 ABI.
+ We have to prevent gas from dropping them. */
+
+int
+mips_force_relocation (fixS *fixp)
+{
+ if (generic_force_reloc (fixp))
+ return 1;
+
+ if (HAVE_NEWABI
+ && S_GET_SEGMENT (fixp->fx_addsy) == bfd_abs_section_ptr
+ && (fixp->fx_r_type == BFD_RELOC_MIPS_SUB
+ || fixp->fx_r_type == BFD_RELOC_HI16_S
+ || fixp->fx_r_type == BFD_RELOC_LO16))
+ return 1;
+
+ return (mips_pic == EMBEDDED_PIC
+ && (fixp->fx_pcrel
+ || SWITCH_TABLE (fixp)
+ || fixp->fx_r_type == BFD_RELOC_PCREL_HI16_S
+ || fixp->fx_r_type == BFD_RELOC_PCREL_LO16));
+}
+
+/* This hook is called before a fix is simplified. We don't really
+ decide whether to skip a fix here. Rather, we turn global symbols
+ used as branch targets into local symbols, such that they undergo
+ simplification. We can only do this if the symbol is defined and
+ it is in the same section as the branch. If this doesn't hold, we
+ emit a better error message than just saying the relocation is not
+ valid for the selected object format.
+
+ FIXP is the fix-up we're going to try to simplify, SEG is the
+ segment in which the fix up occurs. The return value should be
+ non-zero to indicate the fix-up is valid for further
+ simplifications. */
+
+int
+mips_validate_fix (struct fix *fixP, asection *seg)
+{
+ /* There's a lot of discussion on whether it should be possible to
+ use R_MIPS_PC16 to represent branch relocations. The outcome
+ seems to be that it can, but gas/bfd are very broken in creating
+ RELA relocations for this, so for now we only accept branches to
+ symbols in the same section. Anything else is of dubious value,
+ since there's no guarantee that at link time the symbol would be
+ in range. Even for branches to local symbols this is arguably
+ wrong, since it we assume the symbol is not going to be
+ overridden, which should be possible per ELF library semantics,
+ but then, there isn't a dynamic relocation that could be used to
+ this effect, and the target would likely be out of range as well.
+
+ Unfortunately, it seems that there is too much code out there
+ that relies on branches to symbols that are global to be resolved
+ as if they were local, like the IRIX tools do, so we do it as
+ well, but with a warning so that people are reminded to fix their
+ code. If we ever get back to using R_MIPS_PC16 for branch
+ targets, this entire block should go away (and probably the
+ whole function). */
+
+ if (fixP->fx_r_type == BFD_RELOC_16_PCREL_S2
+ && (((OUTPUT_FLAVOR == bfd_target_ecoff_flavour
+ || OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ && mips_pic != EMBEDDED_PIC)
+ || bfd_reloc_type_lookup (stdoutput, BFD_RELOC_16_PCREL_S2) == NULL)
+ && fixP->fx_addsy)
+ {
+ if (! S_IS_DEFINED (fixP->fx_addsy))
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Cannot branch to undefined symbol."));
+ /* Avoid any further errors about this fixup. */
+ fixP->fx_done = 1;
+ }
+ else if (S_GET_SEGMENT (fixP->fx_addsy) != seg)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Cannot branch to symbol in another section."));
+ fixP->fx_done = 1;
+ }
+ else if (S_IS_EXTERNAL (fixP->fx_addsy))
+ {
+ symbolS *sym = fixP->fx_addsy;
+
+ if (mips_pic == SVR4_PIC)
+ as_warn_where (fixP->fx_file, fixP->fx_line,
+ _("Pretending global symbol used as branch target is local."));
+
+ fixP->fx_addsy = symbol_create (S_GET_NAME (sym),
+ S_GET_SEGMENT (sym),
+ S_GET_VALUE (sym),
+ symbol_get_frag (sym));
+ copy_symbol_attributes (fixP->fx_addsy, sym);
+ S_CLEAR_EXTERNAL (fixP->fx_addsy);
+ assert (symbol_resolved_p (sym));
+ symbol_mark_resolved (fixP->fx_addsy);
+ }
+ }
+
+ return 1;
+}
+
+/* Apply a fixup to the object file. */
+
+void
+md_apply_fix3 (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ bfd_byte *buf;
+ long insn;
+ static int previous_fx_r_type = 0;
+ reloc_howto_type *howto;
+
+ /* We ignore generic BFD relocations we don't know about. */
+ howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
+ if (! howto)
+ return;
+
+ assert (fixP->fx_size == 4
+ || fixP->fx_r_type == BFD_RELOC_16
+ || fixP->fx_r_type == BFD_RELOC_64
+ || fixP->fx_r_type == BFD_RELOC_CTOR
+ || fixP->fx_r_type == BFD_RELOC_MIPS_SUB
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY);
+
+ buf = (bfd_byte *) (fixP->fx_frag->fr_literal + fixP->fx_where);
+
+ /* We are not done if this is a composite relocation to set up gp. */
+ if (fixP->fx_addsy == NULL && ! fixP->fx_pcrel
+ && !(fixP->fx_r_type == BFD_RELOC_MIPS_SUB
+ || (fixP->fx_r_type == BFD_RELOC_64
+ && (previous_fx_r_type == BFD_RELOC_GPREL32
+ || previous_fx_r_type == BFD_RELOC_GPREL16))
+ || (previous_fx_r_type == BFD_RELOC_MIPS_SUB
+ && (fixP->fx_r_type == BFD_RELOC_HI16_S
+ || fixP->fx_r_type == BFD_RELOC_LO16))))
+ fixP->fx_done = 1;
+ previous_fx_r_type = fixP->fx_r_type;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_MIPS_JMP:
+ case BFD_RELOC_MIPS_SHIFT5:
+ case BFD_RELOC_MIPS_SHIFT6:
+ case BFD_RELOC_MIPS_GOT_DISP:
+ case BFD_RELOC_MIPS_GOT_PAGE:
+ case BFD_RELOC_MIPS_GOT_OFST:
+ case BFD_RELOC_MIPS_SUB:
+ case BFD_RELOC_MIPS_INSERT_A:
+ case BFD_RELOC_MIPS_INSERT_B:
+ case BFD_RELOC_MIPS_DELETE:
+ case BFD_RELOC_MIPS_HIGHEST:
+ case BFD_RELOC_MIPS_HIGHER:
+ case BFD_RELOC_MIPS_SCN_DISP:
+ case BFD_RELOC_MIPS_REL16:
+ case BFD_RELOC_MIPS_RELGOT:
+ case BFD_RELOC_MIPS_JALR:
+ case BFD_RELOC_HI16:
+ case BFD_RELOC_HI16_S:
+ case BFD_RELOC_GPREL16:
+ case BFD_RELOC_MIPS_LITERAL:
+ case BFD_RELOC_MIPS_CALL16:
+ case BFD_RELOC_MIPS_GOT16:
+ case BFD_RELOC_GPREL32:
+ case BFD_RELOC_MIPS_GOT_HI16:
+ case BFD_RELOC_MIPS_GOT_LO16:
+ case BFD_RELOC_MIPS_CALL_HI16:
+ case BFD_RELOC_MIPS_CALL_LO16:
+ case BFD_RELOC_MIPS16_GPREL:
+ if (fixP->fx_pcrel)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Invalid PC relative reloc"));
+ /* Nothing needed to do. The value comes from the reloc entry */
+ break;
+
+ case BFD_RELOC_MIPS16_JMP:
+ /* We currently always generate a reloc against a symbol, which
+ means that we don't want an addend even if the symbol is
+ defined. */
+ *valP = 0;
+ break;
+
+ case BFD_RELOC_PCREL_HI16_S:
+ /* The addend for this is tricky if it is internal, so we just
+ do everything here rather than in bfd_install_relocation. */
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour && !fixP->fx_done)
+ break;
+ if (fixP->fx_addsy
+ && (symbol_get_bfdsym (fixP->fx_addsy)->flags & BSF_SECTION_SYM) == 0)
+ {
+ /* For an external symbol adjust by the address to make it
+ pcrel_offset. We use the address of the RELLO reloc
+ which follows this one. */
+ *valP += (fixP->fx_next->fx_frag->fr_address
+ + fixP->fx_next->fx_where);
+ }
+ *valP = ((*valP + 0x8000) >> 16) & 0xffff;
+ if (target_big_endian)
+ buf += 2;
+ md_number_to_chars (buf, *valP, 2);
+ break;
+
+ case BFD_RELOC_PCREL_LO16:
+ /* The addend for this is tricky if it is internal, so we just
+ do everything here rather than in bfd_install_relocation. */
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour && !fixP->fx_done)
+ break;
+ if (fixP->fx_addsy
+ && (symbol_get_bfdsym (fixP->fx_addsy)->flags & BSF_SECTION_SYM) == 0)
+ *valP += fixP->fx_frag->fr_address + fixP->fx_where;
+ if (target_big_endian)
+ buf += 2;
+ md_number_to_chars (buf, *valP, 2);
+ break;
+
+ case BFD_RELOC_64:
+ /* This is handled like BFD_RELOC_32, but we output a sign
+ extended value if we are only 32 bits. */
+ if (fixP->fx_done
+ || (mips_pic == EMBEDDED_PIC && SWITCH_TABLE (fixP)))
+ {
+ if (8 <= sizeof (valueT))
+ md_number_to_chars (buf, *valP, 8);
+ else
+ {
+ valueT hiv;
+
+ if ((*valP & 0x80000000) != 0)
+ hiv = 0xffffffff;
+ else
+ hiv = 0;
+ md_number_to_chars ((char *)(buf + target_big_endian ? 4 : 0),
+ *valP, 4);
+ md_number_to_chars ((char *)(buf + target_big_endian ? 0 : 4),
+ hiv, 4);
+ }
+ }
+ break;
+
+ case BFD_RELOC_RVA:
+ case BFD_RELOC_32:
+ /* If we are deleting this reloc entry, we must fill in the
+ value now. This can happen if we have a .word which is not
+ resolved when it appears but is later defined. We also need
+ to fill in the value if this is an embedded PIC switch table
+ entry. */
+ if (fixP->fx_done
+ || (mips_pic == EMBEDDED_PIC && SWITCH_TABLE (fixP)))
+ md_number_to_chars (buf, *valP, 4);
+ break;
+
+ case BFD_RELOC_16:
+ /* If we are deleting this reloc entry, we must fill in the
+ value now. */
+ assert (fixP->fx_size == 2);
+ if (fixP->fx_done)
+ md_number_to_chars (buf, *valP, 2);
+ break;
+
+ case BFD_RELOC_LO16:
+ /* When handling an embedded PIC switch statement, we can wind
+ up deleting a LO16 reloc. See the 'o' case in mips_ip. */
+ if (fixP->fx_done)
+ {
+ if (*valP + 0x8000 > 0xffff)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ if (target_big_endian)
+ buf += 2;
+ md_number_to_chars (buf, *valP, 2);
+ }
+ break;
+
+ case BFD_RELOC_16_PCREL_S2:
+ if ((*valP & 0x3) != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Branch to odd address (%lx)"), (long) *valP);
+
+ /*
+ * We need to save the bits in the instruction since fixup_segment()
+ * might be deleting the relocation entry (i.e., a branch within
+ * the current segment).
+ */
+ if (! fixP->fx_done)
+ break;
+
+ /* update old instruction data */
+ if (target_big_endian)
+ insn = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+ else
+ insn = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+
+ if (*valP + 0x20000 <= 0x3ffff)
+ {
+ insn |= (*valP >> 2) & 0xffff;
+ md_number_to_chars (buf, insn, 4);
+ }
+ else if (mips_pic == NO_PIC
+ && fixP->fx_done
+ && fixP->fx_frag->fr_address >= text_section->vma
+ && (fixP->fx_frag->fr_address
+ < text_section->vma + text_section->_raw_size)
+ && ((insn & 0xffff0000) == 0x10000000 /* beq $0,$0 */
+ || (insn & 0xffff0000) == 0x04010000 /* bgez $0 */
+ || (insn & 0xffff0000) == 0x04110000)) /* bgezal $0 */
+ {
+ /* The branch offset is too large. If this is an
+ unconditional branch, and we are not generating PIC code,
+ we can convert it to an absolute jump instruction. */
+ if ((insn & 0xffff0000) == 0x04110000) /* bgezal $0 */
+ insn = 0x0c000000; /* jal */
+ else
+ insn = 0x08000000; /* j */
+ fixP->fx_r_type = BFD_RELOC_MIPS_JMP;
+ fixP->fx_done = 0;
+ fixP->fx_addsy = section_symbol (text_section);
+ *valP += md_pcrel_from (fixP);
+ md_number_to_chars (buf, insn, 4);
+ }
+ else
+ {
+ /* If we got here, we have branch-relaxation disabled,
+ and there's nothing we can do to fix this instruction
+ without turning it into a longer sequence. */
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Branch out of range"));
+ }
+ break;
+
+ case 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);
+ break;
+
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ break;
+
+ default:
+ internalError ();
+ }
+
+ /* Remember value for tc_gen_reloc. */
+ fixP->fx_addnumber = *valP;
+}
+
+#if 0
+void
+printInsn (unsigned long oc)
+{
+ const struct mips_opcode *p;
+ int treg, sreg, dreg, shamt;
+ short imm;
+ const char *args;
+ int i;
+
+ for (i = 0; i < NUMOPCODES; ++i)
+ {
+ p = &mips_opcodes[i];
+ if (((oc & p->mask) == p->match) && (p->pinfo != INSN_MACRO))
+ {
+ printf ("%08lx %s\t", oc, p->name);
+ treg = (oc >> 16) & 0x1f;
+ sreg = (oc >> 21) & 0x1f;
+ dreg = (oc >> 11) & 0x1f;
+ shamt = (oc >> 6) & 0x1f;
+ imm = oc;
+ for (args = p->args;; ++args)
+ {
+ switch (*args)
+ {
+ case '\0':
+ printf ("\n");
+ break;
+
+ case ',':
+ case '(':
+ case ')':
+ printf ("%c", *args);
+ continue;
+
+ case 'r':
+ assert (treg == sreg);
+ printf ("$%d,$%d", treg, sreg);
+ continue;
+
+ case 'd':
+ case 'G':
+ printf ("$%d", dreg);
+ continue;
+
+ case 't':
+ case 'E':
+ printf ("$%d", treg);
+ continue;
+
+ case 'k':
+ printf ("0x%x", treg);
+ continue;
+
+ case 'b':
+ case 's':
+ printf ("$%d", sreg);
+ continue;
+
+ case 'a':
+ printf ("0x%08lx", oc & 0x1ffffff);
+ continue;
+
+ case 'i':
+ case 'j':
+ case 'o':
+ case 'u':
+ printf ("%d", imm);
+ continue;
+
+ case '<':
+ case '>':
+ printf ("$%d", shamt);
+ continue;
+
+ default:
+ internalError ();
+ }
+ break;
+ }
+ return;
+ }
+ }
+ printf (_("%08lx UNDEFINED\n"), oc);
+}
+#endif
+
+static symbolS *
+get_symbol (void)
+{
+ int c;
+ char *name;
+ symbolS *p;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ p = (symbolS *) symbol_find_or_make (name);
+ *input_line_pointer = c;
+ return p;
+}
+
+/* Align the current frag to a given power of two. The MIPS assembler
+ also automatically adjusts any preceding label. */
+
+static void
+mips_align (int to, int fill, symbolS *label)
+{
+ mips_emit_delays (FALSE);
+ frag_align (to, fill, 0);
+ record_alignment (now_seg, to);
+ if (label != NULL)
+ {
+ assert (S_GET_SEGMENT (label) == now_seg);
+ symbol_set_frag (label, frag_now);
+ S_SET_VALUE (label, (valueT) frag_now_fix ());
+ }
+}
+
+/* Align to a given power of two. .align 0 turns off the automatic
+ alignment used by the data creating pseudo-ops. */
+
+static void
+s_align (int x ATTRIBUTE_UNUSED)
+{
+ register int temp;
+ register long temp_fill;
+ long max_alignment = 15;
+
+ /*
+
+ o Note that the assembler pulls down any immediately preceding label
+ to the aligned address.
+ o It's not documented but auto alignment is reinstated by
+ a .align pseudo instruction.
+ o Note also that after auto alignment is turned off the mips assembler
+ issues an error on attempt to assemble an improperly aligned data item.
+ We don't.
+
+ */
+
+ temp = get_absolute_expression ();
+ if (temp > max_alignment)
+ as_bad (_("Alignment too large: %d. assumed."), temp = max_alignment);
+ else if (temp < 0)
+ {
+ as_warn (_("Alignment negative: 0 assumed."));
+ temp = 0;
+ }
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ temp_fill = get_absolute_expression ();
+ }
+ else
+ temp_fill = 0;
+ if (temp)
+ {
+ auto_align = 1;
+ mips_align (temp, (int) temp_fill,
+ insn_labels != NULL ? insn_labels->label : NULL);
+ }
+ else
+ {
+ auto_align = 0;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+void
+mips_flush_pending_output (void)
+{
+ mips_emit_delays (FALSE);
+ mips_clear_insn_labels ();
+}
+
+static void
+s_change_sec (int sec)
+{
+ segT seg;
+
+ /* When generating embedded PIC code, we only use the .text, .lit8,
+ .sdata and .sbss sections. We change the .data and .rdata
+ pseudo-ops to use .sdata. */
+ if (mips_pic == EMBEDDED_PIC
+ && (sec == 'd' || sec == 'r'))
+ sec = 's';
+
+#ifdef OBJ_ELF
+ /* The ELF backend needs to know that we are changing sections, so
+ that .previous works correctly. We could do something like check
+ for an obj_section_change_hook macro, but that might be confusing
+ as it would not be appropriate to use it in the section changing
+ functions in read.c, since obj-elf.c intercepts those. FIXME:
+ This should be cleaner, somehow. */
+ obj_elf_section_change_hook ();
+#endif
+
+ mips_emit_delays (FALSE);
+ switch (sec)
+ {
+ case 't':
+ s_text (0);
+ break;
+ case 'd':
+ s_data (0);
+ break;
+ case 'b':
+ subseg_set (bss_section, (subsegT) get_absolute_expression ());
+ demand_empty_rest_of_line ();
+ break;
+
+ case 'r':
+ if (USE_GLOBAL_POINTER_OPT)
+ {
+ seg = subseg_new (RDATA_SECTION_NAME,
+ (subsegT) get_absolute_expression ());
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ {
+ bfd_set_section_flags (stdoutput, seg,
+ (SEC_ALLOC
+ | SEC_LOAD
+ | SEC_READONLY
+ | SEC_RELOC
+ | SEC_DATA));
+ if (strcmp (TARGET_OS, "elf") != 0)
+ record_alignment (seg, 4);
+ }
+ demand_empty_rest_of_line ();
+ }
+ else
+ {
+ as_bad (_("No read only data section in this object file format"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ break;
+
+ case 's':
+ if (USE_GLOBAL_POINTER_OPT)
+ {
+ seg = subseg_new (".sdata", (subsegT) get_absolute_expression ());
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ {
+ bfd_set_section_flags (stdoutput, seg,
+ SEC_ALLOC | SEC_LOAD | SEC_RELOC
+ | SEC_DATA);
+ if (strcmp (TARGET_OS, "elf") != 0)
+ record_alignment (seg, 4);
+ }
+ demand_empty_rest_of_line ();
+ break;
+ }
+ else
+ {
+ as_bad (_("Global pointers not supported; recompile -G 0"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ }
+
+ auto_align = 1;
+}
+
+void
+s_change_section (int ignore ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_ELF
+ char *section_name;
+ char c;
+ char next_c = 0;
+ int section_type;
+ int section_flag;
+ int section_entry_size;
+ int section_alignment;
+
+ if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
+ return;
+
+ section_name = input_line_pointer;
+ c = get_symbol_end ();
+ if (c)
+ next_c = *(input_line_pointer + 1);
+
+ /* Do we have .section Name<,"flags">? */
+ if (c != ',' || (c == ',' && next_c == '"'))
+ {
+ /* just after name is now '\0'. */
+ *input_line_pointer = c;
+ input_line_pointer = section_name;
+ obj_elf_section (ignore);
+ return;
+ }
+ input_line_pointer++;
+
+ /* Do we have .section Name<,type><,flag><,entry_size><,alignment> */
+ if (c == ',')
+ section_type = get_absolute_expression ();
+ else
+ section_type = 0;
+ if (*input_line_pointer++ == ',')
+ section_flag = get_absolute_expression ();
+ else
+ section_flag = 0;
+ if (*input_line_pointer++ == ',')
+ section_entry_size = get_absolute_expression ();
+ else
+ section_entry_size = 0;
+ if (*input_line_pointer++ == ',')
+ section_alignment = get_absolute_expression ();
+ else
+ section_alignment = 0;
+
+ section_name = xstrdup (section_name);
+
+ /* When using the generic form of .section (as implemented by obj-elf.c),
+ there's no way to set the section type to SHT_MIPS_DWARF. Users have
+ traditionally had to fall back on the more common @progbits instead.
+
+ There's nothing really harmful in this, since bfd will correct
+ SHT_PROGBITS to SHT_MIPS_DWARF before writing out the file. But it
+ means that, for backwards compatibiltiy, the special_section entries
+ for dwarf sections must use SHT_PROGBITS rather than SHT_MIPS_DWARF.
+
+ Even so, we shouldn't force users of the MIPS .section syntax to
+ incorrectly label the sections as SHT_PROGBITS. The best compromise
+ seems to be to map SHT_MIPS_DWARF to SHT_PROGBITS before calling the
+ generic type-checking code. */
+ if (section_type == SHT_MIPS_DWARF)
+ section_type = SHT_PROGBITS;
+
+ obj_elf_change_section (section_name, section_type, section_flag,
+ section_entry_size, 0, 0, 0);
+
+ if (now_seg->name != section_name)
+ free (section_name);
+#endif /* OBJ_ELF */
+}
+
+void
+mips_enable_auto_align (void)
+{
+ auto_align = 1;
+}
+
+static void
+s_cons (int log_size)
+{
+ symbolS *label;
+
+ label = insn_labels != NULL ? insn_labels->label : NULL;
+ mips_emit_delays (FALSE);
+ if (log_size > 0 && auto_align)
+ mips_align (log_size, 0, label);
+ mips_clear_insn_labels ();
+ cons (1 << log_size);
+}
+
+static void
+s_float_cons (int type)
+{
+ symbolS *label;
+
+ label = insn_labels != NULL ? insn_labels->label : NULL;
+
+ mips_emit_delays (FALSE);
+
+ if (auto_align)
+ {
+ if (type == 'd')
+ mips_align (3, 0, label);
+ else
+ mips_align (2, 0, label);
+ }
+
+ mips_clear_insn_labels ();
+
+ float_cons (type);
+}
+
+/* Handle .globl. We need to override it because on Irix 5 you are
+ permitted to say
+ .globl foo .text
+ where foo is an undefined symbol, to mean that foo should be
+ considered to be the address of a function. */
+
+static void
+s_mips_globl (int x ATTRIBUTE_UNUSED)
+{
+ char *name;
+ int c;
+ symbolS *symbolP;
+ flagword flag;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+ *input_line_pointer = c;
+ SKIP_WHITESPACE ();
+
+ /* On Irix 5, every global symbol that is not explicitly labelled as
+ being a function is apparently labelled as being an object. */
+ flag = BSF_OBJECT;
+
+ if (! is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ char *secname;
+ asection *sec;
+
+ secname = input_line_pointer;
+ c = get_symbol_end ();
+ sec = bfd_get_section_by_name (stdoutput, secname);
+ if (sec == NULL)
+ as_bad (_("%s: no such section"), secname);
+ *input_line_pointer = c;
+
+ if (sec != NULL && (sec->flags & SEC_CODE) != 0)
+ flag = BSF_FUNCTION;
+ }
+
+ symbol_get_bfdsym (symbolP)->flags |= flag;
+
+ S_SET_EXTERNAL (symbolP);
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_option (int x ATTRIBUTE_UNUSED)
+{
+ char *opt;
+ char c;
+
+ opt = input_line_pointer;
+ c = get_symbol_end ();
+
+ if (*opt == 'O')
+ {
+ /* FIXME: What does this mean? */
+ }
+ else if (strncmp (opt, "pic", 3) == 0)
+ {
+ int i;
+
+ i = atoi (opt + 3);
+ if (i == 0)
+ mips_pic = NO_PIC;
+ else if (i == 2)
+ {
+ mips_pic = SVR4_PIC;
+ mips_abicalls = TRUE;
+ }
+ else
+ as_bad (_(".option pic%d not supported"), i);
+
+ if (USE_GLOBAL_POINTER_OPT && mips_pic == SVR4_PIC)
+ {
+ if (g_switch_seen && g_switch_value != 0)
+ as_warn (_("-G may not be used with SVR4 PIC code"));
+ g_switch_value = 0;
+ bfd_set_gp_size (stdoutput, 0);
+ }
+ }
+ else
+ as_warn (_("Unrecognized option \"%s\""), opt);
+
+ *input_line_pointer = c;
+ demand_empty_rest_of_line ();
+}
+
+/* This structure is used to hold a stack of .set values. */
+
+struct mips_option_stack
+{
+ struct mips_option_stack *next;
+ struct mips_set_options options;
+};
+
+static struct mips_option_stack *mips_opts_stack;
+
+/* Handle the .set pseudo-op. */
+
+static void
+s_mipsset (int x ATTRIBUTE_UNUSED)
+{
+ char *name = input_line_pointer, ch;
+
+ while (!is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ ch = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ if (strcmp (name, "reorder") == 0)
+ {
+ if (mips_opts.noreorder && prev_nop_frag != NULL)
+ {
+ /* If we still have pending nops, we can discard them. The
+ usual nop handling will insert any that are still
+ needed. */
+ prev_nop_frag->fr_fix -= (prev_nop_frag_holds
+ * (mips_opts.mips16 ? 2 : 4));
+ prev_nop_frag = NULL;
+ }
+ mips_opts.noreorder = 0;
+ }
+ else if (strcmp (name, "noreorder") == 0)
+ {
+ mips_emit_delays (TRUE);
+ mips_opts.noreorder = 1;
+ mips_any_noreorder = 1;
+ }
+ else if (strcmp (name, "at") == 0)
+ {
+ mips_opts.noat = 0;
+ }
+ else if (strcmp (name, "noat") == 0)
+ {
+ mips_opts.noat = 1;
+ }
+ else if (strcmp (name, "macro") == 0)
+ {
+ mips_opts.warn_about_macros = 0;
+ }
+ else if (strcmp (name, "nomacro") == 0)
+ {
+ if (mips_opts.noreorder == 0)
+ as_bad (_("`noreorder' must be set before `nomacro'"));
+ mips_opts.warn_about_macros = 1;
+ }
+ else if (strcmp (name, "move") == 0 || strcmp (name, "novolatile") == 0)
+ {
+ mips_opts.nomove = 0;
+ }
+ else if (strcmp (name, "nomove") == 0 || strcmp (name, "volatile") == 0)
+ {
+ mips_opts.nomove = 1;
+ }
+ else if (strcmp (name, "bopt") == 0)
+ {
+ mips_opts.nobopt = 0;
+ }
+ else if (strcmp (name, "nobopt") == 0)
+ {
+ mips_opts.nobopt = 1;
+ }
+ else if (strcmp (name, "mips16") == 0
+ || strcmp (name, "MIPS-16") == 0)
+ mips_opts.mips16 = 1;
+ else if (strcmp (name, "nomips16") == 0
+ || strcmp (name, "noMIPS-16") == 0)
+ mips_opts.mips16 = 0;
+ else if (strcmp (name, "mips3d") == 0)
+ mips_opts.ase_mips3d = 1;
+ else if (strcmp (name, "nomips3d") == 0)
+ mips_opts.ase_mips3d = 0;
+ else if (strcmp (name, "mdmx") == 0)
+ mips_opts.ase_mdmx = 1;
+ else if (strcmp (name, "nomdmx") == 0)
+ mips_opts.ase_mdmx = 0;
+ else if (strncmp (name, "mips", 4) == 0 || strncmp (name, "arch=", 5) == 0)
+ {
+ int reset = 0;
+
+ /* Permit the user to change the ISA and architecture on the fly.
+ Needless to say, misuse can cause serious problems. */
+ if (strcmp (name, "mips0") == 0)
+ {
+ reset = 1;
+ mips_opts.isa = file_mips_isa;
+ }
+ else if (strcmp (name, "mips1") == 0)
+ mips_opts.isa = ISA_MIPS1;
+ else if (strcmp (name, "mips2") == 0)
+ mips_opts.isa = ISA_MIPS2;
+ else if (strcmp (name, "mips3") == 0)
+ mips_opts.isa = ISA_MIPS3;
+ else if (strcmp (name, "mips4") == 0)
+ mips_opts.isa = ISA_MIPS4;
+ else if (strcmp (name, "mips5") == 0)
+ mips_opts.isa = ISA_MIPS5;
+ else if (strcmp (name, "mips32") == 0)
+ mips_opts.isa = ISA_MIPS32;
+ else if (strcmp (name, "mips32r2") == 0)
+ mips_opts.isa = ISA_MIPS32R2;
+ else if (strcmp (name, "mips64") == 0)
+ mips_opts.isa = ISA_MIPS64;
+ else if (strcmp (name, "mips64r2") == 0)
+ mips_opts.isa = ISA_MIPS64R2;
+ else if (strcmp (name, "arch=default") == 0)
+ {
+ reset = 1;
+ mips_opts.arch = file_mips_arch;
+ mips_opts.isa = file_mips_isa;
+ }
+ else if (strncmp (name, "arch=", 5) == 0)
+ {
+ const struct mips_cpu_info *p;
+
+ p = mips_parse_cpu("internal use", name + 5);
+ if (!p)
+ as_bad (_("unknown architecture %s"), name + 5);
+ else
+ {
+ mips_opts.arch = p->cpu;
+ mips_opts.isa = p->isa;
+ }
+ }
+ else
+ as_bad (_("unknown ISA level %s"), name + 4);
+
+ switch (mips_opts.isa)
+ {
+ case 0:
+ break;
+ case ISA_MIPS1:
+ case ISA_MIPS2:
+ case ISA_MIPS32:
+ case ISA_MIPS32R2:
+ mips_opts.gp32 = 1;
+ mips_opts.fp32 = 1;
+ break;
+ case ISA_MIPS3:
+ case ISA_MIPS4:
+ case ISA_MIPS5:
+ case ISA_MIPS64:
+ case ISA_MIPS64R2:
+ mips_opts.gp32 = 0;
+ mips_opts.fp32 = 0;
+ break;
+ default:
+ as_bad (_("unknown ISA level %s"), name + 4);
+ break;
+ }
+ if (reset)
+ {
+ mips_opts.gp32 = file_mips_gp32;
+ mips_opts.fp32 = file_mips_fp32;
+ }
+ }
+ else if (strcmp (name, "autoextend") == 0)
+ mips_opts.noautoextend = 0;
+ else if (strcmp (name, "noautoextend") == 0)
+ mips_opts.noautoextend = 1;
+ else if (strcmp (name, "push") == 0)
+ {
+ struct mips_option_stack *s;
+
+ s = (struct mips_option_stack *) xmalloc (sizeof *s);
+ s->next = mips_opts_stack;
+ s->options = mips_opts;
+ mips_opts_stack = s;
+ }
+ else if (strcmp (name, "pop") == 0)
+ {
+ struct mips_option_stack *s;
+
+ s = mips_opts_stack;
+ if (s == NULL)
+ as_bad (_(".set pop with no .set push"));
+ else
+ {
+ /* If we're changing the reorder mode we need to handle
+ delay slots correctly. */
+ if (s->options.noreorder && ! mips_opts.noreorder)
+ mips_emit_delays (TRUE);
+ else if (! s->options.noreorder && mips_opts.noreorder)
+ {
+ if (prev_nop_frag != NULL)
+ {
+ prev_nop_frag->fr_fix -= (prev_nop_frag_holds
+ * (mips_opts.mips16 ? 2 : 4));
+ prev_nop_frag = NULL;
+ }
+ }
+
+ mips_opts = s->options;
+ mips_opts_stack = s->next;
+ free (s);
+ }
+ }
+ else
+ {
+ as_warn (_("Tried to set unrecognized symbol: %s\n"), name);
+ }
+ *input_line_pointer = ch;
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .abicalls pseudo-op. I believe this is equivalent to
+ .option pic2. It means to generate SVR4 PIC calls. */
+
+static void
+s_abicalls (int ignore ATTRIBUTE_UNUSED)
+{
+ mips_pic = SVR4_PIC;
+ mips_abicalls = TRUE;
+ if (USE_GLOBAL_POINTER_OPT)
+ {
+ if (g_switch_seen && g_switch_value != 0)
+ as_warn (_("-G may not be used with SVR4 PIC code"));
+ g_switch_value = 0;
+ }
+ bfd_set_gp_size (stdoutput, 0);
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .cpload pseudo-op. This is used when generating SVR4
+ PIC code. It sets the $gp register for the function based on the
+ function address, which is in the register named in the argument.
+ This uses a relocation against _gp_disp, which is handled specially
+ by the linker. The result is:
+ lui $gp,%hi(_gp_disp)
+ addiu $gp,$gp,%lo(_gp_disp)
+ addu $gp,$gp,.cpload argument
+ The .cpload argument is normally $25 == $t9. */
+
+static void
+s_cpload (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS ex;
+
+ /* If we are not generating SVR4 PIC code, or if this is NewABI code,
+ .cpload is ignored. */
+ if (mips_pic != SVR4_PIC || HAVE_NEWABI)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ /* .cpload should be in a .set noreorder section. */
+ if (mips_opts.noreorder == 0)
+ as_warn (_(".cpload not in noreorder section"));
+
+ ex.X_op = O_symbol;
+ ex.X_add_symbol = symbol_find_or_make ("_gp_disp");
+ ex.X_op_symbol = NULL;
+ ex.X_add_number = 0;
+
+ /* In ELF, this symbol is implicitly an STT_OBJECT symbol. */
+ symbol_get_bfdsym (ex.X_add_symbol)->flags |= BSF_OBJECT;
+
+ macro_start ();
+ macro_build_lui (&ex, mips_gp_register);
+ macro_build (&ex, "addiu", "t,r,j", mips_gp_register,
+ mips_gp_register, BFD_RELOC_LO16);
+ macro_build (NULL, "addu", "d,v,t", mips_gp_register,
+ mips_gp_register, tc_get_register (0));
+ macro_end ();
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .cpsetup pseudo-op defined for NewABI PIC code. The syntax is:
+ .cpsetup $reg1, offset|$reg2, label
+
+ If offset is given, this results in:
+ sd $gp, offset($sp)
+ lui $gp, %hi(%neg(%gp_rel(label)))
+ addiu $gp, $gp, %lo(%neg(%gp_rel(label)))
+ daddu $gp, $gp, $reg1
+
+ If $reg2 is given, this results in:
+ daddu $reg2, $gp, $0
+ lui $gp, %hi(%neg(%gp_rel(label)))
+ addiu $gp, $gp, %lo(%neg(%gp_rel(label)))
+ daddu $gp, $gp, $reg1
+ $reg1 is normally $25 == $t9. */
+static void
+s_cpsetup (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS ex_off;
+ expressionS ex_sym;
+ int reg1;
+ char *f;
+
+ /* If we are not generating SVR4 PIC code, .cpsetup is ignored.
+ We also need NewABI support. */
+ if (mips_pic != SVR4_PIC || ! HAVE_NEWABI)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ reg1 = tc_get_register (0);
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing argument separator ',' for .cpsetup"));
+ return;
+ }
+ else
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '$')
+ {
+ mips_cpreturn_register = tc_get_register (0);
+ mips_cpreturn_offset = -1;
+ }
+ else
+ {
+ mips_cpreturn_offset = get_absolute_expression ();
+ mips_cpreturn_register = -1;
+ }
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing argument separator ',' for .cpsetup"));
+ return;
+ }
+ else
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ expression (&ex_sym);
+
+ macro_start ();
+ if (mips_cpreturn_register == -1)
+ {
+ ex_off.X_op = O_constant;
+ ex_off.X_add_symbol = NULL;
+ ex_off.X_op_symbol = NULL;
+ ex_off.X_add_number = mips_cpreturn_offset;
+
+ macro_build (&ex_off, "sd", "t,o(b)", mips_gp_register,
+ BFD_RELOC_LO16, SP);
+ }
+ else
+ macro_build (NULL, "daddu", "d,v,t", mips_cpreturn_register,
+ mips_gp_register, 0);
+
+ /* Ensure there's room for the next two instructions, so that `f'
+ doesn't end up with an address in the wrong frag. */
+ frag_grow (8);
+ f = frag_more (0);
+ macro_build (&ex_sym, "lui", "t,u", mips_gp_register, BFD_RELOC_GPREL16);
+ fix_new (frag_now, f - frag_now->fr_literal,
+ 8, NULL, 0, 0, BFD_RELOC_MIPS_SUB);
+ fix_new (frag_now, f - frag_now->fr_literal,
+ 4, NULL, 0, 0, BFD_RELOC_HI16_S);
+
+ f = frag_more (0);
+ macro_build (&ex_sym, "addiu", "t,r,j", mips_gp_register,
+ mips_gp_register, BFD_RELOC_GPREL16);
+ fix_new (frag_now, f - frag_now->fr_literal,
+ 8, NULL, 0, 0, BFD_RELOC_MIPS_SUB);
+ fix_new (frag_now, f - frag_now->fr_literal,
+ 4, NULL, 0, 0, BFD_RELOC_LO16);
+
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", mips_gp_register,
+ mips_gp_register, reg1);
+ macro_end ();
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_cplocal (int ignore ATTRIBUTE_UNUSED)
+{
+ /* If we are not generating SVR4 PIC code, or if this is not NewABI code,
+ .cplocal is ignored. */
+ if (mips_pic != SVR4_PIC || ! HAVE_NEWABI)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ mips_gp_register = tc_get_register (0);
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .cprestore pseudo-op. This stores $gp into a given
+ offset from $sp. The offset is remembered, and after making a PIC
+ call $gp is restored from that location. */
+
+static void
+s_cprestore (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS ex;
+
+ /* If we are not generating SVR4 PIC code, or if this is NewABI code,
+ .cprestore is ignored. */
+ if (mips_pic != SVR4_PIC || HAVE_NEWABI)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ mips_cprestore_offset = get_absolute_expression ();
+ mips_cprestore_valid = 1;
+
+ ex.X_op = O_constant;
+ ex.X_add_symbol = NULL;
+ ex.X_op_symbol = NULL;
+ ex.X_add_number = mips_cprestore_offset;
+
+ macro_start ();
+ macro_build_ldst_constoffset (&ex, ADDRESS_STORE_INSN, mips_gp_register,
+ SP, HAVE_64BIT_ADDRESSES);
+ macro_end ();
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .cpreturn pseudo-op defined for NewABI PIC code. If an offset
+ was given in the preceding .cpsetup, it results in:
+ ld $gp, offset($sp)
+
+ If a register $reg2 was given there, it results in:
+ daddu $gp, $reg2, $0
+ */
+static void
+s_cpreturn (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS ex;
+
+ /* If we are not generating SVR4 PIC code, .cpreturn is ignored.
+ We also need NewABI support. */
+ if (mips_pic != SVR4_PIC || ! HAVE_NEWABI)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ macro_start ();
+ if (mips_cpreturn_register == -1)
+ {
+ ex.X_op = O_constant;
+ ex.X_add_symbol = NULL;
+ ex.X_op_symbol = NULL;
+ ex.X_add_number = mips_cpreturn_offset;
+
+ macro_build (&ex, "ld", "t,o(b)", mips_gp_register, BFD_RELOC_LO16, SP);
+ }
+ else
+ macro_build (NULL, "daddu", "d,v,t", mips_gp_register,
+ mips_cpreturn_register, 0);
+ macro_end ();
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .gpvalue pseudo-op. This is used when generating NewABI PIC
+ code. It sets the offset to use in gp_rel relocations. */
+
+static void
+s_gpvalue (int ignore ATTRIBUTE_UNUSED)
+{
+ /* If we are not generating SVR4 PIC code, .gpvalue is ignored.
+ We also need NewABI support. */
+ if (mips_pic != SVR4_PIC || ! HAVE_NEWABI)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ mips_gprel_offset = get_absolute_expression ();
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .gpword pseudo-op. This is used when generating PIC
+ code. It generates a 32 bit GP relative reloc. */
+
+static void
+s_gpword (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *label;
+ expressionS ex;
+ char *p;
+
+ /* When not generating PIC code, this is treated as .word. */
+ if (mips_pic != SVR4_PIC)
+ {
+ s_cons (2);
+ return;
+ }
+
+ label = insn_labels != NULL ? insn_labels->label : NULL;
+ mips_emit_delays (TRUE);
+ if (auto_align)
+ mips_align (2, 0, label);
+ mips_clear_insn_labels ();
+
+ expression (&ex);
+
+ if (ex.X_op != O_symbol || ex.X_add_number != 0)
+ {
+ as_bad (_("Unsupported use of .gpword"));
+ ignore_rest_of_line ();
+ }
+
+ p = frag_more (4);
+ md_number_to_chars (p, 0, 4);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, FALSE,
+ BFD_RELOC_GPREL32);
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_gpdword (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *label;
+ expressionS ex;
+ char *p;
+
+ /* When not generating PIC code, this is treated as .dword. */
+ if (mips_pic != SVR4_PIC)
+ {
+ s_cons (3);
+ return;
+ }
+
+ label = insn_labels != NULL ? insn_labels->label : NULL;
+ mips_emit_delays (TRUE);
+ if (auto_align)
+ mips_align (3, 0, label);
+ mips_clear_insn_labels ();
+
+ expression (&ex);
+
+ if (ex.X_op != O_symbol || ex.X_add_number != 0)
+ {
+ as_bad (_("Unsupported use of .gpdword"));
+ ignore_rest_of_line ();
+ }
+
+ p = frag_more (8);
+ md_number_to_chars (p, 0, 8);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, FALSE,
+ BFD_RELOC_GPREL32);
+
+ /* GPREL32 composed with 64 gives a 64-bit GP offset. */
+ ex.X_op = O_absent;
+ ex.X_add_symbol = 0;
+ ex.X_add_number = 0;
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 8, &ex, FALSE,
+ BFD_RELOC_64);
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .cpadd pseudo-op. This is used when dealing with switch
+ tables in SVR4 PIC code. */
+
+static void
+s_cpadd (int ignore ATTRIBUTE_UNUSED)
+{
+ int reg;
+
+ /* This is ignored when not generating SVR4 PIC code. */
+ if (mips_pic != SVR4_PIC)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ /* Add $gp to the register named as an argument. */
+ macro_start ();
+ reg = tc_get_register (0);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", reg, reg, mips_gp_register);
+ macro_end ();
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .insn pseudo-op. This marks instruction labels in
+ mips16 mode. This permits the linker to handle them specially,
+ such as generating jalx instructions when needed. We also make
+ them odd for the duration of the assembly, in order to generate the
+ right sort of code. We will make them even in the adjust_symtab
+ routine, while leaving them marked. This is convenient for the
+ debugger and the disassembler. The linker knows to make them odd
+ again. */
+
+static void
+s_insn (int ignore ATTRIBUTE_UNUSED)
+{
+ mips16_mark_labels ();
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .stabn directive. We need these in order to mark a label
+ as being a mips16 text label correctly. Sometimes the compiler
+ will emit a label, followed by a .stabn, and then switch sections.
+ If the label and .stabn are in mips16 mode, then the label is
+ really a mips16 text label. */
+
+static void
+s_mips_stab (int type)
+{
+ if (type == 'n')
+ mips16_mark_labels ();
+
+ s_stab (type);
+}
+
+/* Handle the .weakext pseudo-op as defined in Kane and Heinrich.
+ */
+
+static void
+s_mips_weakext (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ int c;
+ symbolS *symbolP;
+ expressionS exp;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+ S_SET_WEAK (symbolP);
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE ();
+
+ if (! is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ if (S_IS_DEFINED (symbolP))
+ {
+ as_bad ("ignoring attempt to redefine symbol %s",
+ S_GET_NAME (symbolP));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ }
+
+ expression (&exp);
+ if (exp.X_op != O_symbol)
+ {
+ as_bad ("bad .weakext directive");
+ ignore_rest_of_line ();
+ return;
+ }
+ symbol_set_value_expression (symbolP, &exp);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Parse a register string into a number. Called from the ECOFF code
+ to parse .frame. The argument is non-zero if this is the frame
+ register, so that we can record it in mips_frame_reg. */
+
+int
+tc_get_register (int frame)
+{
+ int reg;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer++ != '$')
+ {
+ as_warn (_("expected `$'"));
+ reg = ZERO;
+ }
+ else if (ISDIGIT (*input_line_pointer))
+ {
+ reg = get_absolute_expression ();
+ if (reg < 0 || reg >= 32)
+ {
+ as_warn (_("Bad register number"));
+ reg = ZERO;
+ }
+ }
+ else
+ {
+ if (strncmp (input_line_pointer, "ra", 2) == 0)
+ {
+ reg = RA;
+ input_line_pointer += 2;
+ }
+ else if (strncmp (input_line_pointer, "fp", 2) == 0)
+ {
+ reg = FP;
+ input_line_pointer += 2;
+ }
+ else if (strncmp (input_line_pointer, "sp", 2) == 0)
+ {
+ reg = SP;
+ input_line_pointer += 2;
+ }
+ else if (strncmp (input_line_pointer, "gp", 2) == 0)
+ {
+ reg = GP;
+ input_line_pointer += 2;
+ }
+ else if (strncmp (input_line_pointer, "at", 2) == 0)
+ {
+ reg = AT;
+ input_line_pointer += 2;
+ }
+ else if (strncmp (input_line_pointer, "kt0", 3) == 0)
+ {
+ reg = KT0;
+ input_line_pointer += 3;
+ }
+ else if (strncmp (input_line_pointer, "kt1", 3) == 0)
+ {
+ reg = KT1;
+ input_line_pointer += 3;
+ }
+ else if (strncmp (input_line_pointer, "zero", 4) == 0)
+ {
+ reg = ZERO;
+ input_line_pointer += 4;
+ }
+ else
+ {
+ as_warn (_("Unrecognized register name"));
+ reg = ZERO;
+ while (ISALNUM(*input_line_pointer))
+ input_line_pointer++;
+ }
+ }
+ if (frame)
+ {
+ mips_frame_reg = reg != 0 ? reg : SP;
+ mips_frame_reg_valid = 1;
+ mips_cprestore_valid = 0;
+ }
+ return reg;
+}
+
+valueT
+md_section_align (asection *seg, valueT addr)
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+
+#ifdef OBJ_ELF
+ /* We don't need to align ELF sections to the full alignment.
+ However, Irix 5 may prefer that we align them at least to a 16
+ byte boundary. We don't bother to align the sections if we are
+ targeted for an embedded system. */
+ if (strcmp (TARGET_OS, "elf") == 0)
+ return addr;
+ if (align > 4)
+ align = 4;
+#endif
+
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+/* Utility routine, called from above as well. If called while the
+ input file is still being read, it's only an approximation. (For
+ example, a symbol may later become defined which appeared to be
+ undefined earlier.) */
+
+static int
+nopic_need_relax (symbolS *sym, int before_relaxing)
+{
+ if (sym == 0)
+ return 0;
+
+ if (USE_GLOBAL_POINTER_OPT && g_switch_value > 0)
+ {
+ const char *symname;
+ int change;
+
+ /* Find out whether this symbol can be referenced off the $gp
+ register. It can be if it is smaller than the -G size or if
+ it is in the .sdata or .sbss section. Certain symbols can
+ not be referenced off the $gp, although it appears as though
+ they can. */
+ symname = S_GET_NAME (sym);
+ if (symname != (const char *) NULL
+ && (strcmp (symname, "eprol") == 0
+ || strcmp (symname, "etext") == 0
+ || strcmp (symname, "_gp") == 0
+ || strcmp (symname, "edata") == 0
+ || strcmp (symname, "_fbss") == 0
+ || strcmp (symname, "_fdata") == 0
+ || strcmp (symname, "_ftext") == 0
+ || strcmp (symname, "end") == 0
+ || strcmp (symname, "_gp_disp") == 0))
+ change = 1;
+ else if ((! S_IS_DEFINED (sym) || S_IS_COMMON (sym))
+ && (0
+#ifndef NO_ECOFF_DEBUGGING
+ || (symbol_get_obj (sym)->ecoff_extern_size != 0
+ && (symbol_get_obj (sym)->ecoff_extern_size
+ <= g_switch_value))
+#endif
+ /* We must defer this decision until after the whole
+ file has been read, since there might be a .extern
+ after the first use of this symbol. */
+ || (before_relaxing
+#ifndef NO_ECOFF_DEBUGGING
+ && symbol_get_obj (sym)->ecoff_extern_size == 0
+#endif
+ && S_GET_VALUE (sym) == 0)
+ || (S_GET_VALUE (sym) != 0
+ && S_GET_VALUE (sym) <= g_switch_value)))
+ change = 0;
+ else
+ {
+ const char *segname;
+
+ segname = segment_name (S_GET_SEGMENT (sym));
+ assert (strcmp (segname, ".lit8") != 0
+ && strcmp (segname, ".lit4") != 0);
+ change = (strcmp (segname, ".sdata") != 0
+ && strcmp (segname, ".sbss") != 0
+ && strncmp (segname, ".sdata.", 7) != 0
+ && strncmp (segname, ".gnu.linkonce.s.", 16) != 0);
+ }
+ return change;
+ }
+ else
+ /* We are not optimizing for the $gp register. */
+ return 1;
+}
+
+
+/* Return true if the given symbol should be considered local for SVR4 PIC. */
+
+static bfd_boolean
+pic_need_relax (symbolS *sym, asection *segtype)
+{
+ asection *symsec;
+ bfd_boolean linkonce;
+
+ /* Handle the case of a symbol equated to another symbol. */
+ while (symbol_equated_reloc_p (sym))
+ {
+ symbolS *n;
+
+ /* It's possible to get a loop here in a badly written
+ program. */
+ n = symbol_get_value_expression (sym)->X_add_symbol;
+ if (n == sym)
+ break;
+ sym = n;
+ }
+
+ symsec = S_GET_SEGMENT (sym);
+
+ /* duplicate the test for LINK_ONCE sections as in adjust_reloc_syms */
+ linkonce = FALSE;
+ if (symsec != segtype && ! S_IS_LOCAL (sym))
+ {
+ if ((bfd_get_section_flags (stdoutput, symsec) & SEC_LINK_ONCE)
+ != 0)
+ linkonce = TRUE;
+
+ /* The GNU toolchain uses an extension for ELF: a section
+ beginning with the magic string .gnu.linkonce is a linkonce
+ section. */
+ if (strncmp (segment_name (symsec), ".gnu.linkonce",
+ sizeof ".gnu.linkonce" - 1) == 0)
+ linkonce = TRUE;
+ }
+
+ /* This must duplicate the test in adjust_reloc_syms. */
+ return (symsec != &bfd_und_section
+ && symsec != &bfd_abs_section
+ && ! bfd_is_com_section (symsec)
+ && !linkonce
+#ifdef OBJ_ELF
+ /* A global or weak symbol is treated as external. */
+ && (OUTPUT_FLAVOR != bfd_target_elf_flavour
+ || (! S_IS_WEAK (sym)
+ && (! S_IS_EXTERNAL (sym)
+ || mips_pic == EMBEDDED_PIC)))
+#endif
+ );
+}
+
+
+/* Given a mips16 variant frag FRAGP, return non-zero if it needs an
+ extended opcode. SEC is the section the frag is in. */
+
+static int
+mips16_extended_frag (fragS *fragp, asection *sec, long stretch)
+{
+ int type;
+ register const struct mips16_immed_operand *op;
+ offsetT val;
+ int mintiny, maxtiny;
+ segT symsec;
+ fragS *sym_frag;
+
+ if (RELAX_MIPS16_USER_SMALL (fragp->fr_subtype))
+ return 0;
+ if (RELAX_MIPS16_USER_EXT (fragp->fr_subtype))
+ return 1;
+
+ type = RELAX_MIPS16_TYPE (fragp->fr_subtype);
+ op = mips16_immed_operands;
+ while (op->type != type)
+ {
+ ++op;
+ assert (op < mips16_immed_operands + MIPS16_NUM_IMMED);
+ }
+
+ if (op->unsp)
+ {
+ if (type == '<' || type == '>' || type == '[' || type == ']')
+ {
+ mintiny = 1;
+ maxtiny = 1 << op->nbits;
+ }
+ else
+ {
+ mintiny = 0;
+ maxtiny = (1 << op->nbits) - 1;
+ }
+ }
+ else
+ {
+ mintiny = - (1 << (op->nbits - 1));
+ maxtiny = (1 << (op->nbits - 1)) - 1;
+ }
+
+ sym_frag = symbol_get_frag (fragp->fr_symbol);
+ val = S_GET_VALUE (fragp->fr_symbol);
+ symsec = S_GET_SEGMENT (fragp->fr_symbol);
+
+ if (op->pcrel)
+ {
+ addressT addr;
+
+ /* We won't have the section when we are called from
+ mips_relax_frag. However, we will always have been called
+ from md_estimate_size_before_relax first. If this is a
+ branch to a different section, we mark it as such. If SEC is
+ NULL, and the frag is not marked, then it must be a branch to
+ the same section. */
+ if (sec == NULL)
+ {
+ if (RELAX_MIPS16_LONG_BRANCH (fragp->fr_subtype))
+ return 1;
+ }
+ else
+ {
+ /* Must have been called from md_estimate_size_before_relax. */
+ if (symsec != sec)
+ {
+ fragp->fr_subtype =
+ RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
+
+ /* FIXME: We should support this, and let the linker
+ catch branches and loads that are out of range. */
+ as_bad_where (fragp->fr_file, fragp->fr_line,
+ _("unsupported PC relative reference to different section"));
+
+ return 1;
+ }
+ if (fragp != sym_frag && sym_frag->fr_address == 0)
+ /* Assume non-extended on the first relaxation pass.
+ The address we have calculated will be bogus if this is
+ a forward branch to another frag, as the forward frag
+ will have fr_address == 0. */
+ return 0;
+ }
+
+ /* In this case, we know for sure that the symbol fragment is in
+ the same section. If the relax_marker of the symbol fragment
+ differs from the relax_marker of this fragment, we have not
+ yet adjusted the symbol fragment fr_address. We want to add
+ in STRETCH in order to get a better estimate of the address.
+ This particularly matters because of the shift bits. */
+ if (stretch != 0
+ && sym_frag->relax_marker != fragp->relax_marker)
+ {
+ fragS *f;
+
+ /* Adjust stretch for any alignment frag. Note that if have
+ been expanding the earlier code, the symbol may be
+ defined in what appears to be an earlier frag. FIXME:
+ This doesn't handle the fr_subtype field, which specifies
+ a maximum number of bytes to skip when doing an
+ alignment. */
+ for (f = fragp; f != NULL && f != sym_frag; f = f->fr_next)
+ {
+ if (f->fr_type == rs_align || f->fr_type == rs_align_code)
+ {
+ if (stretch < 0)
+ stretch = - ((- stretch)
+ & ~ ((1 << (int) f->fr_offset) - 1));
+ else
+ stretch &= ~ ((1 << (int) f->fr_offset) - 1);
+ if (stretch == 0)
+ break;
+ }
+ }
+ if (f != NULL)
+ val += stretch;
+ }
+
+ addr = fragp->fr_address + fragp->fr_fix;
+
+ /* The base address rules are complicated. The base address of
+ a branch is the following instruction. The base address of a
+ PC relative load or add is the instruction itself, but if it
+ is in a delay slot (in which case it can not be extended) use
+ the address of the instruction whose delay slot it is in. */
+ if (type == 'p' || type == 'q')
+ {
+ addr += 2;
+
+ /* If we are currently assuming that this frag should be
+ extended, then, the current address is two bytes
+ higher. */
+ if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
+ addr += 2;
+
+ /* Ignore the low bit in the target, since it will be set
+ for a text label. */
+ if ((val & 1) != 0)
+ --val;
+ }
+ else if (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype))
+ addr -= 4;
+ else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype))
+ addr -= 2;
+
+ val -= addr & ~ ((1 << op->shift) - 1);
+
+ /* Branch offsets have an implicit 0 in the lowest bit. */
+ if (type == 'p' || type == 'q')
+ val /= 2;
+
+ /* If any of the shifted bits are set, we must use an extended
+ opcode. If the address depends on the size of this
+ instruction, this can lead to a loop, so we arrange to always
+ use an extended opcode. We only check this when we are in
+ the main relaxation loop, when SEC is NULL. */
+ if ((val & ((1 << op->shift) - 1)) != 0 && sec == NULL)
+ {
+ fragp->fr_subtype =
+ RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
+ return 1;
+ }
+
+ /* If we are about to mark a frag as extended because the value
+ is precisely maxtiny + 1, then there is a chance of an
+ infinite loop as in the following code:
+ la $4,foo
+ .skip 1020
+ .align 2
+ foo:
+ In this case when the la is extended, foo is 0x3fc bytes
+ away, so the la can be shrunk, but then foo is 0x400 away, so
+ the la must be extended. To avoid this loop, we mark the
+ frag as extended if it was small, and is about to become
+ extended with a value of maxtiny + 1. */
+ if (val == ((maxtiny + 1) << op->shift)
+ && ! RELAX_MIPS16_EXTENDED (fragp->fr_subtype)
+ && sec == NULL)
+ {
+ fragp->fr_subtype =
+ RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
+ return 1;
+ }
+ }
+ else if (symsec != absolute_section && sec != NULL)
+ as_bad_where (fragp->fr_file, fragp->fr_line, _("unsupported relocation"));
+
+ if ((val & ((1 << op->shift) - 1)) != 0
+ || val < (mintiny << op->shift)
+ || val > (maxtiny << op->shift))
+ return 1;
+ else
+ return 0;
+}
+
+/* Compute the length of a branch sequence, and adjust the
+ RELAX_BRANCH_TOOFAR bit accordingly. If FRAGP is NULL, the
+ worst-case length is computed, with UPDATE being used to indicate
+ whether an unconditional (-1), branch-likely (+1) or regular (0)
+ branch is to be computed. */
+static int
+relaxed_branch_length (fragS *fragp, asection *sec, int update)
+{
+ bfd_boolean toofar;
+ int length;
+
+ if (fragp
+ && S_IS_DEFINED (fragp->fr_symbol)
+ && sec == S_GET_SEGMENT (fragp->fr_symbol))
+ {
+ addressT addr;
+ offsetT val;
+
+ val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
+
+ addr = fragp->fr_address + fragp->fr_fix + 4;
+
+ val -= addr;
+
+ toofar = val < - (0x8000 << 2) || val >= (0x8000 << 2);
+ }
+ else if (fragp)
+ /* If the symbol is not defined or it's in a different segment,
+ assume the user knows what's going on and emit a short
+ branch. */
+ toofar = FALSE;
+ else
+ toofar = TRUE;
+
+ if (fragp && update && toofar != RELAX_BRANCH_TOOFAR (fragp->fr_subtype))
+ fragp->fr_subtype
+ = RELAX_BRANCH_ENCODE (RELAX_BRANCH_UNCOND (fragp->fr_subtype),
+ RELAX_BRANCH_LIKELY (fragp->fr_subtype),
+ RELAX_BRANCH_LINK (fragp->fr_subtype),
+ toofar);
+
+ length = 4;
+ if (toofar)
+ {
+ if (fragp ? RELAX_BRANCH_LIKELY (fragp->fr_subtype) : (update > 0))
+ length += 8;
+
+ if (mips_pic != NO_PIC)
+ {
+ /* Additional space for PIC loading of target address. */
+ length += 8;
+ if (mips_opts.isa == ISA_MIPS1)
+ /* Additional space for $at-stabilizing nop. */
+ length += 4;
+ }
+
+ /* If branch is conditional. */
+ if (fragp ? !RELAX_BRANCH_UNCOND (fragp->fr_subtype) : (update >= 0))
+ length += 8;
+ }
+
+ return length;
+}
+
+/* Estimate the size of a frag before relaxing. Unless this is the
+ mips16, we are not really relaxing here, and the final size is
+ encoded in the subtype information. For the mips16, we have to
+ decide whether we are using an extended opcode or not. */
+
+int
+md_estimate_size_before_relax (fragS *fragp, asection *segtype)
+{
+ int change;
+
+ if (RELAX_BRANCH_P (fragp->fr_subtype))
+ {
+
+ fragp->fr_var = relaxed_branch_length (fragp, segtype, FALSE);
+
+ return fragp->fr_var;
+ }
+
+ if (RELAX_MIPS16_P (fragp->fr_subtype))
+ /* We don't want to modify the EXTENDED bit here; it might get us
+ into infinite loops. We change it only in mips_relax_frag(). */
+ return (RELAX_MIPS16_EXTENDED (fragp->fr_subtype) ? 4 : 2);
+
+ if (mips_pic == NO_PIC)
+ change = nopic_need_relax (fragp->fr_symbol, 0);
+ else if (mips_pic == SVR4_PIC)
+ change = pic_need_relax (fragp->fr_symbol, segtype);
+ else
+ abort ();
+
+ if (change)
+ {
+ fragp->fr_subtype |= RELAX_USE_SECOND;
+ return -RELAX_FIRST (fragp->fr_subtype);
+ }
+ else
+ return -RELAX_SECOND (fragp->fr_subtype);
+}
+
+/* This is called to see whether a reloc against a defined symbol
+ should be converted into a reloc against a section. Don't adjust
+ MIPS16 jump relocations, so we don't have to worry about the format
+ of the offset in the .o file. Don't adjust relocations against
+ mips16 symbols, so that the linker can find them if it needs to set
+ up a stub. */
+
+int
+mips_fix_adjustable (fixS *fixp)
+{
+ if (fixp->fx_r_type == BFD_RELOC_MIPS16_JMP)
+ return 0;
+
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ if (fixp->fx_addsy == NULL)
+ return 1;
+
+#ifdef OBJ_ELF
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour
+ && S_GET_OTHER (fixp->fx_addsy) == STO_MIPS16
+ && fixp->fx_subsy == NULL)
+ return 0;
+#endif
+
+ return 1;
+}
+
+/* Translate internal representation of relocation info to BFD target
+ format. */
+
+arelent **
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ static arelent *retval[4];
+ arelent *reloc;
+ bfd_reloc_code_real_type code;
+
+ memset (retval, 0, sizeof(retval));
+ reloc = retval[0] = (arelent *) xcalloc (1, 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;
+
+ if (mips_pic == EMBEDDED_PIC
+ && SWITCH_TABLE (fixp))
+ {
+ /* For a switch table entry we use a special reloc. The addend
+ is actually the difference between the reloc address and the
+ subtrahend. */
+ reloc->addend = reloc->address - S_GET_VALUE (fixp->fx_subsy);
+ if (OUTPUT_FLAVOR != bfd_target_ecoff_flavour)
+ as_fatal (_("Double check fx_r_type in tc-mips.c:tc_gen_reloc"));
+ fixp->fx_r_type = BFD_RELOC_GPREL32;
+ }
+ else if (fixp->fx_pcrel)
+ {
+ bfd_vma pcrel_address;
+
+ /* Set PCREL_ADDRESS to this relocation's "PC". The PC for high
+ high-part relocs is the address of the low-part reloc. */
+ if (fixp->fx_r_type == BFD_RELOC_PCREL_HI16_S)
+ {
+ assert (fixp->fx_next != NULL
+ && fixp->fx_next->fx_r_type == BFD_RELOC_PCREL_LO16);
+ pcrel_address = (fixp->fx_next->fx_where
+ + fixp->fx_next->fx_frag->fr_address);
+ }
+ else
+ pcrel_address = reloc->address;
+
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ {
+ /* At this point, fx_addnumber is "symbol offset - pcrel_address".
+ Relocations want only the symbol offset. */
+ reloc->addend = fixp->fx_addnumber + pcrel_address;
+ }
+ else if (fixp->fx_r_type == BFD_RELOC_PCREL_LO16
+ || fixp->fx_r_type == BFD_RELOC_PCREL_HI16_S)
+ {
+ /* We use a special addend for an internal RELLO or RELHI reloc. */
+ if (symbol_section_p (fixp->fx_addsy))
+ reloc->addend = pcrel_address - S_GET_VALUE (fixp->fx_subsy);
+ else
+ reloc->addend = fixp->fx_addnumber + pcrel_address;
+ }
+ else
+ {
+ if (OUTPUT_FLAVOR != bfd_target_aout_flavour)
+ /* A gruesome hack which is a result of the gruesome gas reloc
+ handling. */
+ reloc->addend = pcrel_address;
+ else
+ reloc->addend = -pcrel_address;
+ }
+ }
+ else
+ reloc->addend = fixp->fx_addnumber;
+
+ /* Since the old MIPS ELF ABI uses Rel instead of Rela, encode the vtable
+ entry to be used in the relocation's section offset. */
+ if (! HAVE_NEWABI && fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ {
+ reloc->address = reloc->addend;
+ reloc->addend = 0;
+ }
+
+ /* Since DIFF_EXPR_OK is defined in tc-mips.h, it is possible that
+ fixup_segment converted a non-PC relative reloc into a PC
+ relative reloc. In such a case, we need to convert the reloc
+ code. */
+ code = fixp->fx_r_type;
+ if (fixp->fx_pcrel)
+ {
+ switch (code)
+ {
+ case BFD_RELOC_8:
+ code = BFD_RELOC_8_PCREL;
+ break;
+ case BFD_RELOC_16:
+ code = BFD_RELOC_16_PCREL;
+ break;
+ case BFD_RELOC_32:
+ code = BFD_RELOC_32_PCREL;
+ break;
+ case BFD_RELOC_64:
+ code = BFD_RELOC_64_PCREL;
+ break;
+ case BFD_RELOC_8_PCREL:
+ case BFD_RELOC_16_PCREL:
+ case BFD_RELOC_32_PCREL:
+ case BFD_RELOC_64_PCREL:
+ case BFD_RELOC_16_PCREL_S2:
+ case BFD_RELOC_PCREL_HI16_S:
+ case BFD_RELOC_PCREL_LO16:
+ break;
+ default:
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Cannot make %s relocation PC relative"),
+ bfd_get_reloc_code_name (code));
+ }
+ }
+
+ /* To support a PC relative reloc when generating embedded PIC code
+ for ECOFF, we use a Cygnus extension. We check for that here to
+ make sure that we don't let such a reloc escape normally. */
+ if ((OUTPUT_FLAVOR == bfd_target_ecoff_flavour
+ || OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ && code == BFD_RELOC_16_PCREL_S2
+ && mips_pic != EMBEDDED_PIC)
+ reloc->howto = NULL;
+ else
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Can not represent %s relocation in this object file format"),
+ bfd_get_reloc_code_name (code));
+ retval[0] = NULL;
+ }
+
+ return retval;
+}
+
+/* Relax a machine dependent frag. This returns the amount by which
+ the current size of the frag should change. */
+
+int
+mips_relax_frag (asection *sec, fragS *fragp, long stretch)
+{
+ if (RELAX_BRANCH_P (fragp->fr_subtype))
+ {
+ offsetT old_var = fragp->fr_var;
+
+ fragp->fr_var = relaxed_branch_length (fragp, sec, TRUE);
+
+ return fragp->fr_var - old_var;
+ }
+
+ if (! RELAX_MIPS16_P (fragp->fr_subtype))
+ return 0;
+
+ if (mips16_extended_frag (fragp, NULL, stretch))
+ {
+ if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
+ return 0;
+ fragp->fr_subtype = RELAX_MIPS16_MARK_EXTENDED (fragp->fr_subtype);
+ return 2;
+ }
+ else
+ {
+ if (! RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
+ return 0;
+ fragp->fr_subtype = RELAX_MIPS16_CLEAR_EXTENDED (fragp->fr_subtype);
+ return -2;
+ }
+
+ return 0;
+}
+
+/* Convert a machine dependent frag. */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
+{
+ if (RELAX_BRANCH_P (fragp->fr_subtype))
+ {
+ bfd_byte *buf;
+ unsigned long insn;
+ expressionS exp;
+ fixS *fixp;
+
+ buf = (bfd_byte *)fragp->fr_literal + fragp->fr_fix;
+
+ if (target_big_endian)
+ insn = bfd_getb32 (buf);
+ else
+ insn = bfd_getl32 (buf);
+
+ if (!RELAX_BRANCH_TOOFAR (fragp->fr_subtype))
+ {
+ /* We generate a fixup instead of applying it right now
+ because, if there are linker relaxations, we're going to
+ need the relocations. */
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = fragp->fr_symbol;
+ exp.X_add_number = fragp->fr_offset;
+
+ fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
+ 4, &exp, 1,
+ BFD_RELOC_16_PCREL_S2);
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+
+ md_number_to_chars (buf, insn, 4);
+ buf += 4;
+ }
+ else
+ {
+ int i;
+
+ as_warn_where (fragp->fr_file, fragp->fr_line,
+ _("relaxed out-of-range branch into a jump"));
+
+ if (RELAX_BRANCH_UNCOND (fragp->fr_subtype))
+ goto uncond;
+
+ if (!RELAX_BRANCH_LIKELY (fragp->fr_subtype))
+ {
+ /* Reverse the branch. */
+ switch ((insn >> 28) & 0xf)
+ {
+ case 4:
+ /* bc[0-3][tf]l? and bc1any[24][ft] instructions can
+ have the condition reversed by tweaking a single
+ bit, and their opcodes all have 0x4???????. */
+ assert ((insn & 0xf1000000) == 0x41000000);
+ insn ^= 0x00010000;
+ break;
+
+ case 0:
+ /* bltz 0x04000000 bgez 0x04010000
+ bltzal 0x04100000 bgezal 0x04110000 */
+ assert ((insn & 0xfc0e0000) == 0x04000000);
+ insn ^= 0x00010000;
+ break;
+
+ case 1:
+ /* beq 0x10000000 bne 0x14000000
+ blez 0x18000000 bgtz 0x1c000000 */
+ insn ^= 0x04000000;
+ break;
+
+ default:
+ abort ();
+ }
+ }
+
+ if (RELAX_BRANCH_LINK (fragp->fr_subtype))
+ {
+ /* Clear the and-link bit. */
+ assert ((insn & 0xfc1c0000) == 0x04100000);
+
+ /* bltzal 0x04100000 bgezal 0x04110000
+ bltzall 0x04120000 bgezall 0x04130000 */
+ insn &= ~0x00100000;
+ }
+
+ /* Branch over the branch (if the branch was likely) or the
+ full jump (not likely case). Compute the offset from the
+ current instruction to branch to. */
+ if (RELAX_BRANCH_LIKELY (fragp->fr_subtype))
+ i = 16;
+ else
+ {
+ /* How many bytes in instructions we've already emitted? */
+ i = buf - (bfd_byte *)fragp->fr_literal - fragp->fr_fix;
+ /* How many bytes in instructions from here to the end? */
+ i = fragp->fr_var - i;
+ }
+ /* Convert to instruction count. */
+ i >>= 2;
+ /* Branch counts from the next instruction. */
+ i--;
+ insn |= i;
+ /* Branch over the jump. */
+ md_number_to_chars (buf, insn, 4);
+ buf += 4;
+
+ /* Nop */
+ md_number_to_chars (buf, 0, 4);
+ buf += 4;
+
+ if (RELAX_BRANCH_LIKELY (fragp->fr_subtype))
+ {
+ /* beql $0, $0, 2f */
+ insn = 0x50000000;
+ /* Compute the PC offset from the current instruction to
+ the end of the variable frag. */
+ /* How many bytes in instructions we've already emitted? */
+ i = buf - (bfd_byte *)fragp->fr_literal - fragp->fr_fix;
+ /* How many bytes in instructions from here to the end? */
+ i = fragp->fr_var - i;
+ /* Convert to instruction count. */
+ i >>= 2;
+ /* Don't decrement i, because we want to branch over the
+ delay slot. */
+
+ insn |= i;
+ md_number_to_chars (buf, insn, 4);
+ buf += 4;
+
+ md_number_to_chars (buf, 0, 4);
+ buf += 4;
+ }
+
+ uncond:
+ if (mips_pic == NO_PIC)
+ {
+ /* j or jal. */
+ insn = (RELAX_BRANCH_LINK (fragp->fr_subtype)
+ ? 0x0c000000 : 0x08000000);
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = fragp->fr_symbol;
+ exp.X_add_number = fragp->fr_offset;
+
+ fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
+ 4, &exp, 0, BFD_RELOC_MIPS_JMP);
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+
+ md_number_to_chars (buf, insn, 4);
+ buf += 4;
+ }
+ else
+ {
+ /* lw/ld $at, <sym>($gp) R_MIPS_GOT16 */
+ insn = HAVE_64BIT_ADDRESSES ? 0xdf810000 : 0x8f810000;
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = fragp->fr_symbol;
+ exp.X_add_number = fragp->fr_offset;
+
+ if (fragp->fr_offset)
+ {
+ exp.X_add_symbol = make_expr_symbol (&exp);
+ exp.X_add_number = 0;
+ }
+
+ fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
+ 4, &exp, 0, BFD_RELOC_MIPS_GOT16);
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+
+ md_number_to_chars (buf, insn, 4);
+ buf += 4;
+
+ if (mips_opts.isa == ISA_MIPS1)
+ {
+ /* nop */
+ md_number_to_chars (buf, 0, 4);
+ buf += 4;
+ }
+
+ /* d/addiu $at, $at, <sym> R_MIPS_LO16 */
+ insn = HAVE_64BIT_ADDRESSES ? 0x64210000 : 0x24210000;
+
+ fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
+ 4, &exp, 0, BFD_RELOC_LO16);
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+
+ md_number_to_chars (buf, insn, 4);
+ buf += 4;
+
+ /* j(al)r $at. */
+ if (RELAX_BRANCH_LINK (fragp->fr_subtype))
+ insn = 0x0020f809;
+ else
+ insn = 0x00200008;
+
+ md_number_to_chars (buf, insn, 4);
+ buf += 4;
+ }
+ }
+
+ assert (buf == (bfd_byte *)fragp->fr_literal
+ + fragp->fr_fix + fragp->fr_var);
+
+ fragp->fr_fix += fragp->fr_var;
+
+ return;
+ }
+
+ if (RELAX_MIPS16_P (fragp->fr_subtype))
+ {
+ int type;
+ register const struct mips16_immed_operand *op;
+ bfd_boolean small, ext;
+ offsetT val;
+ bfd_byte *buf;
+ unsigned long insn;
+ bfd_boolean use_extend;
+ unsigned short extend;
+
+ type = RELAX_MIPS16_TYPE (fragp->fr_subtype);
+ op = mips16_immed_operands;
+ while (op->type != type)
+ ++op;
+
+ if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
+ {
+ small = FALSE;
+ ext = TRUE;
+ }
+ else
+ {
+ small = TRUE;
+ ext = FALSE;
+ }
+
+ resolve_symbol_value (fragp->fr_symbol);
+ val = S_GET_VALUE (fragp->fr_symbol);
+ if (op->pcrel)
+ {
+ addressT addr;
+
+ addr = fragp->fr_address + fragp->fr_fix;
+
+ /* The rules for the base address of a PC relative reloc are
+ complicated; see mips16_extended_frag. */
+ if (type == 'p' || type == 'q')
+ {
+ addr += 2;
+ if (ext)
+ addr += 2;
+ /* Ignore the low bit in the target, since it will be
+ set for a text label. */
+ if ((val & 1) != 0)
+ --val;
+ }
+ else if (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype))
+ addr -= 4;
+ else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype))
+ addr -= 2;
+
+ addr &= ~ (addressT) ((1 << op->shift) - 1);
+ val -= addr;
+
+ /* Make sure the section winds up with the alignment we have
+ assumed. */
+ if (op->shift > 0)
+ record_alignment (asec, op->shift);
+ }
+
+ if (ext
+ && (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype)
+ || RELAX_MIPS16_DSLOT (fragp->fr_subtype)))
+ as_warn_where (fragp->fr_file, fragp->fr_line,
+ _("extended instruction in delay slot"));
+
+ buf = (bfd_byte *) (fragp->fr_literal + fragp->fr_fix);
+
+ if (target_big_endian)
+ insn = bfd_getb16 (buf);
+ else
+ insn = bfd_getl16 (buf);
+
+ mips16_immed (fragp->fr_file, fragp->fr_line, type, val,
+ RELAX_MIPS16_USER_EXT (fragp->fr_subtype),
+ small, ext, &insn, &use_extend, &extend);
+
+ if (use_extend)
+ {
+ md_number_to_chars (buf, 0xf000 | extend, 2);
+ fragp->fr_fix += 2;
+ buf += 2;
+ }
+
+ md_number_to_chars (buf, insn, 2);
+ fragp->fr_fix += 2;
+ buf += 2;
+ }
+ else
+ {
+ int first, second;
+ fixS *fixp;
+
+ first = RELAX_FIRST (fragp->fr_subtype);
+ second = RELAX_SECOND (fragp->fr_subtype);
+ fixp = (fixS *) fragp->fr_opcode;
+
+ /* Possibly emit a warning if we've chosen the longer option. */
+ if (((fragp->fr_subtype & RELAX_USE_SECOND) != 0)
+ == ((fragp->fr_subtype & RELAX_SECOND_LONGER) != 0))
+ {
+ const char *msg = macro_warning (fragp->fr_subtype);
+ if (msg != 0)
+ as_warn_where (fragp->fr_file, fragp->fr_line, msg);
+ }
+
+ /* Go through all the fixups for the first sequence. Disable them
+ (by marking them as done) if we're going to use the second
+ sequence instead. */
+ while (fixp
+ && fixp->fx_frag == fragp
+ && fixp->fx_where < fragp->fr_fix - second)
+ {
+ if (fragp->fr_subtype & RELAX_USE_SECOND)
+ fixp->fx_done = 1;
+ fixp = fixp->fx_next;
+ }
+
+ /* Go through the fixups for the second sequence. Disable them if
+ we're going to use the first sequence, otherwise adjust their
+ addresses to account for the relaxation. */
+ while (fixp && fixp->fx_frag == fragp)
+ {
+ if (fragp->fr_subtype & RELAX_USE_SECOND)
+ fixp->fx_where -= first;
+ else
+ fixp->fx_done = 1;
+ fixp = fixp->fx_next;
+ }
+
+ /* Now modify the frag contents. */
+ if (fragp->fr_subtype & RELAX_USE_SECOND)
+ {
+ char *start;
+
+ start = fragp->fr_literal + fragp->fr_fix - first - second;
+ memmove (start, start + first, second);
+ fragp->fr_fix -= first;
+ }
+ else
+ fragp->fr_fix -= second;
+ }
+}
+
+#ifdef OBJ_ELF
+
+/* This function is called after the relocs have been generated.
+ We've been storing mips16 text labels as odd. Here we convert them
+ back to even for the convenience of the debugger. */
+
+void
+mips_frob_file_after_relocs (void)
+{
+ asymbol **syms;
+ unsigned int count, i;
+
+ if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
+ return;
+
+ syms = bfd_get_outsymbols (stdoutput);
+ count = bfd_get_symcount (stdoutput);
+ for (i = 0; i < count; i++, syms++)
+ {
+ if (elf_symbol (*syms)->internal_elf_sym.st_other == STO_MIPS16
+ && ((*syms)->value & 1) != 0)
+ {
+ (*syms)->value &= ~1;
+ /* If the symbol has an odd size, it was probably computed
+ incorrectly, so adjust that as well. */
+ if ((elf_symbol (*syms)->internal_elf_sym.st_size & 1) != 0)
+ ++elf_symbol (*syms)->internal_elf_sym.st_size;
+ }
+ }
+}
+
+#endif
+
+/* This function is called whenever a label is defined. It is used
+ when handling branch delays; if a branch has a label, we assume we
+ can not move it. */
+
+void
+mips_define_label (symbolS *sym)
+{
+ struct insn_label_list *l;
+
+ if (free_insn_labels == NULL)
+ l = (struct insn_label_list *) xmalloc (sizeof *l);
+ else
+ {
+ l = free_insn_labels;
+ free_insn_labels = l->next;
+ }
+
+ l->label = sym;
+ l->next = insn_labels;
+ insn_labels = l;
+}
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+
+/* Some special processing for a MIPS ELF file. */
+
+void
+mips_elf_final_processing (void)
+{
+ /* Write out the register information. */
+ if (mips_abi != N64_ABI)
+ {
+ Elf32_RegInfo s;
+
+ s.ri_gprmask = mips_gprmask;
+ s.ri_cprmask[0] = mips_cprmask[0];
+ s.ri_cprmask[1] = mips_cprmask[1];
+ s.ri_cprmask[2] = mips_cprmask[2];
+ s.ri_cprmask[3] = mips_cprmask[3];
+ /* The gp_value field is set by the MIPS ELF backend. */
+
+ bfd_mips_elf32_swap_reginfo_out (stdoutput, &s,
+ ((Elf32_External_RegInfo *)
+ mips_regmask_frag));
+ }
+ else
+ {
+ Elf64_Internal_RegInfo s;
+
+ s.ri_gprmask = mips_gprmask;
+ s.ri_pad = 0;
+ s.ri_cprmask[0] = mips_cprmask[0];
+ s.ri_cprmask[1] = mips_cprmask[1];
+ s.ri_cprmask[2] = mips_cprmask[2];
+ s.ri_cprmask[3] = mips_cprmask[3];
+ /* The gp_value field is set by the MIPS ELF backend. */
+
+ bfd_mips_elf64_swap_reginfo_out (stdoutput, &s,
+ ((Elf64_External_RegInfo *)
+ mips_regmask_frag));
+ }
+
+ /* Set the MIPS ELF flag bits. FIXME: There should probably be some
+ sort of BFD interface for this. */
+ if (mips_any_noreorder)
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_NOREORDER;
+ if (mips_pic != NO_PIC)
+ {
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_PIC;
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_CPIC;
+ }
+ if (mips_abicalls)
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_CPIC;
+
+ /* Set MIPS ELF flags for ASEs. */
+ if (file_ase_mips16)
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_M16;
+#if 0 /* XXX FIXME */
+ if (file_ase_mips3d)
+ elf_elfheader (stdoutput)->e_flags |= ???;
+#endif
+ if (file_ase_mdmx)
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_MDMX;
+
+ /* Set the MIPS ELF ABI flags. */
+ if (mips_abi == O32_ABI && USE_E_MIPS_ABI_O32)
+ elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_O32;
+ else if (mips_abi == O64_ABI)
+ elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_O64;
+ else if (mips_abi == EABI_ABI)
+ {
+ if (!file_mips_gp32)
+ elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_EABI64;
+ else
+ elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_EABI32;
+ }
+ else if (mips_abi == N32_ABI)
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ABI2;
+
+ /* Nothing to do for N64_ABI. */
+
+ if (mips_32bitmode)
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_32BITMODE;
+}
+
+#endif /* OBJ_ELF || OBJ_MAYBE_ELF */
+
+typedef struct proc {
+ symbolS *isym;
+ unsigned long reg_mask;
+ unsigned long reg_offset;
+ unsigned long fpreg_mask;
+ unsigned long fpreg_offset;
+ unsigned long frame_offset;
+ unsigned long frame_reg;
+ unsigned long pc_reg;
+} procS;
+
+static procS cur_proc;
+static procS *cur_proc_ptr;
+static int numprocs;
+
+/* Fill in an rs_align_code fragment. */
+
+void
+mips_handle_align (fragS *fragp)
+{
+ if (fragp->fr_type != rs_align_code)
+ return;
+
+ if (mips_opts.mips16)
+ {
+ static const unsigned char be_nop[] = { 0x65, 0x00 };
+ static const unsigned char le_nop[] = { 0x00, 0x65 };
+
+ int bytes;
+ char *p;
+
+ bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+ p = fragp->fr_literal + fragp->fr_fix;
+
+ if (bytes & 1)
+ {
+ *p++ = 0;
+ fragp->fr_fix++;
+ }
+
+ memcpy (p, (target_big_endian ? be_nop : le_nop), 2);
+ fragp->fr_var = 2;
+ }
+
+ /* For mips32, a nop is a zero, which we trivially get by doing nothing. */
+}
+
+static void
+md_obj_begin (void)
+{
+}
+
+static void
+md_obj_end (void)
+{
+ /* check for premature end, nesting errors, etc */
+ if (cur_proc_ptr)
+ as_warn (_("missing .end at end of assembly"));
+}
+
+static long
+get_number (void)
+{
+ int negative = 0;
+ long val = 0;
+
+ if (*input_line_pointer == '-')
+ {
+ ++input_line_pointer;
+ negative = 1;
+ }
+ if (!ISDIGIT (*input_line_pointer))
+ as_bad (_("expected simple number"));
+ if (input_line_pointer[0] == '0')
+ {
+ if (input_line_pointer[1] == 'x')
+ {
+ input_line_pointer += 2;
+ while (ISXDIGIT (*input_line_pointer))
+ {
+ val <<= 4;
+ val |= hex_value (*input_line_pointer++);
+ }
+ return negative ? -val : val;
+ }
+ else
+ {
+ ++input_line_pointer;
+ while (ISDIGIT (*input_line_pointer))
+ {
+ val <<= 3;
+ val |= *input_line_pointer++ - '0';
+ }
+ return negative ? -val : val;
+ }
+ }
+ if (!ISDIGIT (*input_line_pointer))
+ {
+ printf (_(" *input_line_pointer == '%c' 0x%02x\n"),
+ *input_line_pointer, *input_line_pointer);
+ as_warn (_("invalid number"));
+ return -1;
+ }
+ while (ISDIGIT (*input_line_pointer))
+ {
+ val *= 10;
+ val += *input_line_pointer++ - '0';
+ }
+ return negative ? -val : val;
+}
+
+/* The .file directive; just like the usual .file directive, but there
+ is an initial number which is the ECOFF file index. In the non-ECOFF
+ case .file implies DWARF-2. */
+
+static void
+s_mips_file (int x ATTRIBUTE_UNUSED)
+{
+ static int first_file_directive = 0;
+
+ if (ECOFF_DEBUGGING)
+ {
+ get_number ();
+ s_app_file (0);
+ }
+ else
+ {
+ char *filename;
+
+ filename = dwarf2_directive_file (0);
+
+ /* Versions of GCC up to 3.1 start files with a ".file"
+ directive even for stabs output. Make sure that this
+ ".file" is handled. Note that you need a version of GCC
+ after 3.1 in order to support DWARF-2 on MIPS. */
+ if (filename != NULL && ! first_file_directive)
+ {
+ (void) new_logical_line (filename, -1);
+ s_app_file_string (filename);
+ }
+ first_file_directive = 1;
+ }
+}
+
+/* The .loc directive, implying DWARF-2. */
+
+static void
+s_mips_loc (int x ATTRIBUTE_UNUSED)
+{
+ if (!ECOFF_DEBUGGING)
+ dwarf2_directive_loc (0);
+}
+
+/* The .end directive. */
+
+static void
+s_mips_end (int x ATTRIBUTE_UNUSED)
+{
+ symbolS *p;
+
+ /* Following functions need their own .frame and .cprestore directives. */
+ mips_frame_reg_valid = 0;
+ mips_cprestore_valid = 0;
+
+ if (!is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ p = get_symbol ();
+ demand_empty_rest_of_line ();
+ }
+ else
+ p = NULL;
+
+ if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) == 0)
+ as_warn (_(".end not in text section"));
+
+ if (!cur_proc_ptr)
+ {
+ as_warn (_(".end directive without a preceding .ent directive."));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ if (p != NULL)
+ {
+ assert (S_GET_NAME (p));
+ if (strcmp (S_GET_NAME (p), S_GET_NAME (cur_proc_ptr->isym)))
+ as_warn (_(".end symbol does not match .ent symbol."));
+
+ if (debug_type == DEBUG_STABS)
+ stabs_generate_asm_endfunc (S_GET_NAME (p),
+ S_GET_NAME (p));
+ }
+ else
+ as_warn (_(".end directive missing or unknown symbol"));
+
+#ifdef OBJ_ELF
+ /* Generate a .pdr section. */
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour && ! ECOFF_DEBUGGING
+ && mips_flag_pdr)
+ {
+ segT saved_seg = now_seg;
+ subsegT saved_subseg = now_subseg;
+ valueT dot;
+ expressionS exp;
+ char *fragp;
+
+ dot = frag_now_fix ();
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ assert (pdr_seg);
+ subseg_set (pdr_seg, 0);
+
+ /* Write the symbol. */
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = p;
+ exp.X_add_number = 0;
+ emit_expr (&exp, 4);
+
+ fragp = frag_more (7 * 4);
+
+ md_number_to_chars (fragp, cur_proc_ptr->reg_mask, 4);
+ md_number_to_chars (fragp + 4, cur_proc_ptr->reg_offset, 4);
+ md_number_to_chars (fragp + 8, cur_proc_ptr->fpreg_mask, 4);
+ md_number_to_chars (fragp + 12, cur_proc_ptr->fpreg_offset, 4);
+ md_number_to_chars (fragp + 16, cur_proc_ptr->frame_offset, 4);
+ md_number_to_chars (fragp + 20, cur_proc_ptr->frame_reg, 4);
+ md_number_to_chars (fragp + 24, cur_proc_ptr->pc_reg, 4);
+
+ subseg_set (saved_seg, saved_subseg);
+ }
+#endif /* OBJ_ELF */
+
+ cur_proc_ptr = NULL;
+}
+
+/* The .aent and .ent directives. */
+
+static void
+s_mips_ent (int aent)
+{
+ symbolS *symbolP;
+
+ symbolP = get_symbol ();
+ if (*input_line_pointer == ',')
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ if (ISDIGIT (*input_line_pointer)
+ || *input_line_pointer == '-')
+ get_number ();
+
+ if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) == 0)
+ as_warn (_(".ent or .aent not in text section."));
+
+ if (!aent && cur_proc_ptr)
+ as_warn (_("missing .end"));
+
+ if (!aent)
+ {
+ /* This function needs its own .frame and .cprestore directives. */
+ mips_frame_reg_valid = 0;
+ mips_cprestore_valid = 0;
+
+ cur_proc_ptr = &cur_proc;
+ memset (cur_proc_ptr, '\0', sizeof (procS));
+
+ cur_proc_ptr->isym = symbolP;
+
+ symbol_get_bfdsym (symbolP)->flags |= BSF_FUNCTION;
+
+ ++numprocs;
+
+ if (debug_type == DEBUG_STABS)
+ stabs_generate_asm_func (S_GET_NAME (symbolP),
+ S_GET_NAME (symbolP));
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .frame directive. If the mdebug section is present (IRIX 5 native)
+ then ecoff.c (ecoff_directive_frame) is used. For embedded targets,
+ s_mips_frame is used so that we can set the PDR information correctly.
+ We can't use the ecoff routines because they make reference to the ecoff
+ symbol table (in the mdebug section). */
+
+static void
+s_mips_frame (int ignore ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_ELF
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour && ! ECOFF_DEBUGGING)
+ {
+ long val;
+
+ if (cur_proc_ptr == (procS *) NULL)
+ {
+ as_warn (_(".frame outside of .ent"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ cur_proc_ptr->frame_reg = tc_get_register (1);
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer++ != ','
+ || get_absolute_expression_and_terminator (&val) != ',')
+ {
+ as_warn (_("Bad .frame directive"));
+ --input_line_pointer;
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ cur_proc_ptr->frame_offset = val;
+ cur_proc_ptr->pc_reg = tc_get_register (0);
+
+ demand_empty_rest_of_line ();
+ }
+ else
+#endif /* OBJ_ELF */
+ s_ignore (ignore);
+}
+
+/* The .fmask and .mask directives. If the mdebug section is present
+ (IRIX 5 native) then ecoff.c (ecoff_directive_mask) is used. For
+ embedded targets, s_mips_mask is used so that we can set the PDR
+ information correctly. We can't use the ecoff routines because they
+ make reference to the ecoff symbol table (in the mdebug section). */
+
+static void
+s_mips_mask (int reg_type)
+{
+#ifdef OBJ_ELF
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour && ! ECOFF_DEBUGGING)
+ {
+ long mask, off;
+
+ if (cur_proc_ptr == (procS *) NULL)
+ {
+ as_warn (_(".mask/.fmask outside of .ent"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ if (get_absolute_expression_and_terminator (&mask) != ',')
+ {
+ as_warn (_("Bad .mask/.fmask directive"));
+ --input_line_pointer;
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ off = get_absolute_expression ();
+
+ if (reg_type == 'F')
+ {
+ cur_proc_ptr->fpreg_mask = mask;
+ cur_proc_ptr->fpreg_offset = off;
+ }
+ else
+ {
+ cur_proc_ptr->reg_mask = mask;
+ cur_proc_ptr->reg_offset = off;
+ }
+
+ demand_empty_rest_of_line ();
+ }
+ else
+#endif /* OBJ_ELF */
+ s_ignore (reg_type);
+}
+
+/* The .loc directive. */
+
+#if 0
+static void
+s_loc (int x)
+{
+ symbolS *symbolP;
+ int lineno;
+ int addroff;
+
+ assert (now_seg == text_section);
+
+ lineno = get_number ();
+ addroff = frag_now_fix ();
+
+ symbolP = symbol_new ("", N_SLINE, addroff, frag_now);
+ S_SET_TYPE (symbolP, N_SLINE);
+ S_SET_OTHER (symbolP, 0);
+ S_SET_DESC (symbolP, lineno);
+ symbolP->sy_segment = now_seg;
+}
+#endif
+
+/* A table describing all the processors gas knows about. Names are
+ matched in the order listed.
+
+ To ease comparison, please keep this table in the same order as
+ gcc's mips_cpu_info_table[]. */
+static const struct mips_cpu_info mips_cpu_info_table[] =
+{
+ /* Entries for generic ISAs */
+ { "mips1", 1, ISA_MIPS1, CPU_R3000 },
+ { "mips2", 1, ISA_MIPS2, CPU_R6000 },
+ { "mips3", 1, ISA_MIPS3, CPU_R4000 },
+ { "mips4", 1, ISA_MIPS4, CPU_R8000 },
+ { "mips5", 1, ISA_MIPS5, CPU_MIPS5 },
+ { "mips32", 1, ISA_MIPS32, CPU_MIPS32 },
+ { "mips32r2", 1, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "mips64", 1, ISA_MIPS64, CPU_MIPS64 },
+ { "mips64r2", 1, ISA_MIPS64R2, CPU_MIPS64R2 },
+
+ /* MIPS I */
+ { "r3000", 0, ISA_MIPS1, CPU_R3000 },
+ { "r2000", 0, ISA_MIPS1, CPU_R3000 },
+ { "r3900", 0, ISA_MIPS1, CPU_R3900 },
+
+ /* MIPS II */
+ { "r6000", 0, ISA_MIPS2, CPU_R6000 },
+
+ /* MIPS III */
+ { "r4000", 0, ISA_MIPS3, CPU_R4000 },
+ { "r4010", 0, ISA_MIPS2, CPU_R4010 },
+ { "vr4100", 0, ISA_MIPS3, CPU_VR4100 },
+ { "vr4111", 0, ISA_MIPS3, CPU_R4111 },
+ { "vr4120", 0, ISA_MIPS3, CPU_VR4120 },
+ { "vr4130", 0, ISA_MIPS3, CPU_VR4120 },
+ { "vr4181", 0, ISA_MIPS3, CPU_R4111 },
+ { "vr4300", 0, ISA_MIPS3, CPU_R4300 },
+ { "r4400", 0, ISA_MIPS3, CPU_R4400 },
+ { "r4600", 0, ISA_MIPS3, CPU_R4600 },
+ { "orion", 0, ISA_MIPS3, CPU_R4600 },
+ { "r4650", 0, ISA_MIPS3, CPU_R4650 },
+
+ /* MIPS IV */
+ { "r8000", 0, ISA_MIPS4, CPU_R8000 },
+ { "r10000", 0, ISA_MIPS4, CPU_R10000 },
+ { "r12000", 0, ISA_MIPS4, CPU_R12000 },
+ { "vr5000", 0, ISA_MIPS4, CPU_R5000 },
+ { "vr5400", 0, ISA_MIPS4, CPU_VR5400 },
+ { "vr5500", 0, ISA_MIPS4, CPU_VR5500 },
+ { "rm5200", 0, ISA_MIPS4, CPU_R5000 },
+ { "rm5230", 0, ISA_MIPS4, CPU_R5000 },
+ { "rm5231", 0, ISA_MIPS4, CPU_R5000 },
+ { "rm5261", 0, ISA_MIPS4, CPU_R5000 },
+ { "rm5721", 0, ISA_MIPS4, CPU_R5000 },
+ { "rm7000", 0, ISA_MIPS4, CPU_RM7000 },
+ { "rm9000", 0, ISA_MIPS4, CPU_RM7000 },
+
+ /* MIPS 32 */
+ { "4kc", 0, ISA_MIPS32, CPU_MIPS32 },
+ { "4km", 0, ISA_MIPS32, CPU_MIPS32 },
+ { "4kp", 0, ISA_MIPS32, CPU_MIPS32 },
+
+ /* MIPS 64 */
+ { "5kc", 0, ISA_MIPS64, CPU_MIPS64 },
+ { "20kc", 0, ISA_MIPS64, CPU_MIPS64 },
+
+ /* Broadcom SB-1 CPU core */
+ { "sb1", 0, ISA_MIPS64, CPU_SB1 },
+
+ /* End marker */
+ { NULL, 0, 0, 0 }
+};
+
+
+/* Return true if GIVEN is the same as CANONICAL, or if it is CANONICAL
+ with a final "000" replaced by "k". Ignore case.
+
+ Note: this function is shared between GCC and GAS. */
+
+static bfd_boolean
+mips_strict_matching_cpu_name_p (const char *canonical, const char *given)
+{
+ while (*given != 0 && TOLOWER (*given) == TOLOWER (*canonical))
+ given++, canonical++;
+
+ return ((*given == 0 && *canonical == 0)
+ || (strcmp (canonical, "000") == 0 && strcasecmp (given, "k") == 0));
+}
+
+
+/* Return true if GIVEN matches CANONICAL, where GIVEN is a user-supplied
+ CPU name. We've traditionally allowed a lot of variation here.
+
+ Note: this function is shared between GCC and GAS. */
+
+static bfd_boolean
+mips_matching_cpu_name_p (const char *canonical, const char *given)
+{
+ /* First see if the name matches exactly, or with a final "000"
+ turned into "k". */
+ if (mips_strict_matching_cpu_name_p (canonical, given))
+ return TRUE;
+
+ /* If not, try comparing based on numerical designation alone.
+ See if GIVEN is an unadorned number, or 'r' followed by a number. */
+ if (TOLOWER (*given) == 'r')
+ given++;
+ if (!ISDIGIT (*given))
+ return FALSE;
+
+ /* Skip over some well-known prefixes in the canonical name,
+ hoping to find a number there too. */
+ if (TOLOWER (canonical[0]) == 'v' && TOLOWER (canonical[1]) == 'r')
+ canonical += 2;
+ else if (TOLOWER (canonical[0]) == 'r' && TOLOWER (canonical[1]) == 'm')
+ canonical += 2;
+ else if (TOLOWER (canonical[0]) == 'r')
+ canonical += 1;
+
+ return mips_strict_matching_cpu_name_p (canonical, given);
+}
+
+
+/* Parse an option that takes the name of a processor as its argument.
+ OPTION is the name of the option and CPU_STRING is the argument.
+ Return the corresponding processor enumeration if the CPU_STRING is
+ recognized, otherwise report an error and return null.
+
+ A similar function exists in GCC. */
+
+static const struct mips_cpu_info *
+mips_parse_cpu (const char *option, const char *cpu_string)
+{
+ const struct mips_cpu_info *p;
+
+ /* 'from-abi' selects the most compatible architecture for the given
+ ABI: MIPS I for 32-bit ABIs and MIPS III for 64-bit ABIs. For the
+ EABIs, we have to decide whether we're using the 32-bit or 64-bit
+ version. Look first at the -mgp options, if given, otherwise base
+ the choice on MIPS_DEFAULT_64BIT.
+
+ Treat NO_ABI like the EABIs. One reason to do this is that the
+ plain 'mips' and 'mips64' configs have 'from-abi' as their default
+ architecture. This code picks MIPS I for 'mips' and MIPS III for
+ 'mips64', just as we did in the days before 'from-abi'. */
+ if (strcasecmp (cpu_string, "from-abi") == 0)
+ {
+ if (ABI_NEEDS_32BIT_REGS (mips_abi))
+ return mips_cpu_info_from_isa (ISA_MIPS1);
+
+ if (ABI_NEEDS_64BIT_REGS (mips_abi))
+ return mips_cpu_info_from_isa (ISA_MIPS3);
+
+ if (file_mips_gp32 >= 0)
+ return mips_cpu_info_from_isa (file_mips_gp32 ? ISA_MIPS1 : ISA_MIPS3);
+
+ return mips_cpu_info_from_isa (MIPS_DEFAULT_64BIT
+ ? ISA_MIPS3
+ : ISA_MIPS1);
+ }
+
+ /* 'default' has traditionally been a no-op. Probably not very useful. */
+ if (strcasecmp (cpu_string, "default") == 0)
+ return 0;
+
+ for (p = mips_cpu_info_table; p->name != 0; p++)
+ if (mips_matching_cpu_name_p (p->name, cpu_string))
+ return p;
+
+ as_bad ("Bad value (%s) for %s", cpu_string, option);
+ return 0;
+}
+
+/* Return the canonical processor information for ISA (a member of the
+ ISA_MIPS* enumeration). */
+
+static const struct mips_cpu_info *
+mips_cpu_info_from_isa (int isa)
+{
+ int i;
+
+ for (i = 0; mips_cpu_info_table[i].name != NULL; i++)
+ if (mips_cpu_info_table[i].is_isa
+ && isa == mips_cpu_info_table[i].isa)
+ return (&mips_cpu_info_table[i]);
+
+ return NULL;
+}
+
+static const struct mips_cpu_info *
+mips_cpu_info_from_arch (int arch)
+{
+ int i;
+
+ for (i = 0; mips_cpu_info_table[i].name != NULL; i++)
+ if (arch == mips_cpu_info_table[i].cpu)
+ return (&mips_cpu_info_table[i]);
+
+ return NULL;
+}
+
+static void
+show (FILE *stream, const char *string, int *col_p, int *first_p)
+{
+ if (*first_p)
+ {
+ fprintf (stream, "%24s", "");
+ *col_p = 24;
+ }
+ else
+ {
+ fprintf (stream, ", ");
+ *col_p += 2;
+ }
+
+ if (*col_p + strlen (string) > 72)
+ {
+ fprintf (stream, "\n%24s", "");
+ *col_p = 24;
+ }
+
+ fprintf (stream, "%s", string);
+ *col_p += strlen (string);
+
+ *first_p = 0;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ int column, first;
+ size_t i;
+
+ fprintf (stream, _("\
+MIPS options:\n\
+-membedded-pic generate embedded position independent code\n\
+-EB generate big endian output\n\
+-EL generate little endian output\n\
+-g, -g2 do not remove unneeded NOPs or swap branches\n\
+-G NUM allow referencing objects up to NUM bytes\n\
+ implicitly with the gp register [default 8]\n"));
+ fprintf (stream, _("\
+-mips1 generate MIPS ISA I instructions\n\
+-mips2 generate MIPS ISA II instructions\n\
+-mips3 generate MIPS ISA III instructions\n\
+-mips4 generate MIPS ISA IV instructions\n\
+-mips5 generate MIPS ISA V instructions\n\
+-mips32 generate MIPS32 ISA instructions\n\
+-mips32r2 generate MIPS32 release 2 ISA instructions\n\
+-mips64 generate MIPS64 ISA instructions\n\
+-mips64r2 generate MIPS64 release 2 ISA instructions\n\
+-march=CPU/-mtune=CPU generate code/schedule for CPU, where CPU is one of:\n"));
+
+ first = 1;
+
+ for (i = 0; mips_cpu_info_table[i].name != NULL; i++)
+ show (stream, mips_cpu_info_table[i].name, &column, &first);
+ show (stream, "from-abi", &column, &first);
+ fputc ('\n', stream);
+
+ fprintf (stream, _("\
+-mCPU equivalent to -march=CPU -mtune=CPU. Deprecated.\n\
+-no-mCPU don't generate code specific to CPU.\n\
+ For -mCPU and -no-mCPU, CPU must be one of:\n"));
+
+ first = 1;
+
+ show (stream, "3900", &column, &first);
+ show (stream, "4010", &column, &first);
+ show (stream, "4100", &column, &first);
+ show (stream, "4650", &column, &first);
+ fputc ('\n', stream);
+
+ fprintf (stream, _("\
+-mips16 generate mips16 instructions\n\
+-no-mips16 do not generate mips16 instructions\n"));
+ fprintf (stream, _("\
+-mfix-vr4120 work around certain VR4120 errata\n\
+-mgp32 use 32-bit GPRs, regardless of the chosen ISA\n\
+-mfp32 use 32-bit FPRs, regardless of the chosen ISA\n\
+-O0 remove unneeded NOPs, do not swap branches\n\
+-O remove unneeded NOPs and swap branches\n\
+--[no-]construct-floats [dis]allow floating point values to be constructed\n\
+--trap, --no-break trap exception on div by 0 and mult overflow\n\
+--break, --no-trap break exception on div by 0 and mult overflow\n"));
+#ifdef OBJ_ELF
+ fprintf (stream, _("\
+-KPIC, -call_shared generate SVR4 position independent code\n\
+-non_shared do not generate position independent code\n\
+-xgot assume a 32 bit GOT\n\
+-mpdr, -mno-pdr enable/disable creation of .pdr sections\n\
+-mabi=ABI create ABI conformant object file for:\n"));
+
+ first = 1;
+
+ show (stream, "32", &column, &first);
+ show (stream, "o64", &column, &first);
+ show (stream, "n32", &column, &first);
+ show (stream, "64", &column, &first);
+ show (stream, "eabi", &column, &first);
+
+ fputc ('\n', stream);
+
+ fprintf (stream, _("\
+-32 create o32 ABI object file (default)\n\
+-n32 create n32 ABI object file\n\
+-64 create 64 ABI object file\n"));
+#endif
+}
+
+enum dwarf2_format
+mips_dwarf2_format (void)
+{
+ if (mips_abi == N64_ABI)
+ {
+#ifdef TE_IRIX
+ return dwarf2_format_64bit_irix;
+#else
+ return dwarf2_format_64bit;
+#endif
+ }
+ else
+ return dwarf2_format_32bit;
+}
+
+int
+mips_dwarf2_addr_size (void)
+{
+ if (mips_abi == N64_ABI)
+ return 8;
+ else
+ return 4;
+}
diff --git a/contrib/binutils/gas/config/tc-mips.h b/contrib/binutils/gas/config/tc-mips.h
new file mode 100644
index 0000000..46a7653
--- /dev/null
+++ b/contrib/binutils/gas/config/tc-mips.h
@@ -0,0 +1,188 @@
+/* tc-mips.h -- header file for tc-mips.c.
+ Copyright 1993, 1994, 1995, 1996, 1997, 2000, 2001, 2002, 2003
+ Free Software Foundation, Inc.
+ Contributed by the OSF and Ralph Campbell.
+ Written by Keith Knowles and Ralph Campbell, working independently.
+ Modified for ECOFF support by Ian Lance Taylor of Cygnus Support.
+
+ 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_MIPS
+#define TC_MIPS
+
+struct frag;
+struct expressionS;
+
+/* Default to big endian. */
+#ifndef TARGET_BYTES_BIG_ENDIAN
+#define TARGET_BYTES_BIG_ENDIAN 1
+#endif
+
+#define TARGET_ARCH bfd_arch_mips
+
+#define WORKING_DOT_WORD 1
+#define OLD_FLOAT_READS
+#define REPEAT_CONS_EXPRESSIONS
+#define RELOC_EXPANSION_POSSIBLE
+#define MAX_RELOC_EXPANSION 3
+#define LOCAL_LABELS_FB 1
+
+/* Maximum symbol offset that can be encoded in a BFD_RELOC_GPREL16
+ relocation. */
+#define MAX_GPREL_OFFSET (0x7FF0)
+
+#define md_relax_frag(segment, fragp, stretch) \
+ mips_relax_frag(segment, fragp, stretch)
+extern int mips_relax_frag (asection *, struct frag *, long);
+
+#define md_undefined_symbol(name) (0)
+#define md_operand(x)
+
+extern void mips_handle_align (struct frag *);
+#define HANDLE_ALIGN(fragp) mips_handle_align (fragp)
+
+#define MAX_MEM_FOR_RS_ALIGN_CODE (1 + 2)
+
+/* We permit PC relative difference expressions when generating
+ embedded PIC code. */
+#define DIFF_EXPR_OK
+
+/* Tell assembler that we have an itbl_mips.h header file to include. */
+#define HAVE_ITBL_CPU
+
+/* The endianness of the target format may change based on command
+ line arguments. */
+#define TARGET_FORMAT mips_target_format()
+extern const char *mips_target_format (void);
+
+/* MIPS PIC level. */
+
+enum mips_pic_level
+{
+ /* Do not generate PIC code. */
+ NO_PIC,
+
+ /* Generate PIC code as in the SVR4 MIPS ABI. */
+ SVR4_PIC,
+
+ /* Generate PIC code without using a global offset table: the data
+ segment has a maximum size of 64K, all data references are off
+ the $gp register, and all text references are PC relative. This
+ is used on some embedded systems. */
+ EMBEDDED_PIC
+};
+
+extern enum mips_pic_level mips_pic;
+
+struct mips_cl_insn
+{
+ unsigned long insn_opcode;
+ const struct mips_opcode *insn_mo;
+ /* The next two fields are used when generating mips16 code. */
+ bfd_boolean use_extend;
+ unsigned short extend;
+};
+
+extern int tc_get_register (int frame);
+
+#define md_after_parse_args() mips_after_parse_args()
+extern void mips_after_parse_args (void);
+
+#define tc_init_after_args() mips_init_after_args()
+extern void mips_init_after_args (void);
+
+#define md_parse_long_option(arg) mips_parse_long_option (arg)
+extern int mips_parse_long_option (const char *);
+
+#define tc_frob_label(sym) mips_define_label (sym)
+extern void mips_define_label (symbolS *);
+
+#define tc_frob_file_before_adjust() mips_frob_file_before_adjust ()
+extern void mips_frob_file_before_adjust (void);
+
+#define tc_frob_file_before_fix() mips_frob_file ()
+extern void mips_frob_file (void);
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+#define tc_frob_file_after_relocs mips_frob_file_after_relocs
+extern void mips_frob_file_after_relocs (void);
+#endif
+
+#define tc_fix_adjustable(fixp) mips_fix_adjustable (fixp)
+extern int mips_fix_adjustable (struct fix *);
+
+/* Values passed to md_apply_fix3 don't include symbol values. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+/* Global syms must not be resolved, to support ELF shared libraries.
+ When generating embedded code, we don't have shared libs. */
+#define EXTERN_FORCE_RELOC \
+ (OUTPUT_FLAVOR == bfd_target_elf_flavour \
+ && mips_pic != EMBEDDED_PIC)
+
+/* When generating embedded PIC code we must keep PC relative
+ relocations. */
+#define TC_FORCE_RELOCATION(FIX) mips_force_relocation (FIX)
+extern int mips_force_relocation (struct fix *);
+
+#define TC_FORCE_RELOCATION_SUB_SAME(FIX, SEG) \
+ (! SEG_NORMAL (SEG) || mips_force_relocation (FIX))
+
+/* We use this to turn branches to global symbols into branches to
+ local symbols, so that they can be simplified. */
+#define TC_VALIDATE_FIX(fixp, this_segment, skip_label) \
+ do \
+ if (! mips_validate_fix ((fixp), (this_segment))) \
+ goto skip_label; \
+ while (0)
+extern int mips_validate_fix (struct fix *, asection *);
+
+/* Register mask variables. These are set by the MIPS assembly code
+ and used by ECOFF and possibly other object file formats. */
+extern unsigned long mips_gprmask;
+extern unsigned long mips_cprmask[4];
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+
+#define elf_tc_final_processing mips_elf_final_processing
+extern void mips_elf_final_processing (void);
+
+#endif
+
+extern void md_mips_end (void);
+#define md_end() md_mips_end()
+
+#define USE_GLOBAL_POINTER_OPT (OUTPUT_FLAVOR == bfd_target_ecoff_flavour \
+ || OUTPUT_FLAVOR == bfd_target_coff_flavour \
+ || OUTPUT_FLAVOR == bfd_target_elf_flavour)
+
+extern void mips_pop_insert (void);
+#define md_pop_insert() mips_pop_insert()
+
+extern void mips_flush_pending_output (void);
+#define md_flush_pending_output mips_flush_pending_output
+
+extern void mips_enable_auto_align (void);
+#define md_elf_section_change_hook() mips_enable_auto_align()
+
+extern enum dwarf2_format mips_dwarf2_format (void);
+#define DWARF2_FORMAT() mips_dwarf2_format ()
+
+#define DWARF2_ADDR_SIZE(bfd) mips_dwarf2_addr_size ()
+
+#endif /* TC_MIPS */
diff --git a/contrib/binutils/gas/config/te-tmips.h b/contrib/binutils/gas/config/te-tmips.h
new file mode 100644
index 0000000..2fc6fd9
--- /dev/null
+++ b/contrib/binutils/gas/config/te-tmips.h
@@ -0,0 +1,40 @@
+/* Traditional MIPS targets
+ Copyright 2000 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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. */
+
+/* This file is te-tmips.h and is intended to provide support for
+ traditional mips targets like mips-dde-sysv4.2MP (Supermax ) ,
+ mips-sni-sysv4* (Sinix) etc. The base for this file is te-generic.h.
+ Created by Koundinya.K < kk@ddeorg.soft.net > with the help of
+ Ian Lance Taylor, Cygnus Support, <ian@cygnus.com>. */
+
+/* Added these, because if we don't know what we're targeting we may
+ need an assembler version of libgcc, and that will use local
+ labels. */
+
+#define TE_TMIPS 1
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+/* these define interfaces */
+#ifdef OBJ_HEADER
+#include OBJ_HEADER
+#else
+#include "obj-format.h"
+#endif
diff --git a/contrib/binutils/gas/doc/c-mips.texi b/contrib/binutils/gas/doc/c-mips.texi
new file mode 100644
index 0000000..1375230
--- /dev/null
+++ b/contrib/binutils/gas/doc/c-mips.texi
@@ -0,0 +1,377 @@
+@c Copyright 1991, 1992, 1993, 1994, 1995, 1997, 1999, 2000, 2001,
+@c 2002, 2003, 2004
+@c Free Software Foundation, Inc.
+@c This is part of the GAS manual.
+@c For copying conditions, see the file as.texinfo.
+@ifset GENERIC
+@page
+@node MIPS-Dependent
+@chapter MIPS Dependent Features
+@end ifset
+@ifclear GENERIC
+@node Machine Dependencies
+@chapter MIPS Dependent Features
+@end ifclear
+
+@cindex MIPS processor
+@sc{gnu} @code{@value{AS}} for @sc{mips} architectures supports several
+different @sc{mips} processors, and MIPS ISA levels I through V, MIPS32,
+and MIPS64. For information about the @sc{mips} instruction set, see
+@cite{MIPS RISC Architecture}, by Kane and Heindrich (Prentice-Hall).
+For an overview of @sc{mips} assembly conventions, see ``Appendix D:
+Assembly Language Programming'' in the same work.
+
+@menu
+* MIPS Opts:: Assembler options
+* MIPS Object:: ECOFF object code
+* MIPS Stabs:: Directives for debugging information
+* MIPS ISA:: Directives to override the ISA level
+* MIPS autoextend:: Directives for extending MIPS 16 bit instructions
+* MIPS insn:: Directive to mark data as an instruction
+* MIPS option stack:: Directives to save and restore options
+* MIPS ASE instruction generation overrides:: Directives to control
+ generation of MIPS ASE instructions
+@end menu
+
+@node MIPS Opts
+@section Assembler options
+
+The @sc{mips} configurations of @sc{gnu} @code{@value{AS}} support these
+special options:
+
+@table @code
+@cindex @code{-G} option (MIPS)
+@item -G @var{num}
+This option sets the largest size of an object that can be referenced
+implicitly with the @code{gp} register. It is only accepted for targets
+that use @sc{ecoff} format. The default value is 8.
+
+@cindex @code{-EB} option (MIPS)
+@cindex @code{-EL} option (MIPS)
+@cindex MIPS big-endian output
+@cindex MIPS little-endian output
+@cindex big-endian output, MIPS
+@cindex little-endian output, MIPS
+@item -EB
+@itemx -EL
+Any @sc{mips} configuration of @code{@value{AS}} can select big-endian or
+little-endian output at run time (unlike the other @sc{gnu} development
+tools, which must be configured for one or the other). Use @samp{-EB}
+to select big-endian output, and @samp{-EL} for little-endian.
+
+@cindex MIPS architecture options
+@item -mips1
+@itemx -mips2
+@itemx -mips3
+@itemx -mips4
+@itemx -mips5
+@itemx -mips32
+@itemx -mips32r2
+@itemx -mips64
+@itemx -mips64r2
+Generate code for a particular MIPS Instruction Set Architecture level.
+@samp{-mips1} corresponds to the @sc{r2000} and @sc{r3000} processors,
+@samp{-mips2} to the @sc{r6000} processor, @samp{-mips3} to the
+@sc{r4000} processor, and @samp{-mips4} to the @sc{r8000} and
+@sc{r10000} processors. @samp{-mips5}, @samp{-mips32}, @samp{-mips32r2},
+@samp{-mips64}, and @samp{-mips64r2}
+correspond to generic
+@sc{MIPS V}, @sc{MIPS32}, @sc{MIPS32 Release 2}, @sc{MIPS64},
+and @sc{MIPS64 Release 2}
+ISA processors, respectively. You can also switch
+instruction sets during the assembly; see @ref{MIPS ISA, Directives to
+override the ISA level}.
+
+@item -mgp32
+@itemx -mfp32
+Some macros have different expansions for 32-bit and 64-bit registers.
+The register sizes are normally inferred from the ISA and ABI, but these
+flags force a certain group of registers to be treated as 32 bits wide at
+all times. @samp{-mgp32} controls the size of general-purpose registers
+and @samp{-mfp32} controls the size of floating-point registers.
+
+On some MIPS variants there is a 32-bit mode flag; when this flag is
+set, 64-bit instructions generate a trap. Also, some 32-bit OSes only
+save the 32-bit registers on a context switch, so it is essential never
+to use the 64-bit registers.
+
+@item -mgp64
+Assume that 64-bit general purpose registers are available. This is
+provided in the interests of symmetry with -gp32.
+
+@item -mips16
+@itemx -no-mips16
+Generate code for the MIPS 16 processor. This is equivalent to putting
+@samp{.set mips16} at the start of the assembly file. @samp{-no-mips16}
+turns off this option.
+
+@item -mips3d
+@itemx -no-mips3d
+Generate code for the MIPS-3D Application Specific Extension.
+This tells the assembler to accept MIPS-3D instructions.
+@samp{-no-mips3d} turns off this option.
+
+@item -mdmx
+@itemx -no-mdmx
+Generate code for the MDMX Application Specific Extension.
+This tells the assembler to accept MDMX instructions.
+@samp{-no-mdmx} turns off this option.
+
+@item -mfix7000
+@itemx -mno-fix7000
+Cause nops to be inserted if the read of the destination register
+of an mfhi or mflo instruction occurs in the following two instructions.
+
+@item -mfix-vr4120
+@itemx -no-mfix-vr4120
+Insert nops to work around certain VR4120 errata. This option is
+intended to be used on GCC-generated code: it is not designed to catch
+all problems in hand-written assembler code.
+
+@item -m4010
+@itemx -no-m4010
+Generate code for the LSI @sc{r4010} chip. This tells the assembler to
+accept the @sc{r4010} specific instructions (@samp{addciu}, @samp{ffc},
+etc.), and to not schedule @samp{nop} instructions around accesses to
+the @samp{HI} and @samp{LO} registers. @samp{-no-m4010} turns off this
+option.
+
+@item -m4650
+@itemx -no-m4650
+Generate code for the MIPS @sc{r4650} chip. This tells the assembler to accept
+the @samp{mad} and @samp{madu} instruction, and to not schedule @samp{nop}
+instructions around accesses to the @samp{HI} and @samp{LO} registers.
+@samp{-no-m4650} turns off this option.
+
+@itemx -m3900
+@itemx -no-m3900
+@itemx -m4100
+@itemx -no-m4100
+For each option @samp{-m@var{nnnn}}, generate code for the MIPS
+@sc{r@var{nnnn}} chip. This tells the assembler to accept instructions
+specific to that chip, and to schedule for that chip's hazards.
+
+@item -march=@var{cpu}
+Generate code for a particular MIPS cpu. It is exactly equivalent to
+@samp{-m@var{cpu}}, except that there are more value of @var{cpu}
+understood. Valid @var{cpu} value are:
+
+@quotation
+2000,
+3000,
+3900,
+4000,
+4010,
+4100,
+4111,
+vr4120,
+vr4130,
+vr4181,
+4300,
+4400,
+4600,
+4650,
+5000,
+rm5200,
+rm5230,
+rm5231,
+rm5261,
+rm5721,
+vr5400,
+vr5500,
+6000,
+rm7000,
+8000,
+rm9000,
+10000,
+12000,
+mips32-4k,
+sb1
+@end quotation
+
+@item -mtune=@var{cpu}
+Schedule and tune for a particular MIPS cpu. Valid @var{cpu} values are
+identical to @samp{-march=@var{cpu}}.
+
+@item -mabi=@var{abi}
+Record which ABI the source code uses. The recognized arguments
+are: @samp{32}, @samp{n32}, @samp{o64}, @samp{64} and @samp{eabi}.
+
+@cindex @code{-nocpp} ignored (MIPS)
+@item -nocpp
+This option is ignored. It is accepted for command-line compatibility with
+other assemblers, which use it to turn off C style preprocessing. With
+@sc{gnu} @code{@value{AS}}, there is no need for @samp{-nocpp}, because the
+@sc{gnu} assembler itself never runs the C preprocessor.
+
+@item --construct-floats
+@itemx --no-construct-floats
+@cindex --construct-floats
+@cindex --no-construct-floats
+The @code{--no-construct-floats} option disables the construction of
+double width floating point constants by loading the two halves of the
+value into the two single width floating point registers that make up
+the double width register. This feature is useful if the processor
+support the FR bit in its status register, and this bit is known (by
+the programmer) to be set. This bit prevents the aliasing of the double
+width register by the single width registers.
+
+By default @code{--construct-floats} is selected, allowing construction
+of these floating point constants.
+
+@item --trap
+@itemx --no-break
+@c FIXME! (1) reflect these options (next item too) in option summaries;
+@c (2) stop teasing, say _which_ instructions expanded _how_.
+@code{@value{AS}} automatically macro expands certain division and
+multiplication instructions to check for overflow and division by zero. This
+option causes @code{@value{AS}} to generate code to take a trap exception
+rather than a break exception when an error is detected. The trap instructions
+are only supported at Instruction Set Architecture level 2 and higher.
+
+@item --break
+@itemx --no-trap
+Generate code to take a break exception rather than a trap exception when an
+error is detected. This is the default.
+
+@item -mpdr
+@itemx -mno-pdr
+Control generation of @code{.pdr} sections. Off by default on IRIX, on
+elsewhere.
+@end table
+
+@node MIPS Object
+@section MIPS ECOFF object code
+
+@cindex ECOFF sections
+@cindex MIPS ECOFF sections
+Assembling for a @sc{mips} @sc{ecoff} target supports some additional sections
+besides the usual @code{.text}, @code{.data} and @code{.bss}. The
+additional sections are @code{.rdata}, used for read-only data,
+@code{.sdata}, used for small data, and @code{.sbss}, used for small
+common objects.
+
+@cindex small objects, MIPS ECOFF
+@cindex @code{gp} register, MIPS
+When assembling for @sc{ecoff}, the assembler uses the @code{$gp} (@code{$28})
+register to form the address of a ``small object''. Any object in the
+@code{.sdata} or @code{.sbss} sections is considered ``small'' in this sense.
+For external objects, or for objects in the @code{.bss} section, you can use
+the @code{@value{GCC}} @samp{-G} option to control the size of objects addressed via
+@code{$gp}; the default value is 8, meaning that a reference to any object
+eight bytes or smaller uses @code{$gp}. Passing @samp{-G 0} to
+@code{@value{AS}} prevents it from using the @code{$gp} register on the basis
+of object size (but the assembler uses @code{$gp} for objects in @code{.sdata}
+or @code{sbss} in any case). The size of an object in the @code{.bss} section
+is set by the @code{.comm} or @code{.lcomm} directive that defines it. The
+size of an external object may be set with the @code{.extern} directive. For
+example, @samp{.extern sym,4} declares that the object at @code{sym} is 4 bytes
+in length, whie leaving @code{sym} otherwise undefined.
+
+Using small @sc{ecoff} objects requires linker support, and assumes that the
+@code{$gp} register is correctly initialized (normally done automatically by
+the startup code). @sc{mips} @sc{ecoff} assembly code must not modify the
+@code{$gp} register.
+
+@node MIPS Stabs
+@section Directives for debugging information
+
+@cindex MIPS debugging directives
+@sc{mips} @sc{ecoff} @code{@value{AS}} supports several directives used for
+generating debugging information which are not support by traditional @sc{mips}
+assemblers. These are @code{.def}, @code{.endef}, @code{.dim}, @code{.file},
+@code{.scl}, @code{.size}, @code{.tag}, @code{.type}, @code{.val},
+@code{.stabd}, @code{.stabn}, and @code{.stabs}. The debugging information
+generated by the three @code{.stab} directives can only be read by @sc{gdb},
+not by traditional @sc{mips} debuggers (this enhancement is required to fully
+support C++ debugging). These directives are primarily used by compilers, not
+assembly language programmers!
+
+@node MIPS ISA
+@section Directives to override the ISA level
+
+@cindex MIPS ISA override
+@kindex @code{.set mips@var{n}}
+@sc{gnu} @code{@value{AS}} supports an additional directive to change
+the @sc{mips} Instruction Set Architecture level on the fly: @code{.set
+mips@var{n}}. @var{n} should be a number from 0 to 5, or 32, 32r2, 64
+or 64r2.
+The values other than 0 make the assembler accept instructions
+for the corresponding @sc{isa} level, from that point on in the
+assembly. @code{.set mips@var{n}} affects not only which instructions
+are permitted, but also how certain macros are expanded. @code{.set
+mips0} restores the @sc{isa} level to its original level: either the
+level you selected with command line options, or the default for your
+configuration. You can use this feature to permit specific @sc{r4000}
+instructions while assembling in 32 bit mode. Use this directive with
+care!
+
+The directive @samp{.set mips16} puts the assembler into MIPS 16 mode,
+in which it will assemble instructions for the MIPS 16 processor. Use
+@samp{.set nomips16} to return to normal 32 bit mode.
+
+Traditional @sc{mips} assemblers do not support this directive.
+
+@node MIPS autoextend
+@section Directives for extending MIPS 16 bit instructions
+
+@kindex @code{.set autoextend}
+@kindex @code{.set noautoextend}
+By default, MIPS 16 instructions are automatically extended to 32 bits
+when necessary. The directive @samp{.set noautoextend} will turn this
+off. When @samp{.set noautoextend} is in effect, any 32 bit instruction
+must be explicitly extended with the @samp{.e} modifier (e.g.,
+@samp{li.e $4,1000}). The directive @samp{.set autoextend} may be used
+to once again automatically extend instructions when necessary.
+
+This directive is only meaningful when in MIPS 16 mode. Traditional
+@sc{mips} assemblers do not support this directive.
+
+@node MIPS insn
+@section Directive to mark data as an instruction
+
+@kindex @code{.insn}
+The @code{.insn} directive tells @code{@value{AS}} that the following
+data is actually instructions. This makes a difference in MIPS 16 mode:
+when loading the address of a label which precedes instructions,
+@code{@value{AS}} automatically adds 1 to the value, so that jumping to
+the loaded address will do the right thing.
+
+@node MIPS option stack
+@section Directives to save and restore options
+
+@cindex MIPS option stack
+@kindex @code{.set push}
+@kindex @code{.set pop}
+The directives @code{.set push} and @code{.set pop} may be used to save
+and restore the current settings for all the options which are
+controlled by @code{.set}. The @code{.set push} directive saves the
+current settings on a stack. The @code{.set pop} directive pops the
+stack and restores the settings.
+
+These directives can be useful inside an macro which must change an
+option such as the ISA level or instruction reordering but does not want
+to change the state of the code which invoked the macro.
+
+Traditional @sc{mips} assemblers do not support these directives.
+
+@node MIPS ASE instruction generation overrides
+@section Directives to control generation of MIPS ASE instructions
+
+@cindex MIPS MIPS-3D instruction generation override
+@kindex @code{.set mips3d}
+@kindex @code{.set nomips3d}
+The directive @code{.set mips3d} makes the assembler accept instructions
+from the MIPS-3D Application Specific Extension from that point on
+in the assembly. The @code{.set nomips3d} directive prevents MIPS-3D
+instructions from being accepted.
+
+@cindex MIPS MDMX instruction generation override
+@kindex @code{.set mdmx}
+@kindex @code{.set nomdmx}
+The directive @code{.set mdmx} makes the assembler accept instructions
+from the MDMX Application Specific Extension from that point on
+in the assembly. The @code{.set nomdmx} directive prevents MDMX
+instructions from being accepted.
+
+Traditional @sc{mips} assemblers do not support these directives.
diff --git a/contrib/binutils/gprof/mips.c b/contrib/binutils/gprof/mips.c
new file mode 100644
index 0000000..ef7feae
--- /dev/null
+++ b/contrib/binutils/gprof/mips.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 1983, 1993, 1998
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "gprof.h"
+#include "search_list.h"
+#include "source.h"
+#include "symtab.h"
+#include "cg_arcs.h"
+#include "corefile.h"
+#include "hist.h"
+
+static Sym indirect_child;
+
+void mips_find_call PARAMS ((Sym *, bfd_vma, bfd_vma));
+
+void
+mips_find_call (parent, p_lowpc, p_highpc)
+ Sym *parent;
+ bfd_vma p_lowpc;
+ bfd_vma p_highpc;
+{
+ bfd_vma pc, dest_pc;
+ unsigned int op;
+ int offset;
+ Sym *child;
+ static bfd_boolean inited = FALSE;
+
+ if (!inited)
+ {
+ inited = TRUE;
+ sym_init (&indirect_child);
+ indirect_child.name = _("<indirect child>");
+ indirect_child.cg.prop.fract = 1.0;
+ indirect_child.cg.cyc.head = &indirect_child;
+ }
+
+ if (!core_text_space)
+ {
+ return;
+ }
+ if (p_lowpc < s_lowpc)
+ {
+ p_lowpc = s_lowpc;
+ }
+ if (p_highpc > s_highpc)
+ {
+ p_highpc = s_highpc;
+ }
+ DBG (CALLDEBUG, printf (_("[find_call] %s: 0x%lx to 0x%lx\n"),
+ parent->name, (unsigned long) p_lowpc,
+ (unsigned long) p_highpc));
+ for (pc = p_lowpc; pc < p_highpc; pc += 4)
+ {
+ op = bfd_get_32 (core_bfd, &((char *)core_text_space)[pc - s_lowpc]);
+ if ((op & 0xfc000000) == 0x0c000000)
+ {
+ /* This is a "jal" instruction. Check that the destination
+ is the address of a function. */
+ DBG (CALLDEBUG,
+ printf (_("[find_call] 0x%lx: jal"), (unsigned long) pc));
+ offset = (op & 0x03ffffff) << 2;
+ dest_pc = (pc & ~(bfd_vma) 0xfffffff) | offset;
+ if (dest_pc >= s_lowpc && dest_pc <= s_highpc)
+ {
+ child = sym_lookup (&symtab, dest_pc);
+ DBG (CALLDEBUG,
+ printf (" 0x%lx\t; name=%s, addr=0x%lx",
+ (unsigned long) dest_pc, child->name,
+ (unsigned long) child->addr));
+ if (child->addr == dest_pc)
+ {
+ DBG (CALLDEBUG, printf ("\n"));
+ /* a hit: */
+ arc_add (parent, child, (unsigned long) 0);
+ continue;
+ }
+ }
+ /* Something funny going on. */
+ DBG (CALLDEBUG, printf ("\tbut it's a botch\n"));
+ }
+ else if ((op & 0xfc00f83f) == 0x0000f809)
+ {
+ /* This is a "jalr" instruction (indirect call). */
+ DBG (CALLDEBUG,
+ printf (_("[find_call] 0x%lx: jalr\n"), (unsigned long) pc));
+ arc_add (parent, &indirect_child, (unsigned long) 0);
+ }
+ }
+}
diff --git a/contrib/binutils/include/coff/mips.h b/contrib/binutils/include/coff/mips.h
new file mode 100644
index 0000000..b9503bd
--- /dev/null
+++ b/contrib/binutils/include/coff/mips.h
@@ -0,0 +1,367 @@
+/* ECOFF support on MIPS machines.
+ coff/ecoff.h must be included before this file.
+
+ Copyright 2001 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ 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. */
+
+#define DO_NOT_DEFINE_AOUTHDR
+#define L_LNNO_SIZE 4
+#include "coff/external.h"
+
+/* Magic numbers are defined in coff/ecoff.h. */
+#define MIPS_ECOFF_BADMAG(x) (((x).f_magic!=MIPS_MAGIC_1) && \
+ ((x).f_magic!=MIPS_MAGIC_LITTLE) &&\
+ ((x).f_magic!=MIPS_MAGIC_BIG) && \
+ ((x).f_magic!=MIPS_MAGIC_LITTLE2) && \
+ ((x).f_magic!=MIPS_MAGIC_BIG2) && \
+ ((x).f_magic!=MIPS_MAGIC_LITTLE3) && \
+ ((x).f_magic!=MIPS_MAGIC_BIG3))
+
+
+/********************** AOUT "OPTIONAL HEADER" **********************/
+
+typedef struct external_aouthdr
+{
+ unsigned char magic[2]; /* type of file */
+ unsigned char vstamp[2]; /* version stamp */
+ unsigned char tsize[4]; /* text size in bytes, padded to FW bdry*/
+ unsigned char dsize[4]; /* initialized data " " */
+ unsigned char bsize[4]; /* uninitialized data " " */
+ unsigned char entry[4]; /* entry pt. */
+ unsigned char text_start[4]; /* base of text used for this file */
+ unsigned char data_start[4]; /* base of data used for this file */
+ unsigned char bss_start[4]; /* base of bss used for this file */
+ unsigned char gprmask[4]; /* ?? */
+ unsigned char cprmask[4][4]; /* ?? */
+ unsigned char gp_value[4]; /* value for gp register */
+} AOUTHDR;
+
+/* compute size of a header */
+
+#define AOUTSZ 56
+#define AOUTHDRSZ 56
+
+/********************** RELOCATION DIRECTIVES **********************/
+
+struct external_reloc
+ {
+ unsigned char r_vaddr[4];
+ unsigned char r_bits[4];
+ };
+
+#define RELOC struct external_reloc
+#define RELSZ 8
+
+/* MIPS ECOFF uses a packed 8 byte format for relocs. These constants
+ are used to unpack the r_bits field. */
+
+#define RELOC_BITS0_SYMNDX_SH_LEFT_BIG 16
+#define RELOC_BITS0_SYMNDX_SH_LEFT_LITTLE 0
+
+#define RELOC_BITS1_SYMNDX_SH_LEFT_BIG 8
+#define RELOC_BITS1_SYMNDX_SH_LEFT_LITTLE 8
+
+#define RELOC_BITS2_SYMNDX_SH_LEFT_BIG 0
+#define RELOC_BITS2_SYMNDX_SH_LEFT_LITTLE 16
+
+/* Originally, ECOFF used four bits for the reloc type and had three
+ reserved bits. Irix 4 added another bit for the reloc type, which
+ was easy because it was big endian and one of the spare bits became
+ the new most significant bit. To make this also work for little
+ endian ECOFF, we need to wrap one of the reserved bits around to
+ become the most significant bit of the reloc type. */
+#define RELOC_BITS3_TYPE_BIG 0x3E
+#define RELOC_BITS3_TYPE_SH_BIG 1
+#define RELOC_BITS3_TYPE_LITTLE 0x78
+#define RELOC_BITS3_TYPE_SH_LITTLE 3
+#define RELOC_BITS3_TYPEHI_LITTLE 0x04
+#define RELOC_BITS3_TYPEHI_SH_LITTLE 2
+
+#define RELOC_BITS3_EXTERN_BIG 0x01
+#define RELOC_BITS3_EXTERN_LITTLE 0x80
+
+/* The r_type field in a reloc is one of the following values. I
+ don't know if any other values can appear. These seem to be all
+ that occur in the Ultrix 4.2 libraries. */
+#define MIPS_R_IGNORE 0
+#define MIPS_R_REFHALF 1
+#define MIPS_R_REFWORD 2
+#define MIPS_R_JMPADDR 3
+#define MIPS_R_REFHI 4
+#define MIPS_R_REFLO 5
+#define MIPS_R_GPREL 6
+#define MIPS_R_LITERAL 7
+
+/* These reloc types are a Cygnus extension used when generating
+ position independent code for embedded systems. The numbers are
+ taken from Irix 4, but at least for internal relocs Irix 5 does not
+ give them the same meaning. For an internal reloc the symbol index
+ of RELHI and RELLO is modified as described below for
+ MIPS_R_SWITCH. */
+#define MIPS_R_PCREL16 12
+#define MIPS_R_RELHI 13
+#define MIPS_R_RELLO 14
+
+/* This reloc type is a Cygnus extension used when generating position
+ independent code for embedded systems. It is used for an entry in
+ a switch table, which looks like this:
+ .word $L3-$LS12
+ The object file will contain the correct difference, and does not
+ require adjustment. However, when the linker is relaxing PC
+ relative calls, it is possible for $L3 to move farther away. This
+ reloc always appears in the .text section, and is always against
+ the .text section. However, the symbol index is not
+ RELOC_SECTION_TEXT. It is, instead, the distance between this
+ switch table entry and $LS12. Thus, the original value of $L12 is
+ vaddr - symndx
+ and the original value of $L3 is
+ vaddr - symndx + addend
+ where addend is the value in the object file. Knowing this, the
+ linker can know whether the addend in the object file must be
+ adjusted. */
+#define MIPS_R_SWITCH 22
+
+/********************** STABS **********************/
+
+#define MIPS_IS_STAB ECOFF_IS_STAB
+#define MIPS_MARK_STAB ECOFF_MARK_STAB
+#define MIPS_UNMARK_STAB ECOFF_UNMARK_STAB
+
+/********************** SYMBOLIC INFORMATION **********************/
+
+/* Written by John Gilmore. */
+
+/* ECOFF uses COFF-like section structures, but its own symbol format.
+ This file defines the symbol format in fields whose size and alignment
+ will not vary on different host systems. */
+
+/* File header as a set of bytes */
+
+struct hdr_ext
+{
+ unsigned char h_magic[2];
+ unsigned char h_vstamp[2];
+ unsigned char h_ilineMax[4];
+ unsigned char h_cbLine[4];
+ unsigned char h_cbLineOffset[4];
+ unsigned char h_idnMax[4];
+ unsigned char h_cbDnOffset[4];
+ unsigned char h_ipdMax[4];
+ unsigned char h_cbPdOffset[4];
+ unsigned char h_isymMax[4];
+ unsigned char h_cbSymOffset[4];
+ unsigned char h_ioptMax[4];
+ unsigned char h_cbOptOffset[4];
+ unsigned char h_iauxMax[4];
+ unsigned char h_cbAuxOffset[4];
+ unsigned char h_issMax[4];
+ unsigned char h_cbSsOffset[4];
+ unsigned char h_issExtMax[4];
+ unsigned char h_cbSsExtOffset[4];
+ unsigned char h_ifdMax[4];
+ unsigned char h_cbFdOffset[4];
+ unsigned char h_crfd[4];
+ unsigned char h_cbRfdOffset[4];
+ unsigned char h_iextMax[4];
+ unsigned char h_cbExtOffset[4];
+};
+
+/* File descriptor external record */
+
+struct fdr_ext
+{
+ unsigned char f_adr[4];
+ unsigned char f_rss[4];
+ unsigned char f_issBase[4];
+ unsigned char f_cbSs[4];
+ unsigned char f_isymBase[4];
+ unsigned char f_csym[4];
+ unsigned char f_ilineBase[4];
+ unsigned char f_cline[4];
+ unsigned char f_ioptBase[4];
+ unsigned char f_copt[4];
+ unsigned char f_ipdFirst[2];
+ unsigned char f_cpd[2];
+ unsigned char f_iauxBase[4];
+ unsigned char f_caux[4];
+ unsigned char f_rfdBase[4];
+ unsigned char f_crfd[4];
+ unsigned char f_bits1[1];
+ unsigned char f_bits2[3];
+ unsigned char f_cbLineOffset[4];
+ unsigned char f_cbLine[4];
+};
+
+#define FDR_BITS1_LANG_BIG 0xF8
+#define FDR_BITS1_LANG_SH_BIG 3
+#define FDR_BITS1_LANG_LITTLE 0x1F
+#define FDR_BITS1_LANG_SH_LITTLE 0
+
+#define FDR_BITS1_FMERGE_BIG 0x04
+#define FDR_BITS1_FMERGE_LITTLE 0x20
+
+#define FDR_BITS1_FREADIN_BIG 0x02
+#define FDR_BITS1_FREADIN_LITTLE 0x40
+
+#define FDR_BITS1_FBIGENDIAN_BIG 0x01
+#define FDR_BITS1_FBIGENDIAN_LITTLE 0x80
+
+#define FDR_BITS2_GLEVEL_BIG 0xC0
+#define FDR_BITS2_GLEVEL_SH_BIG 6
+#define FDR_BITS2_GLEVEL_LITTLE 0x03
+#define FDR_BITS2_GLEVEL_SH_LITTLE 0
+
+/* We ignore the `reserved' field in bits2. */
+
+/* Procedure descriptor external record */
+
+struct pdr_ext
+{
+ unsigned char p_adr[4];
+ unsigned char p_isym[4];
+ unsigned char p_iline[4];
+ unsigned char p_regmask[4];
+ unsigned char p_regoffset[4];
+ unsigned char p_iopt[4];
+ unsigned char p_fregmask[4];
+ unsigned char p_fregoffset[4];
+ unsigned char p_frameoffset[4];
+ unsigned char p_framereg[2];
+ unsigned char p_pcreg[2];
+ unsigned char p_lnLow[4];
+ unsigned char p_lnHigh[4];
+ unsigned char p_cbLineOffset[4];
+};
+
+/* Runtime procedure table */
+
+struct rpdr_ext
+{
+ unsigned char p_adr[4];
+ unsigned char p_regmask[4];
+ unsigned char p_regoffset[4];
+ unsigned char p_fregmask[4];
+ unsigned char p_fregoffset[4];
+ unsigned char p_frameoffset[4];
+ unsigned char p_framereg[2];
+ unsigned char p_pcreg[2];
+ unsigned char p_irpss[4];
+ unsigned char p_reserved[4];
+ unsigned char p_exception_info[4];
+};
+
+/* Line numbers */
+
+struct line_ext
+{
+ unsigned char l_line[4];
+};
+
+/* Symbol external record */
+
+struct sym_ext
+{
+ unsigned char s_iss[4];
+ unsigned char s_value[4];
+ unsigned char s_bits1[1];
+ unsigned char s_bits2[1];
+ unsigned char s_bits3[1];
+ unsigned char s_bits4[1];
+};
+
+#define SYM_BITS1_ST_BIG 0xFC
+#define SYM_BITS1_ST_SH_BIG 2
+#define SYM_BITS1_ST_LITTLE 0x3F
+#define SYM_BITS1_ST_SH_LITTLE 0
+
+#define SYM_BITS1_SC_BIG 0x03
+#define SYM_BITS1_SC_SH_LEFT_BIG 3
+#define SYM_BITS1_SC_LITTLE 0xC0
+#define SYM_BITS1_SC_SH_LITTLE 6
+
+#define SYM_BITS2_SC_BIG 0xE0
+#define SYM_BITS2_SC_SH_BIG 5
+#define SYM_BITS2_SC_LITTLE 0x07
+#define SYM_BITS2_SC_SH_LEFT_LITTLE 2
+
+#define SYM_BITS2_RESERVED_BIG 0x10
+#define SYM_BITS2_RESERVED_LITTLE 0x08
+
+#define SYM_BITS2_INDEX_BIG 0x0F
+#define SYM_BITS2_INDEX_SH_LEFT_BIG 16
+#define SYM_BITS2_INDEX_LITTLE 0xF0
+#define SYM_BITS2_INDEX_SH_LITTLE 4
+
+#define SYM_BITS3_INDEX_SH_LEFT_BIG 8
+#define SYM_BITS3_INDEX_SH_LEFT_LITTLE 4
+
+#define SYM_BITS4_INDEX_SH_LEFT_BIG 0
+#define SYM_BITS4_INDEX_SH_LEFT_LITTLE 12
+
+/* External symbol external record */
+
+struct ext_ext
+{
+ unsigned char es_bits1[1];
+ unsigned char es_bits2[1];
+ unsigned char es_ifd[2];
+ struct sym_ext es_asym;
+};
+
+#define EXT_BITS1_JMPTBL_BIG 0x80
+#define EXT_BITS1_JMPTBL_LITTLE 0x01
+
+#define EXT_BITS1_COBOL_MAIN_BIG 0x40
+#define EXT_BITS1_COBOL_MAIN_LITTLE 0x02
+
+#define EXT_BITS1_WEAKEXT_BIG 0x20
+#define EXT_BITS1_WEAKEXT_LITTLE 0x04
+
+/* Dense numbers external record */
+
+struct dnr_ext
+{
+ unsigned char d_rfd[4];
+ unsigned char d_index[4];
+};
+
+/* Relative file descriptor */
+
+struct rfd_ext
+{
+ unsigned char rfd[4];
+};
+
+/* Optimizer symbol external record */
+
+struct opt_ext
+{
+ unsigned char o_bits1[1];
+ unsigned char o_bits2[1];
+ unsigned char o_bits3[1];
+ unsigned char o_bits4[1];
+ struct rndx_ext o_rndx;
+ unsigned char o_offset[4];
+};
+
+#define OPT_BITS2_VALUE_SH_LEFT_BIG 16
+#define OPT_BITS2_VALUE_SH_LEFT_LITTLE 0
+
+#define OPT_BITS3_VALUE_SH_LEFT_BIG 8
+#define OPT_BITS3_VALUE_SH_LEFT_LITTLE 8
+
+#define OPT_BITS4_VALUE_SH_LEFT_BIG 0
+#define OPT_BITS4_VALUE_SH_LEFT_LITTLE 16
diff --git a/contrib/binutils/include/coff/mipspe.h b/contrib/binutils/include/coff/mipspe.h
new file mode 100644
index 0000000..9b4ffab
--- /dev/null
+++ b/contrib/binutils/include/coff/mipspe.h
@@ -0,0 +1,66 @@
+/* coff information for Windows CE with MIPS VR4111
+
+ Copyright 2001 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ 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. */
+
+#define L_LNNO_SIZE 2
+#define INCLUDE_COMDAT_FIELDS_IN_AUXENT
+#include "coff/external.h"
+
+#define MIPS_ARCH_MAGIC_WINCE 0x0166 /* Windows CE - little endian */
+#define MIPS_PE_MAGIC 0x010b
+
+#define MIPSBADMAG(x) ((x).f_magic != MIPS_ARCH_MAGIC_WINCE)
+
+/* define some NT default values */
+/* #define NT_IMAGE_BASE 0x400000 moved to internal.h */
+#define NT_SECTION_ALIGNMENT 0x1000
+#define NT_FILE_ALIGNMENT 0x200
+#define NT_DEF_RESERVE 0x100000
+#define NT_DEF_COMMIT 0x1000
+
+/********************** RELOCATION DIRECTIVES **********************/
+
+/* The external reloc has an offset field, because some of the reloc
+ types on the h8 don't have room in the instruction for the entire
+ offset - eg the strange jump and high page addressing modes. */
+
+struct external_reloc
+{
+ char r_vaddr[4];
+ char r_symndx[4];
+ char r_type[2];
+};
+
+#define RELOC struct external_reloc
+#define RELSZ 10
+
+/* MIPS PE relocation types. */
+
+#define MIPS_R_ABSOLUTE 0 /* ignored */
+#define MIPS_R_REFHALF 1
+#define MIPS_R_REFWORD 2
+#define MIPS_R_JMPADDR 3
+#define MIPS_R_REFHI 4 /* PAIR follows */
+#define MIPS_R_REFLO 5
+#define MIPS_R_GPREL 6
+#define MIPS_R_LITERAL 7 /* same as GPREL */
+#define MIPS_R_SECTION 10
+#define MIPS_R_SECREL 11
+#define MIPS_R_SECRELLO 12
+#define MIPS_R_SECRELHI 13 /* PAIR follows */
+#define MIPS_R_RVA 34 /* 0x22 */
+#define MIPS_R_PAIR 37 /* 0x25 - symndx is really a signed 16-bit addend */
diff --git a/contrib/binutils/include/opcode/mips.h b/contrib/binutils/include/opcode/mips.h
new file mode 100644
index 0000000..5c3ddfc
--- /dev/null
+++ b/contrib/binutils/include/opcode/mips.h
@@ -0,0 +1,914 @@
+/* mips.h. Mips opcode list for GDB, the GNU debugger.
+ Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
+ Free Software Foundation, Inc.
+ Contributed by Ralph Campbell and OSF
+ Commented and modified by Ian Lance Taylor, Cygnus Support
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+1, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+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 file; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef _MIPS_H_
+#define _MIPS_H_
+
+/* These are bit masks and shift counts to use to access the various
+ fields of an instruction. To retrieve the X field of an
+ instruction, use the expression
+ (i >> OP_SH_X) & OP_MASK_X
+ To set the same field (to j), use
+ i = (i &~ (OP_MASK_X << OP_SH_X)) | (j << OP_SH_X)
+
+ Make sure you use fields that are appropriate for the instruction,
+ of course.
+
+ The 'i' format uses OP, RS, RT and IMMEDIATE.
+
+ The 'j' format uses OP and TARGET.
+
+ The 'r' format uses OP, RS, RT, RD, SHAMT and FUNCT.
+
+ The 'b' format uses OP, RS, RT and DELTA.
+
+ The floating point 'i' format uses OP, RS, RT and IMMEDIATE.
+
+ The floating point 'r' format uses OP, FMT, FT, FS, FD and FUNCT.
+
+ A breakpoint instruction uses OP, CODE and SPEC (10 bits of the
+ breakpoint instruction are not defined; Kane says the breakpoint
+ code field in BREAK is 20 bits; yet MIPS assemblers and debuggers
+ only use ten bits). An optional two-operand form of break/sdbbp
+ allows the lower ten bits to be set too, and MIPS32 and later
+ architectures allow 20 bits to be set with a signal operand
+ (using CODE20).
+
+ The syscall instruction uses CODE20.
+
+ The general coprocessor instructions use COPZ. */
+
+#define OP_MASK_OP 0x3f
+#define OP_SH_OP 26
+#define OP_MASK_RS 0x1f
+#define OP_SH_RS 21
+#define OP_MASK_FR 0x1f
+#define OP_SH_FR 21
+#define OP_MASK_FMT 0x1f
+#define OP_SH_FMT 21
+#define OP_MASK_BCC 0x7
+#define OP_SH_BCC 18
+#define OP_MASK_CODE 0x3ff
+#define OP_SH_CODE 16
+#define OP_MASK_CODE2 0x3ff
+#define OP_SH_CODE2 6
+#define OP_MASK_RT 0x1f
+#define OP_SH_RT 16
+#define OP_MASK_FT 0x1f
+#define OP_SH_FT 16
+#define OP_MASK_CACHE 0x1f
+#define OP_SH_CACHE 16
+#define OP_MASK_RD 0x1f
+#define OP_SH_RD 11
+#define OP_MASK_FS 0x1f
+#define OP_SH_FS 11
+#define OP_MASK_PREFX 0x1f
+#define OP_SH_PREFX 11
+#define OP_MASK_CCC 0x7
+#define OP_SH_CCC 8
+#define OP_MASK_CODE20 0xfffff /* 20 bit syscall/breakpoint code. */
+#define OP_SH_CODE20 6
+#define OP_MASK_SHAMT 0x1f
+#define OP_SH_SHAMT 6
+#define OP_MASK_FD 0x1f
+#define OP_SH_FD 6
+#define OP_MASK_TARGET 0x3ffffff
+#define OP_SH_TARGET 0
+#define OP_MASK_COPZ 0x1ffffff
+#define OP_SH_COPZ 0
+#define OP_MASK_IMMEDIATE 0xffff
+#define OP_SH_IMMEDIATE 0
+#define OP_MASK_DELTA 0xffff
+#define OP_SH_DELTA 0
+#define OP_MASK_FUNCT 0x3f
+#define OP_SH_FUNCT 0
+#define OP_MASK_SPEC 0x3f
+#define OP_SH_SPEC 0
+#define OP_SH_LOCC 8 /* FP condition code. */
+#define OP_SH_HICC 18 /* FP condition code. */
+#define OP_MASK_CC 0x7
+#define OP_SH_COP1NORM 25 /* Normal COP1 encoding. */
+#define OP_MASK_COP1NORM 0x1 /* a single bit. */
+#define OP_SH_COP1SPEC 21 /* COP1 encodings. */
+#define OP_MASK_COP1SPEC 0xf
+#define OP_MASK_COP1SCLR 0x4
+#define OP_MASK_COP1CMP 0x3
+#define OP_SH_COP1CMP 4
+#define OP_SH_FORMAT 21 /* FP short format field. */
+#define OP_MASK_FORMAT 0x7
+#define OP_SH_TRUE 16
+#define OP_MASK_TRUE 0x1
+#define OP_SH_GE 17
+#define OP_MASK_GE 0x01
+#define OP_SH_UNSIGNED 16
+#define OP_MASK_UNSIGNED 0x1
+#define OP_SH_HINT 16
+#define OP_MASK_HINT 0x1f
+#define OP_SH_MMI 0 /* Multimedia (parallel) op. */
+#define OP_MASK_MMI 0x3f
+#define OP_SH_MMISUB 6
+#define OP_MASK_MMISUB 0x1f
+#define OP_MASK_PERFREG 0x1f /* Performance monitoring. */
+#define OP_SH_PERFREG 1
+#define OP_SH_SEL 0 /* Coprocessor select field. */
+#define OP_MASK_SEL 0x7 /* The sel field of mfcZ and mtcZ. */
+#define OP_SH_CODE19 6 /* 19 bit wait code. */
+#define OP_MASK_CODE19 0x7ffff
+#define OP_SH_ALN 21
+#define OP_MASK_ALN 0x7
+#define OP_SH_VSEL 21
+#define OP_MASK_VSEL 0x1f
+#define OP_MASK_VECBYTE 0x7 /* Selector field is really 4 bits,
+ but 0x8-0xf don't select bytes. */
+#define OP_SH_VECBYTE 22
+#define OP_MASK_VECALIGN 0x7 /* Vector byte-align (alni.ob) op. */
+#define OP_SH_VECALIGN 21
+#define OP_MASK_INSMSB 0x1f /* "ins" MSB. */
+#define OP_SH_INSMSB 11
+#define OP_MASK_EXTMSBD 0x1f /* "ext" MSBD. */
+#define OP_SH_EXTMSBD 11
+
+#define OP_OP_COP0 0x10
+#define OP_OP_COP1 0x11
+#define OP_OP_COP2 0x12
+#define OP_OP_COP3 0x13
+#define OP_OP_LWC1 0x31
+#define OP_OP_LWC2 0x32
+#define OP_OP_LWC3 0x33 /* a.k.a. pref */
+#define OP_OP_LDC1 0x35
+#define OP_OP_LDC2 0x36
+#define OP_OP_LDC3 0x37 /* a.k.a. ld */
+#define OP_OP_SWC1 0x39
+#define OP_OP_SWC2 0x3a
+#define OP_OP_SWC3 0x3b
+#define OP_OP_SDC1 0x3d
+#define OP_OP_SDC2 0x3e
+#define OP_OP_SDC3 0x3f /* a.k.a. sd */
+
+/* Values in the 'VSEL' field. */
+#define MDMX_FMTSEL_IMM_QH 0x1d
+#define MDMX_FMTSEL_IMM_OB 0x1e
+#define MDMX_FMTSEL_VEC_QH 0x15
+#define MDMX_FMTSEL_VEC_OB 0x16
+
+/* This structure holds information for a particular instruction. */
+
+struct mips_opcode
+{
+ /* The name of the instruction. */
+ const char *name;
+ /* A string describing the arguments for this instruction. */
+ const char *args;
+ /* The basic opcode for the instruction. When assembling, this
+ opcode is modified by the arguments to produce the actual opcode
+ that is used. If pinfo is INSN_MACRO, then this is 0. */
+ unsigned long match;
+ /* If pinfo is not INSN_MACRO, then this is a bit mask for the
+ relevant portions of the opcode when disassembling. If the
+ actual opcode anded with the match field equals the opcode field,
+ then we have found the correct instruction. If pinfo is
+ INSN_MACRO, then this field is the macro identifier. */
+ unsigned long mask;
+ /* For a macro, this is INSN_MACRO. Otherwise, it is a collection
+ of bits describing the instruction, notably any relevant hazard
+ information. */
+ unsigned long pinfo;
+ /* A collection of bits describing the instruction sets of which this
+ instruction or macro is a member. */
+ unsigned long membership;
+};
+
+/* These are the characters which may appear in the args field of an
+ instruction. They appear in the order in which the fields appear
+ when the instruction is used. Commas and parentheses in the args
+ string are ignored when assembling, and written into the output
+ when disassembling.
+
+ Each of these characters corresponds to a mask field defined above.
+
+ "<" 5 bit shift amount (OP_*_SHAMT)
+ ">" shift amount between 32 and 63, stored after subtracting 32 (OP_*_SHAMT)
+ "a" 26 bit target address (OP_*_TARGET)
+ "b" 5 bit base register (OP_*_RS)
+ "c" 10 bit breakpoint code (OP_*_CODE)
+ "d" 5 bit destination register specifier (OP_*_RD)
+ "h" 5 bit prefx hint (OP_*_PREFX)
+ "i" 16 bit unsigned immediate (OP_*_IMMEDIATE)
+ "j" 16 bit signed immediate (OP_*_DELTA)
+ "k" 5 bit cache opcode in target register position (OP_*_CACHE)
+ Also used for immediate operands in vr5400 vector insns.
+ "o" 16 bit signed offset (OP_*_DELTA)
+ "p" 16 bit PC relative branch target address (OP_*_DELTA)
+ "q" 10 bit extra breakpoint code (OP_*_CODE2)
+ "r" 5 bit same register used as both source and target (OP_*_RS)
+ "s" 5 bit source register specifier (OP_*_RS)
+ "t" 5 bit target register (OP_*_RT)
+ "u" 16 bit upper 16 bits of address (OP_*_IMMEDIATE)
+ "v" 5 bit same register used as both source and destination (OP_*_RS)
+ "w" 5 bit same register used as both target and destination (OP_*_RT)
+ "U" 5 bit same destination register in both OP_*_RD and OP_*_RT
+ (used by clo and clz)
+ "C" 25 bit coprocessor function code (OP_*_COPZ)
+ "B" 20 bit syscall/breakpoint function code (OP_*_CODE20)
+ "J" 19 bit wait function code (OP_*_CODE19)
+ "x" accept and ignore register name
+ "z" must be zero register
+ "K" 5 bit Hardware Register (rdhwr instruction) (OP_*_RD)
+ "+A" 5 bit ins/ext position, which becomes LSB (OP_*_SHAMT).
+ Enforces: 0 <= pos < 32.
+ "+B" 5 bit ins size, which becomes MSB (OP_*_INSMSB).
+ Requires that "+A" or "+E" occur first to set position.
+ Enforces: 0 < (pos+size) <= 32.
+ "+C" 5 bit ext size, which becomes MSBD (OP_*_EXTMSBD).
+ Requires that "+A" or "+E" occur first to set position.
+ Enforces: 0 < (pos+size) <= 32.
+ (Also used by "dext" w/ different limits, but limits for
+ that are checked by the M_DEXT macro.)
+ "+E" 5 bit dins/dext position, which becomes LSB-32 (OP_*_SHAMT).
+ Enforces: 32 <= pos < 64.
+ "+F" 5 bit "dinsm" size, which becomes MSB-32 (OP_*_INSMSB).
+ Requires that "+A" or "+E" occur first to set position.
+ Enforces: 32 < (pos+size) <= 64.
+ "+G" 5 bit "dextm" size, which becomes MSBD-32 (OP_*_EXTMSBD).
+ Requires that "+A" or "+E" occur first to set position.
+ Enforces: 32 < (pos+size) <= 64.
+ "+H" 5 bit "dextu" size, which becomes MSBD (OP_*_EXTMSBD).
+ Requires that "+A" or "+E" occur first to set position.
+ Enforces: 32 < (pos+size) <= 64.
+
+ Floating point instructions:
+ "D" 5 bit destination register (OP_*_FD)
+ "M" 3 bit compare condition code (OP_*_CCC) (only used for mips4 and up)
+ "N" 3 bit branch condition code (OP_*_BCC) (only used for mips4 and up)
+ "S" 5 bit fs source 1 register (OP_*_FS)
+ "T" 5 bit ft source 2 register (OP_*_FT)
+ "R" 5 bit fr source 3 register (OP_*_FR)
+ "V" 5 bit same register used as floating source and destination (OP_*_FS)
+ "W" 5 bit same register used as floating target and destination (OP_*_FT)
+
+ Coprocessor instructions:
+ "E" 5 bit target register (OP_*_RT)
+ "G" 5 bit destination register (OP_*_RD)
+ "H" 3 bit sel field for (d)mtc* and (d)mfc* (OP_*_SEL)
+ "P" 5 bit performance-monitor register (OP_*_PERFREG)
+ "e" 5 bit vector register byte specifier (OP_*_VECBYTE)
+ "%" 3 bit immediate vr5400 vector alignment operand (OP_*_VECALIGN)
+ see also "k" above
+ "+D" Combined destination register ("G") and sel ("H") for CP0 ops,
+ for pretty-printing in disassembly only.
+
+ Macro instructions:
+ "A" General 32 bit expression
+ "I" 32 bit immediate (value placed in imm_expr).
+ "+I" 32 bit immediate (value placed in imm2_expr).
+ "F" 64 bit floating point constant in .rdata
+ "L" 64 bit floating point constant in .lit8
+ "f" 32 bit floating point constant
+ "l" 32 bit floating point constant in .lit4
+
+ MDMX instruction operands (note that while these use the FP register
+ fields, they accept both $fN and $vN names for the registers):
+ "O" MDMX alignment offset (OP_*_ALN)
+ "Q" MDMX vector/scalar/immediate source (OP_*_VSEL and OP_*_FT)
+ "X" MDMX destination register (OP_*_FD)
+ "Y" MDMX source register (OP_*_FS)
+ "Z" MDMX source register (OP_*_FT)
+
+ Other:
+ "()" parens surrounding optional value
+ "," separates operands
+ "[]" brackets around index for vector-op scalar operand specifier (vr5400)
+ "+" Start of extension sequence.
+
+ Characters used so far, for quick reference when adding more:
+ "%[]<>(),+"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefhijklopqrstuvwxz"
+
+ Extension character sequences used so far ("+" followed by the
+ following), for quick reference when adding more:
+ "ABCDEFGHI"
+*/
+
+/* These are the bits which may be set in the pinfo field of an
+ instructions, if it is not equal to INSN_MACRO. */
+
+/* Modifies the general purpose register in OP_*_RD. */
+#define INSN_WRITE_GPR_D 0x00000001
+/* Modifies the general purpose register in OP_*_RT. */
+#define INSN_WRITE_GPR_T 0x00000002
+/* Modifies general purpose register 31. */
+#define INSN_WRITE_GPR_31 0x00000004
+/* Modifies the floating point register in OP_*_FD. */
+#define INSN_WRITE_FPR_D 0x00000008
+/* Modifies the floating point register in OP_*_FS. */
+#define INSN_WRITE_FPR_S 0x00000010
+/* Modifies the floating point register in OP_*_FT. */
+#define INSN_WRITE_FPR_T 0x00000020
+/* Reads the general purpose register in OP_*_RS. */
+#define INSN_READ_GPR_S 0x00000040
+/* Reads the general purpose register in OP_*_RT. */
+#define INSN_READ_GPR_T 0x00000080
+/* Reads the floating point register in OP_*_FS. */
+#define INSN_READ_FPR_S 0x00000100
+/* Reads the floating point register in OP_*_FT. */
+#define INSN_READ_FPR_T 0x00000200
+/* Reads the floating point register in OP_*_FR. */
+#define INSN_READ_FPR_R 0x00000400
+/* Modifies coprocessor condition code. */
+#define INSN_WRITE_COND_CODE 0x00000800
+/* Reads coprocessor condition code. */
+#define INSN_READ_COND_CODE 0x00001000
+/* TLB operation. */
+#define INSN_TLB 0x00002000
+/* Reads coprocessor register other than floating point register. */
+#define INSN_COP 0x00004000
+/* Instruction loads value from memory, requiring delay. */
+#define INSN_LOAD_MEMORY_DELAY 0x00008000
+/* Instruction loads value from coprocessor, requiring delay. */
+#define INSN_LOAD_COPROC_DELAY 0x00010000
+/* Instruction has unconditional branch delay slot. */
+#define INSN_UNCOND_BRANCH_DELAY 0x00020000
+/* Instruction has conditional branch delay slot. */
+#define INSN_COND_BRANCH_DELAY 0x00040000
+/* Conditional branch likely: if branch not taken, insn nullified. */
+#define INSN_COND_BRANCH_LIKELY 0x00080000
+/* Moves to coprocessor register, requiring delay. */
+#define INSN_COPROC_MOVE_DELAY 0x00100000
+/* Loads coprocessor register from memory, requiring delay. */
+#define INSN_COPROC_MEMORY_DELAY 0x00200000
+/* Reads the HI register. */
+#define INSN_READ_HI 0x00400000
+/* Reads the LO register. */
+#define INSN_READ_LO 0x00800000
+/* Modifies the HI register. */
+#define INSN_WRITE_HI 0x01000000
+/* Modifies the LO register. */
+#define INSN_WRITE_LO 0x02000000
+/* Takes a trap (easier to keep out of delay slot). */
+#define INSN_TRAP 0x04000000
+/* Instruction stores value into memory. */
+#define INSN_STORE_MEMORY 0x08000000
+/* Instruction uses single precision floating point. */
+#define FP_S 0x10000000
+/* Instruction uses double precision floating point. */
+#define FP_D 0x20000000
+/* Instruction is part of the tx39's integer multiply family. */
+#define INSN_MULT 0x40000000
+/* Instruction synchronize shared memory. */
+#define INSN_SYNC 0x80000000
+/* Instruction reads MDMX accumulator. XXX FIXME: No bits left! */
+#define INSN_READ_MDMX_ACC 0
+/* Instruction writes MDMX accumulator. XXX FIXME: No bits left! */
+#define INSN_WRITE_MDMX_ACC 0
+
+/* Instruction is actually a macro. It should be ignored by the
+ disassembler, and requires special treatment by the assembler. */
+#define INSN_MACRO 0xffffffff
+
+/* Masks used to mark instructions to indicate which MIPS ISA level
+ they were introduced in. ISAs, as defined below, are logical
+ ORs of these bits, indicating that they support the instructions
+ defined at the given level. */
+
+#define INSN_ISA_MASK 0x00000fff
+#define INSN_ISA1 0x00000001
+#define INSN_ISA2 0x00000002
+#define INSN_ISA3 0x00000004
+#define INSN_ISA4 0x00000008
+#define INSN_ISA5 0x00000010
+#define INSN_ISA32 0x00000020
+#define INSN_ISA64 0x00000040
+#define INSN_ISA32R2 0x00000080
+#define INSN_ISA64R2 0x00000100
+
+/* Masks used for MIPS-defined ASEs. */
+#define INSN_ASE_MASK 0x0000f000
+
+/* MIPS 16 ASE */
+#define INSN_MIPS16 0x00002000
+/* MIPS-3D ASE */
+#define INSN_MIPS3D 0x00004000
+/* MDMX ASE */
+#define INSN_MDMX 0x00008000
+
+/* Chip specific instructions. These are bitmasks. */
+
+/* MIPS R4650 instruction. */
+#define INSN_4650 0x00010000
+/* LSI R4010 instruction. */
+#define INSN_4010 0x00020000
+/* NEC VR4100 instruction. */
+#define INSN_4100 0x00040000
+/* Toshiba R3900 instruction. */
+#define INSN_3900 0x00080000
+/* MIPS R10000 instruction. */
+#define INSN_10000 0x00100000
+/* Broadcom SB-1 instruction. */
+#define INSN_SB1 0x00200000
+/* NEC VR4111/VR4181 instruction. */
+#define INSN_4111 0x00400000
+/* NEC VR4120 instruction. */
+#define INSN_4120 0x00800000
+/* NEC VR5400 instruction. */
+#define INSN_5400 0x01000000
+/* NEC VR5500 instruction. */
+#define INSN_5500 0x02000000
+
+/* MIPS ISA defines, use instead of hardcoding ISA level. */
+
+#define ISA_UNKNOWN 0 /* Gas internal use. */
+#define ISA_MIPS1 (INSN_ISA1)
+#define ISA_MIPS2 (ISA_MIPS1 | INSN_ISA2)
+#define ISA_MIPS3 (ISA_MIPS2 | INSN_ISA3)
+#define ISA_MIPS4 (ISA_MIPS3 | INSN_ISA4)
+#define ISA_MIPS5 (ISA_MIPS4 | INSN_ISA5)
+
+#define ISA_MIPS32 (ISA_MIPS2 | INSN_ISA32)
+#define ISA_MIPS64 (ISA_MIPS5 | INSN_ISA32 | INSN_ISA64)
+
+#define ISA_MIPS32R2 (ISA_MIPS32 | INSN_ISA32R2)
+#define ISA_MIPS64R2 (ISA_MIPS64 | INSN_ISA32R2 | INSN_ISA64R2)
+
+
+/* CPU defines, use instead of hardcoding processor number. Keep this
+ in sync with bfd/archures.c in order for machine selection to work. */
+#define CPU_UNKNOWN 0 /* Gas internal use. */
+#define CPU_R3000 3000
+#define CPU_R3900 3900
+#define CPU_R4000 4000
+#define CPU_R4010 4010
+#define CPU_VR4100 4100
+#define CPU_R4111 4111
+#define CPU_VR4120 4120
+#define CPU_R4300 4300
+#define CPU_R4400 4400
+#define CPU_R4600 4600
+#define CPU_R4650 4650
+#define CPU_R5000 5000
+#define CPU_VR5400 5400
+#define CPU_VR5500 5500
+#define CPU_R6000 6000
+#define CPU_RM7000 7000
+#define CPU_R8000 8000
+#define CPU_R10000 10000
+#define CPU_R12000 12000
+#define CPU_MIPS16 16
+#define CPU_MIPS32 32
+#define CPU_MIPS32R2 33
+#define CPU_MIPS5 5
+#define CPU_MIPS64 64
+#define CPU_MIPS64R2 65
+#define CPU_SB1 12310201 /* octal 'SB', 01. */
+
+/* Test for membership in an ISA including chip specific ISAs. INSN
+ is pointer to an element of the opcode table; ISA is the specified
+ ISA/ASE bitmask to test against; and CPU is the CPU specific ISA to
+ test, or zero if no CPU specific ISA test is desired. */
+
+#define OPCODE_IS_MEMBER(insn, isa, cpu) \
+ (((insn)->membership & isa) != 0 \
+ || (cpu == CPU_R4650 && ((insn)->membership & INSN_4650) != 0) \
+ || (cpu == CPU_RM7000 && ((insn)->membership & INSN_4650) != 0) \
+ || (cpu == CPU_R4010 && ((insn)->membership & INSN_4010) != 0) \
+ || (cpu == CPU_VR4100 && ((insn)->membership & INSN_4100) != 0) \
+ || (cpu == CPU_R3900 && ((insn)->membership & INSN_3900) != 0) \
+ || ((cpu == CPU_R10000 || cpu == CPU_R12000) \
+ && ((insn)->membership & INSN_10000) != 0) \
+ || (cpu == CPU_SB1 && ((insn)->membership & INSN_SB1) != 0) \
+ || (cpu == CPU_R4111 && ((insn)->membership & INSN_4111) != 0) \
+ || (cpu == CPU_VR4120 && ((insn)->membership & INSN_4120) != 0) \
+ || (cpu == CPU_VR5400 && ((insn)->membership & INSN_5400) != 0) \
+ || (cpu == CPU_VR5500 && ((insn)->membership & INSN_5500) != 0) \
+ || 0) /* Please keep this term for easier source merging. */
+
+/* This is a list of macro expanded instructions.
+
+ _I appended means immediate
+ _A appended means address
+ _AB appended means address with base register
+ _D appended means 64 bit floating point constant
+ _S appended means 32 bit floating point constant. */
+
+enum
+{
+ M_ABS,
+ M_ADD_I,
+ M_ADDU_I,
+ M_AND_I,
+ M_BEQ,
+ M_BEQ_I,
+ M_BEQL_I,
+ M_BGE,
+ M_BGEL,
+ M_BGE_I,
+ M_BGEL_I,
+ M_BGEU,
+ M_BGEUL,
+ M_BGEU_I,
+ M_BGEUL_I,
+ M_BGT,
+ M_BGTL,
+ M_BGT_I,
+ M_BGTL_I,
+ M_BGTU,
+ M_BGTUL,
+ M_BGTU_I,
+ M_BGTUL_I,
+ M_BLE,
+ M_BLEL,
+ M_BLE_I,
+ M_BLEL_I,
+ M_BLEU,
+ M_BLEUL,
+ M_BLEU_I,
+ M_BLEUL_I,
+ M_BLT,
+ M_BLTL,
+ M_BLT_I,
+ M_BLTL_I,
+ M_BLTU,
+ M_BLTUL,
+ M_BLTU_I,
+ M_BLTUL_I,
+ M_BNE,
+ M_BNE_I,
+ M_BNEL_I,
+ M_DABS,
+ M_DADD_I,
+ M_DADDU_I,
+ M_DDIV_3,
+ M_DDIV_3I,
+ M_DDIVU_3,
+ M_DDIVU_3I,
+ M_DEXT,
+ M_DINS,
+ M_DIV_3,
+ M_DIV_3I,
+ M_DIVU_3,
+ M_DIVU_3I,
+ M_DLA_AB,
+ M_DLCA_AB,
+ M_DLI,
+ M_DMUL,
+ M_DMUL_I,
+ M_DMULO,
+ M_DMULO_I,
+ M_DMULOU,
+ M_DMULOU_I,
+ M_DREM_3,
+ M_DREM_3I,
+ M_DREMU_3,
+ M_DREMU_3I,
+ M_DSUB_I,
+ M_DSUBU_I,
+ M_DSUBU_I_2,
+ M_J_A,
+ M_JAL_1,
+ M_JAL_2,
+ M_JAL_A,
+ M_L_DOB,
+ M_L_DAB,
+ M_LA_AB,
+ M_LB_A,
+ M_LB_AB,
+ M_LBU_A,
+ M_LBU_AB,
+ M_LCA_AB,
+ M_LD_A,
+ M_LD_OB,
+ M_LD_AB,
+ M_LDC1_AB,
+ M_LDC2_AB,
+ M_LDC3_AB,
+ M_LDL_AB,
+ M_LDR_AB,
+ M_LH_A,
+ M_LH_AB,
+ M_LHU_A,
+ M_LHU_AB,
+ M_LI,
+ M_LI_D,
+ M_LI_DD,
+ M_LI_S,
+ M_LI_SS,
+ M_LL_AB,
+ M_LLD_AB,
+ M_LS_A,
+ M_LW_A,
+ M_LW_AB,
+ M_LWC0_A,
+ M_LWC0_AB,
+ M_LWC1_A,
+ M_LWC1_AB,
+ M_LWC2_A,
+ M_LWC2_AB,
+ M_LWC3_A,
+ M_LWC3_AB,
+ M_LWL_A,
+ M_LWL_AB,
+ M_LWR_A,
+ M_LWR_AB,
+ M_LWU_AB,
+ M_MOVE,
+ M_MUL,
+ M_MUL_I,
+ M_MULO,
+ M_MULO_I,
+ M_MULOU,
+ M_MULOU_I,
+ M_NOR_I,
+ M_OR_I,
+ M_REM_3,
+ M_REM_3I,
+ M_REMU_3,
+ M_REMU_3I,
+ M_DROL,
+ M_ROL,
+ M_DROL_I,
+ M_ROL_I,
+ M_DROR,
+ M_ROR,
+ M_DROR_I,
+ M_ROR_I,
+ M_S_DA,
+ M_S_DOB,
+ M_S_DAB,
+ M_S_S,
+ M_SC_AB,
+ M_SCD_AB,
+ M_SD_A,
+ M_SD_OB,
+ M_SD_AB,
+ M_SDC1_AB,
+ M_SDC2_AB,
+ M_SDC3_AB,
+ M_SDL_AB,
+ M_SDR_AB,
+ M_SEQ,
+ M_SEQ_I,
+ M_SGE,
+ M_SGE_I,
+ M_SGEU,
+ M_SGEU_I,
+ M_SGT,
+ M_SGT_I,
+ M_SGTU,
+ M_SGTU_I,
+ M_SLE,
+ M_SLE_I,
+ M_SLEU,
+ M_SLEU_I,
+ M_SLT_I,
+ M_SLTU_I,
+ M_SNE,
+ M_SNE_I,
+ M_SB_A,
+ M_SB_AB,
+ M_SH_A,
+ M_SH_AB,
+ M_SW_A,
+ M_SW_AB,
+ M_SWC0_A,
+ M_SWC0_AB,
+ M_SWC1_A,
+ M_SWC1_AB,
+ M_SWC2_A,
+ M_SWC2_AB,
+ M_SWC3_A,
+ M_SWC3_AB,
+ M_SWL_A,
+ M_SWL_AB,
+ M_SWR_A,
+ M_SWR_AB,
+ M_SUB_I,
+ M_SUBU_I,
+ M_SUBU_I_2,
+ M_TEQ_I,
+ M_TGE_I,
+ M_TGEU_I,
+ M_TLT_I,
+ M_TLTU_I,
+ M_TNE_I,
+ M_TRUNCWD,
+ M_TRUNCWS,
+ M_ULD,
+ M_ULD_A,
+ M_ULH,
+ M_ULH_A,
+ M_ULHU,
+ M_ULHU_A,
+ M_ULW,
+ M_ULW_A,
+ M_USH,
+ M_USH_A,
+ M_USW,
+ M_USW_A,
+ M_USD,
+ M_USD_A,
+ M_XOR_I,
+ M_COP0,
+ M_COP1,
+ M_COP2,
+ M_COP3,
+ M_NUM_MACROS
+};
+
+
+/* The order of overloaded instructions matters. Label arguments and
+ register arguments look the same. Instructions that can have either
+ for arguments must apear in the correct order in this table for the
+ assembler to pick the right one. In other words, entries with
+ immediate operands must apear after the same instruction with
+ registers.
+
+ Many instructions are short hand for other instructions (i.e., The
+ jal <register> instruction is short for jalr <register>). */
+
+extern const struct mips_opcode mips_builtin_opcodes[];
+extern const int bfd_mips_num_builtin_opcodes;
+extern struct mips_opcode *mips_opcodes;
+extern int bfd_mips_num_opcodes;
+#define NUMOPCODES bfd_mips_num_opcodes
+
+
+/* The rest of this file adds definitions for the mips16 TinyRISC
+ processor. */
+
+/* These are the bitmasks and shift counts used for the different
+ fields in the instruction formats. Other than OP, no masks are
+ provided for the fixed portions of an instruction, since they are
+ not needed.
+
+ The I format uses IMM11.
+
+ The RI format uses RX and IMM8.
+
+ The RR format uses RX, and RY.
+
+ The RRI format uses RX, RY, and IMM5.
+
+ The RRR format uses RX, RY, and RZ.
+
+ The RRI_A format uses RX, RY, and IMM4.
+
+ The SHIFT format uses RX, RY, and SHAMT.
+
+ The I8 format uses IMM8.
+
+ The I8_MOVR32 format uses RY and REGR32.
+
+ The IR_MOV32R format uses REG32R and MOV32Z.
+
+ The I64 format uses IMM8.
+
+ The RI64 format uses RY and IMM5.
+ */
+
+#define MIPS16OP_MASK_OP 0x1f
+#define MIPS16OP_SH_OP 11
+#define MIPS16OP_MASK_IMM11 0x7ff
+#define MIPS16OP_SH_IMM11 0
+#define MIPS16OP_MASK_RX 0x7
+#define MIPS16OP_SH_RX 8
+#define MIPS16OP_MASK_IMM8 0xff
+#define MIPS16OP_SH_IMM8 0
+#define MIPS16OP_MASK_RY 0x7
+#define MIPS16OP_SH_RY 5
+#define MIPS16OP_MASK_IMM5 0x1f
+#define MIPS16OP_SH_IMM5 0
+#define MIPS16OP_MASK_RZ 0x7
+#define MIPS16OP_SH_RZ 2
+#define MIPS16OP_MASK_IMM4 0xf
+#define MIPS16OP_SH_IMM4 0
+#define MIPS16OP_MASK_REGR32 0x1f
+#define MIPS16OP_SH_REGR32 0
+#define MIPS16OP_MASK_REG32R 0x1f
+#define MIPS16OP_SH_REG32R 3
+#define MIPS16OP_EXTRACT_REG32R(i) ((((i) >> 5) & 7) | ((i) & 0x18))
+#define MIPS16OP_MASK_MOVE32Z 0x7
+#define MIPS16OP_SH_MOVE32Z 0
+#define MIPS16OP_MASK_IMM6 0x3f
+#define MIPS16OP_SH_IMM6 5
+
+/* These are the characters which may appears in the args field of an
+ instruction. They appear in the order in which the fields appear
+ when the instruction is used. Commas and parentheses in the args
+ string are ignored when assembling, and written into the output
+ when disassembling.
+
+ "y" 3 bit register (MIPS16OP_*_RY)
+ "x" 3 bit register (MIPS16OP_*_RX)
+ "z" 3 bit register (MIPS16OP_*_RZ)
+ "Z" 3 bit register (MIPS16OP_*_MOVE32Z)
+ "v" 3 bit same register as source and destination (MIPS16OP_*_RX)
+ "w" 3 bit same register as source and destination (MIPS16OP_*_RY)
+ "0" zero register ($0)
+ "S" stack pointer ($sp or $29)
+ "P" program counter
+ "R" return address register ($ra or $31)
+ "X" 5 bit MIPS register (MIPS16OP_*_REGR32)
+ "Y" 5 bit MIPS register (MIPS16OP_*_REG32R)
+ "6" 6 bit unsigned break code (MIPS16OP_*_IMM6)
+ "a" 26 bit jump address
+ "e" 11 bit extension value
+ "l" register list for entry instruction
+ "L" register list for exit instruction
+
+ The remaining codes may be extended. Except as otherwise noted,
+ the full extended operand is a 16 bit signed value.
+ "<" 3 bit unsigned shift count * 0 (MIPS16OP_*_RZ) (full 5 bit unsigned)
+ ">" 3 bit unsigned shift count * 0 (MIPS16OP_*_RX) (full 5 bit unsigned)
+ "[" 3 bit unsigned shift count * 0 (MIPS16OP_*_RZ) (full 6 bit unsigned)
+ "]" 3 bit unsigned shift count * 0 (MIPS16OP_*_RX) (full 6 bit unsigned)
+ "4" 4 bit signed immediate * 0 (MIPS16OP_*_IMM4) (full 15 bit signed)
+ "5" 5 bit unsigned immediate * 0 (MIPS16OP_*_IMM5)
+ "H" 5 bit unsigned immediate * 2 (MIPS16OP_*_IMM5)
+ "W" 5 bit unsigned immediate * 4 (MIPS16OP_*_IMM5)
+ "D" 5 bit unsigned immediate * 8 (MIPS16OP_*_IMM5)
+ "j" 5 bit signed immediate * 0 (MIPS16OP_*_IMM5)
+ "8" 8 bit unsigned immediate * 0 (MIPS16OP_*_IMM8)
+ "V" 8 bit unsigned immediate * 4 (MIPS16OP_*_IMM8)
+ "C" 8 bit unsigned immediate * 8 (MIPS16OP_*_IMM8)
+ "U" 8 bit unsigned immediate * 0 (MIPS16OP_*_IMM8) (full 16 bit unsigned)
+ "k" 8 bit signed immediate * 0 (MIPS16OP_*_IMM8)
+ "K" 8 bit signed immediate * 8 (MIPS16OP_*_IMM8)
+ "p" 8 bit conditional branch address (MIPS16OP_*_IMM8)
+ "q" 11 bit branch address (MIPS16OP_*_IMM11)
+ "A" 8 bit PC relative address * 4 (MIPS16OP_*_IMM8)
+ "B" 5 bit PC relative address * 8 (MIPS16OP_*_IMM5)
+ "E" 5 bit PC relative address * 4 (MIPS16OP_*_IMM5)
+ */
+
+/* For the mips16, we use the same opcode table format and a few of
+ the same flags. However, most of the flags are different. */
+
+/* Modifies the register in MIPS16OP_*_RX. */
+#define MIPS16_INSN_WRITE_X 0x00000001
+/* Modifies the register in MIPS16OP_*_RY. */
+#define MIPS16_INSN_WRITE_Y 0x00000002
+/* Modifies the register in MIPS16OP_*_RZ. */
+#define MIPS16_INSN_WRITE_Z 0x00000004
+/* Modifies the T ($24) register. */
+#define MIPS16_INSN_WRITE_T 0x00000008
+/* Modifies the SP ($29) register. */
+#define MIPS16_INSN_WRITE_SP 0x00000010
+/* Modifies the RA ($31) register. */
+#define MIPS16_INSN_WRITE_31 0x00000020
+/* Modifies the general purpose register in MIPS16OP_*_REG32R. */
+#define MIPS16_INSN_WRITE_GPR_Y 0x00000040
+/* Reads the register in MIPS16OP_*_RX. */
+#define MIPS16_INSN_READ_X 0x00000080
+/* Reads the register in MIPS16OP_*_RY. */
+#define MIPS16_INSN_READ_Y 0x00000100
+/* Reads the register in MIPS16OP_*_MOVE32Z. */
+#define MIPS16_INSN_READ_Z 0x00000200
+/* Reads the T ($24) register. */
+#define MIPS16_INSN_READ_T 0x00000400
+/* Reads the SP ($29) register. */
+#define MIPS16_INSN_READ_SP 0x00000800
+/* Reads the RA ($31) register. */
+#define MIPS16_INSN_READ_31 0x00001000
+/* Reads the program counter. */
+#define MIPS16_INSN_READ_PC 0x00002000
+/* Reads the general purpose register in MIPS16OP_*_REGR32. */
+#define MIPS16_INSN_READ_GPR_X 0x00004000
+/* Is a branch insn. */
+#define MIPS16_INSN_BRANCH 0x00010000
+
+/* The following flags have the same value for the mips16 opcode
+ table:
+ INSN_UNCOND_BRANCH_DELAY
+ INSN_COND_BRANCH_DELAY
+ INSN_COND_BRANCH_LIKELY (never used)
+ INSN_READ_HI
+ INSN_READ_LO
+ INSN_WRITE_HI
+ INSN_WRITE_LO
+ INSN_TRAP
+ INSN_ISA3
+ */
+
+extern const struct mips_opcode mips16_opcodes[];
+extern const int bfd_mips16_num_opcodes;
+
+#endif /* _MIPS_H_ */
diff --git a/contrib/binutils/ld/emulparams/elf32bmipn32-defs.sh b/contrib/binutils/ld/emulparams/elf32bmipn32-defs.sh
new file mode 100644
index 0000000..f7b0d08
--- /dev/null
+++ b/contrib/binutils/ld/emulparams/elf32bmipn32-defs.sh
@@ -0,0 +1,58 @@
+# If you change this file, please also look at files which source this one:
+# elf64bmip.sh elf64btsmip.sh elf32btsmipn32.sh elf32bmipn32.sh
+
+# This is an ELF platform.
+SCRIPT_NAME=elf
+
+# Handle both big- and little-ended 32-bit MIPS objects.
+ARCH=mips
+OUTPUT_FORMAT="elf32-bigmips"
+BIG_OUTPUT_FORMAT="elf32-bigmips"
+LITTLE_OUTPUT_FORMAT="elf32-littlemips"
+
+TEMPLATE_NAME=elf32
+
+case "$EMULATION_NAME" in
+elf32*n32*) ELFSIZE=32 ;;
+elf64*) ELFSIZE=64 ;;
+*) echo $0: unhandled emulation $EMULATION_NAME >&2; exit 1 ;;
+esac
+
+if test `echo "$host" | sed -e s/64//` = `echo "$target" | sed -e s/64//`; then
+ case " $EMULATION_LIBPATH " in
+ *" ${EMULATION_NAME} "*)
+ NATIVE=yes
+ ;;
+ esac
+fi
+
+# Look for 64 bit target libraries in /lib64, /usr/lib64 etc., first.
+LIBPATH_SUFFIX=$ELFSIZE
+
+GENERATE_SHLIB_SCRIPT=yes
+
+TEXT_START_ADDR=0x10000000
+MAXPAGESIZE=0x100000
+ENTRY=__start
+
+# GOT-related settings.
+OTHER_GOT_SYMBOLS='
+ _gp = ALIGN(16) + 0x7ff0;
+'
+OTHER_SDATA_SECTIONS="
+ .lit8 ${RELOCATING-0} : { *(.lit8) }
+ .lit4 ${RELOCATING-0} : { *(.lit4) }
+ .srdata ${RELOCATING-0} : { *(.srdata) }
+"
+
+# Magic symbols.
+TEXT_START_SYMBOLS='_ftext = . ;'
+DATA_START_SYMBOLS='_fdata = . ;'
+OTHER_BSS_SYMBOLS='_fbss = .;'
+
+INITIAL_READONLY_SECTIONS=".MIPS.options : { *(.MIPS.options) }"
+# Discard any .MIPS.content* or .MIPS.events* sections. The linker
+# doesn't know how to adjust them.
+OTHER_SECTIONS="/DISCARD/ : { *(.MIPS.content*) *(.MIPS.events*) }"
+
+TEXT_DYNAMIC=
diff --git a/contrib/binutils/ld/emulparams/elf32btsmip.sh b/contrib/binutils/ld/emulparams/elf32btsmip.sh
new file mode 100644
index 0000000..4c5a9ab
--- /dev/null
+++ b/contrib/binutils/ld/emulparams/elf32btsmip.sh
@@ -0,0 +1,9 @@
+# If you change this file, please also look at files which source this one:
+# elf32ltsmip.sh
+
+. ${srcdir}/emulparams/elf32bmip.sh
+OUTPUT_FORMAT="elf32-tradbigmips"
+BIG_OUTPUT_FORMAT="elf32-tradbigmips"
+LITTLE_OUTPUT_FORMAT="elf32-tradlittlemips"
+SHLIB_TEXT_START_ADDR=0
+ENTRY=__start
diff --git a/contrib/binutils/ld/emulparams/elf32btsmipn32.sh b/contrib/binutils/ld/emulparams/elf32btsmipn32.sh
new file mode 100644
index 0000000..5ca6797
--- /dev/null
+++ b/contrib/binutils/ld/emulparams/elf32btsmipn32.sh
@@ -0,0 +1,15 @@
+# If you change this file, please also look at files which source this one:
+# elf32ltsmipn32.sh
+
+. ${srcdir}/emulparams/elf32bmipn32-defs.sh
+OUTPUT_FORMAT="elf32-ntradbigmips"
+BIG_OUTPUT_FORMAT="elf32-ntradbigmips"
+LITTLE_OUTPUT_FORMAT="elf32-ntradlittlemips"
+COMMONPAGESIZE=0x1000
+
+# Magic sections.
+OTHER_TEXT_SECTIONS='*(.mips16.fn.*) *(.mips16.call.*)'
+OTHER_SECTIONS='
+ .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) }
+ .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) }
+'
diff --git a/contrib/binutils/ld/emulparams/elf32ltsmip.sh b/contrib/binutils/ld/emulparams/elf32ltsmip.sh
new file mode 100644
index 0000000..4a660f0
--- /dev/null
+++ b/contrib/binutils/ld/emulparams/elf32ltsmip.sh
@@ -0,0 +1,2 @@
+. ${srcdir}/emulparams/elf32btsmip.sh
+OUTPUT_FORMAT="elf32-tradlittlemips"
diff --git a/contrib/binutils/ld/emulparams/elf32ltsmipn32.sh b/contrib/binutils/ld/emulparams/elf32ltsmipn32.sh
new file mode 100644
index 0000000..276477d
--- /dev/null
+++ b/contrib/binutils/ld/emulparams/elf32ltsmipn32.sh
@@ -0,0 +1,4 @@
+. ${srcdir}/emulparams/elf32btsmipn32.sh
+OUTPUT_FORMAT="elf32-ntradlittlemips"
+BIG_OUTPUT_FORMAT="elf32-ntradbigmips"
+LITTLE_OUTPUT_FORMAT="elf32-ntradlittlemips"
diff --git a/contrib/binutils/ld/emulparams/elf32mipswindiss.sh b/contrib/binutils/ld/emulparams/elf32mipswindiss.sh
new file mode 100644
index 0000000..4f1148b
--- /dev/null
+++ b/contrib/binutils/ld/emulparams/elf32mipswindiss.sh
@@ -0,0 +1,27 @@
+TEMPLATE_NAME=elf32
+SCRIPT_NAME=elf
+OUTPUT_FORMAT="elf32-bigmips"
+BIG_OUTPUT_FORMAT="elf32-bigmips"
+LITTLE_OUTPUT_FORMAT="elf32-littlemips"
+ARCH=mips
+MACHINE=
+EMBEDDED=yes
+MAXPAGESIZE=0x40000
+
+# The data below is taken from the windiss.dld linker script that comes with
+# the Diab linker.
+TEXT_START_ADDR=0x100000
+DATA_START_SYMBOLS='__DATA_ROM = .; __DATA_RAM = .;'
+SDATA_START_SYMBOLS='_SDA_BASE_ = .; _gp = . + 0x7ff0;'
+SDATA2_START_SYMBOLS='_SDA2_BASE_ = .;'
+EXECUTABLE_SYMBOLS='__HEAP_START = .; __SP_INIT = 0x800000; __SP_END = __SP_INIT - 0x20000; __HEAP_END = __SP_END; __DATA_END = _edata; __BSS_START = __bss_start; __BSS_END = _end; __HEAP_START = _end;'
+
+# The Diab tools use a different init/fini convention. Initialization code
+# is place in sections named ".init$NN". These sections are then concatenated
+# into the .init section. It is important that .init$00 be first and .init$99
+# be last. The other sections should be sorted, but the current linker script
+# parse does not seem to allow that with the SORT keyword in this context.
+INIT_START='*(.init$00); *(.init$0[1-9]); *(.init$[1-8][0-9]); *(.init$9[0-8])'
+INIT_END='*(.init$99)'
+FINI_START='*(.fini$00); *(.fini$0[1-9]); *(.fini$[1-8][0-9]); *(.fini$9[0-8])'
+FINI_END='*(.fini$99)'
diff --git a/contrib/binutils/ld/emulparams/elf64btsmip.sh b/contrib/binutils/ld/emulparams/elf64btsmip.sh
new file mode 100644
index 0000000..23a20c7
--- /dev/null
+++ b/contrib/binutils/ld/emulparams/elf64btsmip.sh
@@ -0,0 +1,16 @@
+# If you change this file, please also look at files which source this one:
+# elf64ltsmip.sh
+
+. ${srcdir}/emulparams/elf32bmipn32-defs.sh
+OUTPUT_FORMAT="elf64-tradbigmips"
+BIG_OUTPUT_FORMAT="elf64-tradbigmips"
+LITTLE_OUTPUT_FORMAT="elf64-tradlittlemips"
+
+# Magic sections.
+OTHER_TEXT_SECTIONS='*(.mips16.fn.*) *(.mips16.call.*)'
+OTHER_SECTIONS='
+ .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) }
+ .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) }
+'
+
+TEXT_START_ADDR="0x120000000"
diff --git a/contrib/binutils/ld/emulparams/elf64ltsmip.sh b/contrib/binutils/ld/emulparams/elf64ltsmip.sh
new file mode 100644
index 0000000..efd6b7d
--- /dev/null
+++ b/contrib/binutils/ld/emulparams/elf64ltsmip.sh
@@ -0,0 +1,4 @@
+. ${srcdir}/emulparams/elf64btsmip.sh
+OUTPUT_FORMAT="elf64-tradlittlemips"
+BIG_OUTPUT_FORMAT="elf64-tradbigmips"
+LITTLE_OUTPUT_FORMAT="elf64-tradlittlemips"
diff --git a/contrib/binutils/ld/emulparams/mipsbig.sh b/contrib/binutils/ld/emulparams/mipsbig.sh
new file mode 100644
index 0000000..9fe2953
--- /dev/null
+++ b/contrib/binutils/ld/emulparams/mipsbig.sh
@@ -0,0 +1,6 @@
+SCRIPT_NAME=mips
+OUTPUT_FORMAT="ecoff-bigmips"
+BIG_OUTPUT_FORMAT="ecoff-bigmips"
+LITTLE_OUTPUT_FORMAT="ecoff-littlemips"
+TARGET_PAGE_SIZE=0x1000000
+ARCH=mips
diff --git a/contrib/binutils/ld/emulparams/mipsbsd.sh b/contrib/binutils/ld/emulparams/mipsbsd.sh
new file mode 100644
index 0000000..e8fb35b
--- /dev/null
+++ b/contrib/binutils/ld/emulparams/mipsbsd.sh
@@ -0,0 +1,7 @@
+SCRIPT_NAME=mipsbsd
+OUTPUT_FORMAT="a.out-mips-little"
+BIG_OUTPUT_FORMAT="a.out-mips-big"
+LITTLE_OUTPUT_FORMAT="a.out-mips-little"
+TEXT_START_ADDR=0x1020
+TARGET_PAGE_SIZE=4096
+ARCH=mips
diff --git a/contrib/binutils/ld/emulparams/mipsidt.sh b/contrib/binutils/ld/emulparams/mipsidt.sh
new file mode 100644
index 0000000..63176f5
--- /dev/null
+++ b/contrib/binutils/ld/emulparams/mipsidt.sh
@@ -0,0 +1,11 @@
+SCRIPT_NAME=mips
+OUTPUT_FORMAT="ecoff-bigmips"
+BIG_OUTPUT_FORMAT="ecoff-bigmips"
+LITTLE_OUTPUT_FORMAT="ecoff-littlemips"
+TARGET_PAGE_SIZE=0x1000000
+ARCH=mips
+ENTRY=start
+TEXT_START_ADDR=0xa0012000
+DATA_ADDR=.
+TEMPLATE_NAME=mipsecoff
+EMBEDDED=yes
diff --git a/contrib/binutils/ld/emulparams/mipsidtl.sh b/contrib/binutils/ld/emulparams/mipsidtl.sh
new file mode 100644
index 0000000..02279de
--- /dev/null
+++ b/contrib/binutils/ld/emulparams/mipsidtl.sh
@@ -0,0 +1,11 @@
+SCRIPT_NAME=mips
+OUTPUT_FORMAT="ecoff-littlemips"
+BIG_OUTPUT_FORMAT="ecoff-bigmips"
+LITTLE_OUTPUT_FORMAT="ecoff-littlemips"
+TARGET_PAGE_SIZE=0x1000000
+ARCH=mips
+ENTRY=start
+TEXT_START_ADDR=0xa0012000
+DATA_ADDR=.
+TEMPLATE_NAME=mipsecoff
+EMBEDDED=yes
diff --git a/contrib/binutils/ld/emulparams/mipslit.sh b/contrib/binutils/ld/emulparams/mipslit.sh
new file mode 100644
index 0000000..acb2344
--- /dev/null
+++ b/contrib/binutils/ld/emulparams/mipslit.sh
@@ -0,0 +1,6 @@
+SCRIPT_NAME=mips
+OUTPUT_FORMAT="ecoff-littlemips"
+BIG_OUTPUT_FORMAT="ecoff-bigmips"
+LITTLE_OUTPUT_FORMAT="ecoff-littlemips"
+TARGET_PAGE_SIZE=0x1000000
+ARCH=mips
diff --git a/contrib/binutils/ld/emulparams/mipslnews.sh b/contrib/binutils/ld/emulparams/mipslnews.sh
new file mode 100644
index 0000000..d0bb91c
--- /dev/null
+++ b/contrib/binutils/ld/emulparams/mipslnews.sh
@@ -0,0 +1,9 @@
+SCRIPT_NAME=mips
+OUTPUT_FORMAT="ecoff-littlemips"
+BIG_OUTPUT_FORMAT="ecoff-bigmips"
+LITTLE_OUTPUT_FORMAT="ecoff-littlemips"
+TARGET_PAGE_SIZE=0x1000000
+ARCH=mips
+TEXT_START_ADDR=0x80080000
+DATA_ADDR=.
+EMBEDDED=yes
diff --git a/contrib/binutils/ld/emulparams/mipspe.sh b/contrib/binutils/ld/emulparams/mipspe.sh
new file mode 100644
index 0000000..9dd9ce7
--- /dev/null
+++ b/contrib/binutils/ld/emulparams/mipspe.sh
@@ -0,0 +1,9 @@
+ARCH=mips
+SCRIPT_NAME=pe
+OUTPUT_FORMAT="pei-mips"
+OUTPUT_ARCH="mips"
+RELOCATEABLE_OUTPUT_FORMAT="ecoff-littlemips"
+TEMPLATE_NAME=pe
+SUBSYSTEM=PE_DEF_SUBSYSTEM
+INITIAL_SYMBOL_CHAR=\"_\"
+ENTRY="_mainCRTStartup"
diff --git a/contrib/binutils/ld/emultempl/mipsecoff.em b/contrib/binutils/ld/emultempl/mipsecoff.em
new file mode 100644
index 0000000..dde33c2
--- /dev/null
+++ b/contrib/binutils/ld/emultempl/mipsecoff.em
@@ -0,0 +1,248 @@
+# This shell script emits a C file. -*- C -*-
+# It does some substitutions.
+if [ -z "$MACHINE" ]; then
+ OUTPUT_ARCH=${ARCH}
+else
+ OUTPUT_ARCH=${ARCH}:${MACHINE}
+fi
+cat >e${EMULATION_NAME}.c <<EOF
+/* This file is is generated by a shell script. DO NOT EDIT! */
+
+/* Handle embedded relocs for MIPS.
+ Copyright 1994, 1995, 1997, 2000, 2002, 2003, 2004
+ Free Software Foundation, Inc.
+ Written by Ian Lance Taylor <ian@cygnus.com> based on generic.em.
+
+This file is part of GLD, the Gnu Linker.
+
+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. */
+
+#define TARGET_IS_${EMULATION_NAME}
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "bfdlink.h"
+
+#include "ld.h"
+#include "ldmain.h"
+#include "ldmisc.h"
+
+#include "ldexp.h"
+#include "ldlang.h"
+#include "ldfile.h"
+#include "ldemul.h"
+
+static void check_sections (bfd *, asection *, void *);
+
+static void
+gld${EMULATION_NAME}_before_parse (void)
+{
+#ifndef TARGET_ /* I.e., if not generic. */
+ ldfile_set_output_arch ("${OUTPUT_ARCH}", bfd_arch_`echo ${ARCH} | sed -e 's/:.*//'`);
+#endif /* not TARGET_ */
+}
+
+/* This function is run after all the input files have been opened.
+ We create a .rel.sdata section for each input file with a non zero
+ .sdata section. The BFD backend will fill in these sections with
+ magic numbers which can be used to relocate the data section at run
+ time. This will only do the right thing if all the input files
+ have been compiled using -membedded-pic. */
+
+static void
+gld${EMULATION_NAME}_after_open (void)
+{
+ bfd *abfd;
+
+ if (! command_line.embedded_relocs
+ || link_info.relocatable)
+ return;
+
+ for (abfd = link_info.input_bfds; abfd != NULL; abfd = abfd->link_next)
+ {
+ asection *datasec;
+
+ /* As first-order business, make sure that each input BFD is ECOFF. It
+ better be, as we are directly calling an ECOFF backend function. */
+ if (bfd_get_flavour (abfd) != bfd_target_ecoff_flavour)
+ einfo ("%F%B: all input objects must be ECOFF for --embedded-relocs\n");
+
+ datasec = bfd_get_section_by_name (abfd, ".sdata");
+
+ /* Note that we assume that the reloc_count field has already
+ been set up. We could call bfd_get_reloc_upper_bound, but
+ that returns the size of a memory buffer rather than a reloc
+ count. We do not want to call bfd_canonicalize_reloc,
+ because although it would always work it would force us to
+ read in the relocs into BFD canonical form, which would waste
+ a significant amount of time and memory. */
+ if (datasec != NULL && datasec->reloc_count > 0)
+ {
+ asection *relsec;
+
+ relsec = bfd_make_section (abfd, ".rel.sdata");
+ if (relsec == NULL
+ || ! bfd_set_section_flags (abfd, relsec,
+ (SEC_ALLOC
+ | SEC_LOAD
+ | SEC_HAS_CONTENTS
+ | SEC_IN_MEMORY))
+ || ! bfd_set_section_alignment (abfd, relsec, 2)
+ || ! bfd_set_section_size (abfd, relsec,
+ datasec->reloc_count * 4))
+ einfo ("%F%B: can not create .rel.sdata section: %E\n");
+ }
+
+ /* Double check that all other data sections are empty, as is
+ required for embedded PIC code. */
+ bfd_map_over_sections (abfd, check_sections, datasec);
+ }
+}
+
+/* Check that of the data sections, only the .sdata section has
+ relocs. This is called via bfd_map_over_sections. */
+
+static void
+check_sections (bfd *abfd, asection *sec, void *sdatasec)
+{
+ if ((bfd_get_section_flags (abfd, sec) & SEC_CODE) == 0
+ && sec != sdatasec
+ && sec->reloc_count != 0)
+ einfo ("%B%X: section %s has relocs; can not use --embedded-relocs\n",
+ abfd, bfd_get_section_name (abfd, sec));
+}
+
+/* This function is called after the section sizes and offsets have
+ been set. If we are generating embedded relocs, it calls a special
+ BFD backend routine to do the work. */
+
+static void
+gld${EMULATION_NAME}_after_allocation (void)
+{
+ bfd *abfd;
+
+ if (! command_line.embedded_relocs
+ || link_info.relocatable)
+ return;
+
+ for (abfd = link_info.input_bfds; abfd != NULL; abfd = abfd->link_next)
+ {
+ asection *datasec, *relsec;
+ char *errmsg;
+
+ datasec = bfd_get_section_by_name (abfd, ".sdata");
+
+ if (datasec == NULL || datasec->reloc_count == 0)
+ continue;
+
+ relsec = bfd_get_section_by_name (abfd, ".rel.sdata");
+ ASSERT (relsec != NULL);
+
+ if (! bfd_mips_ecoff_create_embedded_relocs (abfd, &link_info,
+ datasec, relsec,
+ &errmsg))
+ {
+ if (errmsg == NULL)
+ einfo ("%B%X: can not create runtime reloc information: %E\n",
+ abfd);
+ else
+ einfo ("%X%B: can not create runtime reloc information: %s\n",
+ abfd, errmsg);
+ }
+ }
+}
+
+static char *
+gld${EMULATION_NAME}_get_script (int *isfile)
+EOF
+
+if test -n "$COMPILE_IN"
+then
+# Scripts compiled in.
+
+# sed commands to quote an ld script as a C string.
+sc="-f stringify.sed"
+
+cat >>e${EMULATION_NAME}.c <<EOF
+{
+ *isfile = 0;
+
+ if (link_info.relocatable && config.build_constructors)
+ return
+EOF
+sed $sc ldscripts/${EMULATION_NAME}.xu >> e${EMULATION_NAME}.c
+echo ' ; else if (link_info.relocatable) return' >> e${EMULATION_NAME}.c
+sed $sc ldscripts/${EMULATION_NAME}.xr >> e${EMULATION_NAME}.c
+echo ' ; else if (!config.text_read_only) return' >> e${EMULATION_NAME}.c
+sed $sc ldscripts/${EMULATION_NAME}.xbn >> e${EMULATION_NAME}.c
+echo ' ; else if (!config.magic_demand_paged) return' >> e${EMULATION_NAME}.c
+sed $sc ldscripts/${EMULATION_NAME}.xn >> e${EMULATION_NAME}.c
+echo ' ; else return' >> e${EMULATION_NAME}.c
+sed $sc ldscripts/${EMULATION_NAME}.x >> e${EMULATION_NAME}.c
+echo '; }' >> e${EMULATION_NAME}.c
+
+else
+# Scripts read from the filesystem.
+
+cat >>e${EMULATION_NAME}.c <<EOF
+{
+ *isfile = 1;
+
+ if (link_info.relocatable && config.build_constructors)
+ return "ldscripts/${EMULATION_NAME}.xu";
+ else if (link_info.relocatable)
+ return "ldscripts/${EMULATION_NAME}.xr";
+ else if (!config.text_read_only)
+ return "ldscripts/${EMULATION_NAME}.xbn";
+ else if (!config.magic_demand_paged)
+ return "ldscripts/${EMULATION_NAME}.xn";
+ else
+ return "ldscripts/${EMULATION_NAME}.x";
+}
+EOF
+
+fi
+
+cat >>e${EMULATION_NAME}.c <<EOF
+
+struct ld_emulation_xfer_struct ld_${EMULATION_NAME}_emulation =
+{
+ gld${EMULATION_NAME}_before_parse,
+ syslib_default,
+ hll_default,
+ after_parse_default,
+ gld${EMULATION_NAME}_after_open,
+ gld${EMULATION_NAME}_after_allocation,
+ set_output_arch_default,
+ ldemul_default_target,
+ before_allocation_default,
+ gld${EMULATION_NAME}_get_script,
+ "${EMULATION_NAME}",
+ "${OUTPUT_FORMAT}",
+ NULL, /* finish */
+ NULL, /* create output section statements */
+ NULL, /* open dynamic archive */
+ NULL, /* place orphan */
+ NULL, /* set symbols */
+ NULL, /* parse args */
+ NULL, /* add_options */
+ NULL, /* handle_option */
+ NULL, /* unrecognized file */
+ NULL, /* list options */
+ NULL, /* recognized file */
+ NULL, /* find_potential_libraries */
+ NULL /* new_vers_pattern */
+};
+EOF
diff --git a/contrib/binutils/ld/emultempl/mipself.em b/contrib/binutils/ld/emultempl/mipself.em
new file mode 100644
index 0000000..a950fb1
--- /dev/null
+++ b/contrib/binutils/ld/emultempl/mipself.em
@@ -0,0 +1,177 @@
+# This shell script emits a C file. -*- C -*-
+# Copyright 2002, 2003 Free Software Foundation, Inc.
+# Written by Mitch Lichtenberg <mpl@broadcom.com> and
+# Chris Demetriou <cgd@broadcom.com> based on m68kelf.em and mipsecoff.em.
+#
+# This file is part of GLD, the Gnu Linker.
+#
+# 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 is sourced from elf32.em, and defines some extra routines for m68k
+# embedded systems using ELF and for some other systems using m68k ELF. While
+# it is sourced from elf32.em for all m68k ELF configurations, here we include
+# only the features we want depending on the configuration.
+
+case ${target} in
+ mips*-*-elf)
+ echo "#define SUPPORT_EMBEDDED_RELOCS" >>e${EMULATION_NAME}.c
+ ;;
+esac
+
+cat >>e${EMULATION_NAME}.c <<EOF
+
+#ifdef SUPPORT_EMBEDDED_RELOCS
+static void mips_elf${ELFSIZE}_check_sections (bfd *, asection *, void *);
+#endif
+
+/* This function is run after all the input files have been opened. */
+
+static void
+mips_elf${ELFSIZE}_after_open (void)
+{
+ /* Call the standard elf routine. */
+ gld${EMULATION_NAME}_after_open ();
+
+#ifdef SUPPORT_EMBEDDED_RELOCS
+ if (command_line.embedded_relocs && (! link_info.relocatable))
+ {
+ bfd *abfd;
+
+ /* In the embedded relocs mode we create a .rel.sdata section for
+ each input file with a .sdata section which has has
+ relocations. The BFD backend will fill in these sections
+ with magic numbers which can be used to relocate the data
+ section at run time. */
+ for (abfd = link_info.input_bfds; abfd != NULL; abfd = abfd->link_next)
+ {
+ asection *datasec;
+
+ /* As first-order business, make sure that each input BFD is
+ ELF. We need to call a special BFD backend function to
+ generate the embedded relocs, and we have that function
+ only for ELF */
+
+ if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
+ einfo ("%F%B: all input objects must be ELF for --embedded-relocs\n");
+
+ if (bfd_get_arch_size (abfd) != ${ELFSIZE})
+ einfo ("%F%B: all input objects must be ${ELFSIZE}-bit ELF for --embedded-relocs\n");
+
+ datasec = bfd_get_section_by_name (abfd, ".sdata");
+
+ /* Note that we assume that the reloc_count field has already
+ been set up. We could call bfd_get_reloc_upper_bound, but
+ that returns the size of a memory buffer rather than a reloc
+ count. We do not want to call bfd_canonicalize_reloc,
+ because although it would always work it would force us to
+ read in the relocs into BFD canonical form, which would waste
+ a significant amount of time and memory. */
+
+ if (datasec != NULL && datasec->reloc_count > 0)
+ {
+ asection *relsec;
+
+ relsec = bfd_make_section (abfd, ".rel.sdata");
+ if (relsec == NULL
+ || ! bfd_set_section_flags (abfd, relsec,
+ (SEC_ALLOC
+ | SEC_LOAD
+ | SEC_HAS_CONTENTS
+ | SEC_IN_MEMORY))
+ || ! bfd_set_section_alignment (abfd, relsec,
+ (${ELFSIZE} == 32) ? 2 : 3)
+ || ! bfd_set_section_size (abfd, relsec,
+ datasec->reloc_count
+ * ((${ELFSIZE} / 8) + 8)))
+ einfo ("%F%B: cannot create .rel.sdata section: %E\n");
+ }
+
+ /* Double check that all other data sections have no relocs,
+ as is required for embedded PIC code. */
+ bfd_map_over_sections (abfd, mips_elf${ELFSIZE}_check_sections,
+ datasec);
+ }
+ }
+#endif /* SUPPORT_EMBEDDED_RELOCS */
+}
+
+#ifdef SUPPORT_EMBEDDED_RELOCS
+/* Check that of the data sections, only the .sdata section has
+ relocs. This is called via bfd_map_over_sections. */
+
+static void
+mips_elf${ELFSIZE}_check_sections (bfd *abfd, asection *sec, void *sdatasec)
+{
+ if ((bfd_get_section_flags (abfd, sec) & SEC_DATA)
+ && sec != sdatasec
+ && sec->reloc_count != 0)
+ einfo ("%B%X: section %s has relocs; cannot use --embedded-relocs\n",
+ abfd, bfd_get_section_name (abfd, sec));
+}
+#endif /* SUPPORT_EMBEDDED_RELOCS */
+
+/* This function is called after the section sizes and offsets have
+ been set. If we are generating embedded relocs, it calls a special
+ BFD backend routine to do the work. */
+
+static void
+mips_elf${ELFSIZE}_after_allocation (void)
+{
+ /* Call the standard elf routine. */
+ after_allocation_default ();
+
+#ifdef SUPPORT_EMBEDDED_RELOCS
+ if (command_line.embedded_relocs && (! link_info.relocatable))
+ {
+ bfd *abfd;
+
+ for (abfd = link_info.input_bfds; abfd != NULL; abfd = abfd->link_next)
+ {
+ asection *datasec, *relsec;
+ char *errmsg;
+
+ datasec = bfd_get_section_by_name (abfd, ".sdata");
+
+ if (datasec == NULL || datasec->reloc_count == 0)
+ continue;
+
+ relsec = bfd_get_section_by_name (abfd, ".rel.sdata");
+ ASSERT (relsec != NULL);
+
+ if (! bfd_mips_elf${ELFSIZE}_create_embedded_relocs (abfd,
+ &link_info,
+ datasec,
+ relsec,
+ &errmsg))
+ {
+ if (errmsg == NULL)
+ einfo ("%B%X: can not create runtime reloc information: %E\n",
+ abfd);
+ else
+ einfo ("%X%B: can not create runtime reloc information: %s\n",
+ abfd, errmsg);
+ }
+ }
+ }
+#endif /* SUPPORT_EMBEDDED_RELOCS */
+}
+
+EOF
+
+# We have our own after_open and after_allocation functions, but they call
+# the standard routines, so give them a different name.
+LDEMUL_AFTER_OPEN=mips_elf${ELFSIZE}_after_open
+LDEMUL_AFTER_ALLOCATION=mips_elf${ELFSIZE}_after_allocation
diff --git a/contrib/binutils/ld/scripttempl/mips.sc b/contrib/binutils/ld/scripttempl/mips.sc
new file mode 100644
index 0000000..d60aeed
--- /dev/null
+++ b/contrib/binutils/ld/scripttempl/mips.sc
@@ -0,0 +1,72 @@
+# Linker script for MIPS systems.
+# Ian Lance Taylor <ian@cygnus.com>.
+# These variables may be overridden by the emulation file. The
+# defaults are appropriate for a DECstation running Ultrix.
+test -z "$ENTRY" && ENTRY=__start
+
+if [ -z "$EMBEDDED" ]; then
+ test -z "$TEXT_START_ADDR" && TEXT_START_ADDR="0x400000 + SIZEOF_HEADERS"
+else
+ test -z "$TEXT_START_ADDR" && TEXT_START_ADDR="0x400000"
+fi
+if test "x$LD_FLAG" = "xn" -o "x$LD_FLAG" = "xN"; then
+ DATA_ADDR=.
+else
+ test -z "$DATA_ADDR" && DATA_ADDR=0x10000000
+fi
+cat <<EOF
+OUTPUT_FORMAT("${OUTPUT_FORMAT}", "${BIG_OUTPUT_FORMAT}",
+ "${LITTLE_OUTPUT_FORMAT}")
+${LIB_SEARCH_DIRS}
+
+ENTRY(${ENTRY})
+
+SECTIONS
+{
+ ${RELOCATING+. = ${TEXT_START_ADDR};}
+ .text : {
+ ${RELOCATING+ _ftext = . };
+ *(.init)
+ ${RELOCATING+ eprol = .};
+ *(.text)
+ ${RELOCATING+PROVIDE (__runtime_reloc_start = .);}
+ *(.rel.sdata)
+ ${RELOCATING+PROVIDE (__runtime_reloc_stop = .);}
+ *(.fini)
+ ${RELOCATING+ etext = .};
+ ${RELOCATING+ _etext = .};
+ }
+ ${RELOCATING+. = ${DATA_ADDR};}
+ .rdata : {
+ *(.rdata)
+ }
+ ${RELOCATING+ _fdata = ALIGN(16);}
+ .data : {
+ *(.data)
+ ${CONSTRUCTING+CONSTRUCTORS}
+ }
+ ${RELOCATING+ _gp = ALIGN(16) + 0x8000;}
+ .lit8 : {
+ *(.lit8)
+ }
+ .lit4 : {
+ *(.lit4)
+ }
+ .sdata : {
+ *(.sdata)
+ }
+ ${RELOCATING+ edata = .;}
+ ${RELOCATING+ _edata = .;}
+ ${RELOCATING+ _fbss = .;}
+ .sbss : {
+ *(.sbss)
+ *(.scommon)
+ }
+ .bss : {
+ *(.bss)
+ *(COMMON)
+ }
+ ${RELOCATING+ end = .;}
+ ${RELOCATING+ _end = .;}
+}
+EOF
diff --git a/contrib/binutils/ld/scripttempl/mipsbsd.sc b/contrib/binutils/ld/scripttempl/mipsbsd.sc
new file mode 100644
index 0000000..b222b33
--- /dev/null
+++ b/contrib/binutils/ld/scripttempl/mipsbsd.sc
@@ -0,0 +1,30 @@
+cat <<EOF
+OUTPUT_FORMAT("${OUTPUT_FORMAT}", "${BIG_OUTPUT_FORMAT}",
+ "${LITTLE_OUTPUT_FORMAT}")
+OUTPUT_ARCH(${ARCH})
+
+${RELOCATING+${LIB_SEARCH_DIRS}}
+SECTIONS
+{
+ ${RELOCATING+. = ${TEXT_START_ADDR};}
+ .text :
+ {
+ CREATE_OBJECT_SYMBOLS
+ *(.text)
+ ${RELOCATING+etext = ${DATA_ALIGNMENT};}
+ }
+ ${RELOCATING+. = ${DATA_ALIGNMENT};}
+ .data :
+ {
+ *(.data)
+ ${CONSTRUCTING+CONSTRUCTORS}
+ ${RELOCATING+edata = .;}
+ }
+ .bss :
+ {
+ *(.bss)
+ *(COMMON)
+ ${RELOCATING+end = . };
+ }
+}
+EOF
diff --git a/contrib/binutils/opcodes/mips-dis.c b/contrib/binutils/opcodes/mips-dis.c
new file mode 100644
index 0000000..43fcb3c
--- /dev/null
+++ b/contrib/binutils/opcodes/mips-dis.c
@@ -0,0 +1,1835 @@
+/* Print mips instructions for GDB, the GNU debugger, or for objdump.
+ Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ 2000, 2001, 2002, 2003
+ Free Software Foundation, Inc.
+ Contributed by Nobuyuki Hikichi(hikichi@sra.co.jp).
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 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 "sysdep.h"
+#include "dis-asm.h"
+#include "libiberty.h"
+#include "opcode/mips.h"
+#include "opintl.h"
+
+/* FIXME: These are needed to figure out if the code is mips16 or
+ not. The low bit of the address is often a good indicator. No
+ symbol table is available when this code runs out in an embedded
+ system as when it is used for disassembler support in a monitor. */
+
+#if !defined(EMBEDDED_ENV)
+#define SYMTAB_AVAILABLE 1
+#include "elf-bfd.h"
+#include "elf/mips.h"
+#endif
+
+/* Mips instructions are at maximum this many bytes long. */
+#define INSNLEN 4
+
+static void set_default_mips_dis_options
+ PARAMS ((struct disassemble_info *));
+static void parse_mips_dis_option
+ PARAMS ((const char *, unsigned int));
+static void parse_mips_dis_options
+ PARAMS ((const char *));
+static int _print_insn_mips
+ PARAMS ((bfd_vma, struct disassemble_info *, enum bfd_endian));
+static int print_insn_mips
+ PARAMS ((bfd_vma, unsigned long int, struct disassemble_info *));
+static void print_insn_args
+ PARAMS ((const char *, unsigned long, bfd_vma, struct disassemble_info *));
+static int print_insn_mips16
+ PARAMS ((bfd_vma, struct disassemble_info *));
+static int is_newabi
+ PARAMS ((Elf_Internal_Ehdr *));
+static void print_mips16_insn_arg
+ PARAMS ((int, const struct mips_opcode *, int, bfd_boolean, int, bfd_vma,
+ struct disassemble_info *));
+
+/* FIXME: These should be shared with gdb somehow. */
+
+struct mips_cp0sel_name {
+ unsigned int cp0reg;
+ unsigned int sel;
+ const char * const name;
+};
+
+/* The mips16 register names. */
+static const char * const mips16_reg_names[] = {
+ "s0", "s1", "v0", "v1", "a0", "a1", "a2", "a3"
+};
+
+static const char * const mips_gpr_names_numeric[32] = {
+ "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7",
+ "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15",
+ "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23",
+ "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31"
+};
+
+static const char * const mips_gpr_names_oldabi[32] = {
+ "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"
+};
+
+static const char * const mips_gpr_names_newabi[32] = {
+ "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+ "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"
+};
+
+static const char * const mips_fpr_names_numeric[32] = {
+ "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
+ "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
+ "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
+ "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31"
+};
+
+static const char * const mips_fpr_names_32[32] = {
+ "fv0", "fv0f", "fv1", "fv1f", "ft0", "ft0f", "ft1", "ft1f",
+ "ft2", "ft2f", "ft3", "ft3f", "fa0", "fa0f", "fa1", "fa1f",
+ "ft4", "ft4f", "ft5", "ft5f", "fs0", "fs0f", "fs1", "fs1f",
+ "fs2", "fs2f", "fs3", "fs3f", "fs4", "fs4f", "fs5", "fs5f"
+};
+
+static const char * const mips_fpr_names_n32[32] = {
+ "fv0", "ft14", "fv1", "ft15", "ft0", "ft1", "ft2", "ft3",
+ "ft4", "ft5", "ft6", "ft7", "fa0", "fa1", "fa2", "fa3",
+ "fa4", "fa5", "fa6", "fa7", "fs0", "ft8", "fs1", "ft9",
+ "fs2", "ft10", "fs3", "ft11", "fs4", "ft12", "fs5", "ft13"
+};
+
+static const char * const mips_fpr_names_64[32] = {
+ "fv0", "ft12", "fv1", "ft13", "ft0", "ft1", "ft2", "ft3",
+ "ft4", "ft5", "ft6", "ft7", "fa0", "fa1", "fa2", "fa3",
+ "fa4", "fa5", "fa6", "fa7", "ft8", "ft9", "ft10", "ft11",
+ "fs0", "fs1", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7"
+};
+
+static const char * const mips_cp0_names_numeric[32] = {
+ "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7",
+ "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15",
+ "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23",
+ "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31"
+};
+
+static const char * const mips_cp0_names_mips3264[32] = {
+ "c0_index", "c0_random", "c0_entrylo0", "c0_entrylo1",
+ "c0_context", "c0_pagemask", "c0_wired", "$7",
+ "c0_badvaddr", "c0_count", "c0_entryhi", "c0_compare",
+ "c0_status", "c0_cause", "c0_epc", "c0_prid",
+ "c0_config", "c0_lladdr", "c0_watchlo", "c0_watchhi",
+ "c0_xcontext", "$21", "$22", "c0_debug",
+ "c0_depc", "c0_perfcnt", "c0_errctl", "c0_cacheerr",
+ "c0_taglo", "c0_taghi", "c0_errorepc", "c0_desave",
+};
+
+static const struct mips_cp0sel_name mips_cp0sel_names_mips3264[] = {
+ { 16, 1, "c0_config1" },
+ { 16, 2, "c0_config2" },
+ { 16, 3, "c0_config3" },
+ { 18, 1, "c0_watchlo,1" },
+ { 18, 2, "c0_watchlo,2" },
+ { 18, 3, "c0_watchlo,3" },
+ { 18, 4, "c0_watchlo,4" },
+ { 18, 5, "c0_watchlo,5" },
+ { 18, 6, "c0_watchlo,6" },
+ { 18, 7, "c0_watchlo,7" },
+ { 19, 1, "c0_watchhi,1" },
+ { 19, 2, "c0_watchhi,2" },
+ { 19, 3, "c0_watchhi,3" },
+ { 19, 4, "c0_watchhi,4" },
+ { 19, 5, "c0_watchhi,5" },
+ { 19, 6, "c0_watchhi,6" },
+ { 19, 7, "c0_watchhi,7" },
+ { 25, 1, "c0_perfcnt,1" },
+ { 25, 2, "c0_perfcnt,2" },
+ { 25, 3, "c0_perfcnt,3" },
+ { 25, 4, "c0_perfcnt,4" },
+ { 25, 5, "c0_perfcnt,5" },
+ { 25, 6, "c0_perfcnt,6" },
+ { 25, 7, "c0_perfcnt,7" },
+ { 27, 1, "c0_cacheerr,1" },
+ { 27, 2, "c0_cacheerr,2" },
+ { 27, 3, "c0_cacheerr,3" },
+ { 28, 1, "c0_datalo" },
+ { 29, 1, "c0_datahi" }
+};
+
+static const char * const mips_cp0_names_mips3264r2[32] = {
+ "c0_index", "c0_random", "c0_entrylo0", "c0_entrylo1",
+ "c0_context", "c0_pagemask", "c0_wired", "c0_hwrena",
+ "c0_badvaddr", "c0_count", "c0_entryhi", "c0_compare",
+ "c0_status", "c0_cause", "c0_epc", "c0_prid",
+ "c0_config", "c0_lladdr", "c0_watchlo", "c0_watchhi",
+ "c0_xcontext", "$21", "$22", "c0_debug",
+ "c0_depc", "c0_perfcnt", "c0_errctl", "c0_cacheerr",
+ "c0_taglo", "c0_taghi", "c0_errorepc", "c0_desave",
+};
+
+static const struct mips_cp0sel_name mips_cp0sel_names_mips3264r2[] = {
+ { 4, 1, "c0_contextconfig" },
+ { 5, 1, "c0_pagegrain" },
+ { 12, 1, "c0_intctl" },
+ { 12, 2, "c0_srsctl" },
+ { 12, 3, "c0_srsmap" },
+ { 15, 1, "c0_ebase" },
+ { 16, 1, "c0_config1" },
+ { 16, 2, "c0_config2" },
+ { 16, 3, "c0_config3" },
+ { 18, 1, "c0_watchlo,1" },
+ { 18, 2, "c0_watchlo,2" },
+ { 18, 3, "c0_watchlo,3" },
+ { 18, 4, "c0_watchlo,4" },
+ { 18, 5, "c0_watchlo,5" },
+ { 18, 6, "c0_watchlo,6" },
+ { 18, 7, "c0_watchlo,7" },
+ { 19, 1, "c0_watchhi,1" },
+ { 19, 2, "c0_watchhi,2" },
+ { 19, 3, "c0_watchhi,3" },
+ { 19, 4, "c0_watchhi,4" },
+ { 19, 5, "c0_watchhi,5" },
+ { 19, 6, "c0_watchhi,6" },
+ { 19, 7, "c0_watchhi,7" },
+ { 23, 1, "c0_tracecontrol" },
+ { 23, 2, "c0_tracecontrol2" },
+ { 23, 3, "c0_usertracedata" },
+ { 23, 4, "c0_tracebpc" },
+ { 25, 1, "c0_perfcnt,1" },
+ { 25, 2, "c0_perfcnt,2" },
+ { 25, 3, "c0_perfcnt,3" },
+ { 25, 4, "c0_perfcnt,4" },
+ { 25, 5, "c0_perfcnt,5" },
+ { 25, 6, "c0_perfcnt,6" },
+ { 25, 7, "c0_perfcnt,7" },
+ { 27, 1, "c0_cacheerr,1" },
+ { 27, 2, "c0_cacheerr,2" },
+ { 27, 3, "c0_cacheerr,3" },
+ { 28, 1, "c0_datalo" },
+ { 28, 2, "c0_taglo1" },
+ { 28, 3, "c0_datalo1" },
+ { 28, 4, "c0_taglo2" },
+ { 28, 5, "c0_datalo2" },
+ { 28, 6, "c0_taglo3" },
+ { 28, 7, "c0_datalo3" },
+ { 29, 1, "c0_datahi" },
+ { 29, 2, "c0_taghi1" },
+ { 29, 3, "c0_datahi1" },
+ { 29, 4, "c0_taghi2" },
+ { 29, 5, "c0_datahi2" },
+ { 29, 6, "c0_taghi3" },
+ { 29, 7, "c0_datahi3" },
+};
+
+/* SB-1: MIPS64 (mips_cp0_names_mips3264) with minor mods. */
+static const char * const mips_cp0_names_sb1[32] = {
+ "c0_index", "c0_random", "c0_entrylo0", "c0_entrylo1",
+ "c0_context", "c0_pagemask", "c0_wired", "$7",
+ "c0_badvaddr", "c0_count", "c0_entryhi", "c0_compare",
+ "c0_status", "c0_cause", "c0_epc", "c0_prid",
+ "c0_config", "c0_lladdr", "c0_watchlo", "c0_watchhi",
+ "c0_xcontext", "$21", "$22", "c0_debug",
+ "c0_depc", "c0_perfcnt", "c0_errctl", "c0_cacheerr_i",
+ "c0_taglo_i", "c0_taghi_i", "c0_errorepc", "c0_desave",
+};
+
+static const struct mips_cp0sel_name mips_cp0sel_names_sb1[] = {
+ { 16, 1, "c0_config1" },
+ { 18, 1, "c0_watchlo,1" },
+ { 19, 1, "c0_watchhi,1" },
+ { 22, 0, "c0_perftrace" },
+ { 23, 3, "c0_edebug" },
+ { 25, 1, "c0_perfcnt,1" },
+ { 25, 2, "c0_perfcnt,2" },
+ { 25, 3, "c0_perfcnt,3" },
+ { 25, 4, "c0_perfcnt,4" },
+ { 25, 5, "c0_perfcnt,5" },
+ { 25, 6, "c0_perfcnt,6" },
+ { 25, 7, "c0_perfcnt,7" },
+ { 26, 1, "c0_buserr_pa" },
+ { 27, 1, "c0_cacheerr_d" },
+ { 27, 3, "c0_cacheerr_d_pa" },
+ { 28, 1, "c0_datalo_i" },
+ { 28, 2, "c0_taglo_d" },
+ { 28, 3, "c0_datalo_d" },
+ { 29, 1, "c0_datahi_i" },
+ { 29, 2, "c0_taghi_d" },
+ { 29, 3, "c0_datahi_d" },
+};
+
+static const char * const mips_hwr_names_numeric[32] = {
+ "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7",
+ "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15",
+ "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23",
+ "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31"
+};
+
+static const char * const mips_hwr_names_mips3264r2[32] = {
+ "hwr_cpunum", "hwr_synci_step", "hwr_cc", "hwr_ccres",
+ "$4", "$5", "$6", "$7",
+ "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15",
+ "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23",
+ "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31"
+};
+
+struct mips_abi_choice {
+ const char *name;
+ const char * const *gpr_names;
+ const char * const *fpr_names;
+};
+
+struct mips_abi_choice mips_abi_choices[] = {
+ { "numeric", mips_gpr_names_numeric, mips_fpr_names_numeric },
+ { "32", mips_gpr_names_oldabi, mips_fpr_names_32 },
+ { "n32", mips_gpr_names_newabi, mips_fpr_names_n32 },
+ { "64", mips_gpr_names_newabi, mips_fpr_names_64 },
+};
+
+struct mips_arch_choice {
+ const char *name;
+ int bfd_mach_valid;
+ unsigned long bfd_mach;
+ int processor;
+ int isa;
+ const char * const *cp0_names;
+ const struct mips_cp0sel_name *cp0sel_names;
+ unsigned int cp0sel_names_len;
+ const char * const *hwr_names;
+};
+
+const struct mips_arch_choice mips_arch_choices[] = {
+ { "numeric", 0, 0, 0, 0,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+
+ { "r3000", 1, bfd_mach_mips3000, CPU_R3000, ISA_MIPS1,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r3900", 1, bfd_mach_mips3900, CPU_R3900, ISA_MIPS1,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r4000", 1, bfd_mach_mips4000, CPU_R4000, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r4010", 1, bfd_mach_mips4010, CPU_R4010, ISA_MIPS2,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "vr4100", 1, bfd_mach_mips4100, CPU_VR4100, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "vr4111", 1, bfd_mach_mips4111, CPU_R4111, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "vr4120", 1, bfd_mach_mips4120, CPU_VR4120, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r4300", 1, bfd_mach_mips4300, CPU_R4300, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r4400", 1, bfd_mach_mips4400, CPU_R4400, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r4600", 1, bfd_mach_mips4600, CPU_R4600, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r4650", 1, bfd_mach_mips4650, CPU_R4650, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r5000", 1, bfd_mach_mips5000, CPU_R5000, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "vr5400", 1, bfd_mach_mips5400, CPU_VR5400, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "vr5500", 1, bfd_mach_mips5500, CPU_VR5500, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r6000", 1, bfd_mach_mips6000, CPU_R6000, ISA_MIPS2,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "rm7000", 1, bfd_mach_mips7000, CPU_RM7000, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "rm9000", 1, bfd_mach_mips7000, CPU_RM7000, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r8000", 1, bfd_mach_mips8000, CPU_R8000, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r10000", 1, bfd_mach_mips10000, CPU_R10000, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r12000", 1, bfd_mach_mips12000, CPU_R12000, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "mips5", 1, bfd_mach_mips5, CPU_MIPS5, ISA_MIPS5,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+
+ /* For stock MIPS32, disassemble all applicable MIPS-specified ASEs.
+ Note that MIPS-3D and MDMX are not applicable to MIPS32. (See
+ _MIPS32 Architecture For Programmers Volume I: Introduction to the
+ MIPS32 Architecture_ (MIPS Document Number MD00082, Revision 0.95),
+ page 1. */
+ { "mips32", 1, bfd_mach_mipsisa32, CPU_MIPS32,
+ ISA_MIPS32 | INSN_MIPS16,
+ mips_cp0_names_mips3264,
+ mips_cp0sel_names_mips3264, ARRAY_SIZE (mips_cp0sel_names_mips3264),
+ mips_hwr_names_numeric },
+
+ { "mips32r2", 1, bfd_mach_mipsisa32r2, CPU_MIPS32R2,
+ ISA_MIPS32R2 | INSN_MIPS16,
+ mips_cp0_names_mips3264r2,
+ mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
+ mips_hwr_names_mips3264r2 },
+
+ /* For stock MIPS64, disassemble all applicable MIPS-specified ASEs. */
+ { "mips64", 1, bfd_mach_mipsisa64, CPU_MIPS64,
+ ISA_MIPS64 | INSN_MIPS16 | INSN_MIPS3D | INSN_MDMX,
+ mips_cp0_names_mips3264,
+ mips_cp0sel_names_mips3264, ARRAY_SIZE (mips_cp0sel_names_mips3264),
+ mips_hwr_names_numeric },
+
+ { "mips64r2", 1, bfd_mach_mipsisa64r2, CPU_MIPS64R2,
+ ISA_MIPS64R2 | INSN_MIPS16 | INSN_MIPS3D | INSN_MDMX,
+ mips_cp0_names_mips3264r2,
+ mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
+ mips_hwr_names_mips3264r2 },
+
+ { "sb1", 1, bfd_mach_mips_sb1, CPU_SB1,
+ ISA_MIPS64 | INSN_MIPS3D | INSN_SB1,
+ mips_cp0_names_sb1,
+ mips_cp0sel_names_sb1, ARRAY_SIZE (mips_cp0sel_names_sb1),
+ mips_hwr_names_numeric },
+
+ /* This entry, mips16, is here only for ISA/processor selection; do
+ not print its name. */
+ { "", 1, bfd_mach_mips16, CPU_MIPS16, ISA_MIPS3 | INSN_MIPS16,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+};
+
+/* ISA and processor type to disassemble for, and register names to use.
+ set_default_mips_dis_options and parse_mips_dis_options fill in these
+ values. */
+static int mips_processor;
+static int mips_isa;
+static const char * const *mips_gpr_names;
+static const char * const *mips_fpr_names;
+static const char * const *mips_cp0_names;
+static const struct mips_cp0sel_name *mips_cp0sel_names;
+static int mips_cp0sel_names_len;
+static const char * const *mips_hwr_names;
+
+static const struct mips_abi_choice *choose_abi_by_name
+ PARAMS ((const char *, unsigned int));
+static const struct mips_arch_choice *choose_arch_by_name
+ PARAMS ((const char *, unsigned int));
+static const struct mips_arch_choice *choose_arch_by_number
+ PARAMS ((unsigned long));
+static const struct mips_cp0sel_name *lookup_mips_cp0sel_name
+ PARAMS ((const struct mips_cp0sel_name *, unsigned int, unsigned int,
+ unsigned int));
+
+static const struct mips_abi_choice *
+choose_abi_by_name (name, namelen)
+ const char *name;
+ unsigned int namelen;
+{
+ const struct mips_abi_choice *c;
+ unsigned int i;
+
+ for (i = 0, c = NULL; i < ARRAY_SIZE (mips_abi_choices) && c == NULL; i++)
+ {
+ if (strncmp (mips_abi_choices[i].name, name, namelen) == 0
+ && strlen (mips_abi_choices[i].name) == namelen)
+ c = &mips_abi_choices[i];
+ }
+ return c;
+}
+
+static const struct mips_arch_choice *
+choose_arch_by_name (name, namelen)
+ const char *name;
+ unsigned int namelen;
+{
+ const struct mips_arch_choice *c = NULL;
+ unsigned int i;
+
+ for (i = 0, c = NULL; i < ARRAY_SIZE (mips_arch_choices) && c == NULL; i++)
+ {
+ if (strncmp (mips_arch_choices[i].name, name, namelen) == 0
+ && strlen (mips_arch_choices[i].name) == namelen)
+ c = &mips_arch_choices[i];
+ }
+ return c;
+}
+
+static const struct mips_arch_choice *
+choose_arch_by_number (mach)
+ unsigned long mach;
+{
+ static unsigned long hint_bfd_mach;
+ static const struct mips_arch_choice *hint_arch_choice;
+ const struct mips_arch_choice *c;
+ unsigned int i;
+
+ /* We optimize this because even if the user specifies no
+ flags, this will be done for every instruction! */
+ if (hint_bfd_mach == mach
+ && hint_arch_choice != NULL
+ && hint_arch_choice->bfd_mach == hint_bfd_mach)
+ return hint_arch_choice;
+
+ for (i = 0, c = NULL; i < ARRAY_SIZE (mips_arch_choices) && c == NULL; i++)
+ {
+ if (mips_arch_choices[i].bfd_mach_valid
+ && mips_arch_choices[i].bfd_mach == mach)
+ {
+ c = &mips_arch_choices[i];
+ hint_bfd_mach = mach;
+ hint_arch_choice = c;
+ }
+ }
+ return c;
+}
+
+void
+set_default_mips_dis_options (info)
+ struct disassemble_info *info;
+{
+ const struct mips_arch_choice *chosen_arch;
+
+ /* Defaults: mipsIII/r3000 (?!), (o)32-style ("oldabi") GPR names,
+ and numeric FPR, CP0 register, and HWR names. */
+ mips_isa = ISA_MIPS3;
+ mips_processor = CPU_R3000;
+ mips_gpr_names = mips_gpr_names_oldabi;
+ mips_fpr_names = mips_fpr_names_numeric;
+ mips_cp0_names = mips_cp0_names_numeric;
+ mips_cp0sel_names = NULL;
+ mips_cp0sel_names_len = 0;
+ mips_hwr_names = mips_hwr_names_numeric;
+
+ /* If an ELF "newabi" binary, use the n32/(n)64 GPR names. */
+ if (info->flavour == bfd_target_elf_flavour && info->section != NULL)
+ {
+ Elf_Internal_Ehdr *header;
+
+ header = elf_elfheader (info->section->owner);
+ if (is_newabi (header))
+ mips_gpr_names = mips_gpr_names_newabi;
+ }
+
+ /* Set ISA, architecture, and cp0 register names as best we can. */
+#if ! SYMTAB_AVAILABLE
+ /* This is running out on a target machine, not in a host tool.
+ FIXME: Where does mips_target_info come from? */
+ target_processor = mips_target_info.processor;
+ mips_isa = mips_target_info.isa;
+#else
+ chosen_arch = choose_arch_by_number (info->mach);
+ if (chosen_arch != NULL)
+ {
+ mips_processor = chosen_arch->processor;
+ mips_isa = chosen_arch->isa;
+ mips_cp0_names = chosen_arch->cp0_names;
+ mips_cp0sel_names = chosen_arch->cp0sel_names;
+ mips_cp0sel_names_len = chosen_arch->cp0sel_names_len;
+ mips_hwr_names = chosen_arch->hwr_names;
+ }
+#endif
+}
+
+void
+parse_mips_dis_option (option, len)
+ const char *option;
+ unsigned int len;
+{
+ unsigned int i, optionlen, vallen;
+ const char *val;
+ const struct mips_abi_choice *chosen_abi;
+ const struct mips_arch_choice *chosen_arch;
+
+ /* Look for the = that delimits the end of the option name. */
+ for (i = 0; i < len; i++)
+ {
+ if (option[i] == '=')
+ break;
+ }
+ if (i == 0) /* Invalid option: no name before '='. */
+ return;
+ if (i == len) /* Invalid option: no '='. */
+ return;
+ if (i == (len - 1)) /* Invalid option: no value after '='. */
+ return;
+
+ optionlen = i;
+ val = option + (optionlen + 1);
+ vallen = len - (optionlen + 1);
+
+ if (strncmp("gpr-names", option, optionlen) == 0
+ && strlen("gpr-names") == optionlen)
+ {
+ chosen_abi = choose_abi_by_name (val, vallen);
+ if (chosen_abi != NULL)
+ mips_gpr_names = chosen_abi->gpr_names;
+ return;
+ }
+
+ if (strncmp("fpr-names", option, optionlen) == 0
+ && strlen("fpr-names") == optionlen)
+ {
+ chosen_abi = choose_abi_by_name (val, vallen);
+ if (chosen_abi != NULL)
+ mips_fpr_names = chosen_abi->fpr_names;
+ return;
+ }
+
+ if (strncmp("cp0-names", option, optionlen) == 0
+ && strlen("cp0-names") == optionlen)
+ {
+ chosen_arch = choose_arch_by_name (val, vallen);
+ if (chosen_arch != NULL)
+ {
+ mips_cp0_names = chosen_arch->cp0_names;
+ mips_cp0sel_names = chosen_arch->cp0sel_names;
+ mips_cp0sel_names_len = chosen_arch->cp0sel_names_len;
+ }
+ return;
+ }
+
+ if (strncmp("hwr-names", option, optionlen) == 0
+ && strlen("hwr-names") == optionlen)
+ {
+ chosen_arch = choose_arch_by_name (val, vallen);
+ if (chosen_arch != NULL)
+ mips_hwr_names = chosen_arch->hwr_names;
+ return;
+ }
+
+ if (strncmp("reg-names", option, optionlen) == 0
+ && strlen("reg-names") == optionlen)
+ {
+ /* We check both ABI and ARCH here unconditionally, so
+ that "numeric" will do the desirable thing: select
+ numeric register names for all registers. Other than
+ that, a given name probably won't match both. */
+ chosen_abi = choose_abi_by_name (val, vallen);
+ if (chosen_abi != NULL)
+ {
+ mips_gpr_names = chosen_abi->gpr_names;
+ mips_fpr_names = chosen_abi->fpr_names;
+ }
+ chosen_arch = choose_arch_by_name (val, vallen);
+ if (chosen_arch != NULL)
+ {
+ mips_cp0_names = chosen_arch->cp0_names;
+ mips_cp0sel_names = chosen_arch->cp0sel_names;
+ mips_cp0sel_names_len = chosen_arch->cp0sel_names_len;
+ mips_hwr_names = chosen_arch->hwr_names;
+ }
+ return;
+ }
+
+ /* Invalid option. */
+}
+
+void
+parse_mips_dis_options (options)
+ const char *options;
+{
+ const char *option_end;
+
+ if (options == NULL)
+ return;
+
+ while (*options != '\0')
+ {
+ /* Skip empty options. */
+ if (*options == ',')
+ {
+ options++;
+ continue;
+ }
+
+ /* We know that *options is neither NUL or a comma. */
+ option_end = options + 1;
+ while (*option_end != ',' && *option_end != '\0')
+ option_end++;
+
+ parse_mips_dis_option (options, option_end - options);
+
+ /* Go on to the next one. If option_end points to a comma, it
+ will be skipped above. */
+ options = option_end;
+ }
+}
+
+static const struct mips_cp0sel_name *
+lookup_mips_cp0sel_name(names, len, cp0reg, sel)
+ const struct mips_cp0sel_name *names;
+ unsigned int len, cp0reg, sel;
+{
+ unsigned int i;
+
+ for (i = 0; i < len; i++)
+ if (names[i].cp0reg == cp0reg && names[i].sel == sel)
+ return &names[i];
+ return NULL;
+}
+
+/* Print insn arguments for 32/64-bit code. */
+
+static void
+print_insn_args (d, l, pc, info)
+ const char *d;
+ register unsigned long int l;
+ bfd_vma pc;
+ struct disassemble_info *info;
+{
+ int op, delta;
+ unsigned int lsb, msb, msbd;
+
+ lsb = 0;
+
+ for (; *d != '\0'; d++)
+ {
+ switch (*d)
+ {
+ case ',':
+ case '(':
+ case ')':
+ case '[':
+ case ']':
+ (*info->fprintf_func) (info->stream, "%c", *d);
+ break;
+
+ case '+':
+ /* Extension character; switch for second char. */
+ d++;
+ switch (*d)
+ {
+ case '\0':
+ /* xgettext:c-format */
+ (*info->fprintf_func) (info->stream,
+ _("# internal error, incomplete extension sequence (+)"));
+ return;
+
+ case 'A':
+ lsb = (l >> OP_SH_SHAMT) & OP_MASK_SHAMT;
+ (*info->fprintf_func) (info->stream, "0x%x", lsb);
+ break;
+
+ case 'B':
+ msb = (l >> OP_SH_INSMSB) & OP_MASK_INSMSB;
+ (*info->fprintf_func) (info->stream, "0x%x", msb - lsb + 1);
+ break;
+
+ case 'C':
+ case 'H':
+ msbd = (l >> OP_SH_EXTMSBD) & OP_MASK_EXTMSBD;
+ (*info->fprintf_func) (info->stream, "0x%x", msbd + 1);
+ break;
+
+ case 'D':
+ {
+ const struct mips_cp0sel_name *n;
+ unsigned int cp0reg, sel;
+
+ cp0reg = (l >> OP_SH_RD) & OP_MASK_RD;
+ sel = (l >> OP_SH_SEL) & OP_MASK_SEL;
+
+ /* CP0 register including 'sel' code for mtcN (et al.), to be
+ printed textually if known. If not known, print both
+ CP0 register name and sel numerically since CP0 register
+ with sel 0 may have a name unrelated to register being
+ printed. */
+ n = lookup_mips_cp0sel_name(mips_cp0sel_names,
+ mips_cp0sel_names_len, cp0reg, sel);
+ if (n != NULL)
+ (*info->fprintf_func) (info->stream, "%s", n->name);
+ else
+ (*info->fprintf_func) (info->stream, "$%d,%d", cp0reg, sel);
+ break;
+ }
+
+ case 'E':
+ lsb = ((l >> OP_SH_SHAMT) & OP_MASK_SHAMT) + 32;
+ (*info->fprintf_func) (info->stream, "0x%x", lsb);
+ break;
+
+ case 'F':
+ msb = ((l >> OP_SH_INSMSB) & OP_MASK_INSMSB) + 32;
+ (*info->fprintf_func) (info->stream, "0x%x", msb - lsb + 1);
+ break;
+
+ case 'G':
+ msbd = ((l >> OP_SH_EXTMSBD) & OP_MASK_EXTMSBD) + 32;
+ (*info->fprintf_func) (info->stream, "0x%x", msbd + 1);
+ break;
+
+ default:
+ /* xgettext:c-format */
+ (*info->fprintf_func) (info->stream,
+ _("# internal error, undefined extension sequence (+%c)"),
+ *d);
+ return;
+ }
+ break;
+
+ case 's':
+ case 'b':
+ case 'r':
+ case 'v':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[(l >> OP_SH_RS) & OP_MASK_RS]);
+ break;
+
+ case 't':
+ case 'w':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[(l >> OP_SH_RT) & OP_MASK_RT]);
+ break;
+
+ case 'i':
+ case 'u':
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (l >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE);
+ break;
+
+ case 'j': /* Same as i, but sign-extended. */
+ case 'o':
+ delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA;
+ if (delta & 0x8000)
+ delta |= ~0xffff;
+ (*info->fprintf_func) (info->stream, "%d",
+ delta);
+ break;
+
+ case 'h':
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (unsigned int) ((l >> OP_SH_PREFX)
+ & OP_MASK_PREFX));
+ break;
+
+ case 'k':
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (unsigned int) ((l >> OP_SH_CACHE)
+ & OP_MASK_CACHE));
+ break;
+
+ case 'a':
+ info->target = (((pc + 4) & ~(bfd_vma) 0x0fffffff)
+ | (((l >> OP_SH_TARGET) & OP_MASK_TARGET) << 2));
+ (*info->print_address_func) (info->target, info);
+ break;
+
+ case 'p':
+ /* Sign extend the displacement. */
+ delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA;
+ if (delta & 0x8000)
+ delta |= ~0xffff;
+ info->target = (delta << 2) + pc + INSNLEN;
+ (*info->print_address_func) (info->target, info);
+ break;
+
+ case 'd':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[(l >> OP_SH_RD) & OP_MASK_RD]);
+ break;
+
+ case 'U':
+ {
+ /* First check for both rd and rt being equal. */
+ unsigned int reg = (l >> OP_SH_RD) & OP_MASK_RD;
+ if (reg == ((l >> OP_SH_RT) & OP_MASK_RT))
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[reg]);
+ else
+ {
+ /* If one is zero use the other. */
+ if (reg == 0)
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[(l >> OP_SH_RT) & OP_MASK_RT]);
+ else if (((l >> OP_SH_RT) & OP_MASK_RT) == 0)
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[reg]);
+ else /* Bogus, result depends on processor. */
+ (*info->fprintf_func) (info->stream, "%s or %s",
+ mips_gpr_names[reg],
+ mips_gpr_names[(l >> OP_SH_RT) & OP_MASK_RT]);
+ }
+ }
+ break;
+
+ case 'z':
+ (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[0]);
+ break;
+
+ case '<':
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (l >> OP_SH_SHAMT) & OP_MASK_SHAMT);
+ break;
+
+ case 'c':
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (l >> OP_SH_CODE) & OP_MASK_CODE);
+ break;
+
+ case 'q':
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (l >> OP_SH_CODE2) & OP_MASK_CODE2);
+ break;
+
+ case 'C':
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (l >> OP_SH_COPZ) & OP_MASK_COPZ);
+ break;
+
+ case 'B':
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (l >> OP_SH_CODE20) & OP_MASK_CODE20);
+ break;
+
+ case 'J':
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (l >> OP_SH_CODE19) & OP_MASK_CODE19);
+ break;
+
+ case 'S':
+ case 'V':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_fpr_names[(l >> OP_SH_FS) & OP_MASK_FS]);
+ break;
+
+ case 'T':
+ case 'W':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_fpr_names[(l >> OP_SH_FT) & OP_MASK_FT]);
+ break;
+
+ case 'D':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_fpr_names[(l >> OP_SH_FD) & OP_MASK_FD]);
+ break;
+
+ case 'R':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_fpr_names[(l >> OP_SH_FR) & OP_MASK_FR]);
+ break;
+
+ case 'E':
+ /* Coprocessor register for lwcN instructions, et al.
+
+ Note that there is no load/store cp0 instructions, and
+ that FPU (cp1) instructions disassemble this field using
+ 'T' format. Therefore, until we gain understanding of
+ cp2 register names, we can simply print the register
+ numbers. */
+ (*info->fprintf_func) (info->stream, "$%d",
+ (l >> OP_SH_RT) & OP_MASK_RT);
+ break;
+
+ case 'G':
+ /* Coprocessor register for mtcN instructions, et al. Note
+ that FPU (cp1) instructions disassemble this field using
+ 'S' format. Therefore, we only need to worry about cp0,
+ cp2, and cp3. */
+ op = (l >> OP_SH_OP) & OP_MASK_OP;
+ if (op == OP_OP_COP0)
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_cp0_names[(l >> OP_SH_RD) & OP_MASK_RD]);
+ else
+ (*info->fprintf_func) (info->stream, "$%d",
+ (l >> OP_SH_RD) & OP_MASK_RD);
+ break;
+
+ case 'K':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_hwr_names[(l >> OP_SH_RD) & OP_MASK_RD]);
+ break;
+
+ case 'N':
+ (*info->fprintf_func) (info->stream, "$fcc%d",
+ (l >> OP_SH_BCC) & OP_MASK_BCC);
+ break;
+
+ case 'M':
+ (*info->fprintf_func) (info->stream, "$fcc%d",
+ (l >> OP_SH_CCC) & OP_MASK_CCC);
+ break;
+
+ case 'P':
+ (*info->fprintf_func) (info->stream, "%d",
+ (l >> OP_SH_PERFREG) & OP_MASK_PERFREG);
+ break;
+
+ case 'e':
+ (*info->fprintf_func) (info->stream, "%d",
+ (l >> OP_SH_VECBYTE) & OP_MASK_VECBYTE);
+ break;
+
+ case '%':
+ (*info->fprintf_func) (info->stream, "%d",
+ (l >> OP_SH_VECALIGN) & OP_MASK_VECALIGN);
+ break;
+
+ case 'H':
+ (*info->fprintf_func) (info->stream, "%d",
+ (l >> OP_SH_SEL) & OP_MASK_SEL);
+ break;
+
+ case 'O':
+ (*info->fprintf_func) (info->stream, "%d",
+ (l >> OP_SH_ALN) & OP_MASK_ALN);
+ break;
+
+ case 'Q':
+ {
+ unsigned int vsel = (l >> OP_SH_VSEL) & OP_MASK_VSEL;
+ if ((vsel & 0x10) == 0)
+ {
+ int fmt;
+ vsel &= 0x0f;
+ for (fmt = 0; fmt < 3; fmt++, vsel >>= 1)
+ if ((vsel & 1) == 0)
+ break;
+ (*info->fprintf_func) (info->stream, "$v%d[%d]",
+ (l >> OP_SH_FT) & OP_MASK_FT,
+ vsel >> 1);
+ }
+ else if ((vsel & 0x08) == 0)
+ {
+ (*info->fprintf_func) (info->stream, "$v%d",
+ (l >> OP_SH_FT) & OP_MASK_FT);
+ }
+ else
+ {
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (l >> OP_SH_FT) & OP_MASK_FT);
+ }
+ }
+ break;
+
+ case 'X':
+ (*info->fprintf_func) (info->stream, "$v%d",
+ (l >> OP_SH_FD) & OP_MASK_FD);
+ break;
+
+ case 'Y':
+ (*info->fprintf_func) (info->stream, "$v%d",
+ (l >> OP_SH_FS) & OP_MASK_FS);
+ break;
+
+ case 'Z':
+ (*info->fprintf_func) (info->stream, "$v%d",
+ (l >> OP_SH_FT) & OP_MASK_FT);
+ break;
+
+ default:
+ /* xgettext:c-format */
+ (*info->fprintf_func) (info->stream,
+ _("# internal error, undefined modifier(%c)"),
+ *d);
+ return;
+ }
+ }
+}
+
+/* Check if the object uses NewABI conventions. */
+
+static int
+is_newabi (header)
+ Elf_Internal_Ehdr *header;
+{
+ /* There are no old-style ABIs which use 64-bit ELF. */
+ if (header->e_ident[EI_CLASS] == ELFCLASS64)
+ return 1;
+
+ /* If a 32-bit ELF file, n32 is a new-style ABI. */
+ if ((header->e_flags & EF_MIPS_ABI2) != 0)
+ return 1;
+
+ return 0;
+}
+
+/* Print the mips instruction at address MEMADDR in debugged memory,
+ on using INFO. Returns length of the instruction, in bytes, which is
+ always INSNLEN. BIGENDIAN must be 1 if this is big-endian code, 0 if
+ this is little-endian code. */
+
+static int
+print_insn_mips (memaddr, word, info)
+ bfd_vma memaddr;
+ unsigned long int word;
+ struct disassemble_info *info;
+{
+ register const struct mips_opcode *op;
+ static bfd_boolean init = 0;
+ static const struct mips_opcode *mips_hash[OP_MASK_OP + 1];
+
+ /* Build a hash table to shorten the search time. */
+ if (! init)
+ {
+ unsigned int i;
+
+ for (i = 0; i <= OP_MASK_OP; i++)
+ {
+ for (op = mips_opcodes; op < &mips_opcodes[NUMOPCODES]; op++)
+ {
+ if (op->pinfo == INSN_MACRO)
+ continue;
+ if (i == ((op->match >> OP_SH_OP) & OP_MASK_OP))
+ {
+ mips_hash[i] = op;
+ break;
+ }
+ }
+ }
+
+ init = 1;
+ }
+
+ info->bytes_per_chunk = INSNLEN;
+ info->display_endian = info->endian;
+ info->insn_info_valid = 1;
+ info->branch_delay_insns = 0;
+ info->data_size = 0;
+ info->insn_type = dis_nonbranch;
+ info->target = 0;
+ info->target2 = 0;
+
+ op = mips_hash[(word >> OP_SH_OP) & OP_MASK_OP];
+ if (op != NULL)
+ {
+ for (; op < &mips_opcodes[NUMOPCODES]; op++)
+ {
+ if (op->pinfo != INSN_MACRO && (word & op->mask) == op->match)
+ {
+ register const char *d;
+
+ /* We always allow to disassemble the jalx instruction. */
+ if (! OPCODE_IS_MEMBER (op, mips_isa, mips_processor)
+ && strcmp (op->name, "jalx"))
+ continue;
+
+ /* Figure out instruction type and branch delay information. */
+ if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0)
+ {
+ if ((info->insn_type & INSN_WRITE_GPR_31) != 0)
+ info->insn_type = dis_jsr;
+ else
+ info->insn_type = dis_branch;
+ info->branch_delay_insns = 1;
+ }
+ else if ((op->pinfo & (INSN_COND_BRANCH_DELAY
+ | INSN_COND_BRANCH_LIKELY)) != 0)
+ {
+ if ((info->insn_type & INSN_WRITE_GPR_31) != 0)
+ info->insn_type = dis_condjsr;
+ else
+ info->insn_type = dis_condbranch;
+ info->branch_delay_insns = 1;
+ }
+ else if ((op->pinfo & (INSN_STORE_MEMORY
+ | INSN_LOAD_MEMORY_DELAY)) != 0)
+ info->insn_type = dis_dref;
+
+ (*info->fprintf_func) (info->stream, "%s", op->name);
+
+ d = op->args;
+ if (d != NULL && *d != '\0')
+ {
+ (*info->fprintf_func) (info->stream, "\t");
+ print_insn_args (d, word, memaddr, info);
+ }
+
+ return INSNLEN;
+ }
+ }
+ }
+
+ /* Handle undefined instructions. */
+ info->insn_type = dis_noninsn;
+ (*info->fprintf_func) (info->stream, "0x%x", word);
+ return INSNLEN;
+}
+
+/* In an environment where we do not know the symbol type of the
+ instruction we are forced to assume that the low order bit of the
+ instructions' address may mark it as a mips16 instruction. If we
+ are single stepping, or the pc is within the disassembled function,
+ this works. Otherwise, we need a clue. Sometimes. */
+
+static int
+_print_insn_mips (memaddr, info, endianness)
+ bfd_vma memaddr;
+ struct disassemble_info *info;
+ enum bfd_endian endianness;
+{
+ bfd_byte buffer[INSNLEN];
+ int status;
+
+ set_default_mips_dis_options (info);
+ parse_mips_dis_options (info->disassembler_options);
+
+#if 1
+ /* FIXME: If odd address, this is CLEARLY a mips 16 instruction. */
+ /* Only a few tools will work this way. */
+ if (memaddr & 0x01)
+ return print_insn_mips16 (memaddr, info);
+#endif
+
+#if SYMTAB_AVAILABLE
+ if (info->mach == bfd_mach_mips16
+ || (info->flavour == bfd_target_elf_flavour
+ && info->symbols != NULL
+ && ((*(elf_symbol_type **) info->symbols)->internal_elf_sym.st_other
+ == STO_MIPS16)))
+ return print_insn_mips16 (memaddr, info);
+#endif
+
+ status = (*info->read_memory_func) (memaddr, buffer, INSNLEN, info);
+ if (status == 0)
+ {
+ unsigned long insn;
+
+ if (endianness == BFD_ENDIAN_BIG)
+ insn = (unsigned long) bfd_getb32 (buffer);
+ else
+ insn = (unsigned long) bfd_getl32 (buffer);
+
+ return print_insn_mips (memaddr, insn, info);
+ }
+ else
+ {
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+}
+
+int
+print_insn_big_mips (memaddr, info)
+ bfd_vma memaddr;
+ struct disassemble_info *info;
+{
+ return _print_insn_mips (memaddr, info, BFD_ENDIAN_BIG);
+}
+
+int
+print_insn_little_mips (memaddr, info)
+ bfd_vma memaddr;
+ struct disassemble_info *info;
+{
+ return _print_insn_mips (memaddr, info, BFD_ENDIAN_LITTLE);
+}
+
+/* Disassemble mips16 instructions. */
+
+static int
+print_insn_mips16 (memaddr, info)
+ bfd_vma memaddr;
+ struct disassemble_info *info;
+{
+ int status;
+ bfd_byte buffer[2];
+ int length;
+ int insn;
+ bfd_boolean use_extend;
+ int extend = 0;
+ const struct mips_opcode *op, *opend;
+
+ info->bytes_per_chunk = 2;
+ info->display_endian = info->endian;
+ info->insn_info_valid = 1;
+ info->branch_delay_insns = 0;
+ info->data_size = 0;
+ info->insn_type = dis_nonbranch;
+ info->target = 0;
+ info->target2 = 0;
+
+ status = (*info->read_memory_func) (memaddr, buffer, 2, info);
+ if (status != 0)
+ {
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+
+ length = 2;
+
+ if (info->endian == BFD_ENDIAN_BIG)
+ insn = bfd_getb16 (buffer);
+ else
+ insn = bfd_getl16 (buffer);
+
+ /* Handle the extend opcode specially. */
+ use_extend = FALSE;
+ if ((insn & 0xf800) == 0xf000)
+ {
+ use_extend = TRUE;
+ extend = insn & 0x7ff;
+
+ memaddr += 2;
+
+ status = (*info->read_memory_func) (memaddr, buffer, 2, info);
+ if (status != 0)
+ {
+ (*info->fprintf_func) (info->stream, "extend 0x%x",
+ (unsigned int) extend);
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+
+ if (info->endian == BFD_ENDIAN_BIG)
+ insn = bfd_getb16 (buffer);
+ else
+ insn = bfd_getl16 (buffer);
+
+ /* Check for an extend opcode followed by an extend opcode. */
+ if ((insn & 0xf800) == 0xf000)
+ {
+ (*info->fprintf_func) (info->stream, "extend 0x%x",
+ (unsigned int) extend);
+ info->insn_type = dis_noninsn;
+ return length;
+ }
+
+ length += 2;
+ }
+
+ /* FIXME: Should probably use a hash table on the major opcode here. */
+
+ opend = mips16_opcodes + bfd_mips16_num_opcodes;
+ for (op = mips16_opcodes; op < opend; op++)
+ {
+ if (op->pinfo != INSN_MACRO && (insn & op->mask) == op->match)
+ {
+ const char *s;
+
+ if (strchr (op->args, 'a') != NULL)
+ {
+ if (use_extend)
+ {
+ (*info->fprintf_func) (info->stream, "extend 0x%x",
+ (unsigned int) extend);
+ info->insn_type = dis_noninsn;
+ return length - 2;
+ }
+
+ use_extend = FALSE;
+
+ memaddr += 2;
+
+ status = (*info->read_memory_func) (memaddr, buffer, 2,
+ info);
+ if (status == 0)
+ {
+ use_extend = TRUE;
+ if (info->endian == BFD_ENDIAN_BIG)
+ extend = bfd_getb16 (buffer);
+ else
+ extend = bfd_getl16 (buffer);
+ length += 2;
+ }
+ }
+
+ (*info->fprintf_func) (info->stream, "%s", op->name);
+ if (op->args[0] != '\0')
+ (*info->fprintf_func) (info->stream, "\t");
+
+ for (s = op->args; *s != '\0'; s++)
+ {
+ if (*s == ','
+ && s[1] == 'w'
+ && (((insn >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX)
+ == ((insn >> MIPS16OP_SH_RY) & MIPS16OP_MASK_RY)))
+ {
+ /* Skip the register and the comma. */
+ ++s;
+ continue;
+ }
+ if (*s == ','
+ && s[1] == 'v'
+ && (((insn >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ)
+ == ((insn >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX)))
+ {
+ /* Skip the register and the comma. */
+ ++s;
+ continue;
+ }
+ print_mips16_insn_arg (*s, op, insn, use_extend, extend, memaddr,
+ info);
+ }
+
+ if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0)
+ {
+ info->branch_delay_insns = 1;
+ if (info->insn_type != dis_jsr)
+ info->insn_type = dis_branch;
+ }
+
+ return length;
+ }
+ }
+
+ if (use_extend)
+ (*info->fprintf_func) (info->stream, "0x%x", extend | 0xf000);
+ (*info->fprintf_func) (info->stream, "0x%x", insn);
+ info->insn_type = dis_noninsn;
+
+ return length;
+}
+
+/* Disassemble an operand for a mips16 instruction. */
+
+static void
+print_mips16_insn_arg (type, op, l, use_extend, extend, memaddr, info)
+ char type;
+ const struct mips_opcode *op;
+ int l;
+ bfd_boolean use_extend;
+ int extend;
+ bfd_vma memaddr;
+ struct disassemble_info *info;
+{
+ switch (type)
+ {
+ case ',':
+ case '(':
+ case ')':
+ (*info->fprintf_func) (info->stream, "%c", type);
+ break;
+
+ case 'y':
+ case 'w':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips16_reg_names[((l >> MIPS16OP_SH_RY)
+ & MIPS16OP_MASK_RY)]);
+ break;
+
+ case 'x':
+ case 'v':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips16_reg_names[((l >> MIPS16OP_SH_RX)
+ & MIPS16OP_MASK_RX)]);
+ break;
+
+ case 'z':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips16_reg_names[((l >> MIPS16OP_SH_RZ)
+ & MIPS16OP_MASK_RZ)]);
+ break;
+
+ case 'Z':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips16_reg_names[((l >> MIPS16OP_SH_MOVE32Z)
+ & MIPS16OP_MASK_MOVE32Z)]);
+ break;
+
+ case '0':
+ (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[0]);
+ break;
+
+ case 'S':
+ (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[29]);
+ break;
+
+ case 'P':
+ (*info->fprintf_func) (info->stream, "$pc");
+ break;
+
+ case 'R':
+ (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[31]);
+ break;
+
+ case 'X':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[((l >> MIPS16OP_SH_REGR32)
+ & MIPS16OP_MASK_REGR32)]);
+ break;
+
+ case 'Y':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[MIPS16OP_EXTRACT_REG32R (l)]);
+ break;
+
+ case '<':
+ case '>':
+ case '[':
+ case ']':
+ case '4':
+ case '5':
+ case 'H':
+ case 'W':
+ case 'D':
+ case 'j':
+ case '6':
+ case '8':
+ case 'V':
+ case 'C':
+ case 'U':
+ case 'k':
+ case 'K':
+ case 'p':
+ case 'q':
+ case 'A':
+ case 'B':
+ case 'E':
+ {
+ int immed, nbits, shift, signedp, extbits, pcrel, extu, branch;
+
+ shift = 0;
+ signedp = 0;
+ extbits = 16;
+ pcrel = 0;
+ extu = 0;
+ branch = 0;
+ switch (type)
+ {
+ case '<':
+ nbits = 3;
+ immed = (l >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ;
+ extbits = 5;
+ extu = 1;
+ break;
+ case '>':
+ nbits = 3;
+ immed = (l >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX;
+ extbits = 5;
+ extu = 1;
+ break;
+ case '[':
+ nbits = 3;
+ immed = (l >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ;
+ extbits = 6;
+ extu = 1;
+ break;
+ case ']':
+ nbits = 3;
+ immed = (l >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX;
+ extbits = 6;
+ extu = 1;
+ break;
+ case '4':
+ nbits = 4;
+ immed = (l >> MIPS16OP_SH_IMM4) & MIPS16OP_MASK_IMM4;
+ signedp = 1;
+ extbits = 15;
+ break;
+ case '5':
+ nbits = 5;
+ immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
+ info->insn_type = dis_dref;
+ info->data_size = 1;
+ break;
+ case 'H':
+ nbits = 5;
+ shift = 1;
+ immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
+ info->insn_type = dis_dref;
+ info->data_size = 2;
+ break;
+ case 'W':
+ nbits = 5;
+ shift = 2;
+ immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
+ if ((op->pinfo & MIPS16_INSN_READ_PC) == 0
+ && (op->pinfo & MIPS16_INSN_READ_SP) == 0)
+ {
+ info->insn_type = dis_dref;
+ info->data_size = 4;
+ }
+ break;
+ case 'D':
+ nbits = 5;
+ shift = 3;
+ immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
+ info->insn_type = dis_dref;
+ info->data_size = 8;
+ break;
+ case 'j':
+ nbits = 5;
+ immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
+ signedp = 1;
+ break;
+ case '6':
+ nbits = 6;
+ immed = (l >> MIPS16OP_SH_IMM6) & MIPS16OP_MASK_IMM6;
+ break;
+ case '8':
+ nbits = 8;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ break;
+ case 'V':
+ nbits = 8;
+ shift = 2;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ /* FIXME: This might be lw, or it might be addiu to $sp or
+ $pc. We assume it's load. */
+ info->insn_type = dis_dref;
+ info->data_size = 4;
+ break;
+ case 'C':
+ nbits = 8;
+ shift = 3;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ info->insn_type = dis_dref;
+ info->data_size = 8;
+ break;
+ case 'U':
+ nbits = 8;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ extu = 1;
+ break;
+ case 'k':
+ nbits = 8;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ signedp = 1;
+ break;
+ case 'K':
+ nbits = 8;
+ shift = 3;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ signedp = 1;
+ break;
+ case 'p':
+ nbits = 8;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ signedp = 1;
+ pcrel = 1;
+ branch = 1;
+ info->insn_type = dis_condbranch;
+ break;
+ case 'q':
+ nbits = 11;
+ immed = (l >> MIPS16OP_SH_IMM11) & MIPS16OP_MASK_IMM11;
+ signedp = 1;
+ pcrel = 1;
+ branch = 1;
+ info->insn_type = dis_branch;
+ break;
+ case 'A':
+ nbits = 8;
+ shift = 2;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ pcrel = 1;
+ /* FIXME: This can be lw or la. We assume it is lw. */
+ info->insn_type = dis_dref;
+ info->data_size = 4;
+ break;
+ case 'B':
+ nbits = 5;
+ shift = 3;
+ immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
+ pcrel = 1;
+ info->insn_type = dis_dref;
+ info->data_size = 8;
+ break;
+ case 'E':
+ nbits = 5;
+ shift = 2;
+ immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
+ pcrel = 1;
+ break;
+ default:
+ abort ();
+ }
+
+ if (! use_extend)
+ {
+ if (signedp && immed >= (1 << (nbits - 1)))
+ immed -= 1 << nbits;
+ immed <<= shift;
+ if ((type == '<' || type == '>' || type == '[' || type == ']')
+ && immed == 0)
+ immed = 8;
+ }
+ else
+ {
+ if (extbits == 16)
+ immed |= ((extend & 0x1f) << 11) | (extend & 0x7e0);
+ else if (extbits == 15)
+ immed |= ((extend & 0xf) << 11) | (extend & 0x7f0);
+ else
+ immed = ((extend >> 6) & 0x1f) | (extend & 0x20);
+ immed &= (1 << extbits) - 1;
+ if (! extu && immed >= (1 << (extbits - 1)))
+ immed -= 1 << extbits;
+ }
+
+ if (! pcrel)
+ (*info->fprintf_func) (info->stream, "%d", immed);
+ else
+ {
+ bfd_vma baseaddr;
+
+ if (branch)
+ {
+ immed *= 2;
+ baseaddr = memaddr + 2;
+ }
+ else if (use_extend)
+ baseaddr = memaddr - 2;
+ else
+ {
+ int status;
+ bfd_byte buffer[2];
+
+ baseaddr = memaddr;
+
+ /* If this instruction is in the delay slot of a jr
+ instruction, the base address is the address of the
+ jr instruction. If it is in the delay slot of jalr
+ instruction, the base address is the address of the
+ jalr instruction. This test is unreliable: we have
+ no way of knowing whether the previous word is
+ instruction or data. */
+ status = (*info->read_memory_func) (memaddr - 4, buffer, 2,
+ info);
+ if (status == 0
+ && (((info->endian == BFD_ENDIAN_BIG
+ ? bfd_getb16 (buffer)
+ : bfd_getl16 (buffer))
+ & 0xf800) == 0x1800))
+ baseaddr = memaddr - 4;
+ else
+ {
+ status = (*info->read_memory_func) (memaddr - 2, buffer,
+ 2, info);
+ if (status == 0
+ && (((info->endian == BFD_ENDIAN_BIG
+ ? bfd_getb16 (buffer)
+ : bfd_getl16 (buffer))
+ & 0xf81f) == 0xe800))
+ baseaddr = memaddr - 2;
+ }
+ }
+ info->target = (baseaddr & ~((1 << shift) - 1)) + immed;
+ (*info->print_address_func) (info->target, info);
+ }
+ }
+ break;
+
+ case 'a':
+ if (! use_extend)
+ extend = 0;
+ l = ((l & 0x1f) << 23) | ((l & 0x3e0) << 13) | (extend << 2);
+ info->target = ((memaddr + 4) & ~(bfd_vma) 0x0fffffff) | l;
+ (*info->print_address_func) (info->target, info);
+ info->insn_type = dis_jsr;
+ info->branch_delay_insns = 1;
+ break;
+
+ case 'l':
+ case 'L':
+ {
+ int need_comma, amask, smask;
+
+ need_comma = 0;
+
+ l = (l >> MIPS16OP_SH_IMM6) & MIPS16OP_MASK_IMM6;
+
+ amask = (l >> 3) & 7;
+
+ if (amask > 0 && amask < 5)
+ {
+ (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[4]);
+ if (amask > 1)
+ (*info->fprintf_func) (info->stream, "-%s",
+ mips_gpr_names[amask + 3]);
+ need_comma = 1;
+ }
+
+ smask = (l >> 1) & 3;
+ if (smask == 3)
+ {
+ (*info->fprintf_func) (info->stream, "%s??",
+ need_comma ? "," : "");
+ need_comma = 1;
+ }
+ else if (smask > 0)
+ {
+ (*info->fprintf_func) (info->stream, "%s%s",
+ need_comma ? "," : "",
+ mips_gpr_names[16]);
+ if (smask > 1)
+ (*info->fprintf_func) (info->stream, "-%s",
+ mips_gpr_names[smask + 15]);
+ need_comma = 1;
+ }
+
+ if (l & 1)
+ {
+ (*info->fprintf_func) (info->stream, "%s%s",
+ need_comma ? "," : "",
+ mips_gpr_names[31]);
+ need_comma = 1;
+ }
+
+ if (amask == 5 || amask == 6)
+ {
+ (*info->fprintf_func) (info->stream, "%s$f0",
+ need_comma ? "," : "");
+ if (amask == 6)
+ (*info->fprintf_func) (info->stream, "-$f1");
+ }
+ }
+ break;
+
+ default:
+ /* xgettext:c-format */
+ (*info->fprintf_func)
+ (info->stream,
+ _("# internal disassembler error, unrecognised modifier (%c)"),
+ type);
+ abort ();
+ }
+}
+
+void
+print_mips_disassembler_options (stream)
+ FILE *stream;
+{
+ unsigned int i;
+
+ fprintf (stream, _("\n\
+The following MIPS specific disassembler options are supported for use\n\
+with the -M switch (multiple options should be separated by commas):\n"));
+
+ fprintf (stream, _("\n\
+ gpr-names=ABI Print GPR names according to specified ABI.\n\
+ Default: based on binary being disassembled.\n"));
+
+ fprintf (stream, _("\n\
+ fpr-names=ABI Print FPR names according to specified ABI.\n\
+ Default: numeric.\n"));
+
+ fprintf (stream, _("\n\
+ cp0-names=ARCH Print CP0 register names according to\n\
+ specified architecture.\n\
+ Default: based on binary being disassembled.\n"));
+
+ fprintf (stream, _("\n\
+ hwr-names=ARCH Print HWR names according to specified \n\
+ architecture.\n\
+ Default: based on binary being disassembled.\n"));
+
+ fprintf (stream, _("\n\
+ reg-names=ABI Print GPR and FPR names according to\n\
+ specified ABI.\n"));
+
+ fprintf (stream, _("\n\
+ reg-names=ARCH Print CP0 register and HWR names according to\n\
+ specified architecture.\n"));
+
+ fprintf (stream, _("\n\
+ For the options above, the following values are supported for \"ABI\":\n\
+ "));
+ for (i = 0; i < ARRAY_SIZE (mips_abi_choices); i++)
+ fprintf (stream, " %s", mips_abi_choices[i].name);
+ fprintf (stream, _("\n"));
+
+ fprintf (stream, _("\n\
+ For the options above, The following values are supported for \"ARCH\":\n\
+ "));
+ for (i = 0; i < ARRAY_SIZE (mips_arch_choices); i++)
+ if (*mips_arch_choices[i].name != '\0')
+ fprintf (stream, " %s", mips_arch_choices[i].name);
+ fprintf (stream, _("\n"));
+
+ fprintf (stream, _("\n"));
+}
diff --git a/contrib/binutils/opcodes/mips-opc.c b/contrib/binutils/opcodes/mips-opc.c
new file mode 100644
index 0000000..9a80e53
--- /dev/null
+++ b/contrib/binutils/opcodes/mips-opc.c
@@ -0,0 +1,1218 @@
+/* mips-opc.c -- MIPS opcode list.
+ Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
+ Free Software Foundation, Inc.
+ Contributed by Ralph Campbell and OSF
+ Commented and modified by Ian Lance Taylor, Cygnus Support
+ Extended for MIPS32 support by Anders Norlander, and by SiByte, Inc.
+ MIPS-3D, MDMX, and MIPS32 Release 2 support added by Broadcom
+ Corporation (SiByte).
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+1, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+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 file; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <stdio.h>
+#include "sysdep.h"
+#include "opcode/mips.h"
+
+/* Short hand so the lines aren't too long. */
+
+#define LDD INSN_LOAD_MEMORY_DELAY
+#define LCD INSN_LOAD_COPROC_DELAY
+#define UBD INSN_UNCOND_BRANCH_DELAY
+#define CBD INSN_COND_BRANCH_DELAY
+#define COD INSN_COPROC_MOVE_DELAY
+#define CLD INSN_COPROC_MEMORY_DELAY
+#define CBL INSN_COND_BRANCH_LIKELY
+#define TRAP INSN_TRAP
+#define SM INSN_STORE_MEMORY
+
+#define WR_d INSN_WRITE_GPR_D
+#define WR_t INSN_WRITE_GPR_T
+#define WR_31 INSN_WRITE_GPR_31
+#define WR_D INSN_WRITE_FPR_D
+#define WR_T INSN_WRITE_FPR_T
+#define WR_S INSN_WRITE_FPR_S
+#define RD_s INSN_READ_GPR_S
+#define RD_b INSN_READ_GPR_S
+#define RD_t INSN_READ_GPR_T
+#define RD_S INSN_READ_FPR_S
+#define RD_T INSN_READ_FPR_T
+#define RD_R INSN_READ_FPR_R
+#define WR_CC INSN_WRITE_COND_CODE
+#define RD_CC INSN_READ_COND_CODE
+#define RD_C0 INSN_COP
+#define RD_C1 INSN_COP
+#define RD_C2 INSN_COP
+#define RD_C3 INSN_COP
+#define WR_C0 INSN_COP
+#define WR_C1 INSN_COP
+#define WR_C2 INSN_COP
+#define WR_C3 INSN_COP
+
+#define WR_HI INSN_WRITE_HI
+#define RD_HI INSN_READ_HI
+#define MOD_HI WR_HI|RD_HI
+
+#define WR_LO INSN_WRITE_LO
+#define RD_LO INSN_READ_LO
+#define MOD_LO WR_LO|RD_LO
+
+#define WR_HILO WR_HI|WR_LO
+#define RD_HILO RD_HI|RD_LO
+#define MOD_HILO WR_HILO|RD_HILO
+
+#define IS_M INSN_MULT
+
+#define WR_MACC INSN_WRITE_MDMX_ACC
+#define RD_MACC INSN_READ_MDMX_ACC
+
+#define I1 INSN_ISA1
+#define I2 INSN_ISA2
+#define I3 INSN_ISA3
+#define I4 INSN_ISA4
+#define I5 INSN_ISA5
+#define I32 INSN_ISA32
+#define I64 INSN_ISA64
+#define I33 INSN_ISA32R2
+#define I65 INSN_ISA64R2
+
+/* MIPS64 MIPS-3D ASE support. */
+#define I16 INSN_MIPS16
+
+/* MIPS64 MIPS-3D ASE support. */
+#define M3D INSN_MIPS3D
+
+/* MIPS64 MDMX ASE support. */
+#define MX INSN_MDMX
+
+#define P3 INSN_4650
+#define L1 INSN_4010
+#define V1 (INSN_4100 | INSN_4111 | INSN_4120)
+#define T3 INSN_3900
+#define M1 INSN_10000
+#define SB1 INSN_SB1
+#define N411 INSN_4111
+#define N412 INSN_4120
+#define N5 (INSN_5400 | INSN_5500)
+#define N54 INSN_5400
+#define N55 INSN_5500
+
+#define G1 (T3 \
+ )
+
+#define G2 (T3 \
+ )
+
+#define G3 (I4 \
+ )
+
+/* The order of overloaded instructions matters. Label arguments and
+ register arguments look the same. Instructions that can have either
+ for arguments must apear in the correct order in this table for the
+ assembler to pick the right one. In other words, entries with
+ immediate operands must apear after the same instruction with
+ registers.
+
+ Because of the lookup algorithm used, entries with the same opcode
+ name must be contiguous.
+
+ Many instructions are short hand for other instructions (i.e., The
+ jal <register> instruction is short for jalr <register>). */
+
+const struct mips_opcode mips_builtin_opcodes[] =
+{
+/* These instructions appear first so that the disassembler will find
+ them first. The assemblers uses a hash table based on the
+ instruction name anyhow. */
+/* name, args, match, mask, pinfo, membership */
+{"pref", "k,o(b)", 0xcc000000, 0xfc000000, RD_b, I4|I32|G3 },
+{"prefx", "h,t(b)", 0x4c00000f, 0xfc0007ff, RD_b|RD_t, I4 },
+{"nop", "", 0x00000000, 0xffffffff, 0, I1 }, /* sll */
+{"ssnop", "", 0x00000040, 0xffffffff, 0, I32|N55 }, /* sll */
+{"ehb", "", 0x000000c0, 0xffffffff, 0, I33 }, /* sll */
+{"li", "t,j", 0x24000000, 0xffe00000, WR_t, I1 }, /* addiu */
+{"li", "t,i", 0x34000000, 0xffe00000, WR_t, I1 }, /* ori */
+{"li", "t,I", 0, (int) M_LI, INSN_MACRO, I1 },
+{"move", "d,s", 0, (int) M_MOVE, INSN_MACRO, I1 },
+{"move", "d,s", 0x0000002d, 0xfc1f07ff, WR_d|RD_s, I3 },/* daddu */
+{"move", "d,s", 0x00000021, 0xfc1f07ff, WR_d|RD_s, I1 },/* addu */
+{"move", "d,s", 0x00000025, 0xfc1f07ff, WR_d|RD_s, I1 },/* or */
+{"b", "p", 0x10000000, 0xffff0000, UBD, I1 },/* beq 0,0 */
+{"b", "p", 0x04010000, 0xffff0000, UBD, I1 },/* bgez 0 */
+{"bal", "p", 0x04110000, 0xffff0000, UBD|WR_31, I1 },/* bgezal 0*/
+
+{"abs", "d,v", 0, (int) M_ABS, INSN_MACRO, I1 },
+{"abs.s", "D,V", 0x46000005, 0xffff003f, WR_D|RD_S|FP_S, I1 },
+{"abs.d", "D,V", 0x46200005, 0xffff003f, WR_D|RD_S|FP_D, I1 },
+{"abs.ps", "D,V", 0x46c00005, 0xffff003f, WR_D|RD_S|FP_D, I5 },
+{"add", "d,v,t", 0x00000020, 0xfc0007ff, WR_d|RD_s|RD_t, I1 },
+{"add", "t,r,I", 0, (int) M_ADD_I, INSN_MACRO, I1 },
+{"add.s", "D,V,T", 0x46000000, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, I1 },
+{"add.d", "D,V,T", 0x46200000, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I1 },
+{"add.ob", "X,Y,Q", 0x7800000b, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"add.ob", "D,S,T", 0x4ac0000b, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"add.ob", "D,S,T[e]", 0x4800000b, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"add.ob", "D,S,k", 0x4bc0000b, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"add.ps", "D,V,T", 0x46c00000, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 },
+{"add.qh", "X,Y,Q", 0x7820000b, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"adda.ob", "Y,Q", 0x78000037, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 },
+{"adda.qh", "Y,Q", 0x78200037, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX },
+{"addi", "t,r,j", 0x20000000, 0xfc000000, WR_t|RD_s, I1 },
+{"addiu", "t,r,j", 0x24000000, 0xfc000000, WR_t|RD_s, I1 },
+{"addl.ob", "Y,Q", 0x78000437, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 },
+{"addl.qh", "Y,Q", 0x78200437, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX },
+{"addr.ps", "D,S,T", 0x46c00018, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, M3D },
+{"addu", "d,v,t", 0x00000021, 0xfc0007ff, WR_d|RD_s|RD_t, I1 },
+{"addu", "t,r,I", 0, (int) M_ADDU_I, INSN_MACRO, I1 },
+{"alni.ob", "X,Y,Z,O", 0x78000018, 0xff00003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"alni.ob", "D,S,T,%", 0x48000018, 0xff00003f, WR_D|RD_S|RD_T, N54 },
+{"alni.qh", "X,Y,Z,O", 0x7800001a, 0xff00003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"alnv.ps", "D,V,T,s", 0x4c00001e, 0xfc00003f, WR_D|RD_S|RD_T|FP_D, I5 },
+{"alnv.ob", "X,Y,Z,s", 0x78000019, 0xfc00003f, WR_D|RD_S|RD_T|RD_s|FP_D, MX|SB1 },
+{"alnv.qh", "X,Y,Z,s", 0x7800001b, 0xfc00003f, WR_D|RD_S|RD_T|RD_s|FP_D, MX },
+{"and", "d,v,t", 0x00000024, 0xfc0007ff, WR_d|RD_s|RD_t, I1 },
+{"and", "t,r,I", 0, (int) M_AND_I, INSN_MACRO, I1 },
+{"and.ob", "X,Y,Q", 0x7800000c, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"and.ob", "D,S,T", 0x4ac0000c, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"and.ob", "D,S,T[e]", 0x4800000c, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"and.ob", "D,S,k", 0x4bc0000c, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"and.qh", "X,Y,Q", 0x7820000c, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"andi", "t,r,i", 0x30000000, 0xfc000000, WR_t|RD_s, I1 },
+/* b is at the top of the table. */
+/* bal is at the top of the table. */
+{"bc0f", "p", 0x41000000, 0xffff0000, CBD|RD_CC, I1 },
+{"bc0fl", "p", 0x41020000, 0xffff0000, CBL|RD_CC, I2|T3 },
+{"bc0t", "p", 0x41010000, 0xffff0000, CBD|RD_CC, I1 },
+{"bc0tl", "p", 0x41030000, 0xffff0000, CBL|RD_CC, I2|T3 },
+{"bc1any2f", "N,p", 0x45200000, 0xffe30000, CBD|RD_CC|FP_S, M3D },
+{"bc1any2t", "N,p", 0x45210000, 0xffe30000, CBD|RD_CC|FP_S, M3D },
+{"bc1any4f", "N,p", 0x45400000, 0xffe30000, CBD|RD_CC|FP_S, M3D },
+{"bc1any4t", "N,p", 0x45410000, 0xffe30000, CBD|RD_CC|FP_S, M3D },
+{"bc1f", "p", 0x45000000, 0xffff0000, CBD|RD_CC|FP_S, I1 },
+{"bc1f", "N,p", 0x45000000, 0xffe30000, CBD|RD_CC|FP_S, I4|I32 },
+{"bc1fl", "p", 0x45020000, 0xffff0000, CBL|RD_CC|FP_S, I2|T3 },
+{"bc1fl", "N,p", 0x45020000, 0xffe30000, CBL|RD_CC|FP_S, I4|I32 },
+{"bc1t", "p", 0x45010000, 0xffff0000, CBD|RD_CC|FP_S, I1 },
+{"bc1t", "N,p", 0x45010000, 0xffe30000, CBD|RD_CC|FP_S, I4|I32 },
+{"bc1tl", "p", 0x45030000, 0xffff0000, CBL|RD_CC|FP_S, I2|T3 },
+{"bc1tl", "N,p", 0x45030000, 0xffe30000, CBL|RD_CC|FP_S, I4|I32 },
+{"bc2f", "p", 0x49000000, 0xffff0000, CBD|RD_CC, I1 },
+{"bc2fl", "p", 0x49020000, 0xffff0000, CBL|RD_CC, I2|T3 },
+{"bc2t", "p", 0x49010000, 0xffff0000, CBD|RD_CC, I1 },
+{"bc2tl", "p", 0x49030000, 0xffff0000, CBL|RD_CC, I2|T3 },
+{"bc3f", "p", 0x4d000000, 0xffff0000, CBD|RD_CC, I1 },
+{"bc3fl", "p", 0x4d020000, 0xffff0000, CBL|RD_CC, I2|T3 },
+{"bc3t", "p", 0x4d010000, 0xffff0000, CBD|RD_CC, I1 },
+{"bc3tl", "p", 0x4d030000, 0xffff0000, CBL|RD_CC, I2|T3 },
+{"beqz", "s,p", 0x10000000, 0xfc1f0000, CBD|RD_s, I1 },
+{"beqzl", "s,p", 0x50000000, 0xfc1f0000, CBL|RD_s, I2|T3 },
+{"beq", "s,t,p", 0x10000000, 0xfc000000, CBD|RD_s|RD_t, I1 },
+{"beq", "s,I,p", 0, (int) M_BEQ_I, INSN_MACRO, I1 },
+{"beql", "s,t,p", 0x50000000, 0xfc000000, CBL|RD_s|RD_t, I2|T3 },
+{"beql", "s,I,p", 0, (int) M_BEQL_I, INSN_MACRO, I2|T3 },
+{"bge", "s,t,p", 0, (int) M_BGE, INSN_MACRO, I1 },
+{"bge", "s,I,p", 0, (int) M_BGE_I, INSN_MACRO, I1 },
+{"bgel", "s,t,p", 0, (int) M_BGEL, INSN_MACRO, I2|T3 },
+{"bgel", "s,I,p", 0, (int) M_BGEL_I, INSN_MACRO, I2|T3 },
+{"bgeu", "s,t,p", 0, (int) M_BGEU, INSN_MACRO, I1 },
+{"bgeu", "s,I,p", 0, (int) M_BGEU_I, INSN_MACRO, I1 },
+{"bgeul", "s,t,p", 0, (int) M_BGEUL, INSN_MACRO, I2|T3 },
+{"bgeul", "s,I,p", 0, (int) M_BGEUL_I, INSN_MACRO, I2|T3 },
+{"bgez", "s,p", 0x04010000, 0xfc1f0000, CBD|RD_s, I1 },
+{"bgezl", "s,p", 0x04030000, 0xfc1f0000, CBL|RD_s, I2|T3 },
+{"bgezal", "s,p", 0x04110000, 0xfc1f0000, CBD|RD_s|WR_31, I1 },
+{"bgezall", "s,p", 0x04130000, 0xfc1f0000, CBL|RD_s|WR_31, I2|T3 },
+{"bgt", "s,t,p", 0, (int) M_BGT, INSN_MACRO, I1 },
+{"bgt", "s,I,p", 0, (int) M_BGT_I, INSN_MACRO, I1 },
+{"bgtl", "s,t,p", 0, (int) M_BGTL, INSN_MACRO, I2|T3 },
+{"bgtl", "s,I,p", 0, (int) M_BGTL_I, INSN_MACRO, I2|T3 },
+{"bgtu", "s,t,p", 0, (int) M_BGTU, INSN_MACRO, I1 },
+{"bgtu", "s,I,p", 0, (int) M_BGTU_I, INSN_MACRO, I1 },
+{"bgtul", "s,t,p", 0, (int) M_BGTUL, INSN_MACRO, I2|T3 },
+{"bgtul", "s,I,p", 0, (int) M_BGTUL_I, INSN_MACRO, I2|T3 },
+{"bgtz", "s,p", 0x1c000000, 0xfc1f0000, CBD|RD_s, I1 },
+{"bgtzl", "s,p", 0x5c000000, 0xfc1f0000, CBL|RD_s, I2|T3 },
+{"ble", "s,t,p", 0, (int) M_BLE, INSN_MACRO, I1 },
+{"ble", "s,I,p", 0, (int) M_BLE_I, INSN_MACRO, I1 },
+{"blel", "s,t,p", 0, (int) M_BLEL, INSN_MACRO, I2|T3 },
+{"blel", "s,I,p", 0, (int) M_BLEL_I, INSN_MACRO, I2|T3 },
+{"bleu", "s,t,p", 0, (int) M_BLEU, INSN_MACRO, I1 },
+{"bleu", "s,I,p", 0, (int) M_BLEU_I, INSN_MACRO, I1 },
+{"bleul", "s,t,p", 0, (int) M_BLEUL, INSN_MACRO, I2|T3 },
+{"bleul", "s,I,p", 0, (int) M_BLEUL_I, INSN_MACRO, I2|T3 },
+{"blez", "s,p", 0x18000000, 0xfc1f0000, CBD|RD_s, I1 },
+{"blezl", "s,p", 0x58000000, 0xfc1f0000, CBL|RD_s, I2|T3 },
+{"blt", "s,t,p", 0, (int) M_BLT, INSN_MACRO, I1 },
+{"blt", "s,I,p", 0, (int) M_BLT_I, INSN_MACRO, I1 },
+{"bltl", "s,t,p", 0, (int) M_BLTL, INSN_MACRO, I2|T3 },
+{"bltl", "s,I,p", 0, (int) M_BLTL_I, INSN_MACRO, I2|T3 },
+{"bltu", "s,t,p", 0, (int) M_BLTU, INSN_MACRO, I1 },
+{"bltu", "s,I,p", 0, (int) M_BLTU_I, INSN_MACRO, I1 },
+{"bltul", "s,t,p", 0, (int) M_BLTUL, INSN_MACRO, I2|T3 },
+{"bltul", "s,I,p", 0, (int) M_BLTUL_I, INSN_MACRO, I2|T3 },
+{"bltz", "s,p", 0x04000000, 0xfc1f0000, CBD|RD_s, I1 },
+{"bltzl", "s,p", 0x04020000, 0xfc1f0000, CBL|RD_s, I2|T3 },
+{"bltzal", "s,p", 0x04100000, 0xfc1f0000, CBD|RD_s|WR_31, I1 },
+{"bltzall", "s,p", 0x04120000, 0xfc1f0000, CBL|RD_s|WR_31, I2|T3 },
+{"bnez", "s,p", 0x14000000, 0xfc1f0000, CBD|RD_s, I1 },
+{"bnezl", "s,p", 0x54000000, 0xfc1f0000, CBL|RD_s, I2|T3 },
+{"bne", "s,t,p", 0x14000000, 0xfc000000, CBD|RD_s|RD_t, I1 },
+{"bne", "s,I,p", 0, (int) M_BNE_I, INSN_MACRO, I1 },
+{"bnel", "s,t,p", 0x54000000, 0xfc000000, CBL|RD_s|RD_t, I2|T3 },
+{"bnel", "s,I,p", 0, (int) M_BNEL_I, INSN_MACRO, I2|T3 },
+{"break", "", 0x0000000d, 0xffffffff, TRAP, I1 },
+{"break", "B", 0x0000000d, 0xfc00003f, TRAP, I32 },
+{"break", "c", 0x0000000d, 0xfc00ffff, TRAP, I1 },
+{"break", "c,q", 0x0000000d, 0xfc00003f, TRAP, I1 },
+{"c.f.d", "S,T", 0x46200030, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.f.d", "M,S,T", 0x46200030, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.f.s", "S,T", 0x46000030, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.f.s", "M,S,T", 0x46000030, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.f.ps", "S,T", 0x46c00030, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.f.ps", "M,S,T", 0x46c00030, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.un.d", "S,T", 0x46200031, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.un.d", "M,S,T", 0x46200031, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.un.s", "S,T", 0x46000031, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.un.s", "M,S,T", 0x46000031, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.un.ps", "S,T", 0x46c00031, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.un.ps", "M,S,T", 0x46c00031, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.eq.d", "S,T", 0x46200032, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.eq.d", "M,S,T", 0x46200032, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.eq.s", "S,T", 0x46000032, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.eq.s", "M,S,T", 0x46000032, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.eq.ob", "Y,Q", 0x78000001, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, MX|SB1 },
+{"c.eq.ob", "S,T", 0x4ac00001, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"c.eq.ob", "S,T[e]", 0x48000001, 0xfe2007ff, WR_CC|RD_S|RD_T, N54 },
+{"c.eq.ob", "S,k", 0x4bc00001, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"c.eq.ps", "S,T", 0x46c00032, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.eq.ps", "M,S,T", 0x46c00032, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.eq.qh", "Y,Q", 0x78200001, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, MX },
+{"c.ueq.d", "S,T", 0x46200033, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.ueq.d", "M,S,T", 0x46200033, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.ueq.s", "S,T", 0x46000033, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.ueq.s", "M,S,T", 0x46000033, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.ueq.ps","S,T", 0x46c00033, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ueq.ps","M,S,T", 0x46c00033, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.olt.d", "S,T", 0x46200034, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.olt.d", "M,S,T", 0x46200034, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.olt.s", "S,T", 0x46000034, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.olt.s", "M,S,T", 0x46000034, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.olt.ps","S,T", 0x46c00034, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.olt.ps","M,S,T", 0x46c00034, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ult.d", "S,T", 0x46200035, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.ult.d", "M,S,T", 0x46200035, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.ult.s", "S,T", 0x46000035, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.ult.s", "M,S,T", 0x46000035, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.ult.ps","S,T", 0x46c00035, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ult.ps","M,S,T", 0x46c00035, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ole.d", "S,T", 0x46200036, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.ole.d", "M,S,T", 0x46200036, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.ole.s", "S,T", 0x46000036, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.ole.s", "M,S,T", 0x46000036, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.ole.ps","S,T", 0x46c00036, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ole.ps","M,S,T", 0x46c00036, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ule.d", "S,T", 0x46200037, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.ule.d", "M,S,T", 0x46200037, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.ule.s", "S,T", 0x46000037, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.ule.s", "M,S,T", 0x46000037, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.ule.ps","S,T", 0x46c00037, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ule.ps","M,S,T", 0x46c00037, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.sf.d", "S,T", 0x46200038, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.sf.d", "M,S,T", 0x46200038, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.sf.s", "S,T", 0x46000038, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.sf.s", "M,S,T", 0x46000038, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.sf.ps", "S,T", 0x46c00038, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.sf.ps", "M,S,T", 0x46c00038, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ngle.d","S,T", 0x46200039, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.ngle.d","M,S,T", 0x46200039, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.ngle.s","S,T", 0x46000039, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.ngle.s","M,S,T", 0x46000039, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.ngle.ps","S,T", 0x46c00039, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ngle.ps","M,S,T", 0x46c00039, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.seq.d", "S,T", 0x4620003a, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.seq.d", "M,S,T", 0x4620003a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.seq.s", "S,T", 0x4600003a, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.seq.s", "M,S,T", 0x4600003a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.seq.ps","S,T", 0x46c0003a, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.seq.ps","M,S,T", 0x46c0003a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ngl.d", "S,T", 0x4620003b, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.ngl.d", "M,S,T", 0x4620003b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.ngl.s", "S,T", 0x4600003b, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.ngl.s", "M,S,T", 0x4600003b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.ngl.ps","S,T", 0x46c0003b, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ngl.ps","M,S,T", 0x46c0003b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.lt.d", "S,T", 0x4620003c, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.lt.d", "M,S,T", 0x4620003c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.lt.s", "S,T", 0x4600003c, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.lt.s", "M,S,T", 0x4600003c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.lt.ob", "Y,Q", 0x78000004, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, MX|SB1 },
+{"c.lt.ob", "S,T", 0x4ac00004, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"c.lt.ob", "S,T[e]", 0x48000004, 0xfe2007ff, WR_CC|RD_S|RD_T, N54 },
+{"c.lt.ob", "S,k", 0x4bc00004, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"c.lt.ps", "S,T", 0x46c0003c, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.lt.ps", "M,S,T", 0x46c0003c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.lt.qh", "Y,Q", 0x78200004, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, MX },
+{"c.nge.d", "S,T", 0x4620003d, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.nge.d", "M,S,T", 0x4620003d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.nge.s", "S,T", 0x4600003d, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.nge.s", "M,S,T", 0x4600003d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.nge.ps","S,T", 0x46c0003d, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.nge.ps","M,S,T", 0x46c0003d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.le.d", "S,T", 0x4620003e, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.le.d", "M,S,T", 0x4620003e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.le.s", "S,T", 0x4600003e, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.le.s", "M,S,T", 0x4600003e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.le.ob", "Y,Q", 0x78000005, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, MX|SB1 },
+{"c.le.ob", "S,T", 0x4ac00005, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"c.le.ob", "S,T[e]", 0x48000005, 0xfe2007ff, WR_CC|RD_S|RD_T, N54 },
+{"c.le.ob", "S,k", 0x4bc00005, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"c.le.ps", "S,T", 0x46c0003e, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.le.ps", "M,S,T", 0x46c0003e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.le.qh", "Y,Q", 0x78200005, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, MX },
+{"c.ngt.d", "S,T", 0x4620003f, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I1 },
+{"c.ngt.d", "M,S,T", 0x4620003f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I4|I32 },
+{"c.ngt.s", "S,T", 0x4600003f, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, I1 },
+{"c.ngt.s", "M,S,T", 0x4600003f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, I4|I32 },
+{"c.ngt.ps","S,T", 0x46c0003f, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"c.ngt.ps","M,S,T", 0x46c0003f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, I5 },
+{"cabs.eq.d", "M,S,T", 0x46200072, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.eq.ps", "M,S,T", 0x46c00072, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.eq.s", "M,S,T", 0x46000072, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.f.d", "M,S,T", 0x46200070, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.f.ps", "M,S,T", 0x46c00070, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.f.s", "M,S,T", 0x46000070, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.le.d", "M,S,T", 0x4620007e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.le.ps", "M,S,T", 0x46c0007e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.le.s", "M,S,T", 0x4600007e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.lt.d", "M,S,T", 0x4620007c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.lt.ps", "M,S,T", 0x46c0007c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.lt.s", "M,S,T", 0x4600007c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.nge.d", "M,S,T", 0x4620007d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.nge.ps","M,S,T", 0x46c0007d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.nge.s", "M,S,T", 0x4600007d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.ngl.d", "M,S,T", 0x4620007b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ngl.ps","M,S,T", 0x46c0007b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ngl.s", "M,S,T", 0x4600007b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.ngle.d","M,S,T", 0x46200079, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ngle.ps","M,S,T",0x46c00079, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ngle.s","M,S,T", 0x46000079, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.ngt.d", "M,S,T", 0x4620007f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ngt.ps","M,S,T", 0x46c0007f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ngt.s", "M,S,T", 0x4600007f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.ole.d", "M,S,T", 0x46200076, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ole.ps","M,S,T", 0x46c00076, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ole.s", "M,S,T", 0x46000076, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.olt.d", "M,S,T", 0x46200074, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.olt.ps","M,S,T", 0x46c00074, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.olt.s", "M,S,T", 0x46000074, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.seq.d", "M,S,T", 0x4620007a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.seq.ps","M,S,T", 0x46c0007a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.seq.s", "M,S,T", 0x4600007a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.sf.d", "M,S,T", 0x46200078, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.sf.ps", "M,S,T", 0x46c00078, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.sf.s", "M,S,T", 0x46000078, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.ueq.d", "M,S,T", 0x46200073, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ueq.ps","M,S,T", 0x46c00073, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ueq.s", "M,S,T", 0x46000073, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.ule.d", "M,S,T", 0x46200077, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ule.ps","M,S,T", 0x46c00077, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ule.s", "M,S,T", 0x46000077, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.ult.d", "M,S,T", 0x46200075, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ult.ps","M,S,T", 0x46c00075, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.ult.s", "M,S,T", 0x46000075, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cabs.un.d", "M,S,T", 0x46200071, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.un.ps", "M,S,T", 0x46c00071, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, M3D },
+{"cabs.un.s", "M,S,T", 0x46000071, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, M3D },
+{"cache", "k,o(b)", 0xbc000000, 0xfc000000, RD_b, I3|I32|T3},
+{"ceil.l.d", "D,S", 0x4620000a, 0xffff003f, WR_D|RD_S|FP_D, I3 },
+{"ceil.l.s", "D,S", 0x4600000a, 0xffff003f, WR_D|RD_S|FP_S, I3 },
+{"ceil.w.d", "D,S", 0x4620000e, 0xffff003f, WR_D|RD_S|FP_D, I2 },
+{"ceil.w.s", "D,S", 0x4600000e, 0xffff003f, WR_D|RD_S|FP_S, I2 },
+{"cfc0", "t,G", 0x40400000, 0xffe007ff, LCD|WR_t|RD_C0, I1 },
+{"cfc1", "t,G", 0x44400000, 0xffe007ff, LCD|WR_t|RD_C1|FP_S, I1 },
+{"cfc1", "t,S", 0x44400000, 0xffe007ff, LCD|WR_t|RD_C1|FP_S, I1 },
+{"cfc2", "t,G", 0x48400000, 0xffe007ff, LCD|WR_t|RD_C2, I1 },
+{"cfc3", "t,G", 0x4c400000, 0xffe007ff, LCD|WR_t|RD_C3, I1 },
+{"clo", "U,s", 0x70000021, 0xfc0007ff, WR_d|WR_t|RD_s, I32|N55 },
+{"clz", "U,s", 0x70000020, 0xfc0007ff, WR_d|WR_t|RD_s, I32|N55 },
+{"ctc0", "t,G", 0x40c00000, 0xffe007ff, COD|RD_t|WR_CC, I1 },
+{"ctc1", "t,G", 0x44c00000, 0xffe007ff, COD|RD_t|WR_CC|FP_S, I1 },
+{"ctc1", "t,S", 0x44c00000, 0xffe007ff, COD|RD_t|WR_CC|FP_S, I1 },
+{"ctc2", "t,G", 0x48c00000, 0xffe007ff, COD|RD_t|WR_CC, I1 },
+{"ctc3", "t,G", 0x4cc00000, 0xffe007ff, COD|RD_t|WR_CC, I1 },
+{"cvt.d.l", "D,S", 0x46a00021, 0xffff003f, WR_D|RD_S|FP_D, I3 },
+{"cvt.d.s", "D,S", 0x46000021, 0xffff003f, WR_D|RD_S|FP_D|FP_S, I1 },
+{"cvt.d.w", "D,S", 0x46800021, 0xffff003f, WR_D|RD_S|FP_D, I1 },
+{"cvt.l.d", "D,S", 0x46200025, 0xffff003f, WR_D|RD_S|FP_D, I3 },
+{"cvt.l.s", "D,S", 0x46000025, 0xffff003f, WR_D|RD_S|FP_S, I3 },
+{"cvt.s.l", "D,S", 0x46a00020, 0xffff003f, WR_D|RD_S|FP_S, I3 },
+{"cvt.s.d", "D,S", 0x46200020, 0xffff003f, WR_D|RD_S|FP_S|FP_D, I1 },
+{"cvt.s.w", "D,S", 0x46800020, 0xffff003f, WR_D|RD_S|FP_S, I1 },
+{"cvt.s.pl","D,S", 0x46c00028, 0xffff003f, WR_D|RD_S|FP_S|FP_D, I5 },
+{"cvt.s.pu","D,S", 0x46c00020, 0xffff003f, WR_D|RD_S|FP_S|FP_D, I5 },
+{"cvt.w.d", "D,S", 0x46200024, 0xffff003f, WR_D|RD_S|FP_D, I1 },
+{"cvt.w.s", "D,S", 0x46000024, 0xffff003f, WR_D|RD_S|FP_S, I1 },
+{"cvt.ps.pw", "D,S", 0x46800026, 0xffff003f, WR_D|RD_S|FP_S|FP_D, M3D },
+{"cvt.ps.s","D,V,T", 0x46000026, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 },
+{"cvt.pw.ps", "D,S", 0x46c00024, 0xffff003f, WR_D|RD_S|FP_S|FP_D, M3D },
+{"dabs", "d,v", 0, (int) M_DABS, INSN_MACRO, I3 },
+{"dadd", "d,v,t", 0x0000002c, 0xfc0007ff, WR_d|RD_s|RD_t, I3 },
+{"dadd", "t,r,I", 0, (int) M_DADD_I, INSN_MACRO, I3 },
+{"daddi", "t,r,j", 0x60000000, 0xfc000000, WR_t|RD_s, I3 },
+{"daddiu", "t,r,j", 0x64000000, 0xfc000000, WR_t|RD_s, I3 },
+{"daddu", "d,v,t", 0x0000002d, 0xfc0007ff, WR_d|RD_s|RD_t, I3 },
+{"daddu", "t,r,I", 0, (int) M_DADDU_I, INSN_MACRO, I3 },
+{"dbreak", "", 0x7000003f, 0xffffffff, 0, N5 },
+{"dclo", "U,s", 0x70000025, 0xfc0007ff, RD_s|WR_d|WR_t, I64|N55 },
+{"dclz", "U,s", 0x70000024, 0xfc0007ff, RD_s|WR_d|WR_t, I64|N55 },
+/* dctr and dctw are used on the r5000. */
+{"dctr", "o(b)", 0xbc050000, 0xfc1f0000, RD_b, I3 },
+{"dctw", "o(b)", 0xbc090000, 0xfc1f0000, RD_b, I3 },
+{"deret", "", 0x4200001f, 0xffffffff, 0, I32|G2 },
+{"dext", "t,r,I,+I", 0, (int) M_DEXT, INSN_MACRO, I65 },
+{"dext", "t,r,+A,+C", 0x7c000003, 0xfc00003f, WR_t|RD_s, I65 },
+{"dextm", "t,r,+A,+G", 0x7c000001, 0xfc00003f, WR_t|RD_s, I65 },
+{"dextu", "t,r,+E,+H", 0x7c000002, 0xfc00003f, WR_t|RD_s, I65 },
+/* For ddiv, see the comments about div. */
+{"ddiv", "z,s,t", 0x0000001e, 0xfc00ffff, RD_s|RD_t|WR_HILO, I3 },
+{"ddiv", "d,v,t", 0, (int) M_DDIV_3, INSN_MACRO, I3 },
+{"ddiv", "d,v,I", 0, (int) M_DDIV_3I, INSN_MACRO, I3 },
+/* For ddivu, see the comments about div. */
+{"ddivu", "z,s,t", 0x0000001f, 0xfc00ffff, RD_s|RD_t|WR_HILO, I3 },
+{"ddivu", "d,v,t", 0, (int) M_DDIVU_3, INSN_MACRO, I3 },
+{"ddivu", "d,v,I", 0, (int) M_DDIVU_3I, INSN_MACRO, I3 },
+{"di", "", 0x41606000, 0xffffffff, WR_t|WR_C0, I33 },
+{"di", "t", 0x41606000, 0xffe0ffff, WR_t|WR_C0, I33 },
+{"dins", "t,r,I,+I", 0, (int) M_DINS, INSN_MACRO, I65 },
+{"dins", "t,r,+A,+B", 0x7c000007, 0xfc00003f, WR_t|RD_s, I65 },
+{"dinsm", "t,r,+A,+F", 0x7c000005, 0xfc00003f, WR_t|RD_s, I65 },
+{"dinsu", "t,r,+E,+F", 0x7c000006, 0xfc00003f, WR_t|RD_s, I65 },
+/* The MIPS assembler treats the div opcode with two operands as
+ though the first operand appeared twice (the first operand is both
+ a source and a destination). To get the div machine instruction,
+ you must use an explicit destination of $0. */
+{"div", "z,s,t", 0x0000001a, 0xfc00ffff, RD_s|RD_t|WR_HILO, I1 },
+{"div", "z,t", 0x0000001a, 0xffe0ffff, RD_s|RD_t|WR_HILO, I1 },
+{"div", "d,v,t", 0, (int) M_DIV_3, INSN_MACRO, I1 },
+{"div", "d,v,I", 0, (int) M_DIV_3I, INSN_MACRO, I1 },
+{"div.d", "D,V,T", 0x46200003, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I1 },
+{"div.s", "D,V,T", 0x46000003, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, I1 },
+{"div.ps", "D,V,T", 0x46c00003, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, SB1 },
+/* For divu, see the comments about div. */
+{"divu", "z,s,t", 0x0000001b, 0xfc00ffff, RD_s|RD_t|WR_HILO, I1 },
+{"divu", "z,t", 0x0000001b, 0xffe0ffff, RD_s|RD_t|WR_HILO, I1 },
+{"divu", "d,v,t", 0, (int) M_DIVU_3, INSN_MACRO, I1 },
+{"divu", "d,v,I", 0, (int) M_DIVU_3I, INSN_MACRO, I1 },
+{"dla", "t,A(b)", 0, (int) M_DLA_AB, INSN_MACRO, I3 },
+{"dlca", "t,A(b)", 0, (int) M_DLCA_AB, INSN_MACRO, I3 },
+{"dli", "t,j", 0x24000000, 0xffe00000, WR_t, I3 }, /* addiu */
+{"dli", "t,i", 0x34000000, 0xffe00000, WR_t, I3 }, /* ori */
+{"dli", "t,I", 0, (int) M_DLI, INSN_MACRO, I3 },
+{"dmacc", "d,s,t", 0x00000029, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 },
+{"dmacchi", "d,s,t", 0x00000229, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 },
+{"dmacchis", "d,s,t", 0x00000629, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 },
+{"dmacchiu", "d,s,t", 0x00000269, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 },
+{"dmacchius", "d,s,t", 0x00000669, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 },
+{"dmaccs", "d,s,t", 0x00000429, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 },
+{"dmaccu", "d,s,t", 0x00000069, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 },
+{"dmaccus", "d,s,t", 0x00000469, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, N412 },
+{"dmadd16", "s,t", 0x00000029, 0xfc00ffff, RD_s|RD_t|MOD_LO, N411 },
+{"dmfc0", "t,G", 0x40200000, 0xffe007ff, LCD|WR_t|RD_C0, I3 },
+{"dmfc0", "t,+D", 0x40200000, 0xffe007f8, LCD|WR_t|RD_C0, I64 },
+{"dmfc0", "t,G,H", 0x40200000, 0xffe007f8, LCD|WR_t|RD_C0, I64 },
+{"dmtc0", "t,G", 0x40a00000, 0xffe007ff, COD|RD_t|WR_C0|WR_CC, I3 },
+{"dmtc0", "t,+D", 0x40a00000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, I64 },
+{"dmtc0", "t,G,H", 0x40a00000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, I64 },
+{"dmfc1", "t,S", 0x44200000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I3 },
+{"dmfc1", "t,G", 0x44200000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I3 },
+{"dmtc1", "t,S", 0x44a00000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I3 },
+{"dmtc1", "t,G", 0x44a00000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I3 },
+{"dmfc2", "t,G", 0x48200000, 0xffe007ff, LCD|WR_t|RD_C2, I3 },
+{"dmfc2", "t,G,H", 0x48200000, 0xffe007f8, LCD|WR_t|RD_C2, I64 },
+{"dmtc2", "t,G", 0x48a00000, 0xffe007ff, COD|RD_t|WR_C2|WR_CC, I3 },
+{"dmtc2", "t,G,H", 0x48a00000, 0xffe007f8, COD|RD_t|WR_C2|WR_CC, I64 },
+{"dmfc3", "t,G", 0x4c200000, 0xffe007ff, LCD|WR_t|RD_C3, I3 },
+{"dmfc3", "t,G,H", 0x4c200000, 0xffe007f8, LCD|WR_t|RD_C3, I64 },
+{"dmtc3", "t,G", 0x4ca00000, 0xffe007ff, COD|RD_t|WR_C3|WR_CC, I3 },
+{"dmtc3", "t,G,H", 0x4ca00000, 0xffe007f8, COD|RD_t|WR_C3|WR_CC, I64 },
+{"dmul", "d,v,t", 0, (int) M_DMUL, INSN_MACRO, I3 },
+{"dmul", "d,v,I", 0, (int) M_DMUL_I, INSN_MACRO, I3 },
+{"dmulo", "d,v,t", 0, (int) M_DMULO, INSN_MACRO, I3 },
+{"dmulo", "d,v,I", 0, (int) M_DMULO_I, INSN_MACRO, I3 },
+{"dmulou", "d,v,t", 0, (int) M_DMULOU, INSN_MACRO, I3 },
+{"dmulou", "d,v,I", 0, (int) M_DMULOU_I, INSN_MACRO, I3 },
+{"dmult", "s,t", 0x0000001c, 0xfc00ffff, RD_s|RD_t|WR_HILO, I3 },
+{"dmultu", "s,t", 0x0000001d, 0xfc00ffff, RD_s|RD_t|WR_HILO, I3 },
+{"dneg", "d,w", 0x0000002e, 0xffe007ff, WR_d|RD_t, I3 }, /* dsub 0 */
+{"dnegu", "d,w", 0x0000002f, 0xffe007ff, WR_d|RD_t, I3 }, /* dsubu 0*/
+{"drem", "z,s,t", 0x0000001e, 0xfc00ffff, RD_s|RD_t|WR_HILO, I3 },
+{"drem", "d,v,t", 3, (int) M_DREM_3, INSN_MACRO, I3 },
+{"drem", "d,v,I", 3, (int) M_DREM_3I, INSN_MACRO, I3 },
+{"dremu", "z,s,t", 0x0000001f, 0xfc00ffff, RD_s|RD_t|WR_HILO, I3 },
+{"dremu", "d,v,t", 3, (int) M_DREMU_3, INSN_MACRO, I3 },
+{"dremu", "d,v,I", 3, (int) M_DREMU_3I, INSN_MACRO, I3 },
+{"dret", "", 0x7000003e, 0xffffffff, 0, N5 },
+{"drol", "d,v,t", 0, (int) M_DROL, INSN_MACRO, I3 },
+{"drol", "d,v,I", 0, (int) M_DROL_I, INSN_MACRO, I3 },
+{"dror", "d,v,t", 0, (int) M_DROR, INSN_MACRO, I3 },
+{"dror", "d,v,I", 0, (int) M_DROR_I, INSN_MACRO, I3 },
+{"dror", "d,w,<", 0x0020003a, 0xffe0003f, WR_d|RD_t, N5|I65 },
+{"drorv", "d,t,s", 0x00000056, 0xfc0007ff, RD_t|RD_s|WR_d, N5|I65 },
+{"dror32", "d,w,<", 0x0020003e, 0xffe0003f, WR_d|RD_t, N5|I65 },
+{"drotl", "d,v,t", 0, (int) M_DROL, INSN_MACRO, I65 },
+{"drotl", "d,v,I", 0, (int) M_DROL_I, INSN_MACRO, I65 },
+{"drotr", "d,v,t", 0, (int) M_DROR, INSN_MACRO, I65 },
+{"drotr", "d,v,I", 0, (int) M_DROR_I, INSN_MACRO, I65 },
+{"drotrv", "d,t,s", 0x00000056, 0xfc0007ff, RD_t|RD_s|WR_d, I65 },
+{"drotr32", "d,w,<", 0x0020003e, 0xffe0003f, WR_d|RD_t, I65 },
+{"dsbh", "d,w", 0x7c0000a4, 0xffe007ff, WR_d|RD_t, I65 },
+{"dshd", "d,w", 0x7c000164, 0xffe007ff, WR_d|RD_t, I65 },
+{"dsllv", "d,t,s", 0x00000014, 0xfc0007ff, WR_d|RD_t|RD_s, I3 },
+{"dsll32", "d,w,<", 0x0000003c, 0xffe0003f, WR_d|RD_t, I3 },
+{"dsll", "d,w,s", 0x00000014, 0xfc0007ff, WR_d|RD_t|RD_s, I3 }, /* dsllv */
+{"dsll", "d,w,>", 0x0000003c, 0xffe0003f, WR_d|RD_t, I3 }, /* dsll32 */
+{"dsll", "d,w,<", 0x00000038, 0xffe0003f, WR_d|RD_t, I3 },
+{"dsrav", "d,t,s", 0x00000017, 0xfc0007ff, WR_d|RD_t|RD_s, I3 },
+{"dsra32", "d,w,<", 0x0000003f, 0xffe0003f, WR_d|RD_t, I3 },
+{"dsra", "d,w,s", 0x00000017, 0xfc0007ff, WR_d|RD_t|RD_s, I3 }, /* dsrav */
+{"dsra", "d,w,>", 0x0000003f, 0xffe0003f, WR_d|RD_t, I3 }, /* dsra32 */
+{"dsra", "d,w,<", 0x0000003b, 0xffe0003f, WR_d|RD_t, I3 },
+{"dsrlv", "d,t,s", 0x00000016, 0xfc0007ff, WR_d|RD_t|RD_s, I3 },
+{"dsrl32", "d,w,<", 0x0000003e, 0xffe0003f, WR_d|RD_t, I3 },
+{"dsrl", "d,w,s", 0x00000016, 0xfc0007ff, WR_d|RD_t|RD_s, I3 }, /* dsrlv */
+{"dsrl", "d,w,>", 0x0000003e, 0xffe0003f, WR_d|RD_t, I3 }, /* dsrl32 */
+{"dsrl", "d,w,<", 0x0000003a, 0xffe0003f, WR_d|RD_t, I3 },
+{"dsub", "d,v,t", 0x0000002e, 0xfc0007ff, WR_d|RD_s|RD_t, I3 },
+{"dsub", "d,v,I", 0, (int) M_DSUB_I, INSN_MACRO, I3 },
+{"dsubu", "d,v,t", 0x0000002f, 0xfc0007ff, WR_d|RD_s|RD_t, I3 },
+{"dsubu", "d,v,I", 0, (int) M_DSUBU_I, INSN_MACRO, I3 },
+{"ei", "", 0x41606020, 0xffffffff, WR_t|WR_C0, I33 },
+{"ei", "t", 0x41606020, 0xffe0ffff, WR_t|WR_C0, I33 },
+{"eret", "", 0x42000018, 0xffffffff, 0, I3|I32 },
+{"ext", "t,r,+A,+C", 0x7c000000, 0xfc00003f, WR_t|RD_s, I33 },
+{"floor.l.d", "D,S", 0x4620000b, 0xffff003f, WR_D|RD_S|FP_D, I3 },
+{"floor.l.s", "D,S", 0x4600000b, 0xffff003f, WR_D|RD_S|FP_S, I3 },
+{"floor.w.d", "D,S", 0x4620000f, 0xffff003f, WR_D|RD_S|FP_D, I2 },
+{"floor.w.s", "D,S", 0x4600000f, 0xffff003f, WR_D|RD_S|FP_S, I2 },
+{"flushi", "", 0xbc010000, 0xffffffff, 0, L1 },
+{"flushd", "", 0xbc020000, 0xffffffff, 0, L1 },
+{"flushid", "", 0xbc030000, 0xffffffff, 0, L1 },
+{"hibernate","", 0x42000023, 0xffffffff, 0, V1 },
+{"ins", "t,r,+A,+B", 0x7c000004, 0xfc00003f, WR_t|RD_s, I33 },
+{"jr", "s", 0x00000008, 0xfc1fffff, UBD|RD_s, I1 },
+{"jr.hb", "s", 0x00000408, 0xfc1fffff, UBD|RD_s, I33 },
+{"j", "s", 0x00000008, 0xfc1fffff, UBD|RD_s, I1 }, /* jr */
+/* SVR4 PIC code requires special handling for j, so it must be a
+ macro. */
+{"j", "a", 0, (int) M_J_A, INSN_MACRO, I1 },
+/* This form of j is used by the disassembler and internally by the
+ assembler, but will never match user input (because the line above
+ will match first). */
+{"j", "a", 0x08000000, 0xfc000000, UBD, I1 },
+{"jalr", "s", 0x0000f809, 0xfc1fffff, UBD|RD_s|WR_d, I1 },
+{"jalr", "d,s", 0x00000009, 0xfc1f07ff, UBD|RD_s|WR_d, I1 },
+{"jalr.hb", "s", 0x0000fc09, 0xfc1fffff, UBD|RD_s|WR_d, I33 },
+{"jalr.hb", "d,s", 0x00000409, 0xfc1f07ff, UBD|RD_s|WR_d, I33 },
+/* SVR4 PIC code requires special handling for jal, so it must be a
+ macro. */
+{"jal", "d,s", 0, (int) M_JAL_2, INSN_MACRO, I1 },
+{"jal", "s", 0, (int) M_JAL_1, INSN_MACRO, I1 },
+{"jal", "a", 0, (int) M_JAL_A, INSN_MACRO, I1 },
+/* This form of jal is used by the disassembler and internally by the
+ assembler, but will never match user input (because the line above
+ will match first). */
+{"jal", "a", 0x0c000000, 0xfc000000, UBD|WR_31, I1 },
+{"jalx", "a", 0x74000000, 0xfc000000, UBD|WR_31, I16 },
+{"la", "t,A(b)", 0, (int) M_LA_AB, INSN_MACRO, I1 },
+{"lb", "t,o(b)", 0x80000000, 0xfc000000, LDD|RD_b|WR_t, I1 },
+{"lb", "t,A(b)", 0, (int) M_LB_AB, INSN_MACRO, I1 },
+{"lbu", "t,o(b)", 0x90000000, 0xfc000000, LDD|RD_b|WR_t, I1 },
+{"lbu", "t,A(b)", 0, (int) M_LBU_AB, INSN_MACRO, I1 },
+{"lca", "t,A(b)", 0, (int) M_LCA_AB, INSN_MACRO, I1 },
+{"ld", "t,o(b)", 0xdc000000, 0xfc000000, WR_t|RD_b, I3 },
+{"ld", "t,o(b)", 0, (int) M_LD_OB, INSN_MACRO, I1 },
+{"ld", "t,A(b)", 0, (int) M_LD_AB, INSN_MACRO, I1 },
+{"ldc1", "T,o(b)", 0xd4000000, 0xfc000000, CLD|RD_b|WR_T|FP_D, I2 },
+{"ldc1", "E,o(b)", 0xd4000000, 0xfc000000, CLD|RD_b|WR_T|FP_D, I2 },
+{"ldc1", "T,A(b)", 0, (int) M_LDC1_AB, INSN_MACRO, I2 },
+{"ldc1", "E,A(b)", 0, (int) M_LDC1_AB, INSN_MACRO, I2 },
+{"l.d", "T,o(b)", 0xd4000000, 0xfc000000, CLD|RD_b|WR_T|FP_D, I2 }, /* ldc1 */
+{"l.d", "T,o(b)", 0, (int) M_L_DOB, INSN_MACRO, I1 },
+{"l.d", "T,A(b)", 0, (int) M_L_DAB, INSN_MACRO, I1 },
+{"ldc2", "E,o(b)", 0xd8000000, 0xfc000000, CLD|RD_b|WR_CC, I2 },
+{"ldc2", "E,A(b)", 0, (int) M_LDC2_AB, INSN_MACRO, I2 },
+{"ldc3", "E,o(b)", 0xdc000000, 0xfc000000, CLD|RD_b|WR_CC, I2 },
+{"ldc3", "E,A(b)", 0, (int) M_LDC3_AB, INSN_MACRO, I2 },
+{"ldl", "t,o(b)", 0x68000000, 0xfc000000, LDD|WR_t|RD_b, I3 },
+{"ldl", "t,A(b)", 0, (int) M_LDL_AB, INSN_MACRO, I3 },
+{"ldr", "t,o(b)", 0x6c000000, 0xfc000000, LDD|WR_t|RD_b, I3 },
+{"ldr", "t,A(b)", 0, (int) M_LDR_AB, INSN_MACRO, I3 },
+{"ldxc1", "D,t(b)", 0x4c000001, 0xfc00f83f, LDD|WR_D|RD_t|RD_b, I4 },
+{"lh", "t,o(b)", 0x84000000, 0xfc000000, LDD|RD_b|WR_t, I1 },
+{"lh", "t,A(b)", 0, (int) M_LH_AB, INSN_MACRO, I1 },
+{"lhu", "t,o(b)", 0x94000000, 0xfc000000, LDD|RD_b|WR_t, I1 },
+{"lhu", "t,A(b)", 0, (int) M_LHU_AB, INSN_MACRO, I1 },
+/* li is at the start of the table. */
+{"li.d", "t,F", 0, (int) M_LI_D, INSN_MACRO, I1 },
+{"li.d", "T,L", 0, (int) M_LI_DD, INSN_MACRO, I1 },
+{"li.s", "t,f", 0, (int) M_LI_S, INSN_MACRO, I1 },
+{"li.s", "T,l", 0, (int) M_LI_SS, INSN_MACRO, I1 },
+{"ll", "t,o(b)", 0xc0000000, 0xfc000000, LDD|RD_b|WR_t, I2 },
+{"ll", "t,A(b)", 0, (int) M_LL_AB, INSN_MACRO, I2 },
+{"lld", "t,o(b)", 0xd0000000, 0xfc000000, LDD|RD_b|WR_t, I3 },
+{"lld", "t,A(b)", 0, (int) M_LLD_AB, INSN_MACRO, I3 },
+{"lui", "t,u", 0x3c000000, 0xffe00000, WR_t, I1 },
+{"luxc1", "D,t(b)", 0x4c000005, 0xfc00f83f, LDD|WR_D|RD_t|RD_b, I5|N55 },
+{"lw", "t,o(b)", 0x8c000000, 0xfc000000, LDD|RD_b|WR_t, I1 },
+{"lw", "t,A(b)", 0, (int) M_LW_AB, INSN_MACRO, I1 },
+{"lwc0", "E,o(b)", 0xc0000000, 0xfc000000, CLD|RD_b|WR_CC, I1 },
+{"lwc0", "E,A(b)", 0, (int) M_LWC0_AB, INSN_MACRO, I1 },
+{"lwc1", "T,o(b)", 0xc4000000, 0xfc000000, CLD|RD_b|WR_T|FP_S, I1 },
+{"lwc1", "E,o(b)", 0xc4000000, 0xfc000000, CLD|RD_b|WR_T|FP_S, I1 },
+{"lwc1", "T,A(b)", 0, (int) M_LWC1_AB, INSN_MACRO, I1 },
+{"lwc1", "E,A(b)", 0, (int) M_LWC1_AB, INSN_MACRO, I1 },
+{"l.s", "T,o(b)", 0xc4000000, 0xfc000000, CLD|RD_b|WR_T|FP_S, I1 }, /* lwc1 */
+{"l.s", "T,A(b)", 0, (int) M_LWC1_AB, INSN_MACRO, I1 },
+{"lwc2", "E,o(b)", 0xc8000000, 0xfc000000, CLD|RD_b|WR_CC, I1 },
+{"lwc2", "E,A(b)", 0, (int) M_LWC2_AB, INSN_MACRO, I1 },
+{"lwc3", "E,o(b)", 0xcc000000, 0xfc000000, CLD|RD_b|WR_CC, I1 },
+{"lwc3", "E,A(b)", 0, (int) M_LWC3_AB, INSN_MACRO, I1 },
+{"lwl", "t,o(b)", 0x88000000, 0xfc000000, LDD|RD_b|WR_t, I1 },
+{"lwl", "t,A(b)", 0, (int) M_LWL_AB, INSN_MACRO, I1 },
+{"lcache", "t,o(b)", 0x88000000, 0xfc000000, LDD|RD_b|WR_t, I2 }, /* same */
+{"lcache", "t,A(b)", 0, (int) M_LWL_AB, INSN_MACRO, I2 }, /* as lwl */
+{"lwr", "t,o(b)", 0x98000000, 0xfc000000, LDD|RD_b|WR_t, I1 },
+{"lwr", "t,A(b)", 0, (int) M_LWR_AB, INSN_MACRO, I1 },
+{"flush", "t,o(b)", 0x98000000, 0xfc000000, LDD|RD_b|WR_t, I2 }, /* same */
+{"flush", "t,A(b)", 0, (int) M_LWR_AB, INSN_MACRO, I2 }, /* as lwr */
+{"lwu", "t,o(b)", 0x9c000000, 0xfc000000, LDD|RD_b|WR_t, I3 },
+{"lwu", "t,A(b)", 0, (int) M_LWU_AB, INSN_MACRO, I3 },
+{"lwxc1", "D,t(b)", 0x4c000000, 0xfc00f83f, LDD|WR_D|RD_t|RD_b, I4 },
+{"macc", "d,s,t", 0x00000028, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 },
+{"macc", "d,s,t", 0x00000158, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"maccs", "d,s,t", 0x00000428, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 },
+{"macchi", "d,s,t", 0x00000228, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 },
+{"macchi", "d,s,t", 0x00000358, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"macchis", "d,s,t", 0x00000628, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 },
+{"macchiu", "d,s,t", 0x00000268, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 },
+{"macchiu", "d,s,t", 0x00000359, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"macchius","d,s,t", 0x00000668, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 },
+{"maccu", "d,s,t", 0x00000068, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 },
+{"maccu", "d,s,t", 0x00000159, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"maccus", "d,s,t", 0x00000468, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N412 },
+{"mad", "s,t", 0x70000000, 0xfc00ffff, RD_s|RD_t|MOD_HILO, P3 },
+{"madu", "s,t", 0x70000001, 0xfc00ffff, RD_s|RD_t|MOD_HILO, P3 },
+{"madd.d", "D,R,S,T", 0x4c000021, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I4 },
+{"madd.s", "D,R,S,T", 0x4c000020, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, I4 },
+{"madd.ps", "D,R,S,T", 0x4c000026, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I5 },
+{"madd", "s,t", 0x0000001c, 0xfc00ffff, RD_s|RD_t|WR_HILO, L1 },
+{"madd", "s,t", 0x70000000, 0xfc00ffff, RD_s|RD_t|MOD_HILO, I32|N55},
+{"madd", "s,t", 0x70000000, 0xfc00ffff, RD_s|RD_t|WR_HILO|IS_M, G1 },
+{"madd", "d,s,t", 0x70000000, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d|IS_M, G1 },
+{"maddu", "s,t", 0x0000001d, 0xfc00ffff, RD_s|RD_t|WR_HILO, L1 },
+{"maddu", "s,t", 0x70000001, 0xfc00ffff, RD_s|RD_t|MOD_HILO, I32|N55},
+{"maddu", "s,t", 0x70000001, 0xfc00ffff, RD_s|RD_t|WR_HILO|IS_M, G1 },
+{"maddu", "d,s,t", 0x70000001, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d|IS_M, G1 },
+{"madd16", "s,t", 0x00000028, 0xfc00ffff, RD_s|RD_t|MOD_HILO, N411 },
+{"max.ob", "X,Y,Q", 0x78000007, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"max.ob", "D,S,T", 0x4ac00007, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"max.ob", "D,S,T[e]", 0x48000007, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"max.ob", "D,S,k", 0x4bc00007, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"max.qh", "X,Y,Q", 0x78200007, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"mfpc", "t,P", 0x4000c801, 0xffe0ffc1, LCD|WR_t|RD_C0, M1|N5 },
+{"mfps", "t,P", 0x4000c800, 0xffe0ffc1, LCD|WR_t|RD_C0, M1|N5 },
+{"mfc0", "t,G", 0x40000000, 0xffe007ff, LCD|WR_t|RD_C0, I1 },
+{"mfc0", "t,+D", 0x40000000, 0xffe007f8, LCD|WR_t|RD_C0, I32 },
+{"mfc0", "t,G,H", 0x40000000, 0xffe007f8, LCD|WR_t|RD_C0, I32 },
+{"mfc1", "t,S", 0x44000000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I1 },
+{"mfc1", "t,G", 0x44000000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I1 },
+{"mfhc1", "t,S", 0x44600000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I33 },
+{"mfhc1", "t,G", 0x44600000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, I33 },
+{"mfc2", "t,G", 0x48000000, 0xffe007ff, LCD|WR_t|RD_C2, I1 },
+{"mfc2", "t,G,H", 0x48000000, 0xffe007f8, LCD|WR_t|RD_C2, I32 },
+{"mfhc2", "t,i", 0x48600000, 0xffe00000, LCD|WR_t|RD_C2, I33 },
+{"mfc3", "t,G", 0x4c000000, 0xffe007ff, LCD|WR_t|RD_C3, I1 },
+{"mfc3", "t,G,H", 0x4c000000, 0xffe007f8, LCD|WR_t|RD_C3, I32 },
+{"mfdr", "t,G", 0x7000003d, 0xffe007ff, LCD|WR_t|RD_C0, N5 },
+{"mfhi", "d", 0x00000010, 0xffff07ff, WR_d|RD_HI, I1 },
+{"mflo", "d", 0x00000012, 0xffff07ff, WR_d|RD_LO, I1 },
+{"min.ob", "X,Y,Q", 0x78000006, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"min.ob", "D,S,T", 0x4ac00006, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"min.ob", "D,S,T[e]", 0x48000006, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"min.ob", "D,S,k", 0x4bc00006, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"min.qh", "X,Y,Q", 0x78200006, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"mov.d", "D,S", 0x46200006, 0xffff003f, WR_D|RD_S|FP_D, I1 },
+{"mov.s", "D,S", 0x46000006, 0xffff003f, WR_D|RD_S|FP_S, I1 },
+{"mov.ps", "D,S", 0x46c00006, 0xffff003f, WR_D|RD_S|FP_D, I5 },
+{"movf", "d,s,N", 0x00000001, 0xfc0307ff, WR_d|RD_s|RD_CC|FP_D|FP_S, I4|I32},
+{"movf.d", "D,S,N", 0x46200011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, I4|I32 },
+{"movf.l", "D,S,N", 0x46a00011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, MX|SB1 },
+{"movf.l", "X,Y,N", 0x46a00011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, MX|SB1 },
+{"movf.s", "D,S,N", 0x46000011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_S, I4|I32 },
+{"movf.ps", "D,S,N", 0x46c00011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, I5 },
+{"movn", "d,v,t", 0x0000000b, 0xfc0007ff, WR_d|RD_s|RD_t, I4|I32 },
+{"ffc", "d,v", 0x0000000b, 0xfc1f07ff, WR_d|RD_s, L1 },
+{"movn.d", "D,S,t", 0x46200013, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, I4|I32 },
+{"movn.l", "D,S,t", 0x46a00013, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, MX|SB1 },
+{"movn.l", "X,Y,t", 0x46a00013, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, MX|SB1 },
+{"movn.s", "D,S,t", 0x46000013, 0xffe0003f, WR_D|RD_S|RD_t|FP_S, I4|I32 },
+{"movn.ps", "D,S,t", 0x46c00013, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, I5 },
+{"movt", "d,s,N", 0x00010001, 0xfc0307ff, WR_d|RD_s|RD_CC, I4|I32 },
+{"movt.d", "D,S,N", 0x46210011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, I4|I32 },
+{"movt.l", "D,S,N", 0x46a10011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, MX|SB1 },
+{"movt.l", "X,Y,N", 0x46a10011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, MX|SB1 },
+{"movt.s", "D,S,N", 0x46010011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_S, I4|I32 },
+{"movt.ps", "D,S,N", 0x46c10011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, I5 },
+{"movz", "d,v,t", 0x0000000a, 0xfc0007ff, WR_d|RD_s|RD_t, I4|I32 },
+{"ffs", "d,v", 0x0000000a, 0xfc1f07ff, WR_d|RD_s, L1 },
+{"movz.d", "D,S,t", 0x46200012, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, I4|I32 },
+{"movz.l", "D,S,t", 0x46a00012, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, MX|SB1 },
+{"movz.l", "X,Y,t", 0x46a00012, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, MX|SB1 },
+{"movz.s", "D,S,t", 0x46000012, 0xffe0003f, WR_D|RD_S|RD_t|FP_S, I4|I32 },
+{"movz.ps", "D,S,t", 0x46c00012, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, I5 },
+{"msac", "d,s,t", 0x000001d8, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"msacu", "d,s,t", 0x000001d9, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"msachi", "d,s,t", 0x000003d8, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"msachiu", "d,s,t", 0x000003d9, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+/* move is at the top of the table. */
+{"msgn.qh", "X,Y,Q", 0x78200000, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"msub.d", "D,R,S,T", 0x4c000029, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I4 },
+{"msub.s", "D,R,S,T", 0x4c000028, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, I4 },
+{"msub.ps", "D,R,S,T", 0x4c00002e, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I5 },
+{"msub", "s,t", 0x0000001e, 0xfc00ffff, RD_s|RD_t|WR_HILO, L1 },
+{"msub", "s,t", 0x70000004, 0xfc00ffff, RD_s|RD_t|MOD_HILO, I32|N55 },
+{"msubu", "s,t", 0x0000001f, 0xfc00ffff, RD_s|RD_t|WR_HILO, L1 },
+{"msubu", "s,t", 0x70000005, 0xfc00ffff, RD_s|RD_t|MOD_HILO, I32|N55 },
+{"mtpc", "t,P", 0x4080c801, 0xffe0ffc1, COD|RD_t|WR_C0, M1|N5 },
+{"mtps", "t,P", 0x4080c800, 0xffe0ffc1, COD|RD_t|WR_C0, M1|N5 },
+{"mtc0", "t,G", 0x40800000, 0xffe007ff, COD|RD_t|WR_C0|WR_CC, I1 },
+{"mtc0", "t,+D", 0x40800000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, I32 },
+{"mtc0", "t,G,H", 0x40800000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, I32 },
+{"mtc1", "t,S", 0x44800000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I1 },
+{"mtc1", "t,G", 0x44800000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I1 },
+{"mthc1", "t,S", 0x44e00000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I33 },
+{"mthc1", "t,G", 0x44e00000, 0xffe007ff, COD|RD_t|WR_S|FP_S, I33 },
+{"mtc2", "t,G", 0x48800000, 0xffe007ff, COD|RD_t|WR_C2|WR_CC, I1 },
+{"mtc2", "t,G,H", 0x48800000, 0xffe007f8, COD|RD_t|WR_C2|WR_CC, I32 },
+{"mthc2", "t,i", 0x48e00000, 0xffe00000, COD|RD_t|WR_C2|WR_CC, I33 },
+{"mtc3", "t,G", 0x4c800000, 0xffe007ff, COD|RD_t|WR_C3|WR_CC, I1 },
+{"mtc3", "t,G,H", 0x4c800000, 0xffe007f8, COD|RD_t|WR_C3|WR_CC, I32 },
+{"mtdr", "t,G", 0x7080003d, 0xffe007ff, COD|RD_t|WR_C0, N5 },
+{"mthi", "s", 0x00000011, 0xfc1fffff, RD_s|WR_HI, I1 },
+{"mtlo", "s", 0x00000013, 0xfc1fffff, RD_s|WR_LO, I1 },
+{"mul.d", "D,V,T", 0x46200002, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I1 },
+{"mul.s", "D,V,T", 0x46000002, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, I1 },
+{"mul.ob", "X,Y,Q", 0x78000030, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"mul.ob", "D,S,T", 0x4ac00030, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"mul.ob", "D,S,T[e]", 0x48000030, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"mul.ob", "D,S,k", 0x4bc00030, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"mul.ps", "D,V,T", 0x46c00002, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 },
+{"mul.qh", "X,Y,Q", 0x78200030, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"mul", "d,v,t", 0x70000002, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, I32|P3|N55},
+{"mul", "d,s,t", 0x00000058, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N54 },
+{"mul", "d,v,t", 0, (int) M_MUL, INSN_MACRO, I1 },
+{"mul", "d,v,I", 0, (int) M_MUL_I, INSN_MACRO, I1 },
+{"mula.ob", "Y,Q", 0x78000033, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 },
+{"mula.ob", "S,T", 0x4ac00033, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"mula.ob", "S,T[e]", 0x48000033, 0xfe2007ff, WR_CC|RD_S|RD_T, N54 },
+{"mula.ob", "S,k", 0x4bc00033, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"mula.qh", "Y,Q", 0x78200033, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX },
+{"mulhi", "d,s,t", 0x00000258, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"mulhiu", "d,s,t", 0x00000259, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"mull.ob", "Y,Q", 0x78000433, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 },
+{"mull.ob", "S,T", 0x4ac00433, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"mull.ob", "S,T[e]", 0x48000433, 0xfe2007ff, WR_CC|RD_S|RD_T, N54 },
+{"mull.ob", "S,k", 0x4bc00433, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"mull.qh", "Y,Q", 0x78200433, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX },
+{"mulo", "d,v,t", 0, (int) M_MULO, INSN_MACRO, I1 },
+{"mulo", "d,v,I", 0, (int) M_MULO_I, INSN_MACRO, I1 },
+{"mulou", "d,v,t", 0, (int) M_MULOU, INSN_MACRO, I1 },
+{"mulou", "d,v,I", 0, (int) M_MULOU_I, INSN_MACRO, I1 },
+{"mulr.ps", "D,S,T", 0x46c0001a, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, M3D },
+{"muls", "d,s,t", 0x000000d8, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"mulsu", "d,s,t", 0x000000d9, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"mulshi", "d,s,t", 0x000002d8, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"mulshiu", "d,s,t", 0x000002d9, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"muls.ob", "Y,Q", 0x78000032, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 },
+{"muls.ob", "S,T", 0x4ac00032, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"muls.ob", "S,T[e]", 0x48000032, 0xfe2007ff, WR_CC|RD_S|RD_T, N54 },
+{"muls.ob", "S,k", 0x4bc00032, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"muls.qh", "Y,Q", 0x78200032, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX },
+{"mulsl.ob", "Y,Q", 0x78000432, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 },
+{"mulsl.ob", "S,T", 0x4ac00432, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"mulsl.ob", "S,T[e]", 0x48000432, 0xfe2007ff, WR_CC|RD_S|RD_T, N54 },
+{"mulsl.ob", "S,k", 0x4bc00432, 0xffe007ff, WR_CC|RD_S|RD_T, N54 },
+{"mulsl.qh", "Y,Q", 0x78200432, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX },
+{"mult", "s,t", 0x00000018, 0xfc00ffff, RD_s|RD_t|WR_HILO|IS_M, I1 },
+{"mult", "d,s,t", 0x00000018, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d|IS_M, G1 },
+{"multu", "s,t", 0x00000019, 0xfc00ffff, RD_s|RD_t|WR_HILO|IS_M, I1 },
+{"multu", "d,s,t", 0x00000019, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d|IS_M, G1 },
+{"mulu", "d,s,t", 0x00000059, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, N5 },
+{"neg", "d,w", 0x00000022, 0xffe007ff, WR_d|RD_t, I1 }, /* sub 0 */
+{"negu", "d,w", 0x00000023, 0xffe007ff, WR_d|RD_t, I1 }, /* subu 0 */
+{"neg.d", "D,V", 0x46200007, 0xffff003f, WR_D|RD_S|FP_D, I1 },
+{"neg.s", "D,V", 0x46000007, 0xffff003f, WR_D|RD_S|FP_S, I1 },
+{"neg.ps", "D,V", 0x46c00007, 0xffff003f, WR_D|RD_S|FP_D, I5 },
+{"nmadd.d", "D,R,S,T", 0x4c000031, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I4 },
+{"nmadd.s", "D,R,S,T", 0x4c000030, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, I4 },
+{"nmadd.ps","D,R,S,T", 0x4c000036, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I5 },
+{"nmsub.d", "D,R,S,T", 0x4c000039, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I4 },
+{"nmsub.s", "D,R,S,T", 0x4c000038, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, I4 },
+{"nmsub.ps","D,R,S,T", 0x4c00003e, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, I5 },
+/* nop is at the start of the table. */
+{"nor", "d,v,t", 0x00000027, 0xfc0007ff, WR_d|RD_s|RD_t, I1 },
+{"nor", "t,r,I", 0, (int) M_NOR_I, INSN_MACRO, I1 },
+{"nor.ob", "X,Y,Q", 0x7800000f, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"nor.ob", "D,S,T", 0x4ac0000f, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"nor.ob", "D,S,T[e]", 0x4800000f, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"nor.ob", "D,S,k", 0x4bc0000f, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"nor.qh", "X,Y,Q", 0x7820000f, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"not", "d,v", 0x00000027, 0xfc1f07ff, WR_d|RD_s|RD_t, I1 },/*nor d,s,0*/
+{"or", "d,v,t", 0x00000025, 0xfc0007ff, WR_d|RD_s|RD_t, I1 },
+{"or", "t,r,I", 0, (int) M_OR_I, INSN_MACRO, I1 },
+{"or.ob", "X,Y,Q", 0x7800000e, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"or.ob", "D,S,T", 0x4ac0000e, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"or.ob", "D,S,T[e]", 0x4800000e, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"or.ob", "D,S,k", 0x4bc0000e, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"or.qh", "X,Y,Q", 0x7820000e, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"ori", "t,r,i", 0x34000000, 0xfc000000, WR_t|RD_s, I1 },
+{"pabsdiff.ob", "X,Y,Q",0x78000009, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, SB1 },
+{"pabsdiffc.ob", "Y,Q", 0x78000035, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, SB1 },
+{"pavg.ob", "X,Y,Q", 0x78000008, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, SB1 },
+{"pickf.ob", "X,Y,Q", 0x78000002, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"pickf.ob", "D,S,T", 0x4ac00002, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"pickf.ob", "D,S,T[e]",0x48000002, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"pickf.ob", "D,S,k", 0x4bc00002, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"pickf.qh", "X,Y,Q", 0x78200002, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"pickt.ob", "X,Y,Q", 0x78000003, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"pickt.ob", "D,S,T", 0x4ac00003, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"pickt.ob", "D,S,T[e]",0x48000003, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"pickt.ob", "D,S,k", 0x4bc00003, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"pickt.qh", "X,Y,Q", 0x78200003, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"pll.ps", "D,V,T", 0x46c0002c, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 },
+{"plu.ps", "D,V,T", 0x46c0002d, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 },
+ /* pref and prefx are at the start of the table. */
+{"pul.ps", "D,V,T", 0x46c0002e, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 },
+{"puu.ps", "D,V,T", 0x46c0002f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 },
+{"rach.ob", "X", 0x7a00003f, 0xfffff83f, WR_D|RD_MACC|FP_D, MX|SB1 },
+{"rach.ob", "D", 0x4a00003f, 0xfffff83f, WR_D, N54 },
+{"rach.qh", "X", 0x7a20003f, 0xfffff83f, WR_D|RD_MACC|FP_D, MX },
+{"racl.ob", "X", 0x7800003f, 0xfffff83f, WR_D|RD_MACC|FP_D, MX|SB1 },
+{"racl.ob", "D", 0x4800003f, 0xfffff83f, WR_D, N54 },
+{"racl.qh", "X", 0x7820003f, 0xfffff83f, WR_D|RD_MACC|FP_D, MX },
+{"racm.ob", "X", 0x7900003f, 0xfffff83f, WR_D|RD_MACC|FP_D, MX|SB1 },
+{"racm.ob", "D", 0x4900003f, 0xfffff83f, WR_D, N54 },
+{"racm.qh", "X", 0x7920003f, 0xfffff83f, WR_D|RD_MACC|FP_D, MX },
+{"recip.d", "D,S", 0x46200015, 0xffff003f, WR_D|RD_S|FP_D, I4 },
+{"recip.ps","D,S", 0x46c00015, 0xffff003f, WR_D|RD_S|FP_D, SB1 },
+{"recip.s", "D,S", 0x46000015, 0xffff003f, WR_D|RD_S|FP_S, I4 },
+{"recip1.d", "D,S", 0x4620001d, 0xffff003f, WR_D|RD_S|FP_D, M3D },
+{"recip1.ps", "D,S", 0x46c0001d, 0xffff003f, WR_D|RD_S|FP_S, M3D },
+{"recip1.s", "D,S", 0x4600001d, 0xffff003f, WR_D|RD_S|FP_S, M3D },
+{"recip2.d", "D,S,T", 0x4620001c, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, M3D },
+{"recip2.ps", "D,S,T", 0x46c0001c, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, M3D },
+{"recip2.s", "D,S,T", 0x4600001c, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, M3D },
+{"rem", "z,s,t", 0x0000001a, 0xfc00ffff, RD_s|RD_t|WR_HILO, I1 },
+{"rem", "d,v,t", 0, (int) M_REM_3, INSN_MACRO, I1 },
+{"rem", "d,v,I", 0, (int) M_REM_3I, INSN_MACRO, I1 },
+{"remu", "z,s,t", 0x0000001b, 0xfc00ffff, RD_s|RD_t|WR_HILO, I1 },
+{"remu", "d,v,t", 0, (int) M_REMU_3, INSN_MACRO, I1 },
+{"remu", "d,v,I", 0, (int) M_REMU_3I, INSN_MACRO, I1 },
+{"rdhwr", "t,K", 0x7c00003b, 0xffe007ff, WR_t, I33 },
+{"rdpgpr", "d,w", 0x41400000, 0xffe007ff, WR_d, I33 },
+{"rfe", "", 0x42000010, 0xffffffff, 0, I1|T3 },
+{"rnas.qh", "X,Q", 0x78200025, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX },
+{"rnau.ob", "X,Q", 0x78000021, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX|SB1 },
+{"rnau.qh", "X,Q", 0x78200021, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX },
+{"rnes.qh", "X,Q", 0x78200026, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX },
+{"rneu.ob", "X,Q", 0x78000022, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX|SB1 },
+{"rneu.qh", "X,Q", 0x78200022, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX },
+{"rol", "d,v,t", 0, (int) M_ROL, INSN_MACRO, I1 },
+{"rol", "d,v,I", 0, (int) M_ROL_I, INSN_MACRO, I1 },
+{"ror", "d,v,t", 0, (int) M_ROR, INSN_MACRO, I1 },
+{"ror", "d,v,I", 0, (int) M_ROR_I, INSN_MACRO, I1 },
+{"ror", "d,w,<", 0x00200002, 0xffe0003f, WR_d|RD_t, N5|I33 },
+{"rorv", "d,t,s", 0x00000046, 0xfc0007ff, RD_t|RD_s|WR_d, N5|I33 },
+{"rotl", "d,v,t", 0, (int) M_ROL, INSN_MACRO, I33 },
+{"rotl", "d,v,I", 0, (int) M_ROL_I, INSN_MACRO, I33 },
+{"rotr", "d,v,t", 0, (int) M_ROR, INSN_MACRO, I33 },
+{"rotr", "d,v,I", 0, (int) M_ROR_I, INSN_MACRO, I33 },
+{"rotrv", "d,t,s", 0x00000046, 0xfc0007ff, RD_t|RD_s|WR_d, I33 },
+{"round.l.d", "D,S", 0x46200008, 0xffff003f, WR_D|RD_S|FP_D, I3 },
+{"round.l.s", "D,S", 0x46000008, 0xffff003f, WR_D|RD_S|FP_S, I3 },
+{"round.w.d", "D,S", 0x4620000c, 0xffff003f, WR_D|RD_S|FP_D, I2 },
+{"round.w.s", "D,S", 0x4600000c, 0xffff003f, WR_D|RD_S|FP_S, I2 },
+{"rsqrt.d", "D,S", 0x46200016, 0xffff003f, WR_D|RD_S|FP_D, I4 },
+{"rsqrt.ps","D,S", 0x46c00016, 0xffff003f, WR_D|RD_S|FP_D, SB1 },
+{"rsqrt.s", "D,S", 0x46000016, 0xffff003f, WR_D|RD_S|FP_S, I4 },
+{"rsqrt1.d", "D,S", 0x4620001e, 0xffff003f, WR_D|RD_S|FP_D, M3D },
+{"rsqrt1.ps", "D,S", 0x46c0001e, 0xffff003f, WR_D|RD_S|FP_S, M3D },
+{"rsqrt1.s", "D,S", 0x4600001e, 0xffff003f, WR_D|RD_S|FP_S, M3D },
+{"rsqrt2.d", "D,S,T", 0x4620001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, M3D },
+{"rsqrt2.ps", "D,S,T", 0x46c0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, M3D },
+{"rsqrt2.s", "D,S,T", 0x4600001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, M3D },
+{"rzs.qh", "X,Q", 0x78200024, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX },
+{"rzu.ob", "X,Q", 0x78000020, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX|SB1 },
+{"rzu.ob", "D,k", 0x4bc00020, 0xffe0f83f, WR_D|RD_S|RD_T, N54 },
+{"rzu.qh", "X,Q", 0x78200020, 0xfc20f83f, WR_D|RD_MACC|RD_T|FP_D, MX },
+{"sb", "t,o(b)", 0xa0000000, 0xfc000000, SM|RD_t|RD_b, I1 },
+{"sb", "t,A(b)", 0, (int) M_SB_AB, INSN_MACRO, I1 },
+{"sc", "t,o(b)", 0xe0000000, 0xfc000000, SM|RD_t|WR_t|RD_b, I2 },
+{"sc", "t,A(b)", 0, (int) M_SC_AB, INSN_MACRO, I2 },
+{"scd", "t,o(b)", 0xf0000000, 0xfc000000, SM|RD_t|WR_t|RD_b, I3 },
+{"scd", "t,A(b)", 0, (int) M_SCD_AB, INSN_MACRO, I3 },
+{"sd", "t,o(b)", 0xfc000000, 0xfc000000, SM|RD_t|RD_b, I3 },
+{"sd", "t,o(b)", 0, (int) M_SD_OB, INSN_MACRO, I1 },
+{"sd", "t,A(b)", 0, (int) M_SD_AB, INSN_MACRO, I1 },
+{"sdbbp", "", 0x0000000e, 0xffffffff, TRAP, G2 },
+{"sdbbp", "c", 0x0000000e, 0xfc00ffff, TRAP, G2 },
+{"sdbbp", "c,q", 0x0000000e, 0xfc00003f, TRAP, G2 },
+{"sdbbp", "", 0x7000003f, 0xffffffff, TRAP, I32 },
+{"sdbbp", "B", 0x7000003f, 0xfc00003f, TRAP, I32 },
+{"sdc1", "T,o(b)", 0xf4000000, 0xfc000000, SM|RD_T|RD_b|FP_D, I2 },
+{"sdc1", "E,o(b)", 0xf4000000, 0xfc000000, SM|RD_T|RD_b|FP_D, I2 },
+{"sdc1", "T,A(b)", 0, (int) M_SDC1_AB, INSN_MACRO, I2 },
+{"sdc1", "E,A(b)", 0, (int) M_SDC1_AB, INSN_MACRO, I2 },
+{"sdc2", "E,o(b)", 0xf8000000, 0xfc000000, SM|RD_C2|RD_b, I2 },
+{"sdc2", "E,A(b)", 0, (int) M_SDC2_AB, INSN_MACRO, I2 },
+{"sdc3", "E,o(b)", 0xfc000000, 0xfc000000, SM|RD_C3|RD_b, I2 },
+{"sdc3", "E,A(b)", 0, (int) M_SDC3_AB, INSN_MACRO, I2 },
+{"s.d", "T,o(b)", 0xf4000000, 0xfc000000, SM|RD_T|RD_b|FP_D, I2 },
+{"s.d", "T,o(b)", 0, (int) M_S_DOB, INSN_MACRO, I1 },
+{"s.d", "T,A(b)", 0, (int) M_S_DAB, INSN_MACRO, I1 },
+{"sdl", "t,o(b)", 0xb0000000, 0xfc000000, SM|RD_t|RD_b, I3 },
+{"sdl", "t,A(b)", 0, (int) M_SDL_AB, INSN_MACRO, I3 },
+{"sdr", "t,o(b)", 0xb4000000, 0xfc000000, SM|RD_t|RD_b, I3 },
+{"sdr", "t,A(b)", 0, (int) M_SDR_AB, INSN_MACRO, I3 },
+{"sdxc1", "S,t(b)", 0x4c000009, 0xfc0007ff, SM|RD_S|RD_t|RD_b, I4 },
+{"seb", "d,w", 0x7c000420, 0xffe007ff, WR_d|RD_t, I33 },
+{"seh", "d,w", 0x7c000620, 0xffe007ff, WR_d|RD_t, I33 },
+{"selsl", "d,v,t", 0x00000005, 0xfc0007ff, WR_d|RD_s|RD_t, L1 },
+{"selsr", "d,v,t", 0x00000001, 0xfc0007ff, WR_d|RD_s|RD_t, L1 },
+{"seq", "d,v,t", 0, (int) M_SEQ, INSN_MACRO, I1 },
+{"seq", "d,v,I", 0, (int) M_SEQ_I, INSN_MACRO, I1 },
+{"sge", "d,v,t", 0, (int) M_SGE, INSN_MACRO, I1 },
+{"sge", "d,v,I", 0, (int) M_SGE_I, INSN_MACRO, I1 },
+{"sgeu", "d,v,t", 0, (int) M_SGEU, INSN_MACRO, I1 },
+{"sgeu", "d,v,I", 0, (int) M_SGEU_I, INSN_MACRO, I1 },
+{"sgt", "d,v,t", 0, (int) M_SGT, INSN_MACRO, I1 },
+{"sgt", "d,v,I", 0, (int) M_SGT_I, INSN_MACRO, I1 },
+{"sgtu", "d,v,t", 0, (int) M_SGTU, INSN_MACRO, I1 },
+{"sgtu", "d,v,I", 0, (int) M_SGTU_I, INSN_MACRO, I1 },
+{"sh", "t,o(b)", 0xa4000000, 0xfc000000, SM|RD_t|RD_b, I1 },
+{"sh", "t,A(b)", 0, (int) M_SH_AB, INSN_MACRO, I1 },
+{"shfl.bfla.qh", "X,Y,Z", 0x7a20001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"shfl.mixh.ob", "X,Y,Z", 0x7980001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"shfl.mixh.ob", "D,S,T", 0x4980001f, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"shfl.mixh.qh", "X,Y,Z", 0x7820001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"shfl.mixl.ob", "X,Y,Z", 0x79c0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"shfl.mixl.ob", "D,S,T", 0x49c0001f, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"shfl.mixl.qh", "X,Y,Z", 0x78a0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"shfl.pach.ob", "X,Y,Z", 0x7900001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"shfl.pach.ob", "D,S,T", 0x4900001f, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"shfl.pach.qh", "X,Y,Z", 0x7920001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"shfl.pacl.ob", "D,S,T", 0x4940001f, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"shfl.repa.qh", "X,Y,Z", 0x7b20001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"shfl.repb.qh", "X,Y,Z", 0x7ba0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"shfl.upsl.ob", "X,Y,Z", 0x78c0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"sle", "d,v,t", 0, (int) M_SLE, INSN_MACRO, I1 },
+{"sle", "d,v,I", 0, (int) M_SLE_I, INSN_MACRO, I1 },
+{"sleu", "d,v,t", 0, (int) M_SLEU, INSN_MACRO, I1 },
+{"sleu", "d,v,I", 0, (int) M_SLEU_I, INSN_MACRO, I1 },
+{"sllv", "d,t,s", 0x00000004, 0xfc0007ff, WR_d|RD_t|RD_s, I1 },
+{"sll", "d,w,s", 0x00000004, 0xfc0007ff, WR_d|RD_t|RD_s, I1 }, /* sllv */
+{"sll", "d,w,<", 0x00000000, 0xffe0003f, WR_d|RD_t, I1 },
+{"sll.ob", "X,Y,Q", 0x78000010, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"sll.ob", "D,S,T[e]", 0x48000010, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"sll.ob", "D,S,k", 0x4bc00010, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"sll.qh", "X,Y,Q", 0x78200010, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"slt", "d,v,t", 0x0000002a, 0xfc0007ff, WR_d|RD_s|RD_t, I1 },
+{"slt", "d,v,I", 0, (int) M_SLT_I, INSN_MACRO, I1 },
+{"slti", "t,r,j", 0x28000000, 0xfc000000, WR_t|RD_s, I1 },
+{"sltiu", "t,r,j", 0x2c000000, 0xfc000000, WR_t|RD_s, I1 },
+{"sltu", "d,v,t", 0x0000002b, 0xfc0007ff, WR_d|RD_s|RD_t, I1 },
+{"sltu", "d,v,I", 0, (int) M_SLTU_I, INSN_MACRO, I1 },
+{"sne", "d,v,t", 0, (int) M_SNE, INSN_MACRO, I1 },
+{"sne", "d,v,I", 0, (int) M_SNE_I, INSN_MACRO, I1 },
+{"sqrt.d", "D,S", 0x46200004, 0xffff003f, WR_D|RD_S|FP_D, I2 },
+{"sqrt.s", "D,S", 0x46000004, 0xffff003f, WR_D|RD_S|FP_S, I2 },
+{"sqrt.ps", "D,S", 0x46c00004, 0xffff003f, WR_D|RD_S|FP_D, SB1 },
+{"srav", "d,t,s", 0x00000007, 0xfc0007ff, WR_d|RD_t|RD_s, I1 },
+{"sra", "d,w,s", 0x00000007, 0xfc0007ff, WR_d|RD_t|RD_s, I1 }, /* srav */
+{"sra", "d,w,<", 0x00000003, 0xffe0003f, WR_d|RD_t, I1 },
+{"sra.qh", "X,Y,Q", 0x78200013, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"srlv", "d,t,s", 0x00000006, 0xfc0007ff, WR_d|RD_t|RD_s, I1 },
+{"srl", "d,w,s", 0x00000006, 0xfc0007ff, WR_d|RD_t|RD_s, I1 }, /* srlv */
+{"srl", "d,w,<", 0x00000002, 0xffe0003f, WR_d|RD_t, I1 },
+{"srl.ob", "X,Y,Q", 0x78000012, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"srl.ob", "D,S,T[e]", 0x48000012, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"srl.ob", "D,S,k", 0x4bc00012, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"srl.qh", "X,Y,Q", 0x78200012, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+/* ssnop is at the start of the table. */
+{"standby", "", 0x42000021, 0xffffffff, 0, V1 },
+{"sub", "d,v,t", 0x00000022, 0xfc0007ff, WR_d|RD_s|RD_t, I1 },
+{"sub", "d,v,I", 0, (int) M_SUB_I, INSN_MACRO, I1 },
+{"sub.d", "D,V,T", 0x46200001, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I1 },
+{"sub.s", "D,V,T", 0x46000001, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, I1 },
+{"sub.ob", "X,Y,Q", 0x7800000a, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"sub.ob", "D,S,T", 0x4ac0000a, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"sub.ob", "D,S,T[e]", 0x4800000a, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"sub.ob", "D,S,k", 0x4bc0000a, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"sub.ps", "D,V,T", 0x46c00001, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, I5 },
+{"sub.qh", "X,Y,Q", 0x7820000a, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"suba.ob", "Y,Q", 0x78000036, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 },
+{"suba.qh", "Y,Q", 0x78200036, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX },
+{"subl.ob", "Y,Q", 0x78000436, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 },
+{"subl.qh", "Y,Q", 0x78200436, 0xfc2007ff, WR_MACC|RD_S|RD_T|FP_D, MX },
+{"subu", "d,v,t", 0x00000023, 0xfc0007ff, WR_d|RD_s|RD_t, I1 },
+{"subu", "d,v,I", 0, (int) M_SUBU_I, INSN_MACRO, I1 },
+{"suspend", "", 0x42000022, 0xffffffff, 0, V1 },
+{"suxc1", "S,t(b)", 0x4c00000d, 0xfc0007ff, SM|RD_S|RD_t|RD_b, I5|N55 },
+{"sw", "t,o(b)", 0xac000000, 0xfc000000, SM|RD_t|RD_b, I1 },
+{"sw", "t,A(b)", 0, (int) M_SW_AB, INSN_MACRO, I1 },
+{"swc0", "E,o(b)", 0xe0000000, 0xfc000000, SM|RD_C0|RD_b, I1 },
+{"swc0", "E,A(b)", 0, (int) M_SWC0_AB, INSN_MACRO, I1 },
+{"swc1", "T,o(b)", 0xe4000000, 0xfc000000, SM|RD_T|RD_b|FP_S, I1 },
+{"swc1", "E,o(b)", 0xe4000000, 0xfc000000, SM|RD_T|RD_b|FP_S, I1 },
+{"swc1", "T,A(b)", 0, (int) M_SWC1_AB, INSN_MACRO, I1 },
+{"swc1", "E,A(b)", 0, (int) M_SWC1_AB, INSN_MACRO, I1 },
+{"s.s", "T,o(b)", 0xe4000000, 0xfc000000, SM|RD_T|RD_b|FP_S, I1 }, /* swc1 */
+{"s.s", "T,A(b)", 0, (int) M_SWC1_AB, INSN_MACRO, I1 },
+{"swc2", "E,o(b)", 0xe8000000, 0xfc000000, SM|RD_C2|RD_b, I1 },
+{"swc2", "E,A(b)", 0, (int) M_SWC2_AB, INSN_MACRO, I1 },
+{"swc3", "E,o(b)", 0xec000000, 0xfc000000, SM|RD_C3|RD_b, I1 },
+{"swc3", "E,A(b)", 0, (int) M_SWC3_AB, INSN_MACRO, I1 },
+{"swl", "t,o(b)", 0xa8000000, 0xfc000000, SM|RD_t|RD_b, I1 },
+{"swl", "t,A(b)", 0, (int) M_SWL_AB, INSN_MACRO, I1 },
+{"scache", "t,o(b)", 0xa8000000, 0xfc000000, RD_t|RD_b, I2 }, /* same */
+{"scache", "t,A(b)", 0, (int) M_SWL_AB, INSN_MACRO, I2 }, /* as swl */
+{"swr", "t,o(b)", 0xb8000000, 0xfc000000, SM|RD_t|RD_b, I1 },
+{"swr", "t,A(b)", 0, (int) M_SWR_AB, INSN_MACRO, I1 },
+{"invalidate", "t,o(b)",0xb8000000, 0xfc000000, RD_t|RD_b, I2 }, /* same */
+{"invalidate", "t,A(b)",0, (int) M_SWR_AB, INSN_MACRO, I2 }, /* as swr */
+{"swxc1", "S,t(b)", 0x4c000008, 0xfc0007ff, SM|RD_S|RD_t|RD_b, I4 },
+{"sync", "", 0x0000000f, 0xffffffff, INSN_SYNC, I2|G1 },
+{"sync.p", "", 0x0000040f, 0xffffffff, INSN_SYNC, I2 },
+{"sync.l", "", 0x0000000f, 0xffffffff, INSN_SYNC, I2 },
+{"synci", "o(b)", 0x041f0000, 0xfc1f0000, SM|RD_b, I33 },
+{"syscall", "", 0x0000000c, 0xffffffff, TRAP, I1 },
+{"syscall", "B", 0x0000000c, 0xfc00003f, TRAP, I1 },
+{"teqi", "s,j", 0x040c0000, 0xfc1f0000, RD_s|TRAP, I2 },
+{"teq", "s,t", 0x00000034, 0xfc00ffff, RD_s|RD_t|TRAP, I2 },
+{"teq", "s,t,q", 0x00000034, 0xfc00003f, RD_s|RD_t|TRAP, I2 },
+{"teq", "s,j", 0x040c0000, 0xfc1f0000, RD_s|TRAP, I2 }, /* teqi */
+{"teq", "s,I", 0, (int) M_TEQ_I, INSN_MACRO, I2 },
+{"tgei", "s,j", 0x04080000, 0xfc1f0000, RD_s|TRAP, I2 },
+{"tge", "s,t", 0x00000030, 0xfc00ffff, RD_s|RD_t|TRAP, I2 },
+{"tge", "s,t,q", 0x00000030, 0xfc00003f, RD_s|RD_t|TRAP, I2 },
+{"tge", "s,j", 0x04080000, 0xfc1f0000, RD_s|TRAP, I2 }, /* tgei */
+{"tge", "s,I", 0, (int) M_TGE_I, INSN_MACRO, I2 },
+{"tgeiu", "s,j", 0x04090000, 0xfc1f0000, RD_s|TRAP, I2 },
+{"tgeu", "s,t", 0x00000031, 0xfc00ffff, RD_s|RD_t|TRAP, I2 },
+{"tgeu", "s,t,q", 0x00000031, 0xfc00003f, RD_s|RD_t|TRAP, I2 },
+{"tgeu", "s,j", 0x04090000, 0xfc1f0000, RD_s|TRAP, I2 }, /* tgeiu */
+{"tgeu", "s,I", 0, (int) M_TGEU_I, INSN_MACRO, I2 },
+{"tlbp", "", 0x42000008, 0xffffffff, INSN_TLB, I1 },
+{"tlbr", "", 0x42000001, 0xffffffff, INSN_TLB, I1 },
+{"tlbwi", "", 0x42000002, 0xffffffff, INSN_TLB, I1 },
+{"tlbwr", "", 0x42000006, 0xffffffff, INSN_TLB, I1 },
+{"tlti", "s,j", 0x040a0000, 0xfc1f0000, RD_s|TRAP, I2 },
+{"tlt", "s,t", 0x00000032, 0xfc00ffff, RD_s|RD_t|TRAP, I2 },
+{"tlt", "s,t,q", 0x00000032, 0xfc00003f, RD_s|RD_t|TRAP, I2 },
+{"tlt", "s,j", 0x040a0000, 0xfc1f0000, RD_s|TRAP, I2 }, /* tlti */
+{"tlt", "s,I", 0, (int) M_TLT_I, INSN_MACRO, I2 },
+{"tltiu", "s,j", 0x040b0000, 0xfc1f0000, RD_s|TRAP, I2 },
+{"tltu", "s,t", 0x00000033, 0xfc00ffff, RD_s|RD_t|TRAP, I2 },
+{"tltu", "s,t,q", 0x00000033, 0xfc00003f, RD_s|RD_t|TRAP, I2 },
+{"tltu", "s,j", 0x040b0000, 0xfc1f0000, RD_s|TRAP, I2 }, /* tltiu */
+{"tltu", "s,I", 0, (int) M_TLTU_I, INSN_MACRO, I2 },
+{"tnei", "s,j", 0x040e0000, 0xfc1f0000, RD_s|TRAP, I2 },
+{"tne", "s,t", 0x00000036, 0xfc00ffff, RD_s|RD_t|TRAP, I2 },
+{"tne", "s,t,q", 0x00000036, 0xfc00003f, RD_s|RD_t|TRAP, I2 },
+{"tne", "s,j", 0x040e0000, 0xfc1f0000, RD_s|TRAP, I2 }, /* tnei */
+{"tne", "s,I", 0, (int) M_TNE_I, INSN_MACRO, I2 },
+{"trunc.l.d", "D,S", 0x46200009, 0xffff003f, WR_D|RD_S|FP_D, I3 },
+{"trunc.l.s", "D,S", 0x46000009, 0xffff003f, WR_D|RD_S|FP_S, I3 },
+{"trunc.w.d", "D,S", 0x4620000d, 0xffff003f, WR_D|RD_S|FP_D, I2 },
+{"trunc.w.d", "D,S,x", 0x4620000d, 0xffff003f, WR_D|RD_S|FP_D, I2 },
+{"trunc.w.d", "D,S,t", 0, (int) M_TRUNCWD, INSN_MACRO, I1 },
+{"trunc.w.s", "D,S", 0x4600000d, 0xffff003f, WR_D|RD_S|FP_S, I2 },
+{"trunc.w.s", "D,S,x", 0x4600000d, 0xffff003f, WR_D|RD_S|FP_S, I2 },
+{"trunc.w.s", "D,S,t", 0, (int) M_TRUNCWS, INSN_MACRO, I1 },
+{"uld", "t,o(b)", 0, (int) M_ULD, INSN_MACRO, I3 },
+{"uld", "t,A(b)", 0, (int) M_ULD_A, INSN_MACRO, I3 },
+{"ulh", "t,o(b)", 0, (int) M_ULH, INSN_MACRO, I1 },
+{"ulh", "t,A(b)", 0, (int) M_ULH_A, INSN_MACRO, I1 },
+{"ulhu", "t,o(b)", 0, (int) M_ULHU, INSN_MACRO, I1 },
+{"ulhu", "t,A(b)", 0, (int) M_ULHU_A, INSN_MACRO, I1 },
+{"ulw", "t,o(b)", 0, (int) M_ULW, INSN_MACRO, I1 },
+{"ulw", "t,A(b)", 0, (int) M_ULW_A, INSN_MACRO, I1 },
+{"usd", "t,o(b)", 0, (int) M_USD, INSN_MACRO, I3 },
+{"usd", "t,A(b)", 0, (int) M_USD_A, INSN_MACRO, I3 },
+{"ush", "t,o(b)", 0, (int) M_USH, INSN_MACRO, I1 },
+{"ush", "t,A(b)", 0, (int) M_USH_A, INSN_MACRO, I1 },
+{"usw", "t,o(b)", 0, (int) M_USW, INSN_MACRO, I1 },
+{"usw", "t,A(b)", 0, (int) M_USW_A, INSN_MACRO, I1 },
+{"wach.ob", "Y", 0x7a00003e, 0xffff07ff, WR_MACC|RD_S|FP_D, MX|SB1 },
+{"wach.ob", "S", 0x4a00003e, 0xffff07ff, RD_S, N54 },
+{"wach.qh", "Y", 0x7a20003e, 0xffff07ff, WR_MACC|RD_S|FP_D, MX },
+{"wacl.ob", "Y,Z", 0x7800003e, 0xffe007ff, WR_MACC|RD_S|RD_T|FP_D, MX|SB1 },
+{"wacl.ob", "S,T", 0x4800003e, 0xffe007ff, RD_S|RD_T, N54 },
+{"wacl.qh", "Y,Z", 0x7820003e, 0xffe007ff, WR_MACC|RD_S|RD_T|FP_D, MX },
+{"wait", "", 0x42000020, 0xffffffff, TRAP, I3|I32 },
+{"wait", "J", 0x42000020, 0xfe00003f, TRAP, I32|N55 },
+{"waiti", "", 0x42000020, 0xffffffff, TRAP, L1 },
+{"wb", "o(b)", 0xbc040000, 0xfc1f0000, SM|RD_b, L1 },
+{"wrpgpr", "d,w", 0x41c00000, 0xffe007ff, RD_t, I33 },
+{"wsbh", "d,w", 0x7c0000a0, 0xffe007ff, WR_d|RD_t, I33 },
+{"xor", "d,v,t", 0x00000026, 0xfc0007ff, WR_d|RD_s|RD_t, I1 },
+{"xor", "t,r,I", 0, (int) M_XOR_I, INSN_MACRO, I1 },
+{"xor.ob", "X,Y,Q", 0x7800000d, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX|SB1 },
+{"xor.ob", "D,S,T", 0x4ac0000d, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"xor.ob", "D,S,T[e]", 0x4800000d, 0xfe20003f, WR_D|RD_S|RD_T, N54 },
+{"xor.ob", "D,S,k", 0x4bc0000d, 0xffe0003f, WR_D|RD_S|RD_T, N54 },
+{"xor.qh", "X,Y,Q", 0x7820000d, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, MX },
+{"xori", "t,r,i", 0x38000000, 0xfc000000, WR_t|RD_s, I1 },
+
+/* No hazard protection on coprocessor instructions--they shouldn't
+ change the state of the processor and if they do it's up to the
+ user to put in nops as necessary. These are at the end so that the
+ disassembler recognizes more specific versions first. */
+{"c0", "C", 0x42000000, 0xfe000000, 0, I1 },
+{"c1", "C", 0x46000000, 0xfe000000, 0, I1 },
+{"c2", "C", 0x4a000000, 0xfe000000, 0, I1 },
+{"c3", "C", 0x4e000000, 0xfe000000, 0, I1 },
+{"cop0", "C", 0, (int) M_COP0, INSN_MACRO, I1 },
+{"cop1", "C", 0, (int) M_COP1, INSN_MACRO, I1 },
+{"cop2", "C", 0, (int) M_COP2, INSN_MACRO, I1 },
+{"cop3", "C", 0, (int) M_COP3, INSN_MACRO, I1 },
+
+ /* Conflicts with the 4650's "mul" instruction. Nobody's using the
+ 4010 any more, so move this insn out of the way. If the object
+ format gave us more info, we could do this right. */
+{"addciu", "t,r,j", 0x70000000, 0xfc000000, WR_t|RD_s, L1 },
+};
+
+#define MIPS_NUM_OPCODES \
+ ((sizeof mips_builtin_opcodes) / (sizeof (mips_builtin_opcodes[0])))
+const int bfd_mips_num_builtin_opcodes = MIPS_NUM_OPCODES;
+
+/* const removed from the following to allow for dynamic extensions to the
+ * built-in instruction set. */
+struct mips_opcode *mips_opcodes =
+ (struct mips_opcode *) mips_builtin_opcodes;
+int bfd_mips_num_opcodes = MIPS_NUM_OPCODES;
+#undef MIPS_NUM_OPCODES
diff --git a/contrib/binutils/opcodes/mips16-opc.c b/contrib/binutils/opcodes/mips16-opc.c
new file mode 100644
index 0000000..d7fcfc2
--- /dev/null
+++ b/contrib/binutils/opcodes/mips16-opc.c
@@ -0,0 +1,227 @@
+/* mips16-opc.c. Mips16 opcode table.
+ Copyright 1996, 1997, 1998, 2000 Free Software Foundation, Inc.
+ Contributed by Ian Lance Taylor, Cygnus Support
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+1, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+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 file; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
+
+#include <stdio.h>
+#include "sysdep.h"
+#include "opcode/mips.h"
+
+/* This is the opcodes table for the mips16 processor. The format of
+ this table is intentionally identical to the one in mips-opc.c.
+ However, the special letters that appear in the argument string are
+ different, and the table uses some different flags. */
+
+/* Use some short hand macros to keep down the length of the lines in
+ the opcodes table. */
+
+#define UBD INSN_UNCOND_BRANCH_DELAY
+#define BR MIPS16_INSN_BRANCH
+
+#define WR_x MIPS16_INSN_WRITE_X
+#define WR_y MIPS16_INSN_WRITE_Y
+#define WR_z MIPS16_INSN_WRITE_Z
+#define WR_T MIPS16_INSN_WRITE_T
+#define WR_SP MIPS16_INSN_WRITE_SP
+#define WR_31 MIPS16_INSN_WRITE_31
+#define WR_Y MIPS16_INSN_WRITE_GPR_Y
+
+#define RD_x MIPS16_INSN_READ_X
+#define RD_y MIPS16_INSN_READ_Y
+#define RD_Z MIPS16_INSN_READ_Z
+#define RD_T MIPS16_INSN_READ_T
+#define RD_SP MIPS16_INSN_READ_SP
+#define RD_31 MIPS16_INSN_READ_31
+#define RD_PC MIPS16_INSN_READ_PC
+#define RD_X MIPS16_INSN_READ_GPR_X
+
+#define WR_HI INSN_WRITE_HI
+#define WR_LO INSN_WRITE_LO
+#define RD_HI INSN_READ_HI
+#define RD_LO INSN_READ_LO
+
+#define TRAP INSN_TRAP
+
+#define I3 INSN_ISA3
+
+#define T3 INSN_3900
+
+const struct mips_opcode mips16_opcodes[] =
+{
+{"nop", "", 0x6500, 0xffff, RD_Z, 0 }, /* move $0,$Z */
+{"la", "x,A", 0x0800, 0xf800, WR_x|RD_PC, 0 },
+{"abs", "x,w", 0, (int) M_ABS, INSN_MACRO, 0 },
+{"addiu", "y,x,4", 0x4000, 0xf810, WR_y|RD_x, 0 },
+{"addiu", "x,k", 0x4800, 0xf800, WR_x|RD_x, 0 },
+{"addiu", "S,K", 0x6300, 0xff00, WR_SP|RD_SP, 0 },
+{"addiu", "S,S,K", 0x6300, 0xff00, WR_SP|RD_SP, 0 },
+{"addiu", "x,P,V", 0x0800, 0xf800, WR_x|RD_PC, 0 },
+{"addiu", "x,S,V", 0x0000, 0xf800, WR_x|RD_SP, 0 },
+{"addu", "z,v,y", 0xe001, 0xf803, WR_z|RD_x|RD_y, 0 },
+{"addu", "y,x,4", 0x4000, 0xf810, WR_y|RD_x, 0 },
+{"addu", "x,k", 0x4800, 0xf800, WR_x|RD_x, 0 },
+{"addu", "S,K", 0x6300, 0xff00, WR_SP|RD_SP, 0 },
+{"addu", "S,S,K", 0x6300, 0xff00, WR_SP|RD_SP, 0 },
+{"addu", "x,P,V", 0x0800, 0xf800, WR_x|RD_PC, 0 },
+{"addu", "x,S,V", 0x0000, 0xf800, WR_x|RD_SP, 0 },
+{"and", "x,y", 0xe80c, 0xf81f, WR_x|RD_x|RD_y, 0 },
+{"b", "q", 0x1000, 0xf800, BR, 0 },
+{"beq", "x,y,p", 0, (int) M_BEQ, INSN_MACRO, 0 },
+{"beq", "x,U,p", 0, (int) M_BEQ_I, INSN_MACRO, 0 },
+{"beqz", "x,p", 0x2000, 0xf800, BR|RD_x, 0 },
+{"bge", "x,y,p", 0, (int) M_BGE, INSN_MACRO, 0 },
+{"bge", "x,8,p", 0, (int) M_BGE_I, INSN_MACRO, 0 },
+{"bgeu", "x,y,p", 0, (int) M_BGEU, INSN_MACRO, 0 },
+{"bgeu", "x,8,p", 0, (int) M_BGEU_I, INSN_MACRO, 0 },
+{"bgt", "x,y,p", 0, (int) M_BGT, INSN_MACRO, 0 },
+{"bgt", "x,8,p", 0, (int) M_BGT_I, INSN_MACRO, 0 },
+{"bgtu", "x,y,p", 0, (int) M_BGTU, INSN_MACRO, 0 },
+{"bgtu", "x,8,p", 0, (int) M_BGTU_I, INSN_MACRO, 0 },
+{"ble", "x,y,p", 0, (int) M_BLE, INSN_MACRO, 0 },
+{"ble", "x,8,p", 0, (int) M_BLE_I, INSN_MACRO, 0 },
+{"bleu", "x,y,p", 0, (int) M_BLEU, INSN_MACRO, 0 },
+{"bleu", "x,8,p", 0, (int) M_BLEU_I, INSN_MACRO, 0 },
+{"blt", "x,y,p", 0, (int) M_BLT, INSN_MACRO, 0 },
+{"blt", "x,8,p", 0, (int) M_BLT_I, INSN_MACRO, 0 },
+{"bltu", "x,y,p", 0, (int) M_BLTU, INSN_MACRO, 0 },
+{"bltu", "x,8,p", 0, (int) M_BLTU_I, INSN_MACRO, 0 },
+{"bne", "x,y,p", 0, (int) M_BNE, INSN_MACRO, 0 },
+{"bne", "x,U,p", 0, (int) M_BNE_I, INSN_MACRO, 0 },
+{"bnez", "x,p", 0x2800, 0xf800, BR|RD_x, 0 },
+{"break", "6", 0xe805, 0xf81f, TRAP, 0 },
+{"bteqz", "p", 0x6000, 0xff00, BR|RD_T, 0 },
+{"btnez", "p", 0x6100, 0xff00, BR|RD_T, 0 },
+{"cmpi", "x,U", 0x7000, 0xf800, WR_T|RD_x, 0 },
+{"cmp", "x,y", 0xe80a, 0xf81f, WR_T|RD_x|RD_y, 0 },
+{"cmp", "x,U", 0x7000, 0xf800, WR_T|RD_x, 0 },
+{"dla", "y,E", 0xfe00, 0xff00, WR_y|RD_PC, I3 },
+{"daddiu", "y,x,4", 0x4010, 0xf810, WR_y|RD_x, I3 },
+{"daddiu", "y,j", 0xfd00, 0xff00, WR_y|RD_y, I3 },
+{"daddiu", "S,K", 0xfb00, 0xff00, WR_SP|RD_SP, I3 },
+{"daddiu", "S,S,K", 0xfb00, 0xff00, WR_SP|RD_SP, I3 },
+{"daddiu", "y,P,W", 0xfe00, 0xff00, WR_y|RD_PC, I3 },
+{"daddiu", "y,S,W", 0xff00, 0xff00, WR_y|RD_SP, I3 },
+{"daddu", "z,v,y", 0xe000, 0xf803, WR_z|RD_x|RD_y, I3 },
+{"daddu", "y,x,4", 0x4010, 0xf810, WR_y|RD_x, I3 },
+{"daddu", "y,j", 0xfd00, 0xff00, WR_y|RD_y, I3 },
+{"daddu", "S,K", 0xfb00, 0xff00, WR_SP|RD_SP, I3 },
+{"daddu", "S,S,K", 0xfb00, 0xff00, WR_SP|RD_SP, I3 },
+{"daddu", "y,P,W", 0xfe00, 0xff00, WR_y|RD_PC, I3 },
+{"daddu", "y,S,W", 0xff00, 0xff00, WR_y|RD_SP, I3 },
+{"ddiv", "0,x,y", 0xe81e, 0xf81f, RD_x|RD_y|WR_HI|WR_LO, I3 },
+{"ddiv", "z,v,y", 0, (int) M_DDIV_3, INSN_MACRO, 0 },
+{"ddivu", "0,x,y", 0xe81f, 0xf81f, RD_x|RD_y|WR_HI|WR_LO, I3 },
+{"ddivu", "z,v,y", 0, (int) M_DDIVU_3, INSN_MACRO, 0 },
+{"div", "0,x,y", 0xe81a, 0xf81f, RD_x|RD_y|WR_HI|WR_LO, 0 },
+{"div", "z,v,y", 0, (int) M_DIV_3, INSN_MACRO, 0 },
+{"divu", "0,x,y", 0xe81b, 0xf81f, RD_x|RD_y|WR_HI|WR_LO, 0 },
+{"divu", "z,v,y", 0, (int) M_DIVU_3, INSN_MACRO, 0 },
+{"dmul", "z,v,y", 0, (int) M_DMUL, INSN_MACRO, I3 },
+{"dmult", "x,y", 0xe81c, 0xf81f, RD_x|RD_y|WR_HI|WR_LO, I3 },
+{"dmultu", "x,y", 0xe81d, 0xf81f, RD_x|RD_y|WR_HI|WR_LO, I3 },
+{"drem", "0,x,y", 0xe81e, 0xf81f, RD_x|RD_y|WR_HI|WR_LO, I3 },
+{"drem", "z,v,y", 0, (int) M_DREM_3, INSN_MACRO, 0 },
+{"dremu", "0,x,y", 0xe81f, 0xf81f, RD_x|RD_y|WR_HI|WR_LO, I3 },
+{"dremu", "z,v,y", 0, (int) M_DREMU_3, INSN_MACRO, 0 },
+{"dsllv", "y,x", 0xe814, 0xf81f, WR_y|RD_y|RD_x, I3 },
+{"dsll", "x,w,[", 0x3001, 0xf803, WR_x|RD_y, I3 },
+{"dsll", "y,x", 0xe814, 0xf81f, WR_y|RD_y|RD_x, I3 },
+{"dsrav", "y,x", 0xe817, 0xf81f, WR_y|RD_y|RD_x, I3 },
+{"dsra", "y,]", 0xe813, 0xf81f, WR_y|RD_y, I3 },
+{"dsra", "y,x", 0xe817, 0xf81f, WR_y|RD_y|RD_x, I3 },
+{"dsrlv", "y,x", 0xe816, 0xf81f, WR_y|RD_y|RD_x, I3 },
+{"dsrl", "y,]", 0xe808, 0xf81f, WR_y|RD_y, I3 },
+{"dsrl", "y,x", 0xe816, 0xf81f, WR_y|RD_y|RD_x, I3 },
+{"dsubu", "z,v,y", 0xe002, 0xf803, WR_z|RD_x|RD_y, I3 },
+{"dsubu", "y,x,4", 0, (int) M_DSUBU_I, INSN_MACRO, 0 },
+{"dsubu", "y,j", 0, (int) M_DSUBU_I_2, INSN_MACRO, 0 },
+{"exit", "L", 0xed09, 0xff1f, TRAP, 0 },
+{"exit", "L", 0xee09, 0xff1f, TRAP, 0 },
+{"exit", "L", 0xef09, 0xff1f, TRAP, 0 },
+{"entry", "l", 0xe809, 0xf81f, TRAP, 0 },
+{"extend", "e", 0xf000, 0xf800, 0, 0 },
+{"jalr", "x", 0xe840, 0xf8ff, UBD|WR_31|RD_x, 0 },
+{"jalr", "R,x", 0xe840, 0xf8ff, UBD|WR_31|RD_x, 0 },
+{"jal", "x", 0xe840, 0xf8ff, UBD|WR_31|RD_x, 0 },
+{"jal", "R,x", 0xe840, 0xf8ff, UBD|WR_31|RD_x, 0 },
+{"jal", "a", 0x1800, 0xfc00, UBD|WR_31, 0 },
+{"jalx", "a", 0x1c00, 0xfc00, UBD|WR_31, 0 },
+{"jr", "x", 0xe800, 0xf8ff, UBD|RD_x, 0 },
+{"jr", "R", 0xe820, 0xffff, UBD|RD_31, 0 },
+{"j", "x", 0xe800, 0xf8ff, UBD|RD_x, 0 },
+{"j", "R", 0xe820, 0xffff, UBD|RD_31, 0 },
+{"lb", "y,5(x)", 0x8000, 0xf800, WR_y|RD_x, 0 },
+{"lbu", "y,5(x)", 0xa000, 0xf800, WR_y|RD_x, 0 },
+{"ld", "y,D(x)", 0x3800, 0xf800, WR_y|RD_x, I3 },
+{"ld", "y,B", 0xfc00, 0xff00, WR_y|RD_PC, I3 },
+{"ld", "y,D(P)", 0xfc00, 0xff00, WR_y|RD_PC, I3 },
+{"ld", "y,D(S)", 0xf800, 0xff00, WR_y|RD_SP, I3 },
+{"lh", "y,H(x)", 0x8800, 0xf800, WR_y|RD_x, 0 },
+{"lhu", "y,H(x)", 0xa800, 0xf800, WR_y|RD_x, 0 },
+{"li", "x,U", 0x6800, 0xf800, WR_x, 0 },
+{"lw", "y,W(x)", 0x9800, 0xf800, WR_y|RD_x, 0 },
+{"lw", "x,A", 0xb000, 0xf800, WR_x|RD_PC, 0 },
+{"lw", "x,V(P)", 0xb000, 0xf800, WR_x|RD_PC, 0 },
+{"lw", "x,V(S)", 0x9000, 0xf800, WR_x|RD_SP, 0 },
+{"lwu", "y,W(x)", 0xb800, 0xf800, WR_y|RD_x, I3 },
+{"mfhi", "x", 0xe810, 0xf8ff, WR_x|RD_HI, 0 },
+{"mflo", "x", 0xe812, 0xf8ff, WR_x|RD_LO, 0 },
+{"move", "y,X", 0x6700, 0xff00, WR_y|RD_X, 0 },
+{"move", "Y,Z", 0x6500, 0xff00, WR_Y|RD_Z, 0 },
+{"mul", "z,v,y", 0, (int) M_MUL, INSN_MACRO, 0 },
+{"mult", "x,y", 0xe818, 0xf81f, RD_x|RD_y|WR_HI|WR_LO, 0 },
+{"multu", "x,y", 0xe819, 0xf81f, RD_x|RD_y|WR_HI|WR_LO, 0 },
+{"neg", "x,w", 0xe80b, 0xf81f, WR_x|RD_y, 0 },
+{"not", "x,w", 0xe80f, 0xf81f, WR_x|RD_y, 0 },
+{"or", "x,y", 0xe80d, 0xf81f, WR_x|RD_x|RD_y, 0 },
+{"rem", "0,x,y", 0xe81a, 0xf81f, RD_x|RD_y|WR_HI|WR_LO, 0 },
+{"rem", "z,v,y", 0, (int) M_REM_3, INSN_MACRO, 0 },
+{"remu", "0,x,y", 0xe81b, 0xf81f, RD_x|RD_y|WR_HI|WR_LO, 0 },
+{"remu", "z,v,y", 0, (int) M_REMU_3, INSN_MACRO, 0 },
+{"sb", "y,5(x)", 0xc000, 0xf800, RD_y|RD_x, 0 },
+{"sd", "y,D(x)", 0x7800, 0xf800, RD_y|RD_x, I3 },
+{"sd", "y,D(S)", 0xf900, 0xff00, RD_y|RD_PC, I3 },
+{"sd", "R,C(S)", 0xfa00, 0xff00, RD_31|RD_PC, 0 },
+{"sh", "y,H(x)", 0xc800, 0xf800, RD_y|RD_x, 0 },
+{"sllv", "y,x", 0xe804, 0xf81f, WR_y|RD_y|RD_x, 0 },
+{"sll", "x,w,<", 0x3000, 0xf803, WR_x|RD_y, 0 },
+{"sll", "y,x", 0xe804, 0xf81f, WR_y|RD_y|RD_x, 0 },
+{"slti", "x,8", 0x5000, 0xf800, WR_T|RD_x, 0 },
+{"slt", "x,y", 0xe802, 0xf81f, WR_T|RD_x|RD_y, 0 },
+{"slt", "x,8", 0x5000, 0xf800, WR_T|RD_x, 0 },
+{"sltiu", "x,8", 0x5800, 0xf800, WR_T|RD_x, 0 },
+{"sltu", "x,y", 0xe803, 0xf81f, WR_T|RD_x|RD_y, 0 },
+{"sltu", "x,8", 0x5800, 0xf800, WR_T|RD_x, 0 },
+{"srav", "y,x", 0xe807, 0xf81f, WR_y|RD_y|RD_x, 0 },
+{"sra", "x,w,<", 0x3003, 0xf803, WR_x|RD_y, 0 },
+{"sra", "y,x", 0xe807, 0xf81f, WR_y|RD_y|RD_x, 0 },
+{"srlv", "y,x", 0xe806, 0xf81f, WR_y|RD_y|RD_x, 0 },
+{"srl", "x,w,<", 0x3002, 0xf803, WR_x|RD_y, 0 },
+{"srl", "y,x", 0xe806, 0xf81f, WR_y|RD_y|RD_x, 0 },
+{"subu", "z,v,y", 0xe003, 0xf803, WR_z|RD_x|RD_y, 0 },
+{"subu", "y,x,4", 0, (int) M_SUBU_I, INSN_MACRO, 0 },
+{"subu", "x,k", 0, (int) M_SUBU_I_2, INSN_MACRO,0 },
+{"sw", "y,W(x)", 0xd800, 0xf800, RD_y|RD_x, 0 },
+{"sw", "x,V(S)", 0xd000, 0xf800, RD_x|RD_SP, 0 },
+{"sw", "R,V(S)", 0x6200, 0xff00, RD_31|RD_SP, 0 },
+{"xor", "x,y", 0xe80e, 0xf81f, WR_x|RD_x|RD_y, 0 },
+};
+
+const int bfd_mips16_num_opcodes =
+ ((sizeof mips16_opcodes) / (sizeof (mips16_opcodes[0])));
OpenPOWER on IntegriCloud