diff options
author | obrien <obrien@FreeBSD.org> | 2008-05-29 02:43:05 +0000 |
---|---|---|
committer | obrien <obrien@FreeBSD.org> | 2008-05-29 02:43:05 +0000 |
commit | b89ade4fa4e42a8cfe097478fd794045f383a2a3 (patch) | |
tree | 4106ab9e451c154a0987e8590d543bcf60624ed6 | |
parent | 8bca5d6f6b7e9d571d007db92c6430dd75c85c80 (diff) | |
download | FreeBSD-src-b89ade4fa4e42a8cfe097478fd794045f383a2a3.zip FreeBSD-src-b89ade4fa4e42a8cfe097478fd794045f383a2a3.tar.gz |
Bring these back to HEAD.
(I thought ncvs@ had rm'ed these MIPS files a long time ago... SVN had
better work out - else 7 more files off the vendor branch.)
-rw-r--r-- | contrib/binutils/bfd/coff-mips.c | 2701 | ||||
-rw-r--r-- | contrib/binutils/bfd/cpu-mips.c | 127 | ||||
-rw-r--r-- | contrib/binutils/bfd/elf32-mips.c | 1618 | ||||
-rw-r--r-- | contrib/binutils/bfd/elf64-mips.c | 2815 | ||||
-rw-r--r-- | contrib/binutils/bfd/mipsbsd.c | 486 | ||||
-rw-r--r-- | contrib/binutils/bfd/pe-mips.c | 1000 | ||||
-rw-r--r-- | contrib/binutils/include/opcode/mips.h | 914 |
7 files changed, 9661 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/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/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_ */ |