diff options
author | imp <imp@FreeBSD.org> | 1999-02-28 23:08:24 +0000 |
---|---|---|
committer | imp <imp@FreeBSD.org> | 1999-02-28 23:08:24 +0000 |
commit | a93821a373772ef6be3264da6e9a85b32dcbd513 (patch) | |
tree | 643e5e8eb0da196a1157f7df3a513d015b4cf692 /contrib | |
parent | e0e0a9ec8c2548fefca114c1387c8cf27e1c484f (diff) | |
parent | eb98fb7651721c42287ba7edb8578d35500973c0 (diff) | |
download | FreeBSD-src-a93821a373772ef6be3264da6e9a85b32dcbd513.zip FreeBSD-src-a93821a373772ef6be3264da6e9a85b32dcbd513.tar.gz |
This commit was generated by cvs2svn to compensate for changes in r44348,
which included commits to RCS files with non-trunk default branches.
Diffstat (limited to 'contrib')
-rw-r--r-- | contrib/binutils/bfd/coff-mips.c | 2716 | ||||
-rw-r--r-- | contrib/binutils/bfd/cpu-mips.c | 86 | ||||
-rw-r--r-- | contrib/binutils/bfd/elf32-mips.c | 7419 | ||||
-rw-r--r-- | contrib/binutils/bfd/elf64-mips.c | 2172 | ||||
-rw-r--r-- | contrib/binutils/bfd/hosts/decstation.h | 17 | ||||
-rw-r--r-- | contrib/binutils/bfd/mipsbsd.c | 468 | ||||
-rw-r--r-- | contrib/binutils/config/mh-decstation | 5 | ||||
-rw-r--r-- | contrib/binutils/include/elf/mips.h | 509 | ||||
-rw-r--r-- | contrib/binutils/include/opcode/mips.h | 715 | ||||
-rw-r--r-- | contrib/binutils/ld/emulparams/elf32bmip.sh | 30 | ||||
-rwxr-xr-x | contrib/binutils/ld/emulparams/elf32bsmip.sh | 31 | ||||
-rw-r--r-- | contrib/binutils/ld/emulparams/elf32ebmip.sh | 29 | ||||
-rw-r--r-- | contrib/binutils/ld/emulparams/elf32elmip.sh | 29 | ||||
-rw-r--r-- | contrib/binutils/ld/emulparams/elf32lmip.sh | 30 | ||||
-rwxr-xr-x | contrib/binutils/ld/emulparams/elf32lsmip.sh | 31 |
15 files changed, 14287 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..23c38a1 --- /dev/null +++ b/contrib/binutils/bfd/coff-mips.c @@ -0,0 +1,2716 @@ +/* BFD back-end for MIPS Extended-Coff files. + Copyright 1990, 91, 92, 93, 94, 95, 96, 97, 1998 + 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 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, + boolean pcrel)); +static boolean mips_relocate_section PARAMS ((bfd *, struct bfd_link_info *, + bfd *, asection *, + bfd_byte *, PTR)); +static boolean mips_read_relocs PARAMS ((bfd *, asection *)); +static boolean mips_relax_section PARAMS ((bfd *, asection *, + struct bfd_link_info *, + boolean *)); +static 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 */ + + { 8 }, + { 9 }, + { 10 }, + { 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 */ + + { 15 }, + { 16 }, + { 17 }, + { 18 }, + { 19 }, + { 20 }, + { 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 a 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 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 = bfd_h_get_32 (abfd, (bfd_byte *) 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; + } + + bfd_h_put_32 (abfd, intern->r_vaddr, (bfd_byte *) 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; + 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 relocateable 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 + relocateable 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; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message; +{ + 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; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message; +{ + 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 (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 &~ 0xffff) | ((val >> 16) & 0xffff); + bfd_put_32 (abfd, 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; +{ + boolean relocateable; + 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) + relocateable = true; + else + { + relocateable = false; + output_bfd = symbol->section->output_section->owner; + } + + if (bfd_is_und_section (symbol->section) + && relocateable == false) + 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 relocateable output. */ + gp = _bfd_get_gp_value (output_bfd); + if (gp == 0 + && (relocateable == false + || (symbol->flags & BSF_SECTION_SYM) != 0)) + { + if (relocateable != false) + { + /* 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 relocateable output, we don't want to do this for + an external symbol. */ + if (relocateable == false + || (symbol->flags & BSF_SECTION_SYM) != 0) + val += relocation - gp; + + insn = (insn &~ 0xffff) | (val & 0xffff); + bfd_put_32 (abfd, insn, (bfd_byte *) data + reloc_entry->address); + + if (relocateable != false) + reloc_entry->address += input_section->output_offset; + + /* Make sure it fit in 16 bits. */ + if (val >= 0x8000 && val < 0xffff8000) + 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; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message; +{ + 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 (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 &~ 0xffff) | ((val >> 16) & 0xffff); + bfd_put_32 (abfd, 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 relocateable 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. */ + +/*ARGSUSED*/ +static bfd_reloc_status_type +mips_switch_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; +{ + 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; + 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_MIPS_GPREL: + 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; + boolean pcrel; +{ + unsigned long insn; + unsigned long val; + unsigned long vallo; + + insn = bfd_get_32 (input_bfd, + contents + adjust + refhi->r_vaddr - input_section->vma); + 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 &~ 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 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; + boolean gp_undefined; + size_t adjust; + long *offsets; + struct external_reloc *ext_rel; + struct external_reloc *ext_rel_end; + unsigned int i; + boolean got_lo; + struct internal_reloc lo_int_rel; + + 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) + { + symndx_to_section = ((asection **) + bfd_alloc (input_bfd, + (NUM_RELOC_SECTIONS + * sizeof (asection *)))); + 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; + 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 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->relocateable + || 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 relocateable 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->relocateable); + 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->relocateable) + { + /* We are generating relocateable 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))) + 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 boolean +mips_read_relocs (abfd, sec) + bfd *abfd; + asection *sec; +{ + struct ecoff_section_tdata *section_tdata; + + section_tdata = ecoff_section_data (abfd, sec); + if (section_tdata == (struct ecoff_section_tdata *) NULL) + { + sec->used_by_bfd = + (PTR) bfd_alloc (abfd, sizeof (struct ecoff_section_tdata)); + 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) + { + bfd_size_type external_relocs_size; + + external_relocs_size = (ecoff_backend (abfd)->external_reloc_size + * sec->reloc_count); + + section_tdata->external_relocs = + (PTR) bfd_alloc (abfd, external_relocs_size); + if (section_tdata->external_relocs == NULL && external_relocs_size != 0) + return false; + + if (bfd_seek (abfd, sec->rel_filepos, SEEK_SET) != 0 + || (bfd_read (section_tdata->external_relocs, 1, + external_relocs_size, abfd) + != external_relocs_size)) + 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 boolean +mips_relax_section (abfd, sec, info, again) + bfd *abfd; + asection *sec; + struct bfd_link_info *info; + 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; + + /* 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 ((size_t) 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) + { + size_t size; + + size = sec->reloc_count * sizeof (long); + offsets = (long *) bfd_alloc (abfd, size); + if (offsets == (long *) NULL) + goto error_return; + memset (offsets, 0, size); + 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. */ + adjust = ((struct ecoff_value_adjust *) + bfd_alloc (abfd, sizeof (struct ecoff_value_adjust))); + 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 boolean +mips_relax_pcrel16 (info, input_bfd, input_section, h, location, address) + struct bfd_link_info *info; + bfd *input_bfd; + asection *input_section; + 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, 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, 0x003f0821, location + 12); /* addu $at,$at,$ra */ + bfd_put_32 (input_bfd, 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. */ + +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_ASSERT (! info->relocateable); + + *errmsg = NULL; + + if (datasec->reloc_count == 0) + return true; + + sym_hashes = ecoff_data (abfd)->sym_hashes; + + if (! mips_read_relocs (abfd, datasec)) + return false; + + relsec->contents = (bfd_byte *) bfd_alloc (abfd, datasec->reloc_count * 4); + 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; + 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, true, false, 4, + 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 + }, + /* 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 + +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), + + (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), + + (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), + + (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..20a4d28 --- /dev/null +++ b/contrib/binutils/bfd/cpu-mips.c @@ -0,0 +1,86 @@ +/* bfd back-end for mips support + Copyright (C) 1990, 91-97, 1998 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" + +#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, \ + bfd_default_compatible, \ + bfd_default_scan, \ + NEXT, \ + } + +enum { +I_mips3000, +I_mips3900, +I_mips4000, +I_mips4010, +I_mips4100, +I_mips4300, +I_mips4400, +I_mips4600, +I_mips4650, +I_mips5000, +I_mips6000, +I_mips8000, +I_mips10000, +I_mips16 +}; + + +#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_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 (32, 32, bfd_mach_mips6000, "mips:6000", false, NN(I_mips6000)), + 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_mips16, "mips:16", 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..fa6ec06 --- /dev/null +++ b/contrib/binutils/bfd/elf32-mips.c @@ -0,0 +1,7419 @@ +/* MIPS-specific support for 32-bit ELF + Copyright 1993, 94, 95, 96, 97, 1998 Free Software Foundation, Inc. + + Most of the information added by Ian Lance Taylor, Cygnus Support, + <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. */ + +/* 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 "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_32 +#include "ecoffswap.h" + +static bfd_reloc_status_type mips32_64bit_reloc + PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); +static reloc_howto_type *bfd_elf32_bfd_reloc_type_lookup + PARAMS ((bfd *, bfd_reloc_code_real_type)); +static void mips_info_to_howto_rel + PARAMS ((bfd *, arelent *, Elf32_Internal_Rel *)); +static void bfd_mips_elf32_swap_gptab_in + PARAMS ((bfd *, const Elf32_External_gptab *, Elf32_gptab *)); +static void bfd_mips_elf32_swap_gptab_out + PARAMS ((bfd *, const Elf32_gptab *, Elf32_External_gptab *)); +static boolean mips_elf_sym_is_global PARAMS ((bfd *, asymbol *)); +static boolean mips_elf32_object_p PARAMS ((bfd *)); +static boolean mips_elf_create_procedure_table + PARAMS ((PTR, bfd *, struct bfd_link_info *, asection *, + struct ecoff_debug_info *)); +static int mips_elf_additional_program_headers PARAMS ((bfd *)); +static boolean mips_elf_modify_segment_map PARAMS ((bfd *)); +static INLINE int elf_mips_isa PARAMS ((flagword)); +static boolean mips_elf32_section_from_shdr + PARAMS ((bfd *, Elf32_Internal_Shdr *, char *)); +static boolean mips_elf32_section_processing + PARAMS ((bfd *, Elf32_Internal_Shdr *)); +static boolean mips_elf_is_local_label_name + PARAMS ((bfd *, const char *)); +static struct bfd_hash_entry *mips_elf_link_hash_newfunc + PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *)); +static struct bfd_link_hash_table *mips_elf_link_hash_table_create + PARAMS ((bfd *)); +static int gptab_compare PARAMS ((const void *, const void *)); +static boolean mips_elf_final_link + PARAMS ((bfd *, struct bfd_link_info *)); +static void mips_elf_relocate_hi16 + PARAMS ((bfd *, Elf_Internal_Rela *, Elf_Internal_Rela *, bfd_byte *, + bfd_vma)); +static boolean mips_elf_relocate_got_local + PARAMS ((bfd *, bfd *, asection *, Elf_Internal_Rela *, + Elf_Internal_Rela *, bfd_byte *, bfd_vma)); +static void mips_elf_relocate_global_got + PARAMS ((bfd *, Elf_Internal_Rela *, bfd_byte *, bfd_vma)); +static bfd_reloc_status_type mips16_jump_reloc + PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); +static bfd_reloc_status_type mips16_gprel_reloc + PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); +static boolean mips_elf_adjust_dynindx + PARAMS ((struct elf_link_hash_entry *, PTR)); +static boolean mips_elf_relocate_section + PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *, + Elf_Internal_Rela *, Elf_Internal_Sym *, asection **)); +static boolean mips_elf_link_output_symbol_hook + PARAMS ((bfd *, struct bfd_link_info *, const char *, Elf_Internal_Sym *, + asection *)); +static boolean mips_elf_create_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *)); +static boolean mips_elf_create_compact_rel_section + PARAMS ((bfd *, struct bfd_link_info *)); +static boolean mips_elf_create_got_section + PARAMS ((bfd *, struct bfd_link_info *)); +static boolean mips_elf_check_relocs + PARAMS ((bfd *, struct bfd_link_info *, asection *, + const Elf_Internal_Rela *)); +static boolean mips_elf_adjust_dynamic_symbol + PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *)); +static boolean mips_elf_always_size_sections + PARAMS ((bfd *, struct bfd_link_info *)); +static boolean mips_elf_size_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *)); +static boolean mips_elf_finish_dynamic_symbol + PARAMS ((bfd *, struct bfd_link_info *, struct elf_link_hash_entry *, + Elf_Internal_Sym *)); +static boolean mips_elf_finish_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *)); +static boolean mips_elf_add_symbol_hook + PARAMS ((bfd *, struct bfd_link_info *, const Elf_Internal_Sym *, + const char **, flagword *, asection **, bfd_vma *)); +static bfd_reloc_status_type mips_elf_final_gp + PARAMS ((bfd *, asymbol *, boolean, char **, bfd_vma *)); +static bfd_byte *elf32_mips_get_relocated_section_contents + PARAMS ((bfd *, struct bfd_link_info *, struct bfd_link_order *, + bfd_byte *, boolean, asymbol **)); + +/* This is true for Irix 5 executables, false for normal MIPS ELF ABI + executables. FIXME: At the moment, we default to always generating + Irix 5 executables. */ + +#define SGI_COMPAT(abfd) (1) + +/* This structure is used to hold .got information when linking. It + is stored in the tdata field of the bfd_elf_section_data structure. */ + +struct mips_got_info +{ + /* The symbol index of the first global .got symbol. */ + unsigned long global_gotsym; + /* The number of local .got entries. */ + unsigned int local_gotno; + /* The number of local .got entries we have used. */ + unsigned int assigned_gotno; +}; + +/* The number of local .got entries we reserve. */ +#define MIPS_RESERVED_GOTNO (2) + +/* Instructions which appear in a stub. For some reason the stub is + slightly different on an SGI system. */ +#define ELF_MIPS_GP_OFFSET(abfd) (SGI_COMPAT (abfd) ? 0x7ff0 : 0x8000) +#define STUB_LW(abfd) \ + (SGI_COMPAT (abfd) \ + ? 0x8f998010 /* lw t9,0x8010(gp) */ \ + : 0x8f998000) /* lw t9,0x8000(gp) */ +#define STUB_MOVE 0x03e07825 /* move t7,ra */ +#define STUB_JALR 0x0320f809 /* jal t9 */ +#define STUB_LI16 0x34180000 /* ori t8,zero,0 */ +#define MIPS_FUNCTION_STUB_SIZE (16) + +/* Names of sections which appear in the .dynsym section in an Irix 5 + executable. */ + +static const char * const mips_elf_dynsym_sec_names[] = +{ + ".text", + ".init", + ".fini", + ".data", + ".rodata", + ".sdata", + ".sbss", + ".bss", + NULL +}; + +#define SIZEOF_MIPS_DYNSYM_SECNAMES \ + (sizeof mips_elf_dynsym_sec_names / sizeof mips_elf_dynsym_sec_names[0]) + +/* The number of entries in mips_elf_dynsym_sec_names which go in the + text segment. */ + +#define MIPS_TEXT_DYNSYM_SECNO (3) + +/* The names of the runtime procedure table symbols used on Irix 5. */ + +static const char * const mips_elf_dynsym_rtproc_names[] = +{ + "_procedure_table", + "_procedure_string_table", + "_procedure_table_size", + NULL +}; + +/* These structures are used to generate the .compact_rel section on + Irix 5. */ + +typedef struct +{ + unsigned long id1; /* Always one? */ + unsigned long num; /* Number of compact relocation entries. */ + unsigned long id2; /* Always two? */ + unsigned long offset; /* The file offset of the first relocation. */ + unsigned long reserved0; /* Zero? */ + unsigned long reserved1; /* Zero? */ +} Elf32_compact_rel; + +typedef struct +{ + bfd_byte id1[4]; + bfd_byte num[4]; + bfd_byte id2[4]; + bfd_byte offset[4]; + bfd_byte reserved0[4]; + bfd_byte reserved1[4]; +} Elf32_External_compact_rel; + +typedef struct +{ + unsigned int ctype : 1; /* 1: long 0: short format. See below. */ + unsigned int rtype : 4; /* Relocation types. See below. */ + unsigned int dist2to : 8; + unsigned int relvaddr : 19; /* (VADDR - vaddr of the previous entry)/ 4 */ + unsigned long konst; /* KONST field. See below. */ + unsigned long vaddr; /* VADDR to be relocated. */ +} Elf32_crinfo; + +typedef struct +{ + unsigned int ctype : 1; /* 1: long 0: short format. See below. */ + unsigned int rtype : 4; /* Relocation types. See below. */ + unsigned int dist2to : 8; + unsigned int relvaddr : 19; /* (VADDR - vaddr of the previous entry)/ 4 */ + unsigned long konst; /* KONST field. See below. */ +} Elf32_crinfo2; + +typedef struct +{ + bfd_byte info[4]; + bfd_byte konst[4]; + bfd_byte vaddr[4]; +} Elf32_External_crinfo; + +typedef struct +{ + bfd_byte info[4]; + bfd_byte konst[4]; +} Elf32_External_crinfo2; + +/* These are the constants used to swap the bitfields in a crinfo. */ + +#define CRINFO_CTYPE (0x1) +#define CRINFO_CTYPE_SH (31) +#define CRINFO_RTYPE (0xf) +#define CRINFO_RTYPE_SH (27) +#define CRINFO_DIST2TO (0xff) +#define CRINFO_DIST2TO_SH (19) +#define CRINFO_RELVADDR (0x7ffff) +#define CRINFO_RELVADDR_SH (0) + +/* A compact relocation info has long (3 words) or short (2 words) + formats. A short format doesn't have VADDR field and relvaddr + fields contains ((VADDR - vaddr of the previous entry) >> 2). */ +#define CRF_MIPS_LONG 1 +#define CRF_MIPS_SHORT 0 + +/* There are 4 types of compact relocation at least. The value KONST + has different meaning for each type: + + (type) (konst) + CT_MIPS_REL32 Address in data + CT_MIPS_WORD Address in word (XXX) + CT_MIPS_GPHI_LO GP - vaddr + CT_MIPS_JMPAD Address to jump + */ + +#define CRT_MIPS_REL32 0xa +#define CRT_MIPS_WORD 0xb +#define CRT_MIPS_GPHI_LO 0xc +#define CRT_MIPS_JMPAD 0xd + +#define mips_elf_set_cr_format(x,format) ((x).ctype = (format)) +#define mips_elf_set_cr_type(x,type) ((x).rtype = (type)) +#define mips_elf_set_cr_dist2to(x,v) ((x).dist2to = (v)) +#define mips_elf_set_cr_relvaddr(x,d) ((x).relvaddr = (d)<<2) + +static void bfd_elf32_swap_compact_rel_out + PARAMS ((bfd *, const Elf32_compact_rel *, Elf32_External_compact_rel *)); +static void bfd_elf32_swap_crinfo_out + PARAMS ((bfd *, const Elf32_crinfo *, Elf32_External_crinfo *)); + +#define USE_REL 1 /* MIPS uses REL relocations instead of RELA */ + +enum reloc_type +{ + R_MIPS_NONE = 0, + R_MIPS_16, R_MIPS_32, + R_MIPS_REL32, R_MIPS_26, + R_MIPS_HI16, R_MIPS_LO16, + R_MIPS_GPREL16, R_MIPS_LITERAL, + R_MIPS_GOT16, R_MIPS_PC16, + R_MIPS_CALL16, R_MIPS_GPREL32, + /* The remaining relocs are defined on Irix, although they are not + in the MIPS ELF ABI. */ + R_MIPS_UNUSED1, R_MIPS_UNUSED2, + R_MIPS_UNUSED3, + R_MIPS_SHIFT5, R_MIPS_SHIFT6, + R_MIPS_64, R_MIPS_GOT_DISP, + R_MIPS_GOT_PAGE, R_MIPS_GOT_OFST, + R_MIPS_GOT_HI16, R_MIPS_GOT_LO16, + R_MIPS_SUB, R_MIPS_INSERT_A, + R_MIPS_INSERT_B, R_MIPS_DELETE, + R_MIPS_HIGHER, R_MIPS_HIGHEST, + R_MIPS_CALL_HI16, R_MIPS_CALL_LO16, + R_MIPS_max, + /* These relocs are used for the mips16. */ + R_MIPS16_26 = 100, + R_MIPS16_GPREL = 101 +}; + +static reloc_howto_type elf_mips_howto_table[] = +{ + /* 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_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 */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_16", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* 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_bitfield, /* complain_on_overflow */ + bfd_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_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_REL32", /* name */ + true, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* 26 bit branch 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. */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_26", /* name */ + true, /* partial_inplace */ + 0x3ffffff, /* src_mask */ + 0x3ffffff, /* 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_hi16_reloc, /* special_function */ + "R_MIPS_HI16", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* 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 */ + 0xffff, /* src_mask */ + 0xffff, /* 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_elf_gprel16_reloc, /* special_function */ + "R_MIPS_GPREL16", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* 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_elf_gprel16_reloc, /* special_function */ + "R_MIPS_LITERAL", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* 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 */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* 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_elf_generic_reloc, /* special_function */ + "R_MIPS_PC16", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* 16 bit call through global offset table. */ + /* FIXME: This is not handled correctly. */ + 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_elf_generic_reloc, /* special_function */ + "R_MIPS_CALL16", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* 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_bitfield, /* complain_on_overflow */ + _bfd_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. */ + { 13 }, + { 14 }, + { 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_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_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. This is used in 32 bit ELF when addresses + are 64 bits long; the upper 32 bits are simply a sign extension. + The fields of the howto should be the same as for R_MIPS_32, + other than the type, name, and special_function. */ + HOWTO (R_MIPS_64, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + mips32_64bit_reloc, /* special_function */ + "R_MIPS_64", /* name */ + true, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* Displacement in the global offset table. */ + /* FIXME: Not handled correctly. */ + 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_bitfield, /* complain_on_overflow */ + bfd_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. */ + /* FIXME: Not handled correctly. */ + 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_bitfield, /* complain_on_overflow */ + bfd_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. */ + /* FIXME: Not handled correctly. */ + 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_bitfield, /* complain_on_overflow */ + bfd_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. */ + /* FIXME: Not handled correctly. */ + 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_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. */ + /* FIXME: Not handled correctly. */ + 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_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. Presumably not used in 32 bit ELF. */ + { R_MIPS_SUB }, + + /* Used to cause the linker to insert and delete instructions? */ + { R_MIPS_INSERT_A }, + { R_MIPS_INSERT_B }, + { R_MIPS_DELETE }, + + /* Get the higher values of a 64 bit addend. Presumably not used in + 32 bit ELF. */ + { R_MIPS_HIGHER }, + { R_MIPS_HIGHEST }, + + /* High 16 bits of displacement in global offset table. */ + /* FIXME: Not handled correctly. */ + 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_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. */ + /* FIXME: Not handled correctly. */ + 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_elf_generic_reloc, /* special_function */ + "R_MIPS_CALL_LO16", /* name */ + true, /* partial_inplace */ + 0x0000ffff, /* src_mask */ + 0x0000ffff, /* 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. The src_mask and + dsk_mask for this howto do not reflect the actual instruction, in + which the value is not contiguous; the masks are for the + convenience of the relocate_section routine. */ +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 */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + false); /* pcrel_offset */ + + +/* Do a R_MIPS_HI16 relocation. This has to be done in combination + with a R_MIPS_LO16 reloc, because there is a carry from the LO16 to + the HI16. Here we just save the information we need; we do the + actual relocation when we see the LO16. MIPS ELF requires that the + LO16 immediately follow the HI16. As a GNU extension, we permit an + arbitrary number of HI16 relocs to be associated with a single LO16 + reloc. This extension permits gcc to output the HI and LO relocs + itself. */ + +struct mips_hi16 +{ + struct mips_hi16 *next; + bfd_byte *addr; + bfd_vma addend; +}; + +/* FIXME: This should not be a static variable. */ + +static struct mips_hi16 *mips_hi16_list; + +bfd_reloc_status_type +_bfd_mips_elf_hi16_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_reloc_status_type ret; + bfd_vma relocation; + struct mips_hi16 *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 (strcmp (bfd_asymbol_name (symbol), "_gp_disp") == 0) + { + boolean relocateable; + bfd_vma gp; + + if (ret == bfd_reloc_undefined) + abort (); + + if (output_bfd != NULL) + relocateable = true; + else + { + relocateable = false; + output_bfd = symbol->section->output_section->owner; + } + + ret = mips_elf_final_gp (output_bfd, symbol, relocateable, + error_message, &gp); + if (ret != bfd_reloc_ok) + return ret; + + relocation = gp - reloc_entry->address; + } + else + { + 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 LO16 do the actual relocation. */ + n = (struct mips_hi16 *) bfd_malloc (sizeof *n); + if (n == NULL) + return bfd_reloc_outofrange; + n->addr = (bfd_byte *) data + reloc_entry->address; + n->addend = relocation; + n->next = mips_hi16_list; + mips_hi16_list = n; + + if (output_bfd != (bfd *) NULL) + reloc_entry->address += input_section->output_offset; + + return ret; +} + +/* Do a R_MIPS_LO16 relocation. This is a straightforward 16 bit + inplace relocation; this function exists in order to do the + R_MIPS_HI16 relocation described above. */ + +bfd_reloc_status_type +_bfd_mips_elf_lo16_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; +{ + arelent gp_disp_relent; + + if (mips_hi16_list != NULL) + { + struct mips_hi16 *l; + + l = mips_hi16_list; + while (l != NULL) + { + unsigned long insn; + unsigned long val; + unsigned long vallo; + struct mips_hi16 *next; + + /* Do the HI16 relocation. Note that we actually don't need + to know anything about the LO16 itself, except where to + find the low 16 bits of the addend needed by the LO16. */ + 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 &~ 0xffff) | ((val >> 16) & 0xffff); + bfd_put_32 (abfd, insn, l->addr); + + if (strcmp (bfd_asymbol_name (symbol), "_gp_disp") == 0) + { + gp_disp_relent = *reloc_entry; + reloc_entry = &gp_disp_relent; + reloc_entry->addend = l->addend; + } + + next = l->next; + free (l); + l = next; + } + + mips_hi16_list = NULL; + } + else if (strcmp (bfd_asymbol_name (symbol), "_gp_disp") == 0) + { + bfd_reloc_status_type ret; + bfd_vma gp, relocation; + + /* FIXME: Does this case ever occur? */ + + ret = mips_elf_final_gp (output_bfd, symbol, true, error_message, &gp); + if (ret != bfd_reloc_ok) + return ret; + + relocation = gp - reloc_entry->address; + 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; + + gp_disp_relent = *reloc_entry; + reloc_entry = &gp_disp_relent; + reloc_entry->addend = relocation - 4; + } + + /* Now do the LO16 reloc in the usual way. */ + return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message); +} + +/* Do a R_MIPS_GOT16 reloc. This is a reloc against the global offset + table used for PIC code. If the symbol is an external symbol, the + instruction is modified to contain the offset of the appropriate + entry in the global offset table. If the symbol is a section + symbol, the next reloc is a R_MIPS_LO16 reloc. The two 16 bit + addends are combined to form the real addend against the section + symbol; the GOT16 is modified to contain the offset of an entry in + the global offset table, and the LO16 is modified to offset it + appropriately. Thus an offset larger than 16 bits requires a + modified value in the global offset table. + + This implementation suffices for the assembler, but the linker does + not yet know how to create global offset tables. */ + +bfd_reloc_status_type +_bfd_mips_elf_got16_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 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; + } + + /* If we're relocating, and this is a local symbol, we can handle it + just like HI16. */ + if (output_bfd != (bfd *) NULL + && (symbol->flags & BSF_SECTION_SYM) != 0) + return _bfd_mips_elf_hi16_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message); + + abort (); +} + +/* 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 relocateable output. */ + +static bfd_reloc_status_type +mips_elf_final_gp (output_bfd, symbol, relocateable, error_message, pgp) + bfd *output_bfd; + asymbol *symbol; + boolean relocateable; + char **error_message; + bfd_vma *pgp; +{ + if (bfd_is_und_section (symbol->section) + && ! relocateable) + { + *pgp = 0; + return bfd_reloc_undefined; + } + + *pgp = _bfd_get_gp_value (output_bfd); + if (*pgp == 0 + && (! relocateable + || (symbol->flags & BSF_SECTION_SYM) != 0)) + { + if (relocateable) + { + /* Make up a value. */ + *pgp = symbol->section->output_section->vma + 0x4000; + _bfd_set_gp_value (output_bfd, *pgp); + } + 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) + { + *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); + *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. */ + +static bfd_reloc_status_type gprel16_with_gp PARAMS ((bfd *, asymbol *, + arelent *, asection *, + boolean, PTR, bfd_vma)); + +bfd_reloc_status_type +_bfd_mips_elf_gprel16_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; +{ + boolean relocateable; + bfd_reloc_status_type ret; + bfd_vma gp; + + /* 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 ELF + 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) + relocateable = true; + else + { + relocateable = false; + output_bfd = symbol->section->output_section->owner; + } + + ret = mips_elf_final_gp (output_bfd, symbol, relocateable, error_message, + &gp); + if (ret != bfd_reloc_ok) + return ret; + + return gprel16_with_gp (abfd, symbol, reloc_entry, input_section, + relocateable, data, gp); +} + +static bfd_reloc_status_type +gprel16_with_gp (abfd, symbol, reloc_entry, input_section, relocateable, data, + gp) + bfd *abfd; + asymbol *symbol; + arelent *reloc_entry; + asection *input_section; + boolean relocateable; + PTR data; + bfd_vma gp; +{ + bfd_vma relocation; + unsigned long insn; + unsigned long val; + + if (bfd_is_com_section (symbol->section)) + relocation = 0; + else + relocation = symbol->value; + + relocation += symbol->section->output_section->vma; + relocation += symbol->section->output_offset; + + if (reloc_entry->address > input_section->_cooked_size) + return bfd_reloc_outofrange; + + insn = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address); + + /* Set val to the offset into the section or symbol. */ + if (reloc_entry->howto->src_mask == 0) + { + /* This case occurs with the 64-bit MIPS ELF ABI. */ + val = reloc_entry->addend; + } + else + { + 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 relocateable output, we don't want to do this for + an external symbol. */ + if (! relocateable + || (symbol->flags & BSF_SECTION_SYM) != 0) + val += relocation - gp; + + insn = (insn &~ 0xffff) | (val & 0xffff); + bfd_put_32 (abfd, insn, (bfd_byte *) data + reloc_entry->address); + + if (relocateable) + reloc_entry->address += input_section->output_offset; + + /* Make sure it fit in 16 bits. */ + if (val >= 0x8000 && val < 0xffff8000) + return bfd_reloc_overflow; + + return bfd_reloc_ok; +} + +/* Do a R_MIPS_GPREL32 relocation. Is this 32 bit value the offset + from the gp register? XXX */ + +static bfd_reloc_status_type gprel32_with_gp PARAMS ((bfd *, asymbol *, + arelent *, asection *, + boolean, PTR, bfd_vma)); + +bfd_reloc_status_type +_bfd_mips_elf_gprel32_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; +{ + boolean relocateable; + bfd_reloc_status_type ret; + bfd_vma gp; + + /* 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 ELF + file. */ + if (output_bfd != (bfd *) NULL + && (symbol->flags & BSF_SECTION_SYM) == 0 + && reloc_entry->addend == 0) + { + *error_message = (char *) + "32bits gp relative relocation occurs for an external symbol"; + return bfd_reloc_outofrange; + } + + if (output_bfd != (bfd *) NULL) + { + relocateable = true; + gp = _bfd_get_gp_value (output_bfd); + } + else + { + relocateable = false; + output_bfd = symbol->section->output_section->owner; + + ret = mips_elf_final_gp (output_bfd, symbol, relocateable, + error_message, &gp); + if (ret != bfd_reloc_ok) + return ret; + } + + return gprel32_with_gp (abfd, symbol, reloc_entry, input_section, + relocateable, data, gp); +} + +static bfd_reloc_status_type +gprel32_with_gp (abfd, symbol, reloc_entry, input_section, relocateable, data, + gp) + bfd *abfd; + asymbol *symbol; + arelent *reloc_entry; + asection *input_section; + boolean relocateable; + PTR data; + bfd_vma gp; +{ + bfd_vma relocation; + unsigned long val; + + if (bfd_is_com_section (symbol->section)) + relocation = 0; + else + relocation = symbol->value; + + relocation += symbol->section->output_section->vma; + relocation += symbol->section->output_offset; + + if (reloc_entry->address > input_section->_cooked_size) + return bfd_reloc_outofrange; + + if (reloc_entry->howto->src_mask == 0) + { + /* This case arises with the 64-bit MIPS ELF ABI. */ + val = 0; + } + else + val = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address); + + /* Set val to the offset into the section or symbol. */ + val += reloc_entry->addend; + + /* Adjust val for the final section location and GP value. If we + are producing relocateable output, we don't want to do this for + an external symbol. */ + if (! relocateable + || (symbol->flags & BSF_SECTION_SYM) != 0) + val += relocation - gp; + + bfd_put_32 (abfd, val, (bfd_byte *) data + reloc_entry->address); + + if (relocateable) + 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 addreses are 64 bits. The upper 32 bits are a simle + sign extension. */ + +static bfd_reloc_status_type +mips32_64bit_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_reloc_status_type r; + arelent reloc32; + unsigned long val; + bfd_size_type addr; + + r = bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message); + if (r != bfd_reloc_continue) + return r; + + /* 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[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 (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 (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; + } + + /* FIXME. */ + { + static 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 (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; +{ + boolean relocateable; + bfd_reloc_status_type ret; + bfd_vma gp; + unsigned short extend, insn; + unsigned long final; + + /* 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 ELF + file. */ + 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; + } + + if (output_bfd != NULL) + relocateable = true; + else + { + relocateable = false; + output_bfd = symbol->section->output_section->owner; + } + + ret = mips_elf_final_gp (output_bfd, symbol, relocateable, error_message, + &gp); + if (ret != bfd_reloc_ok) + return ret; + + if (reloc_entry->address > input_section->_cooked_size) + return bfd_reloc_outofrange; + + /* 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); + + /* Stuff the current addend back as a 32 bit value, do the usual + relocation, and then clean up. */ + bfd_put_32 (abfd, + (((extend & 0x1f) << 11) + | (extend & 0x7e0) + | (insn & 0x1f)), + (bfd_byte *) data + reloc_entry->address); + + ret = gprel16_with_gp (abfd, symbol, reloc_entry, input_section, + relocateable, data, gp); + + final = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address); + bfd_put_16 (abfd, + ((extend & 0xf800) + | ((final >> 11) & 0x1f) + | (final & 0x7e0)), + (bfd_byte *) data + reloc_entry->address); + bfd_put_16 (abfd, + ((insn & 0xffe0) + | (final & 0x1f)), + (bfd_byte *) data + reloc_entry->address + 2); + + return ret; +} + +/* Return the ISA for a MIPS e_flags value. */ + +static INLINE int +elf_mips_isa (flags) + flagword flags; +{ + switch (flags & EF_MIPS_ARCH) + { + case E_MIPS_ARCH_1: + return 1; + case E_MIPS_ARCH_2: + return 2; + case E_MIPS_ARCH_3: + return 3; + case E_MIPS_ARCH_4: + return 4; + } + return 4; +} + +/* A mapping from BFD reloc types to MIPS ELF reloc types. */ + +struct elf_reloc_map { + bfd_reloc_code_real_type bfd_reloc_val; + enum reloc_type elf_reloc_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 }, + { 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_MIPS_GPREL, 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_MIPS_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 }, +}; + +/* Given a BFD reloc type, return a howto structure. */ + +static reloc_howto_type * +bfd_elf32_bfd_reloc_type_lookup (abfd, code) + bfd *abfd; + bfd_reloc_code_real_type code; +{ + unsigned int i; + + for (i = 0; i < sizeof (mips_reloc_map) / sizeof (struct elf_reloc_map); i++) + { + if (mips_reloc_map[i].bfd_reloc_val == code) + return &elf_mips_howto_table[(int) mips_reloc_map[i].elf_reloc_val]; + } + + /* 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 on this architecture. */ + if (code == BFD_RELOC_CTOR) + { + if (bfd_arch_bits_per_address (abfd) == 32) + return &elf_mips_howto_table[(int) R_MIPS_32]; + else + return &elf_mips_ctor64_howto; + } + + /* Special handling for the MIPS16 relocs, since they are made up + reloc types with a large value. */ + if (code == BFD_RELOC_MIPS16_JMP) + return &elf_mips16_jump_howto; + else if (code == BFD_RELOC_MIPS16_GPREL) + return &elf_mips16_gprel_howto; + + return NULL; +} + +/* Given a MIPS reloc type, fill in an arelent structure. */ + +static void +mips_info_to_howto_rel (abfd, cache_ptr, dst) + bfd *abfd; + arelent *cache_ptr; + Elf32_Internal_Rel *dst; +{ + unsigned int r_type; + + r_type = ELF32_R_TYPE (dst->r_info); + if (r_type == R_MIPS16_26) + cache_ptr->howto = &elf_mips16_jump_howto; + else if (r_type == R_MIPS16_GPREL) + cache_ptr->howto = &elf_mips16_gprel_howto; + else + { + BFD_ASSERT (r_type < (unsigned int) R_MIPS_max); + cache_ptr->howto = &elf_mips_howto_table[r_type]; + } + + /* 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); +} + +/* A .reginfo section holds a single Elf32_RegInfo structure. These + routines swap this structure in and out. They are used outside of + BFD, so they are globally visible. */ + +void +bfd_mips_elf32_swap_reginfo_in (abfd, ex, in) + bfd *abfd; + const Elf32_External_RegInfo *ex; + Elf32_RegInfo *in; +{ + in->ri_gprmask = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_gprmask); + in->ri_cprmask[0] = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_cprmask[0]); + in->ri_cprmask[1] = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_cprmask[1]); + in->ri_cprmask[2] = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_cprmask[2]); + in->ri_cprmask[3] = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_cprmask[3]); + in->ri_gp_value = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_gp_value); +} + +void +bfd_mips_elf32_swap_reginfo_out (abfd, in, ex) + bfd *abfd; + const Elf32_RegInfo *in; + Elf32_External_RegInfo *ex; +{ + bfd_h_put_32 (abfd, (bfd_vma) in->ri_gprmask, + (bfd_byte *) ex->ri_gprmask); + bfd_h_put_32 (abfd, (bfd_vma) in->ri_cprmask[0], + (bfd_byte *) ex->ri_cprmask[0]); + bfd_h_put_32 (abfd, (bfd_vma) in->ri_cprmask[1], + (bfd_byte *) ex->ri_cprmask[1]); + bfd_h_put_32 (abfd, (bfd_vma) in->ri_cprmask[2], + (bfd_byte *) ex->ri_cprmask[2]); + bfd_h_put_32 (abfd, (bfd_vma) in->ri_cprmask[3], + (bfd_byte *) ex->ri_cprmask[3]); + bfd_h_put_32 (abfd, (bfd_vma) in->ri_gp_value, + (bfd_byte *) ex->ri_gp_value); +} + +/* In the 64 bit ABI, the .MIPS.options section holds register + information in an Elf64_Reginfo structure. These routines swap + them in and out. They are globally visible because they are used + outside of BFD. These routines are here so that gas can call them + without worrying about whether the 64 bit ABI has been included. */ + +void +bfd_mips_elf64_swap_reginfo_in (abfd, ex, in) + bfd *abfd; + const Elf64_External_RegInfo *ex; + Elf64_Internal_RegInfo *in; +{ + in->ri_gprmask = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_gprmask); + in->ri_pad = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_pad); + in->ri_cprmask[0] = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_cprmask[0]); + in->ri_cprmask[1] = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_cprmask[1]); + in->ri_cprmask[2] = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_cprmask[2]); + in->ri_cprmask[3] = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_cprmask[3]); + in->ri_gp_value = bfd_h_get_64 (abfd, (bfd_byte *) ex->ri_gp_value); +} + +void +bfd_mips_elf64_swap_reginfo_out (abfd, in, ex) + bfd *abfd; + const Elf64_Internal_RegInfo *in; + Elf64_External_RegInfo *ex; +{ + bfd_h_put_32 (abfd, (bfd_vma) in->ri_gprmask, + (bfd_byte *) ex->ri_gprmask); + bfd_h_put_32 (abfd, (bfd_vma) in->ri_pad, + (bfd_byte *) ex->ri_pad); + bfd_h_put_32 (abfd, (bfd_vma) in->ri_cprmask[0], + (bfd_byte *) ex->ri_cprmask[0]); + bfd_h_put_32 (abfd, (bfd_vma) in->ri_cprmask[1], + (bfd_byte *) ex->ri_cprmask[1]); + bfd_h_put_32 (abfd, (bfd_vma) in->ri_cprmask[2], + (bfd_byte *) ex->ri_cprmask[2]); + bfd_h_put_32 (abfd, (bfd_vma) in->ri_cprmask[3], + (bfd_byte *) ex->ri_cprmask[3]); + bfd_h_put_64 (abfd, (bfd_vma) in->ri_gp_value, + (bfd_byte *) ex->ri_gp_value); +} + +/* Swap an entry in a .gptab section. Note that these routines rely + on the equivalence of the two elements of the union. */ + +static void +bfd_mips_elf32_swap_gptab_in (abfd, ex, in) + bfd *abfd; + const Elf32_External_gptab *ex; + Elf32_gptab *in; +{ + in->gt_entry.gt_g_value = bfd_h_get_32 (abfd, ex->gt_entry.gt_g_value); + in->gt_entry.gt_bytes = bfd_h_get_32 (abfd, ex->gt_entry.gt_bytes); +} + +static void +bfd_mips_elf32_swap_gptab_out (abfd, in, ex) + bfd *abfd; + const Elf32_gptab *in; + Elf32_External_gptab *ex; +{ + bfd_h_put_32 (abfd, (bfd_vma) in->gt_entry.gt_g_value, + ex->gt_entry.gt_g_value); + bfd_h_put_32 (abfd, (bfd_vma) in->gt_entry.gt_bytes, + ex->gt_entry.gt_bytes); +} + +static void +bfd_elf32_swap_compact_rel_out (abfd, in, ex) + bfd *abfd; + const Elf32_compact_rel *in; + Elf32_External_compact_rel *ex; +{ + bfd_h_put_32 (abfd, (bfd_vma) in->id1, ex->id1); + bfd_h_put_32 (abfd, (bfd_vma) in->num, ex->num); + bfd_h_put_32 (abfd, (bfd_vma) in->id2, ex->id2); + bfd_h_put_32 (abfd, (bfd_vma) in->offset, ex->offset); + bfd_h_put_32 (abfd, (bfd_vma) in->reserved0, ex->reserved0); + bfd_h_put_32 (abfd, (bfd_vma) in->reserved1, ex->reserved1); +} + +static void +bfd_elf32_swap_crinfo_out (abfd, in, ex) + bfd *abfd; + const Elf32_crinfo *in; + Elf32_External_crinfo *ex; +{ + unsigned long l; + + l = (((in->ctype & CRINFO_CTYPE) << CRINFO_CTYPE_SH) + | ((in->rtype & CRINFO_RTYPE) << CRINFO_RTYPE_SH) + | ((in->dist2to & CRINFO_DIST2TO) << CRINFO_DIST2TO_SH) + | ((in->relvaddr & CRINFO_RELVADDR) << CRINFO_RELVADDR_SH)); + bfd_h_put_32 (abfd, (bfd_vma) l, ex->info); + bfd_h_put_32 (abfd, (bfd_vma) in->konst, ex->konst); + bfd_h_put_32 (abfd, (bfd_vma) in->vaddr, ex->vaddr); +} + +/* Swap in an options header. */ + +void +bfd_mips_elf_swap_options_in (abfd, ex, in) + bfd *abfd; + const Elf_External_Options *ex; + Elf_Internal_Options *in; +{ + in->kind = bfd_h_get_8 (abfd, ex->kind); + in->size = bfd_h_get_8 (abfd, ex->size); + in->section = bfd_h_get_16 (abfd, ex->section); + in->info = bfd_h_get_32 (abfd, ex->info); +} + +/* Swap out an options header. */ + +void +bfd_mips_elf_swap_options_out (abfd, in, ex) + bfd *abfd; + const Elf_Internal_Options *in; + Elf_External_Options *ex; +{ + bfd_h_put_8 (abfd, in->kind, ex->kind); + bfd_h_put_8 (abfd, in->size, ex->size); + bfd_h_put_16 (abfd, in->section, ex->section); + bfd_h_put_32 (abfd, in->info, ex->info); +} + +/* 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. */ + +/*ARGSUSED*/ +static boolean +mips_elf_sym_is_global (abfd, sym) + bfd *abfd; + asymbol *sym; +{ + return (sym->flags & BSF_SECTION_SYM) == 0 ? true : false; +} + +/* Set the right machine number for a MIPS ELF file. This is used for + both the 32-bit and the 64-bit ABI. */ + +boolean +_bfd_mips_elf_object_p (abfd) + bfd *abfd; +{ + switch (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) + { + default: + case E_MIPS_ARCH_1: + (void) bfd_default_set_arch_mach (abfd, bfd_arch_mips, 3000); + break; + + case E_MIPS_ARCH_2: + (void) bfd_default_set_arch_mach (abfd, bfd_arch_mips, 6000); + break; + + case E_MIPS_ARCH_3: + (void) bfd_default_set_arch_mach (abfd, bfd_arch_mips, 4000); + break; + + case E_MIPS_ARCH_4: + (void) bfd_default_set_arch_mach (abfd, bfd_arch_mips, 8000); + break; + } + + return true; +} + +/* Set the right machine number for a 32-bit MIPS ELF file. */ + +static boolean +mips_elf32_object_p (abfd) + bfd *abfd; +{ + /* Irix 5 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. */ + elf_bad_symtab (abfd) = true; + + return _bfd_mips_elf_object_p (abfd); +} + +/* The final processing done just before writing out a MIPS ELF object + file. This gets the MIPS architecture right based on the machine + number. This is used by both the 32-bit and the 64-bit ABI. */ + +/*ARGSUSED*/ +void +_bfd_mips_elf_final_write_processing (abfd, linker) + bfd *abfd; + boolean linker; +{ + unsigned long val; + unsigned int i; + Elf_Internal_Shdr **hdrpp; + const char *name; + asection *sec; + + switch (bfd_get_mach (abfd)) + { + case 3000: + val = E_MIPS_ARCH_1; + break; + + case 6000: + val = E_MIPS_ARCH_2; + break; + + case 4000: + val = E_MIPS_ARCH_3; + break; + + case 8000: + val = E_MIPS_ARCH_4; + break; + + default: + val = 0; + break; + } + + elf_elfheader (abfd)->e_flags &=~ EF_MIPS_ARCH; + elf_elfheader (abfd)->e_flags |= val; + + /* Set the sh_info field for .gptab sections. */ + for (i = 1, hdrpp = elf_elfsections (abfd) + 1; + i < elf_elfheader (abfd)->e_shnum; + i++, hdrpp++) + { + switch ((*hdrpp)->sh_type) + { + case SHT_MIPS_LIBLIST: + sec = bfd_get_section_by_name (abfd, ".dynstr"); + if (sec != NULL) + (*hdrpp)->sh_link = elf_section_data (sec)->this_idx; + break; + + case SHT_MIPS_GPTAB: + BFD_ASSERT ((*hdrpp)->bfd_section != NULL); + name = bfd_get_section_name (abfd, (*hdrpp)->bfd_section); + BFD_ASSERT (name != NULL + && strncmp (name, ".gptab.", sizeof ".gptab." - 1) == 0); + sec = bfd_get_section_by_name (abfd, name + sizeof ".gptab" - 1); + BFD_ASSERT (sec != NULL); + (*hdrpp)->sh_info = elf_section_data (sec)->this_idx; + break; + + case SHT_MIPS_CONTENT: + BFD_ASSERT ((*hdrpp)->bfd_section != NULL); + name = bfd_get_section_name (abfd, (*hdrpp)->bfd_section); + BFD_ASSERT (name != NULL + && strncmp (name, ".MIPS.content", + sizeof ".MIPS.content" - 1) == 0); + sec = bfd_get_section_by_name (abfd, + name + sizeof ".MIPS.content" - 1); + BFD_ASSERT (sec != NULL); + (*hdrpp)->sh_info = elf_section_data (sec)->this_idx; + break; + + case SHT_MIPS_SYMBOL_LIB: + sec = bfd_get_section_by_name (abfd, ".dynsym"); + if (sec != NULL) + (*hdrpp)->sh_link = elf_section_data (sec)->this_idx; + sec = bfd_get_section_by_name (abfd, ".liblist"); + if (sec != NULL) + (*hdrpp)->sh_info = elf_section_data (sec)->this_idx; + break; + + case SHT_MIPS_EVENTS: + BFD_ASSERT ((*hdrpp)->bfd_section != NULL); + name = bfd_get_section_name (abfd, (*hdrpp)->bfd_section); + BFD_ASSERT (name != NULL); + if (strncmp (name, ".MIPS.events", sizeof ".MIPS.events" - 1) == 0) + sec = bfd_get_section_by_name (abfd, + name + sizeof ".MIPS.events" - 1); + else + { + BFD_ASSERT (strncmp (name, ".MIPS.post_rel", + sizeof ".MIPS.post_rel" - 1) == 0); + sec = bfd_get_section_by_name (abfd, + (name + + sizeof ".MIPS.post_rel" - 1)); + } + BFD_ASSERT (sec != NULL); + (*hdrpp)->sh_link = elf_section_data (sec)->this_idx; + break; + } + } +} + +/* Function to keep MIPS specific file flags like as EF_MIPS_PIC. */ + +boolean +_bfd_mips_elf_set_private_flags (abfd, flags) + bfd *abfd; + flagword flags; +{ + BFD_ASSERT (!elf_flags_init (abfd) + || elf_elfheader (abfd)->e_flags == flags); + + elf_elfheader (abfd)->e_flags = flags; + elf_flags_init (abfd) = true; + return true; +} + +/* Copy backend specific data from one object module to another */ + +boolean +_bfd_mips_elf_copy_private_bfd_data (ibfd, obfd) + bfd *ibfd; + bfd *obfd; +{ + if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour + || bfd_get_flavour (obfd) != bfd_target_elf_flavour) + return true; + + BFD_ASSERT (!elf_flags_init (obfd) + || (elf_elfheader (obfd)->e_flags + == elf_elfheader (ibfd)->e_flags)); + + elf_gp (obfd) = elf_gp (ibfd); + elf_elfheader (obfd)->e_flags = elf_elfheader (ibfd)->e_flags; + elf_flags_init (obfd) = true; + return true; +} + +/* Merge backend specific data from an object file to the output + object file when linking. */ + +boolean +_bfd_mips_elf_merge_private_bfd_data (ibfd, obfd) + bfd *ibfd; + bfd *obfd; +{ + flagword old_flags; + flagword new_flags; + boolean ok; + + /* Check if we have the same endianess */ + if (ibfd->xvec->byteorder != obfd->xvec->byteorder + && obfd->xvec->byteorder != BFD_ENDIAN_UNKNOWN) + { + (*_bfd_error_handler) + ("%s: compiled for a %s endian system and target is %s endian", + bfd_get_filename (ibfd), + bfd_big_endian (ibfd) ? "big" : "little", + bfd_big_endian (obfd) ? "big" : "little"); + + bfd_set_error (bfd_error_wrong_format); + return false; + } + + if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour + || bfd_get_flavour (obfd) != bfd_target_elf_flavour) + return true; + + new_flags = elf_elfheader (ibfd)->e_flags; + elf_elfheader (obfd)->e_flags |= new_flags & EF_MIPS_NOREORDER; + old_flags = elf_elfheader (obfd)->e_flags; + + if (! elf_flags_init (obfd)) + { + elf_flags_init (obfd) = true; + elf_elfheader (obfd)->e_flags = new_flags; + + if (bfd_get_arch (obfd) == bfd_get_arch (ibfd) + && bfd_get_arch_info (obfd)->the_default) + { + if (! bfd_set_arch_mach (obfd, bfd_get_arch (ibfd), + bfd_get_mach (ibfd))) + return false; + } + + return true; + } + + /* Check flag compatibility. */ + + new_flags &= ~EF_MIPS_NOREORDER; + old_flags &= ~EF_MIPS_NOREORDER; + + if (new_flags == old_flags) + return true; + + ok = true; + + if ((new_flags & EF_MIPS_PIC) != (old_flags & EF_MIPS_PIC)) + { + new_flags &= ~EF_MIPS_PIC; + old_flags &= ~EF_MIPS_PIC; + (*_bfd_error_handler) + ("%s: linking PIC files with non-PIC files", + bfd_get_filename (ibfd)); + ok = false; + } + + if ((new_flags & EF_MIPS_CPIC) != (old_flags & EF_MIPS_CPIC)) + { + new_flags &= ~EF_MIPS_CPIC; + old_flags &= ~EF_MIPS_CPIC; + (*_bfd_error_handler) + ("%s: linking abicalls files with non-abicalls files", + bfd_get_filename (ibfd)); + ok = false; + } + + /* Don't warn about mixing -mips1 and -mips2 code, or mixing -mips3 + and -mips4 code. They will normally use the same data sizes and + calling conventions. */ + if ((new_flags & EF_MIPS_ARCH) != (old_flags & EF_MIPS_ARCH)) + { + int new_isa, old_isa; + + new_isa = elf_mips_isa (new_flags); + old_isa = elf_mips_isa (old_flags); + if ((new_isa == 1 || new_isa == 2) + ? (old_isa != 1 && old_isa != 2) + : (old_isa == 1 || old_isa == 2)) + { + (*_bfd_error_handler) + ("%s: ISA mismatch (-mips%d) with previous modules (-mips%d)", + bfd_get_filename (ibfd), new_isa, old_isa); + ok = false; + } + + new_flags &= ~ EF_MIPS_ARCH; + old_flags &= ~ EF_MIPS_ARCH; + } + + /* Warn about any other mismatches */ + if (new_flags != old_flags) + { + (*_bfd_error_handler) + ("%s: uses different e_flags (0x%lx) fields than previous modules (0x%lx)", + bfd_get_filename (ibfd), (unsigned long) new_flags, + (unsigned long) old_flags); + ok = false; + } + + if (! ok) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + + return true; +} + +/* Handle a MIPS specific section when reading an object file. This + is called when elfcode.h finds a section with an unknown type. + This routine supports both the 32-bit and 64-bit ELF ABI. + + FIXME: We need to handle the SHF_MIPS_GPREL flag, but I'm not sure + how to. */ + +boolean +_bfd_mips_elf_section_from_shdr (abfd, hdr, name) + bfd *abfd; + Elf_Internal_Shdr *hdr; + const char *name; +{ + /* There ought to be a place to keep ELF backend specific flags, but + at the moment there isn't one. We just keep track of the + sections by their name, instead. Fortunately, the ABI gives + suggested names for all the MIPS specific sections, so we will + probably get away with this. */ + switch (hdr->sh_type) + { + case SHT_MIPS_LIBLIST: + if (strcmp (name, ".liblist") != 0) + return false; + break; + case SHT_MIPS_MSYM: + if (strcmp (name, ".msym") != 0) + return false; + break; + case SHT_MIPS_CONFLICT: + if (strcmp (name, ".conflict") != 0) + return false; + break; + case SHT_MIPS_GPTAB: + if (strncmp (name, ".gptab.", sizeof ".gptab." - 1) != 0) + return false; + break; + case SHT_MIPS_UCODE: + if (strcmp (name, ".ucode") != 0) + return false; + break; + case SHT_MIPS_DEBUG: + if (strcmp (name, ".mdebug") != 0) + return false; + break; + case SHT_MIPS_REGINFO: + if (strcmp (name, ".reginfo") != 0 + || hdr->sh_size != sizeof (Elf32_External_RegInfo)) + return false; + break; + case SHT_MIPS_IFACE: + if (strcmp (name, ".MIPS.interfaces") != 0) + return false; + break; + case SHT_MIPS_CONTENT: + if (strncmp (name, ".MIPS.content", sizeof ".MIPS.content" - 1) != 0) + return false; + break; + case SHT_MIPS_OPTIONS: + if (strcmp (name, ".options") != 0 + && strcmp (name, ".MIPS.options") != 0) + return false; + break; + case SHT_MIPS_DWARF: + if (strncmp (name, ".debug_", sizeof ".debug_" - 1) != 0) + return false; + break; + case SHT_MIPS_SYMBOL_LIB: + if (strcmp (name, ".MIPS.symlib") != 0) + return false; + break; + case SHT_MIPS_EVENTS: + if (strncmp (name, ".MIPS.events", sizeof ".MIPS.events" - 1) != 0 + && strncmp (name, ".MIPS.post_rel", + sizeof ".MIPS.post_rel" - 1) != 0) + return false; + break; + default: + return false; + } + + if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name)) + return false; + + if (hdr->sh_type == SHT_MIPS_DEBUG) + { + if (! bfd_set_section_flags (abfd, hdr->bfd_section, + (bfd_get_section_flags (abfd, + hdr->bfd_section) + | SEC_DEBUGGING))) + return false; + } + + return true; +} + +/* Handle a 32-bit MIPS ELF specific section. */ + +static boolean +mips_elf32_section_from_shdr (abfd, hdr, name) + bfd *abfd; + Elf_Internal_Shdr *hdr; + char *name; +{ + if (! _bfd_mips_elf_section_from_shdr (abfd, hdr, name)) + return false; + + /* FIXME: We should record sh_info for a .gptab section. */ + + /* For a .reginfo section, set the gp value in the tdata information + from the contents of this section. We need the gp value while + processing relocs, so we just get it now. The .reginfo section + is not used in the 64-bit MIPS ELF ABI. */ + if (hdr->sh_type == SHT_MIPS_REGINFO) + { + Elf32_External_RegInfo ext; + Elf32_RegInfo s; + + if (! bfd_get_section_contents (abfd, hdr->bfd_section, (PTR) &ext, + (file_ptr) 0, sizeof ext)) + return false; + bfd_mips_elf32_swap_reginfo_in (abfd, &ext, &s); + elf_gp (abfd) = s.ri_gp_value; + } + + /* For a SHT_MIPS_OPTIONS section, look for a ODK_REGINFO entry, and + set the gp value based on what we find. We may see both + SHT_MIPS_REGINFO and SHT_MIPS_OPTIONS/ODK_REGINFO; in that case, + they should agree. */ + if (hdr->sh_type == SHT_MIPS_OPTIONS) + { + bfd_byte *contents, *l, *lend; + + contents = (bfd_byte *) bfd_malloc (hdr->sh_size); + if (contents == NULL) + return false; + if (! bfd_get_section_contents (abfd, hdr->bfd_section, contents, + (file_ptr) 0, hdr->sh_size)) + { + free (contents); + return false; + } + l = contents; + lend = contents + hdr->sh_size; + while (l + sizeof (Elf_External_Options) <= lend) + { + Elf_Internal_Options intopt; + + bfd_mips_elf_swap_options_in (abfd, (Elf_External_Options *) l, + &intopt); + if (intopt.kind == ODK_REGINFO) + { + Elf32_RegInfo intreg; + + bfd_mips_elf32_swap_reginfo_in + (abfd, + ((Elf32_External_RegInfo *) + (l + sizeof (Elf_External_Options))), + &intreg); + elf_gp (abfd) = intreg.ri_gp_value; + } + l += intopt.size; + } + free (contents); + } + + return true; +} + +/* Set the correct type for a MIPS ELF section. We do this by the + section name, which is a hack, but ought to work. This routine is + used by both the 32-bit and the 64-bit ABI. */ + +boolean +_bfd_mips_elf_fake_sections (abfd, hdr, sec) + bfd *abfd; + Elf32_Internal_Shdr *hdr; + asection *sec; +{ + register const char *name; + + name = bfd_get_section_name (abfd, sec); + + if (strcmp (name, ".liblist") == 0) + { + hdr->sh_type = SHT_MIPS_LIBLIST; + hdr->sh_info = sec->_raw_size / sizeof (Elf32_Lib); + /* The sh_link field is set in final_write_processing. */ + } + else if (strcmp (name, ".msym") == 0) + { + hdr->sh_type = SHT_MIPS_MSYM; + hdr->sh_entsize = 8; + /* FIXME: Set the sh_info field. */ + } + else if (strcmp (name, ".conflict") == 0) + hdr->sh_type = SHT_MIPS_CONFLICT; + else if (strncmp (name, ".gptab.", sizeof ".gptab." - 1) == 0) + { + hdr->sh_type = SHT_MIPS_GPTAB; + hdr->sh_entsize = sizeof (Elf32_External_gptab); + /* The sh_info field is set in final_write_processing. */ + } + else if (strcmp (name, ".ucode") == 0) + hdr->sh_type = SHT_MIPS_UCODE; + else if (strcmp (name, ".mdebug") == 0) + { + hdr->sh_type = SHT_MIPS_DEBUG; + /* In a shared object on Irix 5.3, the .mdebug section has an + entsize of 0. FIXME: Does this matter? */ + if (SGI_COMPAT (abfd) && (abfd->flags & DYNAMIC) != 0) + hdr->sh_entsize = 0; + else + hdr->sh_entsize = 1; + } + else if (strcmp (name, ".reginfo") == 0) + { + hdr->sh_type = SHT_MIPS_REGINFO; + /* In a shared object on Irix 5.3, the .reginfo section has an + entsize of 0x18. FIXME: Does this matter? */ + if (SGI_COMPAT (abfd) && (abfd->flags & DYNAMIC) != 0) + hdr->sh_entsize = sizeof (Elf32_External_RegInfo); + else + hdr->sh_entsize = 1; + + /* Force the section size to the correct value, even if the + linker thinks it is larger. The link routine below will only + write out this much data for .reginfo. */ + hdr->sh_size = sec->_raw_size = sizeof (Elf32_External_RegInfo); + } + else if (SGI_COMPAT (abfd) + && (strcmp (name, ".hash") == 0 + || strcmp (name, ".dynamic") == 0 + || strcmp (name, ".dynstr") == 0)) + { + hdr->sh_entsize = 0; + hdr->sh_info = SIZEOF_MIPS_DYNSYM_SECNAMES; + } + else if (strcmp (name, ".got") == 0 + || strcmp (name, ".sdata") == 0 + || strcmp (name, ".sbss") == 0 + || strcmp (name, ".lit4") == 0 + || strcmp (name, ".lit8") == 0) + hdr->sh_flags |= SHF_MIPS_GPREL; + else if (strcmp (name, ".MIPS.interfaces") == 0) + { + hdr->sh_type = SHT_MIPS_IFACE; + hdr->sh_flags |= SHF_MIPS_NOSTRIP; + } + else if (strcmp (name, ".MIPS.content") == 0) + { + hdr->sh_type = SHT_MIPS_CONTENT; + /* The sh_info field is set in final_write_processing. */ + } + else if (strcmp (name, ".options") == 0 + || strcmp (name, ".MIPS.options") == 0) + { + hdr->sh_type = SHT_MIPS_OPTIONS; + hdr->sh_entsize = 1; + hdr->sh_flags |= SHF_MIPS_NOSTRIP; + } + else if (strncmp (name, ".debug_", sizeof ".debug_" - 1) == 0) + hdr->sh_type = SHT_MIPS_DWARF; + else if (strcmp (name, ".MIPS.symlib") == 0) + { + hdr->sh_type = SHT_MIPS_SYMBOL_LIB; + /* The sh_link and sh_info fields are set in + final_write_processing. */ + } + else if (strncmp (name, ".MIPS.events", sizeof ".MIPS.events" - 1) == 0 + || strncmp (name, ".MIPS.post_rel", + sizeof ".MIPS.post_rel" - 1) == 0) + { + hdr->sh_type = SHT_MIPS_EVENTS; + hdr->sh_flags |= SHF_MIPS_NOSTRIP; + /* The sh_link field is set in final_write_processing. */ + } + + return true; +} + +/* Given a BFD section, try to locate the corresponding ELF section + index. This is used by both the 32-bit and the 64-bit ABI. + Actually, it's not clear to me that the 64-bit ABI supports these, + but for non-PIC objects we will certainly want support for at least + the .scommon section. */ + +boolean +_bfd_mips_elf_section_from_bfd_section (abfd, hdr, sec, retval) + bfd *abfd; + Elf32_Internal_Shdr *hdr; + asection *sec; + int *retval; +{ + if (strcmp (bfd_get_section_name (abfd, sec), ".scommon") == 0) + { + *retval = SHN_MIPS_SCOMMON; + return true; + } + if (strcmp (bfd_get_section_name (abfd, sec), ".acommon") == 0) + { + *retval = SHN_MIPS_ACOMMON; + return true; + } + return false; +} + +/* When are writing out the .options or .MIPS.options section, + remember the bytes we are writing out, so that we can install the + GP value in the section_processing routine. */ + +boolean +_bfd_mips_elf_set_section_contents (abfd, section, location, offset, count) + bfd *abfd; + sec_ptr section; + PTR location; + file_ptr offset; + bfd_size_type count; +{ + if (strcmp (section->name, ".options") == 0 + || strcmp (section->name, ".MIPS.options") == 0) + { + bfd_byte *c; + + if (elf_section_data (section) == NULL) + { + section->used_by_bfd = + (PTR) bfd_zalloc (abfd, sizeof (struct bfd_elf_section_data)); + if (elf_section_data (section) == NULL) + return false; + } + c = (bfd_byte *) elf_section_data (section)->tdata; + if (c == NULL) + { + bfd_size_type size; + + if (section->_cooked_size != 0) + size = section->_cooked_size; + else + size = section->_raw_size; + c = (bfd_byte *) bfd_zalloc (abfd, size); + if (c == NULL) + return false; + elf_section_data (section)->tdata = (PTR) c; + } + + memcpy (c + offset, location, count); + } + + return _bfd_elf_set_section_contents (abfd, section, location, offset, + count); +} + +/* Work over a section just before writing it out. This routine is + used by both the 32-bit and the 64-bit ABI. FIXME: We recognize + sections that need the SHF_MIPS_GPREL flag by name; there has to be + a better way. */ + +boolean +_bfd_mips_elf_section_processing (abfd, hdr) + bfd *abfd; + Elf_Internal_Shdr *hdr; +{ + if (hdr->bfd_section != NULL) + { + const char *name = bfd_get_section_name (abfd, hdr->bfd_section); + + if (strcmp (name, ".sdata") == 0) + { + hdr->sh_flags |= SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL; + hdr->sh_type = SHT_PROGBITS; + } + else if (strcmp (name, ".sbss") == 0) + { + hdr->sh_flags |= SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL; + hdr->sh_type = SHT_NOBITS; + } + else if (strcmp (name, ".lit8") == 0 + || strcmp (name, ".lit4") == 0) + { + hdr->sh_flags |= SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL; + hdr->sh_type = SHT_PROGBITS; + } + else if (strcmp (name, ".compact_rel") == 0) + { + hdr->sh_flags = 0; + hdr->sh_type = SHT_PROGBITS; + } + else if (strcmp (name, ".rtproc") == 0) + { + if (hdr->sh_addralign != 0 && hdr->sh_entsize == 0) + { + unsigned int adjust; + + adjust = hdr->sh_size % hdr->sh_addralign; + if (adjust != 0) + hdr->sh_size += hdr->sh_addralign - adjust; + } + } + } + + return true; +} + +/* Work over a section just before writing it out. We update the GP + value in the SHT_MIPS_REGINFO and SHT_MIPS_OPTIONS sections based + on the value we are using. */ + +static boolean +mips_elf32_section_processing (abfd, hdr) + bfd *abfd; + Elf32_Internal_Shdr *hdr; +{ + if (hdr->sh_type == SHT_MIPS_REGINFO) + { + bfd_byte buf[4]; + + BFD_ASSERT (hdr->sh_size == sizeof (Elf32_External_RegInfo)); + BFD_ASSERT (hdr->contents == NULL); + + if (bfd_seek (abfd, + hdr->sh_offset + sizeof (Elf32_External_RegInfo) - 4, + SEEK_SET) == -1) + return false; + bfd_h_put_32 (abfd, (bfd_vma) elf_gp (abfd), buf); + if (bfd_write (buf, (bfd_size_type) 1, (bfd_size_type) 4, abfd) != 4) + return false; + } + + if (hdr->sh_type == SHT_MIPS_OPTIONS + && hdr->bfd_section != NULL + && elf_section_data (hdr->bfd_section) != NULL + && elf_section_data (hdr->bfd_section)->tdata != NULL) + { + bfd_byte *contents, *l, *lend; + + /* We stored the section contents in the elf_section_data tdata + field in the set_section_contents routine. We save the + section contents so that we don't have to read them again. + At this point we know that elf_gp is set, so we can look + through the section contents to see if there is an + ODK_REGINFO structure. */ + + contents = (bfd_byte *) elf_section_data (hdr->bfd_section)->tdata; + l = contents; + lend = contents + hdr->sh_size; + while (l + sizeof (Elf_External_Options) <= lend) + { + Elf_Internal_Options intopt; + + bfd_mips_elf_swap_options_in (abfd, (Elf_External_Options *) l, + &intopt); + if (intopt.kind == ODK_REGINFO) + { + bfd_byte buf[4]; + + if (bfd_seek (abfd, + (hdr->sh_offset + + (l - contents) + + sizeof (Elf_External_Options) + + (sizeof (Elf32_External_RegInfo) - 4)), + SEEK_SET) == -1) + return false; + bfd_h_put_32 (abfd, elf_gp (abfd), buf); + if (bfd_write (buf, 1, 4, abfd) != 4) + return false; + } + l += intopt.size; + } + } + + return _bfd_mips_elf_section_processing (abfd, hdr); +} + +/* MIPS ELF uses two common sections. One is the usual one, and the + other is for small objects. All the small objects are kept + together, and then referenced via the gp pointer, which yields + faster assembler code. This is what we use for the small common + section. This approach is copied from ecoff.c. */ +static asection mips_elf_scom_section; +static asymbol mips_elf_scom_symbol; +static asymbol *mips_elf_scom_symbol_ptr; + +/* MIPS ELF also uses an acommon section, which represents an + allocated common symbol which may be overridden by a + definition in a shared library. */ +static asection mips_elf_acom_section; +static asymbol mips_elf_acom_symbol; +static asymbol *mips_elf_acom_symbol_ptr; + +/* The Irix 5 support uses two virtual sections, which represent + text/data symbols defined in dynamic objects. */ +static asection mips_elf_text_section; +static asection *mips_elf_text_section_ptr; +static asymbol mips_elf_text_symbol; +static asymbol *mips_elf_text_symbol_ptr; + +static asection mips_elf_data_section; +static asection *mips_elf_data_section_ptr; +static asymbol mips_elf_data_symbol; +static asymbol *mips_elf_data_symbol_ptr; + +/* Handle the special MIPS section numbers that a symbol may use. + This is used for both the 32-bit and the 64-bit ABI. */ + +void +_bfd_mips_elf_symbol_processing (abfd, asym) + bfd *abfd; + asymbol *asym; +{ + elf_symbol_type *elfsym; + + elfsym = (elf_symbol_type *) asym; + switch (elfsym->internal_elf_sym.st_shndx) + { + case SHN_MIPS_ACOMMON: + /* This section is used in a dynamically linked executable file. + It is an allocated common section. The dynamic linker can + either resolve these symbols to something in a shared + library, or it can just leave them here. For our purposes, + we can consider these symbols to be in a new section. */ + if (mips_elf_acom_section.name == NULL) + { + /* Initialize the acommon section. */ + mips_elf_acom_section.name = ".acommon"; + mips_elf_acom_section.flags = SEC_ALLOC; + mips_elf_acom_section.output_section = &mips_elf_acom_section; + mips_elf_acom_section.symbol = &mips_elf_acom_symbol; + mips_elf_acom_section.symbol_ptr_ptr = &mips_elf_acom_symbol_ptr; + mips_elf_acom_symbol.name = ".acommon"; + mips_elf_acom_symbol.flags = BSF_SECTION_SYM; + mips_elf_acom_symbol.section = &mips_elf_acom_section; + mips_elf_acom_symbol_ptr = &mips_elf_acom_symbol; + } + asym->section = &mips_elf_acom_section; + break; + + case SHN_COMMON: + /* Common symbols less than the GP size are automatically + treated as SHN_MIPS_SCOMMON symbols. */ + if (asym->value > elf_gp_size (abfd)) + break; + /* Fall through. */ + case SHN_MIPS_SCOMMON: + if (mips_elf_scom_section.name == NULL) + { + /* Initialize the small common section. */ + mips_elf_scom_section.name = ".scommon"; + mips_elf_scom_section.flags = SEC_IS_COMMON; + mips_elf_scom_section.output_section = &mips_elf_scom_section; + mips_elf_scom_section.symbol = &mips_elf_scom_symbol; + mips_elf_scom_section.symbol_ptr_ptr = &mips_elf_scom_symbol_ptr; + mips_elf_scom_symbol.name = ".scommon"; + mips_elf_scom_symbol.flags = BSF_SECTION_SYM; + mips_elf_scom_symbol.section = &mips_elf_scom_section; + mips_elf_scom_symbol_ptr = &mips_elf_scom_symbol; + } + asym->section = &mips_elf_scom_section; + asym->value = elfsym->internal_elf_sym.st_size; + break; + + case SHN_MIPS_SUNDEFINED: + asym->section = bfd_und_section_ptr; + break; + +#if 0 /* for SGI_COMPAT */ + case SHN_MIPS_TEXT: + asym->section = mips_elf_text_section_ptr; + break; + + case SHN_MIPS_DATA: + asym->section = mips_elf_data_section_ptr; + break; +#endif + } +} + +/* When creating an Irix 5 executable, we need REGINFO and RTPROC + segments. */ + +static int +mips_elf_additional_program_headers (abfd) + bfd *abfd; +{ + asection *s; + int ret; + + ret = 0; + + if (! SGI_COMPAT (abfd)) + return ret; + + s = bfd_get_section_by_name (abfd, ".reginfo"); + if (s != NULL && (s->flags & SEC_LOAD) != 0) + { + /* We need a PT_MIPS_REGINFO segment. */ + ++ret; + } + + if (bfd_get_section_by_name (abfd, ".dynamic") != NULL + && bfd_get_section_by_name (abfd, ".mdebug") != NULL) + { + /* We need a PT_MIPS_RTPROC segment. */ + ++ret; + } + + return ret; +} + +/* Modify the segment map for an Irix 5 executable. */ + +static boolean +mips_elf_modify_segment_map (abfd) + bfd *abfd; +{ + asection *s; + struct elf_segment_map *m, **pm; + + if (! SGI_COMPAT (abfd)) + return true; + + /* If there is a .reginfo section, we need a PT_MIPS_REGINFO + segment. */ + s = bfd_get_section_by_name (abfd, ".reginfo"); + if (s != NULL && (s->flags & SEC_LOAD) != 0) + { + for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next) + if (m->p_type == PT_MIPS_REGINFO) + break; + if (m == NULL) + { + m = (struct elf_segment_map *) bfd_zalloc (abfd, sizeof *m); + if (m == NULL) + return false; + + m->p_type = PT_MIPS_REGINFO; + m->count = 1; + m->sections[0] = s; + + /* We want to put it after the PHDR and INTERP segments. */ + pm = &elf_tdata (abfd)->segment_map; + while (*pm != NULL + && ((*pm)->p_type == PT_PHDR + || (*pm)->p_type == PT_INTERP)) + pm = &(*pm)->next; + + m->next = *pm; + *pm = m; + } + } + + /* If there are .dynamic and .mdebug sections, we make a room for + the RTPROC header. FIXME: Rewrite without section names. */ + if (bfd_get_section_by_name (abfd, ".interp") == NULL + && bfd_get_section_by_name (abfd, ".dynamic") != NULL + && bfd_get_section_by_name (abfd, ".mdebug") != NULL) + { + for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next) + if (m->p_type == PT_MIPS_RTPROC) + break; + if (m == NULL) + { + m = (struct elf_segment_map *) bfd_zalloc (abfd, sizeof *m); + if (m == NULL) + return false; + + m->p_type = PT_MIPS_RTPROC; + + s = bfd_get_section_by_name (abfd, ".rtproc"); + if (s == NULL) + { + m->count = 0; + m->p_flags = 0; + m->p_flags_valid = 1; + } + else + { + m->count = 1; + m->sections[0] = s; + } + + /* We want to put it after the DYNAMIC segment. */ + pm = &elf_tdata (abfd)->segment_map; + while (*pm != NULL && (*pm)->p_type != PT_DYNAMIC) + pm = &(*pm)->next; + if (*pm != NULL) + pm = &(*pm)->next; + + m->next = *pm; + *pm = m; + } + } + + /* On Irix 5, the PT_DYNAMIC segment includes the .dynamic, .dynstr, + .dynsym, and .hash sections, and everything in between. */ + for (pm = &elf_tdata (abfd)->segment_map; *pm != NULL; pm = &(*pm)->next) + if ((*pm)->p_type == PT_DYNAMIC) + break; + m = *pm; + if (m != NULL + && m->count == 1 + && strcmp (m->sections[0]->name, ".dynamic") == 0) + { + static const char *sec_names[] = + { ".dynamic", ".dynstr", ".dynsym", ".hash" }; + bfd_vma low, high; + unsigned int i, c; + struct elf_segment_map *n; + + low = 0xffffffff; + high = 0; + for (i = 0; i < sizeof sec_names / sizeof sec_names[0]; i++) + { + s = bfd_get_section_by_name (abfd, sec_names[i]); + if (s != NULL && (s->flags & SEC_LOAD) != 0) + { + bfd_size_type sz; + + if (low > s->vma) + low = s->vma; + sz = s->_cooked_size; + if (sz == 0) + sz = s->_raw_size; + if (high < s->vma + sz) + high = s->vma + sz; + } + } + + c = 0; + for (s = abfd->sections; s != NULL; s = s->next) + if ((s->flags & SEC_LOAD) != 0 + && s->vma >= low + && ((s->vma + + (s->_cooked_size != 0 ? s->_cooked_size : s->_raw_size)) + <= high)) + ++c; + + n = ((struct elf_segment_map *) + bfd_zalloc (abfd, sizeof *n + (c - 1) * sizeof (asection *))); + if (n == NULL) + return false; + *n = *m; + n->count = c; + + i = 0; + for (s = abfd->sections; s != NULL; s = s->next) + { + if ((s->flags & SEC_LOAD) != 0 + && s->vma >= low + && ((s->vma + + (s->_cooked_size != 0 ? s->_cooked_size : s->_raw_size)) + <= high)) + { + n->sections[i] = s; + ++i; + } + } + + *pm = n; + } + + return true; +} + +/* The structure of the runtime procedure descriptor created by the + loader for use by the static exception system. */ + +typedef struct runtime_pdr { + bfd_vma adr; /* memory address of start of procedure */ + long regmask; /* save register mask */ + long regoffset; /* save register offset */ + long fregmask; /* save floating point register mask */ + long fregoffset; /* save floating point register offset */ + long frameoffset; /* frame size */ + short framereg; /* frame pointer register */ + short pcreg; /* offset or reg of return pc */ + long irpss; /* index into the runtime string table */ + long reserved; + struct exception_info *exception_info;/* pointer to exception array */ +} RPDR, *pRPDR; +#define cbRPDR sizeof(RPDR) +#define rpdNil ((pRPDR) 0) + +/* Swap RPDR (runtime procedure table entry) for output. */ + +static void ecoff_swap_rpdr_out + PARAMS ((bfd *, const RPDR *, struct rpdr_ext *)); + +static void +ecoff_swap_rpdr_out (abfd, in, ex) + bfd *abfd; + const RPDR *in; + struct rpdr_ext *ex; +{ + /* ecoff_put_off was defined in ecoffswap.h. */ + ecoff_put_off (abfd, in->adr, (bfd_byte *) ex->p_adr); + bfd_h_put_32 (abfd, in->regmask, (bfd_byte *) ex->p_regmask); + bfd_h_put_32 (abfd, in->regoffset, (bfd_byte *) ex->p_regoffset); + bfd_h_put_32 (abfd, in->fregmask, (bfd_byte *) ex->p_fregmask); + bfd_h_put_32 (abfd, in->fregoffset, (bfd_byte *) ex->p_fregoffset); + bfd_h_put_32 (abfd, in->frameoffset, (bfd_byte *) ex->p_frameoffset); + + bfd_h_put_16 (abfd, in->framereg, (bfd_byte *) ex->p_framereg); + bfd_h_put_16 (abfd, in->pcreg, (bfd_byte *) ex->p_pcreg); + + bfd_h_put_32 (abfd, in->irpss, (bfd_byte *) ex->p_irpss); +#if 0 /* FIXME */ + ecoff_put_off (abfd, in->exception_info, (bfd_byte *) ex->p_exception_info); +#endif +} + +/* Read ECOFF debugging information from a .mdebug section into a + ecoff_debug_info structure. */ + +boolean +_bfd_mips_elf_read_ecoff_info (abfd, section, debug) + bfd *abfd; + asection *section; + struct ecoff_debug_info *debug; +{ + HDRR *symhdr; + const struct ecoff_debug_swap *swap; + char *ext_hdr = NULL; + + swap = get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap; + + ext_hdr = (char *) bfd_malloc ((size_t) swap->external_hdr_size); + if (ext_hdr == NULL && swap->external_hdr_size != 0) + goto error_return; + + if (bfd_get_section_contents (abfd, section, ext_hdr, (file_ptr) 0, + swap->external_hdr_size) + == false) + goto error_return; + + symhdr = &debug->symbolic_header; + (*swap->swap_hdr_in) (abfd, ext_hdr, symhdr); + + /* The symbolic header contains absolute file offsets and sizes to + read. */ +#define READ(ptr, offset, count, size, type) \ + if (symhdr->count == 0) \ + debug->ptr = NULL; \ + else \ + { \ + debug->ptr = (type) bfd_malloc ((size_t) (size * symhdr->count)); \ + if (debug->ptr == NULL) \ + goto error_return; \ + if (bfd_seek (abfd, (file_ptr) symhdr->offset, SEEK_SET) != 0 \ + || (bfd_read (debug->ptr, size, symhdr->count, \ + abfd) != size * symhdr->count)) \ + goto error_return; \ + } + + READ (line, cbLineOffset, cbLine, sizeof (unsigned char), unsigned char *); + READ (external_dnr, cbDnOffset, idnMax, swap->external_dnr_size, PTR); + READ (external_pdr, cbPdOffset, ipdMax, swap->external_pdr_size, PTR); + READ (external_sym, cbSymOffset, isymMax, swap->external_sym_size, PTR); + READ (external_opt, cbOptOffset, ioptMax, swap->external_opt_size, PTR); + READ (external_aux, cbAuxOffset, iauxMax, sizeof (union aux_ext), + union aux_ext *); + READ (ss, cbSsOffset, issMax, sizeof (char), char *); + READ (ssext, cbSsExtOffset, issExtMax, sizeof (char), char *); + READ (external_fdr, cbFdOffset, ifdMax, swap->external_fdr_size, PTR); + READ (external_rfd, cbRfdOffset, crfd, swap->external_rfd_size, PTR); + READ (external_ext, cbExtOffset, iextMax, swap->external_ext_size, PTR); +#undef READ + + debug->fdr = NULL; + debug->adjust = NULL; + + return true; + + error_return: + if (ext_hdr != NULL) + free (ext_hdr); + if (debug->line != NULL) + free (debug->line); + if (debug->external_dnr != NULL) + free (debug->external_dnr); + if (debug->external_pdr != NULL) + free (debug->external_pdr); + if (debug->external_sym != NULL) + free (debug->external_sym); + if (debug->external_opt != NULL) + free (debug->external_opt); + if (debug->external_aux != NULL) + free (debug->external_aux); + if (debug->ss != NULL) + free (debug->ss); + if (debug->ssext != NULL) + free (debug->ssext); + if (debug->external_fdr != NULL) + free (debug->external_fdr); + if (debug->external_rfd != NULL) + free (debug->external_rfd); + if (debug->external_ext != NULL) + free (debug->external_ext); + return false; +} + +/* MIPS ELF local labels start with '$', not 'L'. */ + +/*ARGSUSED*/ +static boolean +mips_elf_is_local_label_name (abfd, 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); +} + +/* MIPS ELF uses a special find_nearest_line routine in order the + handle the ECOFF debugging information. */ + +struct mips_elf_find_line +{ + struct ecoff_debug_info d; + struct ecoff_find_line i; +}; + +boolean +_bfd_mips_elf_find_nearest_line (abfd, section, symbols, offset, filename_ptr, + functionname_ptr, line_ptr) + bfd *abfd; + asection *section; + asymbol **symbols; + bfd_vma offset; + const char **filename_ptr; + const char **functionname_ptr; + unsigned int *line_ptr; +{ + asection *msec; + + if (_bfd_dwarf2_find_nearest_line (abfd, section, symbols, offset, + filename_ptr, functionname_ptr, + line_ptr)) + return true; + + msec = bfd_get_section_by_name (abfd, ".mdebug"); + if (msec != NULL) + { + flagword origflags; + struct mips_elf_find_line *fi; + const struct ecoff_debug_swap * const swap = + get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap; + + /* If we are called during a link, mips_elf_final_link may have + cleared the SEC_HAS_CONTENTS field. We force it back on here + if appropriate (which it normally will be). */ + origflags = msec->flags; + if (elf_section_data (msec)->this_hdr.sh_type != SHT_NOBITS) + msec->flags |= SEC_HAS_CONTENTS; + + fi = elf_tdata (abfd)->find_line_info; + if (fi == NULL) + { + bfd_size_type external_fdr_size; + char *fraw_src; + char *fraw_end; + struct fdr *fdr_ptr; + + fi = ((struct mips_elf_find_line *) + bfd_zalloc (abfd, sizeof (struct mips_elf_find_line))); + if (fi == NULL) + { + msec->flags = origflags; + return false; + } + + if (! _bfd_mips_elf_read_ecoff_info (abfd, msec, &fi->d)) + { + msec->flags = origflags; + return false; + } + + /* Swap in the FDR information. */ + fi->d.fdr = ((struct fdr *) + bfd_alloc (abfd, + (fi->d.symbolic_header.ifdMax * + sizeof (struct fdr)))); + if (fi->d.fdr == NULL) + { + msec->flags = origflags; + return false; + } + external_fdr_size = swap->external_fdr_size; + fdr_ptr = fi->d.fdr; + fraw_src = (char *) fi->d.external_fdr; + fraw_end = (fraw_src + + fi->d.symbolic_header.ifdMax * external_fdr_size); + for (; fraw_src < fraw_end; fraw_src += external_fdr_size, fdr_ptr++) + (*swap->swap_fdr_in) (abfd, (PTR) fraw_src, fdr_ptr); + + elf_tdata (abfd)->find_line_info = fi; + + /* Note that we don't bother to ever free this information. + find_nearest_line is either called all the time, as in + objdump -l, so the information should be saved, or it is + rarely called, as in ld error messages, so the memory + wasted is unimportant. Still, it would probably be a + good idea for free_cached_info to throw it away. */ + } + + if (_bfd_ecoff_locate_line (abfd, section, offset, &fi->d, swap, + &fi->i, filename_ptr, functionname_ptr, + line_ptr)) + { + msec->flags = origflags; + return true; + } + + msec->flags = origflags; + } + + /* Fall back on the generic ELF find_nearest_line routine. */ + + return _bfd_elf_find_nearest_line (abfd, section, symbols, offset, + filename_ptr, functionname_ptr, + line_ptr); +} + + /* The mips16 compiler uses a couple of special sections to handle + floating point arguments. + + Section names that look like .mips16.fn.FNNAME contain stubs that + copy floating point arguments from the fp regs to the gp regs and + then jump to FNNAME. If any 32 bit function calls FNNAME, the + call should be redirected to the stub instead. If no 32 bit + function calls FNNAME, the stub should be discarded. We need to + consider any reference to the function, not just a call, because + if the address of the function is taken we will need the stub, + since the address might be passed to a 32 bit function. + + Section names that look like .mips16.call.FNNAME contain stubs + that copy floating point arguments from the gp regs to the fp + regs and then jump to FNNAME. If FNNAME is a 32 bit function, + then any 16 bit function that calls FNNAME should be redirected + to the stub instead. If FNNAME is not a 32 bit function, the + stub should be discarded. + + .mips16.call.fp.FNNAME sections are similar, but contain stubs + which call FNNAME and then copy the return value from the fp regs + to the gp regs. These stubs store the return value in $18 while + calling FNNAME; any function which might call one of these stubs + must arrange to save $18 around the call. (This case is not + needed for 32 bit functions that call 16 bit functions, because + 16 bit functions always return floating point values in both + $f0/$f1 and $2/$3.) + + Note that in all cases FNNAME might be defined statically. + Therefore, FNNAME is not used literally. Instead, the relocation + information will indicate which symbol the section is for. + + We record any stubs that we find in the symbol table. */ + +#define FN_STUB ".mips16.fn." +#define CALL_STUB ".mips16.call." +#define CALL_FP_STUB ".mips16.call.fp." + +/* The MIPS ELF linker needs additional information for each symbol in + the global hash table. */ + +struct mips_elf_link_hash_entry +{ + struct elf_link_hash_entry root; + + /* External symbol information. */ + EXTR esym; + + /* Number of MIPS_32 or MIPS_REL32 relocs against this symbol. */ + unsigned int mips_32_relocs; + + /* If there is a stub that 32 bit functions should use to call this + 16 bit function, this points to the section containing the stub. */ + asection *fn_stub; + + /* Whether we need the fn_stub; this is set if this symbol appears + in any relocs other than a 16 bit call. */ + boolean need_fn_stub; + + /* If there is a stub that 16 bit functions should use to call this + 32 bit function, this points to the section containing the stub. */ + asection *call_stub; + + /* This is like the call_stub field, but it is used if the function + being called returns a floating point value. */ + asection *call_fp_stub; +}; + +/* MIPS ELF linker hash table. */ + +struct mips_elf_link_hash_table +{ + struct elf_link_hash_table root; + /* String section indices for the dynamic section symbols. */ + bfd_size_type dynsym_sec_strindex[SIZEOF_MIPS_DYNSYM_SECNAMES]; + /* The number of .rtproc entries. */ + bfd_size_type procedure_count; + /* The size of the .compact_rel section (if SGI_COMPAT). */ + bfd_size_type compact_rel_size; + /* This flag indicates that the value of DT_MIPS_RLD_MAP dynamic + entry is set to the address of __rld_obj_head as in Irix 5. */ + boolean use_rld_obj_head; + /* This is the value of the __rld_map or __rld_obj_head symbol. */ + bfd_vma rld_value; + /* This is set if we see any mips16 stub sections. */ + boolean mips16_stubs_seen; +}; + +/* Look up an entry in a MIPS ELF linker hash table. */ + +#define mips_elf_link_hash_lookup(table, string, create, copy, follow) \ + ((struct mips_elf_link_hash_entry *) \ + elf_link_hash_lookup (&(table)->root, (string), (create), \ + (copy), (follow))) + +/* Traverse a MIPS ELF linker hash table. */ + +#define mips_elf_link_hash_traverse(table, func, info) \ + (elf_link_hash_traverse \ + (&(table)->root, \ + (boolean (*) PARAMS ((struct elf_link_hash_entry *, PTR))) (func), \ + (info))) + +/* Get the MIPS ELF linker hash table from a link_info structure. */ + +#define mips_elf_hash_table(p) \ + ((struct mips_elf_link_hash_table *) ((p)->hash)) + +static boolean mips_elf_output_extsym + PARAMS ((struct mips_elf_link_hash_entry *, PTR)); + +/* Create an entry in a MIPS ELF linker hash table. */ + +static struct bfd_hash_entry * +mips_elf_link_hash_newfunc (entry, table, string) + struct bfd_hash_entry *entry; + struct bfd_hash_table *table; + const char *string; +{ + struct mips_elf_link_hash_entry *ret = + (struct mips_elf_link_hash_entry *) entry; + + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (ret == (struct mips_elf_link_hash_entry *) NULL) + ret = ((struct mips_elf_link_hash_entry *) + bfd_hash_allocate (table, + sizeof (struct mips_elf_link_hash_entry))); + if (ret == (struct mips_elf_link_hash_entry *) NULL) + return (struct bfd_hash_entry *) ret; + + /* Call the allocation method of the superclass. */ + ret = ((struct mips_elf_link_hash_entry *) + _bfd_elf_link_hash_newfunc ((struct bfd_hash_entry *) ret, + table, string)); + if (ret != (struct mips_elf_link_hash_entry *) NULL) + { + /* Set local fields. */ + memset (&ret->esym, 0, sizeof (EXTR)); + /* We use -2 as a marker to indicate that the information has + not been set. -1 means there is no associated ifd. */ + ret->esym.ifd = -2; + ret->mips_32_relocs = 0; + ret->fn_stub = NULL; + ret->need_fn_stub = false; + ret->call_stub = NULL; + ret->call_fp_stub = NULL; + } + + return (struct bfd_hash_entry *) ret; +} + +/* Create a MIPS ELF linker hash table. */ + +static struct bfd_link_hash_table * +mips_elf_link_hash_table_create (abfd) + bfd *abfd; +{ + struct mips_elf_link_hash_table *ret; + unsigned int i; + + ret = ((struct mips_elf_link_hash_table *) + bfd_alloc (abfd, sizeof (struct mips_elf_link_hash_table))); + if (ret == (struct mips_elf_link_hash_table *) NULL) + return NULL; + + if (! _bfd_elf_link_hash_table_init (&ret->root, abfd, + mips_elf_link_hash_newfunc)) + { + bfd_release (abfd, ret); + return NULL; + } + + for (i = 0; i < SIZEOF_MIPS_DYNSYM_SECNAMES; i++) + ret->dynsym_sec_strindex[i] = (bfd_size_type) -1; + ret->procedure_count = 0; + ret->compact_rel_size = 0; + ret->use_rld_obj_head = false; + ret->rld_value = 0; + ret->mips16_stubs_seen = false; + + return &ret->root.root; +} + +/* Hook called by the linker routine which adds symbols from an object + file. We must handle the special MIPS section numbers here. */ + +/*ARGSUSED*/ +static boolean +mips_elf_add_symbol_hook (abfd, info, sym, namep, flagsp, secp, valp) + bfd *abfd; + struct bfd_link_info *info; + const Elf_Internal_Sym *sym; + const char **namep; + flagword *flagsp; + asection **secp; + bfd_vma *valp; +{ + if (SGI_COMPAT (abfd) + && (abfd->flags & DYNAMIC) != 0 + && strcmp (*namep, "_rld_new_interface") == 0) + { + /* Skip Irix 5 rld entry name. */ + *namep = NULL; + return true; + } + + switch (sym->st_shndx) + { + case SHN_COMMON: + /* Common symbols less than the GP size are automatically + treated as SHN_MIPS_SCOMMON symbols. */ + if (sym->st_size > elf_gp_size (abfd)) + break; + /* Fall through. */ + case SHN_MIPS_SCOMMON: + *secp = bfd_make_section_old_way (abfd, ".scommon"); + (*secp)->flags |= SEC_IS_COMMON; + *valp = sym->st_size; + break; + + case SHN_MIPS_TEXT: + /* This section is used in a shared object. */ + if (mips_elf_text_section_ptr == NULL) + { + /* Initialize the section. */ + mips_elf_text_section.name = ".text"; + mips_elf_text_section.flags = SEC_NO_FLAGS; + mips_elf_text_section.output_section = NULL; + mips_elf_text_section.symbol = &mips_elf_text_symbol; + mips_elf_text_section.symbol_ptr_ptr = &mips_elf_text_symbol_ptr; + mips_elf_text_symbol.name = ".text"; + mips_elf_text_symbol.flags = BSF_SECTION_SYM; + mips_elf_text_symbol.section = &mips_elf_text_section; + mips_elf_text_symbol_ptr = &mips_elf_text_symbol; + mips_elf_text_section_ptr = &mips_elf_text_section; + } + /* This code used to do *secp = bfd_und_section_ptr if + info->shared. I don't know why, and that doesn't make sense, + so I took it out. */ + *secp = mips_elf_text_section_ptr; + break; + + case SHN_MIPS_ACOMMON: + /* Fall through. XXX Can we treat this as allocated data? */ + case SHN_MIPS_DATA: + /* This section is used in a shared object. */ + if (mips_elf_data_section_ptr == NULL) + { + /* Initialize the section. */ + mips_elf_data_section.name = ".data"; + mips_elf_data_section.flags = SEC_NO_FLAGS; + mips_elf_data_section.output_section = NULL; + mips_elf_data_section.symbol = &mips_elf_data_symbol; + mips_elf_data_section.symbol_ptr_ptr = &mips_elf_data_symbol_ptr; + mips_elf_data_symbol.name = ".data"; + mips_elf_data_symbol.flags = BSF_SECTION_SYM; + mips_elf_data_symbol.section = &mips_elf_data_section; + mips_elf_data_symbol_ptr = &mips_elf_data_symbol; + mips_elf_data_section_ptr = &mips_elf_data_section; + } + /* This code used to do *secp = bfd_und_section_ptr if + info->shared. I don't know why, and that doesn't make sense, + so I took it out. */ + *secp = mips_elf_data_section_ptr; + break; + + case SHN_MIPS_SUNDEFINED: + *secp = bfd_und_section_ptr; + break; + } + + if (SGI_COMPAT (abfd) + && ! info->shared + && info->hash->creator == abfd->xvec + && strcmp (*namep, "__rld_obj_head") == 0) + { + struct elf_link_hash_entry *h; + + /* Mark __rld_obj_head as dynamic. */ + h = NULL; + if (! (_bfd_generic_link_add_one_symbol + (info, abfd, *namep, BSF_GLOBAL, *secp, + (bfd_vma) *valp, (const char *) NULL, false, + get_elf_backend_data (abfd)->collect, + (struct bfd_link_hash_entry **) &h))) + return false; + h->elf_link_hash_flags &=~ ELF_LINK_NON_ELF; + h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR; + h->type = STT_OBJECT; + + if (! bfd_elf32_link_record_dynamic_symbol (info, h)) + return false; + + mips_elf_hash_table (info)->use_rld_obj_head = true; + } + + /* If this is a mips16 text symbol, add 1 to the value to make it + odd. This will cause something like .word SYM to come up with + the right value when it is loaded into the PC. */ + if (sym->st_other == STO_MIPS16) + ++*valp; + + return true; +} + +/* Structure used to pass information to mips_elf_output_extsym. */ + +struct extsym_info +{ + bfd *abfd; + struct bfd_link_info *info; + struct ecoff_debug_info *debug; + const struct ecoff_debug_swap *swap; + boolean failed; +}; + +/* This routine is used to write out ECOFF debugging external symbol + information. It is called via mips_elf_link_hash_traverse. The + ECOFF external symbol information must match the ELF external + symbol information. Unfortunately, at this point we don't know + whether a symbol is required by reloc information, so the two + tables may wind up being different. We must sort out the external + symbol information before we can set the final size of the .mdebug + section, and we must set the size of the .mdebug section before we + can relocate any sections, and we can't know which symbols are + required by relocation until we relocate the sections. + Fortunately, it is relatively unlikely that any symbol will be + stripped but required by a reloc. In particular, it can not happen + when generating a final executable. */ + +static boolean +mips_elf_output_extsym (h, data) + struct mips_elf_link_hash_entry *h; + PTR data; +{ + struct extsym_info *einfo = (struct extsym_info *) data; + boolean strip; + asection *sec, *output_section; + + if (h->root.indx == -2) + strip = false; + else if (((h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0 + || (h->root.elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) != 0) + && (h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0 + && (h->root.elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) == 0) + strip = true; + else if (einfo->info->strip == strip_all + || (einfo->info->strip == strip_some + && bfd_hash_lookup (einfo->info->keep_hash, + h->root.root.root.string, + false, false) == NULL)) + strip = true; + else + strip = false; + + if (strip) + return true; + + if (h->esym.ifd == -2) + { + h->esym.jmptbl = 0; + h->esym.cobol_main = 0; + h->esym.weakext = 0; + h->esym.reserved = 0; + h->esym.ifd = ifdNil; + h->esym.asym.value = 0; + h->esym.asym.st = stGlobal; + + if (SGI_COMPAT (einfo->abfd) + && (h->root.root.type == bfd_link_hash_undefined + || h->root.root.type == bfd_link_hash_undefweak)) + { + const char *name; + + /* Use undefined class. Also, set class and type for some + special symbols. */ + name = h->root.root.root.string; + if (strcmp (name, mips_elf_dynsym_rtproc_names[0]) == 0 + || strcmp (name, mips_elf_dynsym_rtproc_names[1]) == 0) + { + h->esym.asym.sc = scData; + h->esym.asym.st = stLabel; + h->esym.asym.value = 0; + } + else if (strcmp (name, mips_elf_dynsym_rtproc_names[2]) == 0) + { + h->esym.asym.sc = scAbs; + h->esym.asym.st = stLabel; + h->esym.asym.value = + mips_elf_hash_table (einfo->info)->procedure_count; + } + else if (strcmp (name, "_gp_disp") == 0) + { + h->esym.asym.sc = scAbs; + h->esym.asym.st = stLabel; + h->esym.asym.value = elf_gp (einfo->abfd); + } + else + h->esym.asym.sc = scUndefined; + } + else if (h->root.root.type != bfd_link_hash_defined + && h->root.root.type != bfd_link_hash_defweak) + h->esym.asym.sc = scAbs; + else + { + const char *name; + + sec = h->root.root.u.def.section; + output_section = sec->output_section; + + /* When making a shared library and symbol h is the one from + the another shared library, OUTPUT_SECTION may be null. */ + if (output_section == NULL) + h->esym.asym.sc = scUndefined; + else + { + name = bfd_section_name (output_section->owner, output_section); + + if (strcmp (name, ".text") == 0) + h->esym.asym.sc = scText; + else if (strcmp (name, ".data") == 0) + h->esym.asym.sc = scData; + else if (strcmp (name, ".sdata") == 0) + h->esym.asym.sc = scSData; + else if (strcmp (name, ".rodata") == 0 + || strcmp (name, ".rdata") == 0) + h->esym.asym.sc = scRData; + else if (strcmp (name, ".bss") == 0) + h->esym.asym.sc = scBss; + else if (strcmp (name, ".sbss") == 0) + h->esym.asym.sc = scSBss; + else if (strcmp (name, ".init") == 0) + h->esym.asym.sc = scInit; + else if (strcmp (name, ".fini") == 0) + h->esym.asym.sc = scFini; + else + h->esym.asym.sc = scAbs; + } + } + + h->esym.asym.reserved = 0; + h->esym.asym.index = indexNil; + } + + if (h->root.root.type == bfd_link_hash_common) + h->esym.asym.value = h->root.root.u.c.size; + else if (h->root.root.type == bfd_link_hash_defined + || h->root.root.type == bfd_link_hash_defweak) + { + if (h->esym.asym.sc == scCommon) + h->esym.asym.sc = scBss; + else if (h->esym.asym.sc == scSCommon) + h->esym.asym.sc = scSBss; + + sec = h->root.root.u.def.section; + output_section = sec->output_section; + if (output_section != NULL) + h->esym.asym.value = (h->root.root.u.def.value + + sec->output_offset + + output_section->vma); + else + h->esym.asym.value = 0; + } + else if ((h->root.elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0) + { + /* Set type and value for a symbol with a function stub. */ + h->esym.asym.st = stProc; + sec = h->root.root.u.def.section; + if (sec == NULL) + h->esym.asym.value = 0; + else + { + output_section = sec->output_section; + if (output_section != NULL) + h->esym.asym.value = (h->root.plt_offset + + sec->output_offset + + output_section->vma); + else + h->esym.asym.value = 0; + } +#if 0 /* FIXME? */ + h->esym.ifd = 0; +#endif + } + + if (! bfd_ecoff_debug_one_external (einfo->abfd, einfo->debug, einfo->swap, + h->root.root.root.string, + &h->esym)) + { + einfo->failed = true; + return false; + } + + return true; +} + +/* Create a runtime procedure table from the .mdebug section. */ + +static boolean +mips_elf_create_procedure_table (handle, abfd, info, s, debug) + PTR handle; + bfd *abfd; + struct bfd_link_info *info; + asection *s; + struct ecoff_debug_info *debug; +{ + const struct ecoff_debug_swap *swap; + HDRR *hdr = &debug->symbolic_header; + RPDR *rpdr, *rp; + struct rpdr_ext *erp; + PTR rtproc; + struct pdr_ext *epdr; + struct sym_ext *esym; + char *ss, **sv; + char *str; + unsigned long size, count; + unsigned long sindex; + unsigned long i; + PDR pdr; + SYMR sym; + const char *no_name_func = "static procedure (no name)"; + + epdr = NULL; + rpdr = NULL; + esym = NULL; + ss = NULL; + sv = NULL; + + swap = get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap; + + sindex = strlen (no_name_func) + 1; + count = hdr->ipdMax; + if (count > 0) + { + size = swap->external_pdr_size; + + epdr = (struct pdr_ext *) bfd_malloc (size * count); + if (epdr == NULL) + goto error_return; + + if (! _bfd_ecoff_get_accumulated_pdr (handle, (PTR) epdr)) + goto error_return; + + size = sizeof (RPDR); + rp = rpdr = (RPDR *) bfd_malloc (size * count); + if (rpdr == NULL) + goto error_return; + + sv = (char **) bfd_malloc (sizeof (char *) * count); + if (sv == NULL) + goto error_return; + + count = hdr->isymMax; + size = swap->external_sym_size; + esym = (struct sym_ext *) bfd_malloc (size * count); + if (esym == NULL) + goto error_return; + + if (! _bfd_ecoff_get_accumulated_sym (handle, (PTR) esym)) + goto error_return; + + count = hdr->issMax; + ss = (char *) bfd_malloc (count); + if (ss == NULL) + goto error_return; + if (! _bfd_ecoff_get_accumulated_ss (handle, (PTR) ss)) + goto error_return; + + count = hdr->ipdMax; + for (i = 0; i < count; i++, rp++) + { + (*swap->swap_pdr_in) (abfd, (PTR) (epdr + i), &pdr); + (*swap->swap_sym_in) (abfd, (PTR) &esym[pdr.isym], &sym); + rp->adr = sym.value; + rp->regmask = pdr.regmask; + rp->regoffset = pdr.regoffset; + rp->fregmask = pdr.fregmask; + rp->fregoffset = pdr.fregoffset; + rp->frameoffset = pdr.frameoffset; + rp->framereg = pdr.framereg; + rp->pcreg = pdr.pcreg; + rp->irpss = sindex; + sv[i] = ss + sym.iss; + sindex += strlen (sv[i]) + 1; + } + } + + size = sizeof (struct rpdr_ext) * (count + 2) + sindex; + size = BFD_ALIGN (size, 16); + rtproc = (PTR) bfd_alloc (abfd, size); + if (rtproc == NULL) + { + mips_elf_hash_table (info)->procedure_count = 0; + goto error_return; + } + + mips_elf_hash_table (info)->procedure_count = count + 2; + + erp = (struct rpdr_ext *) rtproc; + memset (erp, 0, sizeof (struct rpdr_ext)); + erp++; + str = (char *) rtproc + sizeof (struct rpdr_ext) * (count + 2); + strcpy (str, no_name_func); + str += strlen (no_name_func) + 1; + for (i = 0; i < count; i++) + { + ecoff_swap_rpdr_out (abfd, rpdr + i, erp + i); + strcpy (str, sv[i]); + str += strlen (sv[i]) + 1; + } + ecoff_put_off (abfd, (bfd_vma) -1, (bfd_byte *) (erp + count)->p_adr); + + /* Set the size and contents of .rtproc section. */ + s->_raw_size = size; + s->contents = (bfd_byte *) rtproc; + + /* Skip this section later on (I don't think this currently + matters, but someday it might). */ + s->link_order_head = (struct bfd_link_order *) NULL; + + if (epdr != NULL) + free (epdr); + if (rpdr != NULL) + free (rpdr); + if (esym != NULL) + free (esym); + if (ss != NULL) + free (ss); + if (sv != NULL) + free (sv); + + return true; + + error_return: + if (epdr != NULL) + free (epdr); + if (rpdr != NULL) + free (rpdr); + if (esym != NULL) + free (esym); + if (ss != NULL) + free (ss); + if (sv != NULL) + free (sv); + return false; +} + +/* A comparison routine used to sort .gptab entries. */ + +static int +gptab_compare (p1, p2) + const PTR p1; + const PTR p2; +{ + const Elf32_gptab *a1 = (const Elf32_gptab *) p1; + const Elf32_gptab *a2 = (const Elf32_gptab *) p2; + + return a1->gt_entry.gt_g_value - a2->gt_entry.gt_g_value; +} + +/* We need to use a special link routine to handle the .reginfo and + the .mdebug sections. We need to merge all instances of these + sections together, not write them all out sequentially. */ + +static boolean +mips_elf_final_link (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + asection **secpp; + asection *o; + struct bfd_link_order *p; + asection *reginfo_sec, *mdebug_sec, *gptab_data_sec, *gptab_bss_sec; + asection *rtproc_sec; + Elf32_RegInfo reginfo; + struct ecoff_debug_info debug; + const struct ecoff_debug_swap *swap + = get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap; + HDRR *symhdr = &debug.symbolic_header; + PTR mdebug_handle = NULL; + + /* Drop the .options section, since it has special semantics which I + haven't bothered to figure out. */ + for (secpp = &abfd->sections; *secpp != NULL; secpp = &(*secpp)->next) + { + if (strcmp ((*secpp)->name, ".options") == 0) + { + for (p = (*secpp)->link_order_head; p != NULL; p = p->next) + if (p->type == bfd_indirect_link_order) + p->u.indirect.section->flags &=~ SEC_HAS_CONTENTS; + (*secpp)->link_order_head = NULL; + *secpp = (*secpp)->next; + --abfd->section_count; + break; + } + } + + /* Get a value for the GP register. */ + if (elf_gp (abfd) == 0) + { + struct bfd_link_hash_entry *h; + + h = bfd_link_hash_lookup (info->hash, "_gp", false, false, true); + if (h != (struct bfd_link_hash_entry *) NULL + && h->type == bfd_link_hash_defined) + elf_gp (abfd) = (h->u.def.value + + h->u.def.section->output_section->vma + + h->u.def.section->output_offset); + else if (info->relocateable) + { + bfd_vma lo; + + /* Make up a value. */ + lo = (bfd_vma) -1; + for (o = abfd->sections; o != (asection *) NULL; o = o->next) + { + if (o->vma < lo + && (strcmp (o->name, ".sbss") == 0 + || strcmp (o->name, ".sdata") == 0 + || strcmp (o->name, ".lit4") == 0 + || strcmp (o->name, ".lit8") == 0)) + lo = o->vma; + } + elf_gp (abfd) = lo + ELF_MIPS_GP_OFFSET (abfd); + } + else + { + /* If the relocate_section function needs to do a reloc + involving the GP value, it should make a reloc_dangerous + callback to warn that GP is not defined. */ + } + } + + /* Go through the sections and collect the .reginfo and .mdebug + information. */ + reginfo_sec = NULL; + mdebug_sec = NULL; + gptab_data_sec = NULL; + gptab_bss_sec = NULL; + for (o = abfd->sections; o != (asection *) NULL; o = o->next) + { + if (strcmp (o->name, ".reginfo") == 0) + { + memset (®info, 0, sizeof reginfo); + + /* We have found the .reginfo section in the output file. + Look through all the link_orders comprising it and merge + the information together. */ + for (p = o->link_order_head; + p != (struct bfd_link_order *) NULL; + p = p->next) + { + asection *input_section; + bfd *input_bfd; + Elf32_External_RegInfo ext; + Elf32_RegInfo sub; + + if (p->type != bfd_indirect_link_order) + { + if (p->type == bfd_fill_link_order) + continue; + abort (); + } + + input_section = p->u.indirect.section; + input_bfd = input_section->owner; + + /* The linker emulation code has probably clobbered the + size to be zero bytes. */ + if (input_section->_raw_size == 0) + input_section->_raw_size = sizeof (Elf32_External_RegInfo); + + if (! bfd_get_section_contents (input_bfd, input_section, + (PTR) &ext, + (file_ptr) 0, + sizeof ext)) + return false; + + bfd_mips_elf32_swap_reginfo_in (input_bfd, &ext, &sub); + + reginfo.ri_gprmask |= sub.ri_gprmask; + reginfo.ri_cprmask[0] |= sub.ri_cprmask[0]; + reginfo.ri_cprmask[1] |= sub.ri_cprmask[1]; + reginfo.ri_cprmask[2] |= sub.ri_cprmask[2]; + reginfo.ri_cprmask[3] |= sub.ri_cprmask[3]; + + /* ri_gp_value is set by the function + mips_elf32_section_processing when the section is + finally written out. */ + + /* Hack: reset the SEC_HAS_CONTENTS flag so that + elf_link_input_bfd ignores this section. */ + input_section->flags &=~ SEC_HAS_CONTENTS; + } + + /* Force the section size to the value we want. */ + o->_raw_size = sizeof (Elf32_External_RegInfo); + + /* Skip this section later on (I don't think this currently + matters, but someday it might). */ + o->link_order_head = (struct bfd_link_order *) NULL; + + reginfo_sec = o; + } + + if (strcmp (o->name, ".mdebug") == 0) + { + struct extsym_info einfo; + + /* We have found the .mdebug section in the output file. + Look through all the link_orders comprising it and merge + the information together. */ + symhdr->magic = swap->sym_magic; + /* FIXME: What should the version stamp be? */ + symhdr->vstamp = 0; + symhdr->ilineMax = 0; + symhdr->cbLine = 0; + symhdr->idnMax = 0; + symhdr->ipdMax = 0; + symhdr->isymMax = 0; + symhdr->ioptMax = 0; + symhdr->iauxMax = 0; + symhdr->issMax = 0; + symhdr->issExtMax = 0; + symhdr->ifdMax = 0; + symhdr->crfd = 0; + symhdr->iextMax = 0; + + /* We accumulate the debugging information itself in the + debug_info structure. */ + debug.line = NULL; + debug.external_dnr = NULL; + debug.external_pdr = NULL; + debug.external_sym = NULL; + debug.external_opt = NULL; + debug.external_aux = NULL; + debug.ss = NULL; + debug.ssext = debug.ssext_end = NULL; + debug.external_fdr = NULL; + debug.external_rfd = NULL; + debug.external_ext = debug.external_ext_end = NULL; + + mdebug_handle = bfd_ecoff_debug_init (abfd, &debug, swap, info); + if (mdebug_handle == (PTR) NULL) + return false; + + if (SGI_COMPAT (abfd)) + { + asection *s; + EXTR esym; + bfd_vma last; + unsigned int i; + static const char * const name[] = + { ".text", ".init", ".fini", ".data", + ".rodata", ".sdata", ".sbss", ".bss" }; + static const int sc[] = { scText, scInit, scFini, scData, + scRData, scSData, scSBss, scBss }; + + esym.jmptbl = 0; + esym.cobol_main = 0; + esym.weakext = 0; + esym.reserved = 0; + esym.ifd = ifdNil; + esym.asym.iss = issNil; + esym.asym.st = stLocal; + esym.asym.reserved = 0; + esym.asym.index = indexNil; + last = 0; + for (i = 0; i < 8; i++) + { + esym.asym.sc = sc[i]; + s = bfd_get_section_by_name (abfd, name[i]); + if (s != NULL) + { + esym.asym.value = s->vma; + last = s->vma + s->_raw_size; + } + else + esym.asym.value = last; + + if (! bfd_ecoff_debug_one_external (abfd, &debug, swap, + name[i], &esym)) + return false; + } + } + + for (p = o->link_order_head; + p != (struct bfd_link_order *) NULL; + p = p->next) + { + asection *input_section; + bfd *input_bfd; + const struct ecoff_debug_swap *input_swap; + struct ecoff_debug_info input_debug; + char *eraw_src; + char *eraw_end; + + if (p->type != bfd_indirect_link_order) + { + if (p->type == bfd_fill_link_order) + continue; + abort (); + } + + input_section = p->u.indirect.section; + input_bfd = input_section->owner; + + if (bfd_get_flavour (input_bfd) != bfd_target_elf_flavour + || (get_elf_backend_data (input_bfd) + ->elf_backend_ecoff_debug_swap) == NULL) + { + /* I don't know what a non MIPS ELF bfd would be + doing with a .mdebug section, but I don't really + want to deal with it. */ + continue; + } + + input_swap = (get_elf_backend_data (input_bfd) + ->elf_backend_ecoff_debug_swap); + + BFD_ASSERT (p->size == input_section->_raw_size); + + /* The ECOFF linking code expects that we have already + read in the debugging information and set up an + ecoff_debug_info structure, so we do that now. */ + if (! _bfd_mips_elf_read_ecoff_info (input_bfd, input_section, + &input_debug)) + return false; + + if (! (bfd_ecoff_debug_accumulate + (mdebug_handle, abfd, &debug, swap, input_bfd, + &input_debug, input_swap, info))) + return false; + + /* Loop through the external symbols. For each one with + interesting information, try to find the symbol in + the linker global hash table and save the information + for the output external symbols. */ + eraw_src = input_debug.external_ext; + eraw_end = (eraw_src + + (input_debug.symbolic_header.iextMax + * input_swap->external_ext_size)); + for (; + eraw_src < eraw_end; + eraw_src += input_swap->external_ext_size) + { + EXTR ext; + const char *name; + struct mips_elf_link_hash_entry *h; + + (*input_swap->swap_ext_in) (input_bfd, (PTR) eraw_src, &ext); + if (ext.asym.sc == scNil + || ext.asym.sc == scUndefined + || ext.asym.sc == scSUndefined) + continue; + + name = input_debug.ssext + ext.asym.iss; + h = mips_elf_link_hash_lookup (mips_elf_hash_table (info), + name, false, false, true); + if (h == NULL || h->esym.ifd != -2) + continue; + + if (ext.ifd != -1) + { + BFD_ASSERT (ext.ifd + < input_debug.symbolic_header.ifdMax); + ext.ifd = input_debug.ifdmap[ext.ifd]; + } + + h->esym = ext; + } + + /* Free up the information we just read. */ + free (input_debug.line); + free (input_debug.external_dnr); + free (input_debug.external_pdr); + free (input_debug.external_sym); + free (input_debug.external_opt); + free (input_debug.external_aux); + free (input_debug.ss); + free (input_debug.ssext); + free (input_debug.external_fdr); + free (input_debug.external_rfd); + free (input_debug.external_ext); + + /* Hack: reset the SEC_HAS_CONTENTS flag so that + elf_link_input_bfd ignores this section. */ + input_section->flags &=~ SEC_HAS_CONTENTS; + } + + if (SGI_COMPAT (abfd) && info->shared) + { + /* Create .rtproc section. */ + rtproc_sec = bfd_get_section_by_name (abfd, ".rtproc"); + if (rtproc_sec == NULL) + { + flagword flags = (SEC_HAS_CONTENTS | SEC_IN_MEMORY + | SEC_LINKER_CREATED | SEC_READONLY); + + rtproc_sec = bfd_make_section (abfd, ".rtproc"); + if (rtproc_sec == NULL + || ! bfd_set_section_flags (abfd, rtproc_sec, flags) + || ! bfd_set_section_alignment (abfd, rtproc_sec, 4)) + return false; + } + + if (! mips_elf_create_procedure_table (mdebug_handle, abfd, + info, rtproc_sec, &debug)) + return false; + } + + /* Build the external symbol information. */ + einfo.abfd = abfd; + einfo.info = info; + einfo.debug = &debug; + einfo.swap = swap; + einfo.failed = false; + mips_elf_link_hash_traverse (mips_elf_hash_table (info), + mips_elf_output_extsym, + (PTR) &einfo); + if (einfo.failed) + return false; + + /* Set the size of the .mdebug section. */ + o->_raw_size = bfd_ecoff_debug_size (abfd, &debug, swap); + + /* Skip this section later on (I don't think this currently + matters, but someday it might). */ + o->link_order_head = (struct bfd_link_order *) NULL; + + mdebug_sec = o; + } + + if (strncmp (o->name, ".gptab.", sizeof ".gptab." - 1) == 0) + { + const char *subname; + unsigned int c; + Elf32_gptab *tab; + Elf32_External_gptab *ext_tab; + unsigned int i; + + /* The .gptab.sdata and .gptab.sbss sections hold + information describing how the small data area would + change depending upon the -G switch. These sections + not used in executables files. */ + if (! info->relocateable) + { + asection **secpp; + + for (p = o->link_order_head; + p != (struct bfd_link_order *) NULL; + p = p->next) + { + asection *input_section; + + if (p->type != bfd_indirect_link_order) + { + if (p->type == bfd_fill_link_order) + continue; + abort (); + } + + input_section = p->u.indirect.section; + + /* Hack: reset the SEC_HAS_CONTENTS flag so that + elf_link_input_bfd ignores this section. */ + input_section->flags &=~ SEC_HAS_CONTENTS; + } + + /* Skip this section later on (I don't think this + currently matters, but someday it might). */ + o->link_order_head = (struct bfd_link_order *) NULL; + + /* Really remove the section. */ + for (secpp = &abfd->sections; + *secpp != o; + secpp = &(*secpp)->next) + ; + *secpp = (*secpp)->next; + --abfd->section_count; + + continue; + } + + /* There is one gptab for initialized data, and one for + uninitialized data. */ + if (strcmp (o->name, ".gptab.sdata") == 0) + gptab_data_sec = o; + else if (strcmp (o->name, ".gptab.sbss") == 0) + gptab_bss_sec = o; + else + { + (*_bfd_error_handler) + ("%s: illegal section name `%s'", + bfd_get_filename (abfd), o->name); + bfd_set_error (bfd_error_nonrepresentable_section); + return false; + } + + /* The linker script always combines .gptab.data and + .gptab.sdata into .gptab.sdata, and likewise for + .gptab.bss and .gptab.sbss. It is possible that there is + no .sdata or .sbss section in the output file, in which + case we must change the name of the output section. */ + subname = o->name + sizeof ".gptab" - 1; + if (bfd_get_section_by_name (abfd, subname) == NULL) + { + if (o == gptab_data_sec) + o->name = ".gptab.data"; + else + o->name = ".gptab.bss"; + subname = o->name + sizeof ".gptab" - 1; + BFD_ASSERT (bfd_get_section_by_name (abfd, subname) != NULL); + } + + /* Set up the first entry. */ + c = 1; + tab = (Elf32_gptab *) bfd_malloc (c * sizeof (Elf32_gptab)); + if (tab == NULL) + return false; + tab[0].gt_header.gt_current_g_value = elf_gp_size (abfd); + tab[0].gt_header.gt_unused = 0; + + /* Combine the input sections. */ + for (p = o->link_order_head; + p != (struct bfd_link_order *) NULL; + p = p->next) + { + asection *input_section; + bfd *input_bfd; + bfd_size_type size; + unsigned long last; + bfd_size_type gpentry; + + if (p->type != bfd_indirect_link_order) + { + if (p->type == bfd_fill_link_order) + continue; + abort (); + } + + input_section = p->u.indirect.section; + input_bfd = input_section->owner; + + /* Combine the gptab entries for this input section one + by one. We know that the input gptab entries are + sorted by ascending -G value. */ + size = bfd_section_size (input_bfd, input_section); + last = 0; + for (gpentry = sizeof (Elf32_External_gptab); + gpentry < size; + gpentry += sizeof (Elf32_External_gptab)) + { + Elf32_External_gptab ext_gptab; + Elf32_gptab int_gptab; + unsigned long val; + unsigned long add; + boolean exact; + unsigned int look; + + if (! (bfd_get_section_contents + (input_bfd, input_section, (PTR) &ext_gptab, + gpentry, sizeof (Elf32_External_gptab)))) + { + free (tab); + return false; + } + + bfd_mips_elf32_swap_gptab_in (input_bfd, &ext_gptab, + &int_gptab); + val = int_gptab.gt_entry.gt_g_value; + add = int_gptab.gt_entry.gt_bytes - last; + + exact = false; + for (look = 1; look < c; look++) + { + if (tab[look].gt_entry.gt_g_value >= val) + tab[look].gt_entry.gt_bytes += add; + + if (tab[look].gt_entry.gt_g_value == val) + exact = true; + } + + if (! exact) + { + Elf32_gptab *new_tab; + unsigned int max; + + /* We need a new table entry. */ + new_tab = ((Elf32_gptab *) + bfd_realloc ((PTR) tab, + (c + 1) * sizeof (Elf32_gptab))); + if (new_tab == NULL) + { + free (tab); + return false; + } + tab = new_tab; + tab[c].gt_entry.gt_g_value = val; + tab[c].gt_entry.gt_bytes = add; + + /* Merge in the size for the next smallest -G + value, since that will be implied by this new + value. */ + max = 0; + for (look = 1; look < c; look++) + { + if (tab[look].gt_entry.gt_g_value < val + && (max == 0 + || (tab[look].gt_entry.gt_g_value + > tab[max].gt_entry.gt_g_value))) + max = look; + } + if (max != 0) + tab[c].gt_entry.gt_bytes += + tab[max].gt_entry.gt_bytes; + + ++c; + } + + last = int_gptab.gt_entry.gt_bytes; + } + + /* Hack: reset the SEC_HAS_CONTENTS flag so that + elf_link_input_bfd ignores this section. */ + input_section->flags &=~ SEC_HAS_CONTENTS; + } + + /* The table must be sorted by -G value. */ + if (c > 2) + qsort (tab + 1, c - 1, sizeof (tab[0]), gptab_compare); + + /* Swap out the table. */ + ext_tab = ((Elf32_External_gptab *) + bfd_alloc (abfd, c * sizeof (Elf32_External_gptab))); + if (ext_tab == NULL) + { + free (tab); + return false; + } + + for (i = 0; i < c; i++) + bfd_mips_elf32_swap_gptab_out (abfd, tab + i, ext_tab + i); + free (tab); + + o->_raw_size = c * sizeof (Elf32_External_gptab); + o->contents = (bfd_byte *) ext_tab; + + /* Skip this section later on (I don't think this currently + matters, but someday it might). */ + o->link_order_head = (struct bfd_link_order *) NULL; + } + } + + /* Invoke the regular ELF backend linker to do all the work. */ + if (! bfd_elf32_bfd_final_link (abfd, info)) + return false; + + /* Now write out the computed sections. */ + + if (reginfo_sec != (asection *) NULL) + { + Elf32_External_RegInfo ext; + + bfd_mips_elf32_swap_reginfo_out (abfd, ®info, &ext); + if (! bfd_set_section_contents (abfd, reginfo_sec, (PTR) &ext, + (file_ptr) 0, sizeof ext)) + return false; + } + + if (mdebug_sec != (asection *) NULL) + { + BFD_ASSERT (abfd->output_has_begun); + if (! bfd_ecoff_write_accumulated_debug (mdebug_handle, abfd, &debug, + swap, info, + mdebug_sec->filepos)) + return false; + + bfd_ecoff_debug_free (mdebug_handle, abfd, &debug, swap, info); + } + + if (gptab_data_sec != (asection *) NULL) + { + if (! bfd_set_section_contents (abfd, gptab_data_sec, + gptab_data_sec->contents, + (file_ptr) 0, + gptab_data_sec->_raw_size)) + return false; + } + + if (gptab_bss_sec != (asection *) NULL) + { + if (! bfd_set_section_contents (abfd, gptab_bss_sec, + gptab_bss_sec->contents, + (file_ptr) 0, + gptab_bss_sec->_raw_size)) + return false; + } + + if (SGI_COMPAT (abfd)) + { + rtproc_sec = bfd_get_section_by_name (abfd, ".rtproc"); + if (rtproc_sec != NULL) + { + if (! bfd_set_section_contents (abfd, rtproc_sec, + rtproc_sec->contents, + (file_ptr) 0, + rtproc_sec->_raw_size)) + return false; + } + } + + return true; +} + +/* Handle a MIPS ELF HI16 reloc. */ + +static void +mips_elf_relocate_hi16 (input_bfd, relhi, rello, contents, addend) + bfd *input_bfd; + Elf_Internal_Rela *relhi; + Elf_Internal_Rela *rello; + bfd_byte *contents; + bfd_vma addend; +{ + bfd_vma insn; + bfd_vma addlo; + + insn = bfd_get_32 (input_bfd, contents + relhi->r_offset); + + addlo = bfd_get_32 (input_bfd, contents + rello->r_offset); + addlo &= 0xffff; + + addend += ((insn & 0xffff) << 16) + addlo; + + if ((addlo & 0x8000) != 0) + addend -= 0x10000; + if ((addend & 0x8000) != 0) + addend += 0x10000; + + bfd_put_32 (input_bfd, + (insn & 0xffff0000) | ((addend >> 16) & 0xffff), + contents + relhi->r_offset); +} + +/* Handle a MIPS ELF local GOT16 reloc. */ + +static boolean +mips_elf_relocate_got_local (output_bfd, input_bfd, sgot, relhi, rello, + contents, addend) + bfd *output_bfd; + bfd *input_bfd; + asection *sgot; + Elf_Internal_Rela *relhi; + Elf_Internal_Rela *rello; + bfd_byte *contents; + bfd_vma addend; +{ + unsigned int assigned_gotno; + unsigned int i; + bfd_vma insn; + bfd_vma addlo; + bfd_vma address; + bfd_vma hipage; + bfd_byte *got_contents; + struct mips_got_info *g; + + insn = bfd_get_32 (input_bfd, contents + relhi->r_offset); + + addlo = bfd_get_32 (input_bfd, contents + rello->r_offset); + addlo &= 0xffff; + + addend += ((insn & 0xffff) << 16) + addlo; + + if ((addlo & 0x8000) != 0) + addend -= 0x10000; + if ((addend & 0x8000) != 0) + addend += 0x10000; + + /* Get a got entry representing requested hipage. */ + BFD_ASSERT (elf_section_data (sgot) != NULL); + g = (struct mips_got_info *) elf_section_data (sgot)->tdata; + BFD_ASSERT (g != NULL); + + assigned_gotno = g->assigned_gotno; + got_contents = sgot->contents; + hipage = addend & 0xffff0000; + + for (i = MIPS_RESERVED_GOTNO; i < assigned_gotno; i++) + { + address = bfd_get_32 (input_bfd, got_contents + i * 4); + if (hipage == (address & 0xffff0000)) + break; + } + + if (i == assigned_gotno) + { + if (assigned_gotno >= g->local_gotno) + { + (*_bfd_error_handler) + ("more got entries are needed for hipage relocations"); + bfd_set_error (bfd_error_bad_value); + return false; + } + + bfd_put_32 (input_bfd, hipage, got_contents + assigned_gotno * 4); + ++g->assigned_gotno; + } + + i = - ELF_MIPS_GP_OFFSET (output_bfd) + i * 4; + bfd_put_32 (input_bfd, (insn & 0xffff0000) | (i & 0xffff), + contents + relhi->r_offset); + + return true; +} + +/* Handle MIPS ELF CALL16 reloc and global GOT16 reloc. */ + +static void +mips_elf_relocate_global_got (input_bfd, rel, contents, offset) + bfd *input_bfd; + Elf_Internal_Rela *rel; + bfd_byte *contents; + bfd_vma offset; +{ + bfd_vma insn; + + insn = bfd_get_32 (input_bfd, contents + rel->r_offset); + bfd_put_32 (input_bfd, + (insn & 0xffff0000) | (offset & 0xffff), + contents + rel->r_offset); +} + +/* Relocate a MIPS ELF section. */ + +static boolean +mips_elf_relocate_section (output_bfd, info, input_bfd, input_section, + contents, relocs, local_syms, local_sections) + bfd *output_bfd; + struct bfd_link_info *info; + bfd *input_bfd; + asection *input_section; + bfd_byte *contents; + Elf_Internal_Rela *relocs; + Elf_Internal_Sym *local_syms; + asection **local_sections; +{ + Elf_Internal_Shdr *symtab_hdr; + size_t locsymcount; + size_t extsymoff; + asection *sgot, *sreloc, *scpt; + bfd *dynobj; + bfd_vma gp; + Elf_Internal_Rela *rel; + Elf_Internal_Rela *relend; + struct mips_got_info *g; + + dynobj = elf_hash_table (info)->dynobj; + symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + + sgot = NULL; + sreloc = NULL; + if (dynobj == NULL || ! SGI_COMPAT (output_bfd)) + scpt = NULL; + else + scpt = bfd_get_section_by_name (dynobj, ".compact_rel"); + g = NULL; + + if (elf_bad_symtab (input_bfd)) + { + locsymcount = symtab_hdr->sh_size / sizeof (Elf32_External_Sym); + extsymoff = 0; + } + else + { + locsymcount = symtab_hdr->sh_info; + extsymoff = symtab_hdr->sh_info; + } + + gp = _bfd_get_gp_value (output_bfd); + + rel = relocs; + relend = relocs + input_section->reloc_count; + for (; rel < relend; rel++) + { + int r_type; + reloc_howto_type *howto; + unsigned long r_symndx; + bfd_vma addend; + struct elf_link_hash_entry *h; + asection *sec; + Elf_Internal_Sym *sym; + struct mips_elf_link_hash_entry *mh; + int other; + bfd_reloc_status_type r; + + r_type = ELF32_R_TYPE (rel->r_info); + if ((r_type < 0 || r_type >= (int) R_MIPS_max) + && r_type != R_MIPS16_26 + && r_type != R_MIPS16_GPREL) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + if (r_type == R_MIPS16_26) + howto = &elf_mips16_jump_howto; + else if (r_type == R_MIPS16_GPREL) + howto = &elf_mips16_gprel_howto; + else + howto = elf_mips_howto_table + r_type; + + if (dynobj != NULL + && (r_type == R_MIPS_CALL16 + || r_type == R_MIPS_GOT16 + || r_type == R_MIPS_CALL_HI16 + || r_type == R_MIPS_CALL_LO16 + || r_type == R_MIPS_GOT_HI16 + || r_type == R_MIPS_GOT_LO16)) + { + /* We need the .got section. */ + if (sgot == NULL) + { + sgot = bfd_get_section_by_name (dynobj, ".got"); + BFD_ASSERT (sgot != NULL); + BFD_ASSERT (elf_section_data (sgot) != NULL); + g = (struct mips_got_info *) elf_section_data (sgot)->tdata; + BFD_ASSERT (g != NULL); + } + } + + r_symndx = ELF32_R_SYM (rel->r_info); + + /* Mix in the change in GP address for a GP relative reloc. */ + if (r_type != R_MIPS_GPREL16 + && r_type != R_MIPS_LITERAL + && r_type != R_MIPS_GPREL32 + && r_type != R_MIPS16_GPREL) + addend = 0; + else + { + if (gp == 0) + { + if (! ((*info->callbacks->reloc_dangerous) + (info, + "GP relative relocation when GP not defined", + input_bfd, input_section, + rel->r_offset))) + return false; + /* Only give the error once per link. */ + gp = 4; + _bfd_set_gp_value (output_bfd, gp); + } + + if (r_symndx < extsymoff + || (elf_bad_symtab (input_bfd) + && local_sections[r_symndx] != NULL)) + { + /* 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 = elf_gp (input_bfd) - gp; + } + else if (! info->relocateable) + { + /* We are doing a final link. The current addend in the + instruction is simply the desired offset into the + symbol (normally zero). 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 + { + /* We are generating relocateable output, and we aren't + going to define this symbol, so we just leave the + instruction alone. */ + addend = 0; + } + } + + h = NULL; + sym = NULL; + sec = NULL; + if (info->relocateable) + { + /* This is a relocateable link. We don't have to change + anything, unless the reloc is against a section symbol, + in which case we have to adjust according to where the + section symbol winds up in the output section. */ + if (r_symndx >= locsymcount + || (elf_bad_symtab (input_bfd) + && local_sections[r_symndx] == NULL)) + r = bfd_reloc_ok; + else + { + sym = local_syms + r_symndx; + if (ELF_ST_TYPE (sym->st_info) != STT_SECTION) + r = bfd_reloc_ok; + else + { + sec = local_sections[r_symndx]; + + /* It would be logical to add sym->st_value here, + but Irix 5 sometimes generates a garbage symbol + value. */ + addend += sec->output_offset; + + /* If this is HI16 or GOT16 with an associated LO16, + adjust the addend accordingly. Otherwise, just + relocate. */ + if (r_type == R_MIPS_64 && bfd_big_endian (input_bfd)) + r = _bfd_relocate_contents (howto, input_bfd, + addend, + contents + rel->r_offset + 4); + else if (r_type != R_MIPS_HI16 && r_type != R_MIPS_GOT16) + r = _bfd_relocate_contents (howto, input_bfd, + addend, + contents + rel->r_offset); + else + { + Elf_Internal_Rela *lorel; + + /* As a GNU extension, permit an arbitrary + number of R_MIPS_HI16 relocs before the + R_MIPS_LO16 reloc. This permits gcc to emit + the HI and LO relocs itself. */ + if (r_type == R_MIPS_GOT16) + lorel = rel + 1; + else + { + for (lorel = rel + 1; + (lorel < relend + && (ELF32_R_TYPE (lorel->r_info) + == R_MIPS_HI16)); + lorel++) + ; + } + if (lorel < relend + && ELF32_R_TYPE (lorel->r_info) == R_MIPS_LO16) + { + mips_elf_relocate_hi16 (input_bfd, rel, lorel, + contents, addend); + r = bfd_reloc_ok; + } + else + r = _bfd_relocate_contents (howto, input_bfd, + addend, + contents + rel->r_offset); + } + } + } + } + else + { + bfd_vma relocation; + boolean local; + + /* This is a final link. */ + sym = NULL; + if (r_symndx < extsymoff + || (elf_bad_symtab (input_bfd) + && local_sections[r_symndx] != NULL)) + { + local = true; + sym = local_syms + r_symndx; + sec = local_sections[r_symndx]; + relocation = (sec->output_section->vma + + sec->output_offset); + + /* It would be logical to always add sym->st_value here, + but Irix 5 sometimes generates a garbage symbol + value. */ + if (ELF_ST_TYPE (sym->st_info) != STT_SECTION) + relocation += sym->st_value; + + /* mips16 text labels should be treated as odd. */ + if (sym->st_other == STO_MIPS16) + ++relocation; + } + else + { + long indx; + + local = false; + indx = r_symndx - extsymoff; + h = elf_sym_hashes (input_bfd)[indx]; + while (h->root.type == bfd_link_hash_indirect + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + if (strcmp (h->root.root.string, "_gp_disp") == 0) + { + if (gp == 0) + { + if (! ((*info->callbacks->reloc_dangerous) + (info, + "_gp_disp used when GP not defined", + input_bfd, input_section, + rel->r_offset))) + return false; + /* Only give the error once per link. */ + gp = 4; + _bfd_set_gp_value (output_bfd, gp); + relocation = 0; + } + else + { + sec = input_section; + if (sec->output_section != NULL) + relocation = (gp + - (rel->r_offset + + sec->output_section->vma + + sec->output_offset)); + else + relocation = gp - rel->r_offset; + if (r_type == R_MIPS_LO16) + relocation += 4; + } + } + else if (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + { + sec = h->root.u.def.section; + if (sec->output_section == NULL) + relocation = 0; + else + relocation = (h->root.u.def.value + + sec->output_section->vma + + sec->output_offset); + } + else if (h->root.type == bfd_link_hash_undefweak) + relocation = 0; + else if (info->shared && ! info->symbolic) + relocation = 0; + else if (strcmp (h->root.root.string, "_DYNAMIC_LINK") == 0) + { + /* If this is a dynamic link, we should have created + a _DYNAMIC_LINK symbol in + mips_elf_create_dynamic_sections. Otherwise, we + should define the symbol with a value of 0. + FIXME: It should probably get into the symbol + table somehow as well. */ + BFD_ASSERT (! info->shared); + BFD_ASSERT (bfd_get_section_by_name (output_bfd, + ".dynamic") == NULL); + relocation = 0; + } + else + { + if (! ((*info->callbacks->undefined_symbol) + (info, h->root.root.string, input_bfd, + input_section, rel->r_offset))) + return false; + relocation = 0; + } + } + + mh = (struct mips_elf_link_hash_entry *) h; + if (h != NULL) + other = h->other; + else if (sym != NULL) + other = sym->st_other; + else + other = 0; + + /* If this function has an fn_stub, then it is a mips16 + function which needs a stub if it is called by a 32 bit + function. If this reloc is anything other than a 16 bit + call, redirect the reloc to the stub. We don't redirect + relocs from other stub functions. */ + if (r_type != R_MIPS16_26 + && ((mh != NULL + && mh->fn_stub != NULL) + || (mh == NULL + && elf_tdata (input_bfd)->local_stubs != NULL + && elf_tdata (input_bfd)->local_stubs[r_symndx] != NULL)) + && strncmp (bfd_get_section_name (input_bfd, input_section), + FN_STUB, sizeof FN_STUB - 1) != 0 + && strncmp (bfd_get_section_name (input_bfd, input_section), + CALL_STUB, sizeof CALL_STUB - 1) != 0 + && strncmp (bfd_get_section_name (input_bfd, input_section), + CALL_FP_STUB, sizeof CALL_FP_STUB - 1) != 0) + { + if (mh != NULL) + { + BFD_ASSERT (mh->need_fn_stub); + relocation = (mh->fn_stub->output_section->vma + + mh->fn_stub->output_offset); + } + else + { + asection *fn_stub; + + fn_stub = elf_tdata (input_bfd)->local_stubs[r_symndx]; + relocation = (fn_stub->output_section->vma + + fn_stub->output_offset); + } + + /* RELOCATION now points to 32 bit code. */ + other = 0; + } + + /* If this function has a call_stub, then it is called by a + mips16 function; the call needs to go through a stub if + this function is a 32 bit function. If this reloc is a + 16 bit call, and the symbol is not a 16 bit function, + then redirect the reloc to the stub. Note that we don't + need to worry about calling the function through a + function pointer; such calls are handled by routing + through a special mips16 routine. We don't have to check + whether this call is from a stub; it can't be, because a + stub contains 32 bit code, and hence can not have a 16 + bit reloc. */ + if (r_type == R_MIPS16_26 + && mh != NULL + && (mh->call_stub != NULL || mh->call_fp_stub != NULL) + && other != STO_MIPS16) + { + asection *stub; + + /* If both call_stub and call_fp_stub are defined, we + can figure out which one to use by seeing which one + appears in the input file. */ + if (mh->call_stub != NULL && mh->call_fp_stub != NULL) + { + asection *o; + + stub = NULL; + for (o = input_bfd->sections; o != NULL; o = o->next) + { + if (strncmp (bfd_get_section_name (input_bfd, o), + CALL_FP_STUB, sizeof CALL_FP_STUB - 1) == 0) + { + stub = mh->call_fp_stub; + break; + } + } + if (stub == NULL) + stub = mh->call_stub; + } + else if (mh->call_stub != NULL) + stub = mh->call_stub; + else + stub = mh->call_fp_stub; + + BFD_ASSERT (stub->_raw_size > 0); + relocation = stub->output_section->vma + stub->output_offset; + } + + if (r_type == R_MIPS_HI16) + { + Elf_Internal_Rela *lorel; + + /* As a GNU extension, permit an arbitrary number of + R_MIPS_HI16 relocs before the R_MIPS_LO16 reloc. + This permits gcc to emit the HI and LO relocs itself. */ + for (lorel = rel + 1; + (lorel < relend + && ELF32_R_TYPE (lorel->r_info) == R_MIPS_HI16); + lorel++) + ; + if (lorel < relend + && ELF32_R_TYPE (lorel->r_info) == R_MIPS_LO16) + { + mips_elf_relocate_hi16 (input_bfd, rel, lorel, + contents, relocation + addend); + r = bfd_reloc_ok; + } + else + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, addend); + } + else if (r_type == R_MIPS_GOT16 && local) + { + /* GOT16 must also have an associated LO16 in the local + case. In this case, the addend is extracted and the + section in which the referenced object is determined. + Then the final address of the object is computed and + the GOT entry for the hipage (an aligned 64kb chunk) + is added to .got section if needed. The offset field + of the GOT16-relocated instruction is replaced by the + index of this GOT entry for the hipage. */ + if ((rel + 1) < relend + && ELF32_R_TYPE ((rel + 1)->r_info) == R_MIPS_LO16) + { + if (! mips_elf_relocate_got_local (output_bfd, input_bfd, + sgot, rel, rel + 1, + contents, + relocation + addend)) + return false; + r = bfd_reloc_ok; + } + else + r = bfd_reloc_outofrange; + } + else if (r_type == R_MIPS_CALL16 + || r_type == R_MIPS_GOT16 + || r_type == R_MIPS_CALL_LO16 + || r_type == R_MIPS_GOT_LO16) + { + bfd_vma offset; + + /* This symbol must be registered as a global symbol + having the corresponding got entry. */ + BFD_ASSERT (h->got_offset != (bfd_vma) -1); + + offset = (h->dynindx - g->global_gotsym + g->local_gotno) * 4; + BFD_ASSERT (g->local_gotno <= offset + && offset < sgot->_raw_size); + bfd_put_32 (output_bfd, relocation + addend, + sgot->contents + offset); + offset = (sgot->output_section->vma + sgot->output_offset + + offset - gp); + mips_elf_relocate_global_got (input_bfd, rel, contents, + offset); + r = bfd_reloc_ok; + } + else if (r_type == R_MIPS_CALL_HI16 + || r_type == R_MIPS_GOT_HI16) + { + bfd_vma offset; + + /* This must be a global symbol with a got entry. The + next reloc must be the corresponding LO16 reloc. */ + BFD_ASSERT (h != NULL && h->got_offset != (bfd_vma) -1); + BFD_ASSERT ((rel + 1) < relend); + BFD_ASSERT ((int) ELF32_R_TYPE ((rel + 1)->r_info) + == (r_type == R_MIPS_CALL_HI16 + ? (int) R_MIPS_CALL_LO16 + : (int) R_MIPS_GOT_LO16)); + + offset = (h->dynindx - g->global_gotsym + g->local_gotno) * 4; + BFD_ASSERT (g->local_gotno <= offset + && offset < sgot->_raw_size); + bfd_put_32 (output_bfd, relocation + addend, + sgot->contents + offset); + offset = (sgot->output_section->vma + sgot->output_offset + + offset - gp); + mips_elf_relocate_hi16 (input_bfd, rel, rel + 1, contents, + offset); + r = bfd_reloc_ok; + } + else if (r_type == R_MIPS_REL32 + || r_type == R_MIPS_32) + { + Elf_Internal_Rel outrel; + Elf32_crinfo cptrel; + bfd_byte *cr; + + if ((info->shared + || (elf_hash_table (info)->dynamic_sections_created + && h != NULL + && ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) + == 0))) + && (input_section->flags & SEC_ALLOC) != 0) + { + boolean skip; + + /* When generating a shared object, these + relocations are copied into the output file to be + resolved at run time. */ + if (sreloc == NULL) + { + sreloc = bfd_get_section_by_name (dynobj, ".rel.dyn"); + BFD_ASSERT (sreloc != NULL); + } + + skip = false; + + if (elf_section_data (input_section)->stab_info == NULL) + outrel.r_offset = rel->r_offset; + else + { + bfd_vma off; + + off = (_bfd_stab_section_offset + (output_bfd, &elf_hash_table (info)->stab_info, + input_section, + &elf_section_data (input_section)->stab_info, + rel->r_offset)); + if (off == (bfd_vma) -1) + skip = true; + outrel.r_offset = off; + } + + outrel.r_offset += (input_section->output_section->vma + + input_section->output_offset); + + addend = bfd_get_32 (input_bfd, contents + rel->r_offset); + + if (skip) + memset (&outrel, 0, sizeof outrel); + else if (h != NULL + && (! info->symbolic + || (h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) == 0)) + { + BFD_ASSERT (h->dynindx != -1); + outrel.r_info = ELF32_R_INFO (h->dynindx, R_MIPS_REL32); + sec = input_section; + } + else + { + long indx; + + if (h == NULL) + sec = local_sections[r_symndx]; + else + { + BFD_ASSERT (h->root.type == bfd_link_hash_defined + || (h->root.type + == bfd_link_hash_defweak)); + sec = h->root.u.def.section; + } + if (sec != NULL && bfd_is_abs_section (sec)) + indx = 0; + else if (sec == NULL || sec->owner == NULL) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + else + { + asection *osec; + + osec = sec->output_section; + indx = elf_section_data (osec)->dynindx; + if (indx == 0) + abort (); + } + + outrel.r_info = ELF32_R_INFO (indx, R_MIPS_REL32); + addend += relocation; + } + + if (! skip) + bfd_put_32 (output_bfd, addend, contents + rel->r_offset); + + bfd_elf32_swap_reloc_out (output_bfd, &outrel, + (((Elf32_External_Rel *) + sreloc->contents) + + sreloc->reloc_count)); + ++sreloc->reloc_count; + + if (! skip && SGI_COMPAT (output_bfd)) + { + if (scpt == NULL) + continue; + + /* Make an entry of compact relocation info. */ + mips_elf_set_cr_format (cptrel, CRF_MIPS_LONG); + cptrel.vaddr = (rel->r_offset + + input_section->output_section->vma + + input_section->output_offset); + if (r_type == R_MIPS_REL32) + mips_elf_set_cr_type (cptrel, CRT_MIPS_REL32); + else + mips_elf_set_cr_type (cptrel, CRT_MIPS_WORD); + mips_elf_set_cr_dist2to (cptrel, 0); + cptrel.konst = addend; + + cr = (scpt->contents + + sizeof (Elf32_External_compact_rel)); + bfd_elf32_swap_crinfo_out (output_bfd, &cptrel, + ((Elf32_External_crinfo *) cr + + scpt->reloc_count)); + ++scpt->reloc_count; + } + + /* This reloc will be computed at runtime, so + there's no need to do anything now. */ + continue; + } + else + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, addend); + } + else if (r_type == R_MIPS_64) + { + bfd_size_type addr; + unsigned long val; + + /* Do a 32 bit relocation, and sign extend to 64 bits. */ + addr = rel->r_offset; + if (bfd_big_endian (input_bfd)) + addr += 4; + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, addr, relocation, + addend); + val = bfd_get_32 (input_bfd, contents + addr); + if ((val & 0x80000000) != 0) + val = 0xffffffff; + else + val = 0; + addr = rel->r_offset; + if (bfd_little_endian (input_bfd)) + addr += 4; + bfd_put_32 (input_bfd, val, contents + addr); + } + else if (r_type == R_MIPS_26 && other == STO_MIPS16) + { + unsigned long insn; + + /* This is a jump to a mips16 routine from a mips32 + routine. We need to change jal into jalx. */ + insn = bfd_get_32 (input_bfd, contents + rel->r_offset); + if (((insn >> 26) & 0x3f) != 0x3 + && ((insn >> 26) & 0x3f) != 0x1d) + { + (*_bfd_error_handler) + ("%s: %s+0x%lx: jump to mips16 routine which is not jal", + bfd_get_filename (input_bfd), + input_section->name, + (unsigned long) rel->r_offset); + bfd_set_error (bfd_error_bad_value); + return false; + } + insn = (insn & 0x3ffffff) | (0x1d << 26); + bfd_put_32 (input_bfd, insn, contents + rel->r_offset); + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, addend); + } + else if (r_type == R_MIPS16_26) + { + /* It's easiest to do the normal relocation, and then + dig out the instruction and swap the first word the + way the mips16 expects it. If this is little endian, + though, we need to swap the two words first, and then + swap them back again later, so that the address looks + right. */ + + if (bfd_little_endian (input_bfd)) + { + unsigned long insn; + + insn = bfd_get_32 (input_bfd, contents + rel->r_offset); + insn = ((insn >> 16) & 0xffff) | ((insn & 0xffff) << 16); + bfd_put_32 (input_bfd, insn, contents + rel->r_offset); + } + + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, addend); + if (r == bfd_reloc_ok) + { + unsigned long insn; + + if (bfd_little_endian (input_bfd)) + { + insn = bfd_get_32 (input_bfd, contents + rel->r_offset); + insn = ((insn >> 16) & 0xffff) | ((insn & 0xffff) << 16); + bfd_put_32 (input_bfd, insn, contents + rel->r_offset); + } + + insn = bfd_get_16 (input_bfd, contents + rel->r_offset); + insn = ((insn & 0xfc00) + | ((insn & 0x1f) << 5) + | ((insn & 0x3e0) >> 5)); + /* If this is a jump to a 32 bit routine, then make + it jalx. */ + if (other != STO_MIPS16) + insn |= 0x400; + bfd_put_16 (input_bfd, insn, contents + rel->r_offset); + } + } + else if (r_type == R_MIPS16_GPREL) + { + unsigned short extend, insn; + bfd_byte buf[4]; + unsigned long final; + + /* Extract the addend into buf, run the regular reloc, + and stuff the resulting value back into the + instructions. */ + if (rel->r_offset > input_section->_raw_size) + r = bfd_reloc_outofrange; + else + { + extend = bfd_get_16 (input_bfd, contents + rel->r_offset); + insn = bfd_get_16 (input_bfd, contents + rel->r_offset + 2); + bfd_put_32 (input_bfd, + (((extend & 0x1f) << 11) + | (extend & 0x7e0) + | (insn & 0x1f)), + buf); + r = _bfd_final_link_relocate (howto, input_bfd, + input_section, buf, + (bfd_vma) 0, relocation, + addend); + final = bfd_get_32 (input_bfd, buf); + bfd_put_16 (input_bfd, + ((extend & 0xf800) + | ((final >> 11) & 0x1f) + | (final & 0x7e0)), + contents + rel->r_offset); + bfd_put_16 (input_bfd, + ((insn & 0xffe0) + | (final & 0x1f)), + contents + rel->r_offset + 2); + } + } + else + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, addend); + + /* The jal instruction can only jump to an address which is + divisible by 4, and it can only jump to an address with + the same upper 4 bits as the PC. */ + if (r == bfd_reloc_ok + && (r_type == R_MIPS16_26 || r_type == R_MIPS_26)) + { + bfd_vma addr; + + addr = relocation; + if (other == STO_MIPS16) + addr &= ~ (bfd_vma) 1; + addr += addend; + if ((addr & 3) != 0 + || ((addr & 0xf0000000) + != ((input_section->output_section->vma + + input_section->output_offset + + rel->r_offset) + & 0xf0000000))) + r = bfd_reloc_overflow; + } + + if (SGI_COMPAT (abfd) + && scpt != NULL + && (input_section->flags & SEC_ALLOC) != 0) + { + Elf32_crinfo cptrel; + bfd_byte *cr; + + /* Make an entry of compact relocation info. */ + mips_elf_set_cr_format (cptrel, CRF_MIPS_LONG); + cptrel.vaddr = (rel->r_offset + + input_section->output_section->vma + + input_section->output_offset); + + switch (r_type) + { + case R_MIPS_26: + mips_elf_set_cr_type (cptrel, CRT_MIPS_JMPAD); + /* XXX How should we set dist2to in this case. */ + mips_elf_set_cr_dist2to (cptrel, 8); + cptrel.konst = addend + relocation; + cr = scpt->contents + sizeof (Elf32_External_compact_rel); + bfd_elf32_swap_crinfo_out (output_bfd, &cptrel, + ((Elf32_External_crinfo *) cr + + scpt->reloc_count)); + ++scpt->reloc_count; + break; + + case R_MIPS_GPREL16: + case R_MIPS_LITERAL: + case R_MIPS_GPREL32: + mips_elf_set_cr_type (cptrel, CRT_MIPS_GPHI_LO); + cptrel.konst = gp - cptrel.vaddr; + mips_elf_set_cr_dist2to (cptrel, 4); + cr = scpt->contents + sizeof (Elf32_External_compact_rel); + bfd_elf32_swap_crinfo_out (output_bfd, &cptrel, + ((Elf32_External_crinfo *) cr + + scpt->reloc_count)); + ++scpt->reloc_count; + break; + + default: + break; + } + } + } + + if (r != bfd_reloc_ok) + { + switch (r) + { + default: + case bfd_reloc_outofrange: + abort (); + case bfd_reloc_overflow: + { + const char *name; + + if (h != NULL) + name = h->root.root.string; + else + { + name = bfd_elf_string_from_elf_section (input_bfd, + symtab_hdr->sh_link, + sym->st_name); + if (name == NULL) + return false; + if (*name == '\0') + name = bfd_section_name (input_bfd, sec); + } + if (! ((*info->callbacks->reloc_overflow) + (info, name, howto->name, (bfd_vma) 0, + input_bfd, input_section, rel->r_offset))) + return false; + } + break; + } + } + } + + return true; +} + +/* This hook function is called before the linker writes out a global + symbol. We mark symbols as small common if appropriate. This is + also where we undo the increment of the value for a mips16 symbol. */ + +/*ARGSIGNORED*/ +static boolean +mips_elf_link_output_symbol_hook (abfd, info, name, sym, input_sec) + bfd *abfd; + struct bfd_link_info *info; + const char *name; + Elf_Internal_Sym *sym; + asection *input_sec; +{ + /* If we see a common symbol, which implies a relocatable link, then + if a symbol was small common in an input file, mark it as small + common in the output file. */ + if (sym->st_shndx == SHN_COMMON + && strcmp (input_sec->name, ".scommon") == 0) + sym->st_shndx = SHN_MIPS_SCOMMON; + + if (sym->st_other == STO_MIPS16 + && (sym->st_value & 1) != 0) + --sym->st_value; + + return true; +} + +/* Functions for the dynamic linker. */ + +/* The name of the dynamic interpreter. This is put in the .interp + section. */ + +#define ELF_DYNAMIC_INTERPRETER "/usr/lib/libc.so.1" + +/* Create dynamic sections when linking against a dynamic object. */ + +static boolean +mips_elf_create_dynamic_sections (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + struct elf_link_hash_entry *h; + flagword flags; + register asection *s; + const char * const *namep; + + flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY + | SEC_LINKER_CREATED | SEC_READONLY); + + /* Mips ABI requests the .dynamic section to be read only. */ + s = bfd_get_section_by_name (abfd, ".dynamic"); + if (s != NULL) + { + if (! bfd_set_section_flags (abfd, s, flags)) + return false; + } + + /* We need to create .got section. */ + if (! mips_elf_create_got_section (abfd, info)) + return false; + + /* Create .stub section. */ + if (bfd_get_section_by_name (abfd, ".stub") == NULL) + { + s = bfd_make_section (abfd, ".stub"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, flags) + || ! bfd_set_section_alignment (abfd, s, 2)) + return false; + } + + if (SGI_COMPAT (abfd) + && !info->shared + && bfd_get_section_by_name (abfd, ".rld_map") == NULL) + { + s = bfd_make_section (abfd, ".rld_map"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, flags & ~SEC_READONLY) + || ! bfd_set_section_alignment (abfd, s, 2)) + return false; + } + + if (SGI_COMPAT (abfd)) + { + for (namep = mips_elf_dynsym_rtproc_names; *namep != NULL; namep++) + { + h = NULL; + if (! (_bfd_generic_link_add_one_symbol + (info, abfd, *namep, BSF_GLOBAL, bfd_und_section_ptr, + (bfd_vma) 0, (const char *) NULL, false, + get_elf_backend_data (abfd)->collect, + (struct bfd_link_hash_entry **) &h))) + return false; + h->elf_link_hash_flags &=~ ELF_LINK_NON_ELF; + h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR; + h->type = STT_SECTION; + + if (! bfd_elf32_link_record_dynamic_symbol (info, h)) + return false; + } + + /* We need to create a .compact_rel section. */ + if (! mips_elf_create_compact_rel_section (abfd, info)) + return false; + + /* Change aligments of some sections. */ + s = bfd_get_section_by_name (abfd, ".hash"); + if (s != NULL) + bfd_set_section_alignment (abfd, s, 4); + s = bfd_get_section_by_name (abfd, ".dynsym"); + if (s != NULL) + bfd_set_section_alignment (abfd, s, 4); + s = bfd_get_section_by_name (abfd, ".dynstr"); + if (s != NULL) + bfd_set_section_alignment (abfd, s, 4); + s = bfd_get_section_by_name (abfd, ".reginfo"); + if (s != NULL) + bfd_set_section_alignment (abfd, s, 4); + s = bfd_get_section_by_name (abfd, ".dynamic"); + if (s != NULL) + bfd_set_section_alignment (abfd, s, 4); + } + + if (!info->shared) + { + h = NULL; + if (! (_bfd_generic_link_add_one_symbol + (info, abfd, "_DYNAMIC_LINK", BSF_GLOBAL, bfd_abs_section_ptr, + (bfd_vma) 0, (const char *) NULL, false, + get_elf_backend_data (abfd)->collect, + (struct bfd_link_hash_entry **) &h))) + return false; + h->elf_link_hash_flags &=~ ELF_LINK_NON_ELF; + h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR; + h->type = STT_SECTION; + + if (! bfd_elf32_link_record_dynamic_symbol (info, h)) + return false; + + if (! mips_elf_hash_table (info)->use_rld_obj_head) + { + /* __rld_map is a four byte word located in the .data section + and is filled in by the rtld to contain a pointer to + the _r_debug structure. Its symbol value will be set in + mips_elf_finish_dynamic_symbol. */ + s = bfd_get_section_by_name (abfd, ".rld_map"); + BFD_ASSERT (s != NULL); + + h = NULL; + if (! (_bfd_generic_link_add_one_symbol + (info, abfd, "__rld_map", BSF_GLOBAL, s, + (bfd_vma) 0, (const char *) NULL, false, + get_elf_backend_data (abfd)->collect, + (struct bfd_link_hash_entry **) &h))) + return false; + h->elf_link_hash_flags &=~ ELF_LINK_NON_ELF; + h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR; + h->type = STT_OBJECT; + + if (! bfd_elf32_link_record_dynamic_symbol (info, h)) + return false; + } + } + + return true; +} + +/* Create the .compact_rel section. */ + +static boolean +mips_elf_create_compact_rel_section (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + flagword flags; + register asection *s; + + if (bfd_get_section_by_name (abfd, ".compact_rel") == NULL) + { + flags = (SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED + | SEC_READONLY); + + s = bfd_make_section (abfd, ".compact_rel"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, flags) + || ! bfd_set_section_alignment (abfd, s, 2)) + return false; + + s->_raw_size = sizeof (Elf32_External_compact_rel); + } + + return true; +} + +/* Create the .got section to hold the global offset table. */ + +static boolean +mips_elf_create_got_section (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + flagword flags; + register asection *s; + struct elf_link_hash_entry *h; + struct mips_got_info *g; + + /* This function may be called more than once. */ + if (bfd_get_section_by_name (abfd, ".got") != NULL) + return true; + + flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY + | SEC_LINKER_CREATED); + + s = bfd_make_section (abfd, ".got"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, flags) + || ! bfd_set_section_alignment (abfd, s, 4)) + return false; + + /* Define the symbol _GLOBAL_OFFSET_TABLE_. We don't do this in the + linker script because we don't want to define the symbol if we + are not creating a global offset table. */ + h = NULL; + if (! (_bfd_generic_link_add_one_symbol + (info, abfd, "_GLOBAL_OFFSET_TABLE_", BSF_GLOBAL, s, + (bfd_vma) 0, (const char *) NULL, false, + get_elf_backend_data (abfd)->collect, + (struct bfd_link_hash_entry **) &h))) + return false; + h->elf_link_hash_flags &=~ ELF_LINK_NON_ELF; + h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR; + h->type = STT_OBJECT; + + if (info->shared + && ! bfd_elf32_link_record_dynamic_symbol (info, h)) + return false; + + /* The first several global offset table entries are reserved. */ + s->_raw_size = MIPS_RESERVED_GOTNO * 4; + + g = (struct mips_got_info *) bfd_alloc (abfd, + sizeof (struct mips_got_info)); + if (g == NULL) + return false; + g->global_gotsym = 0; + g->local_gotno = MIPS_RESERVED_GOTNO; + g->assigned_gotno = MIPS_RESERVED_GOTNO; + if (elf_section_data (s) == NULL) + { + s->used_by_bfd = + (PTR) bfd_zalloc (abfd, sizeof (struct bfd_elf_section_data)); + if (elf_section_data (s) == NULL) + return false; + } + elf_section_data (s)->tdata = (PTR) g; + + return true; +} + +/* Look through the relocs for a section during the first phase, and + allocate space in the global offset table. */ + +static boolean +mips_elf_check_relocs (abfd, info, sec, relocs) + bfd *abfd; + struct bfd_link_info *info; + asection *sec; + const Elf_Internal_Rela *relocs; +{ + const char *name; + bfd *dynobj; + Elf_Internal_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes; + struct mips_got_info *g; + size_t extsymoff; + const Elf_Internal_Rela *rel; + const Elf_Internal_Rela *rel_end; + asection *sgot; + asection *sreloc; + + if (info->relocateable) + return true; + + dynobj = elf_hash_table (info)->dynobj; + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (abfd); + extsymoff = (elf_bad_symtab (abfd)) ? 0 : symtab_hdr->sh_info; + + /* Check for the mips16 stub sections. */ + + name = bfd_get_section_name (abfd, sec); + if (strncmp (name, FN_STUB, sizeof FN_STUB - 1) == 0) + { + unsigned long r_symndx; + + /* Look at the relocation information to figure out which symbol + this is for. */ + + r_symndx = ELF32_R_SYM (relocs->r_info); + + if (r_symndx < extsymoff + || sym_hashes[r_symndx - extsymoff] == NULL) + { + asection *o; + + /* This stub is for a local symbol. This stub will only be + needed if there is some relocation in this BFD, other + than a 16 bit function call, which refers to this symbol. */ + for (o = abfd->sections; o != NULL; o = o->next) + { + Elf_Internal_Rela *sec_relocs; + const Elf_Internal_Rela *r, *rend; + + /* We can ignore stub sections when looking for relocs. */ + if ((o->flags & SEC_RELOC) == 0 + || o->reloc_count == 0 + || strncmp (bfd_get_section_name (abfd, o), FN_STUB, + sizeof FN_STUB - 1) == 0 + || strncmp (bfd_get_section_name (abfd, o), CALL_STUB, + sizeof CALL_STUB - 1) == 0 + || strncmp (bfd_get_section_name (abfd, o), CALL_FP_STUB, + sizeof CALL_FP_STUB - 1) == 0) + continue; + + sec_relocs = (_bfd_elf32_link_read_relocs + (abfd, o, (PTR) NULL, + (Elf_Internal_Rela *) NULL, + info->keep_memory)); + if (sec_relocs == NULL) + return false; + + rend = sec_relocs + o->reloc_count; + for (r = sec_relocs; r < rend; r++) + if (ELF32_R_SYM (r->r_info) == r_symndx + && ELF32_R_TYPE (r->r_info) != R_MIPS16_26) + break; + + if (! info->keep_memory) + free (sec_relocs); + + if (r < rend) + break; + } + + if (o == NULL) + { + /* There is no non-call reloc for this stub, so we do + not need it. Since this function is called before + the linker maps input sections to output sections, we + can easily discard it by setting the SEC_EXCLUDE + flag. */ + sec->flags |= SEC_EXCLUDE; + return true; + } + + /* Record this stub in an array of local symbol stubs for + this BFD. */ + if (elf_tdata (abfd)->local_stubs == NULL) + { + unsigned long symcount; + asection **n; + + if (elf_bad_symtab (abfd)) + symcount = symtab_hdr->sh_size / sizeof (Elf32_External_Sym); + else + symcount = symtab_hdr->sh_info; + n = (asection **) bfd_zalloc (abfd, + symcount * sizeof (asection *)); + if (n == NULL) + return false; + elf_tdata (abfd)->local_stubs = n; + } + + elf_tdata (abfd)->local_stubs[r_symndx] = sec; + + /* We don't need to set mips16_stubs_seen in this case. + That flag is used to see whether we need to look through + the global symbol table for stubs. We don't need to set + it here, because we just have a local stub. */ + } + else + { + struct mips_elf_link_hash_entry *h; + + h = ((struct mips_elf_link_hash_entry *) + sym_hashes[r_symndx - extsymoff]); + + /* H is the symbol this stub is for. */ + + h->fn_stub = sec; + mips_elf_hash_table (info)->mips16_stubs_seen = true; + } + } + else if (strncmp (name, CALL_STUB, sizeof CALL_STUB - 1) == 0 + || strncmp (name, CALL_FP_STUB, sizeof CALL_FP_STUB - 1) == 0) + { + unsigned long r_symndx; + struct mips_elf_link_hash_entry *h; + asection **loc; + + /* Look at the relocation information to figure out which symbol + this is for. */ + + r_symndx = ELF32_R_SYM (relocs->r_info); + + if (r_symndx < extsymoff + || sym_hashes[r_symndx - extsymoff] == NULL) + { + /* This stub was actually built for a static symbol defined + in the same file. We assume that all static symbols in + mips16 code are themselves mips16, so we can simply + discard this stub. Since this function is called before + the linker maps input sections to output sections, we can + easily discard it by setting the SEC_EXCLUDE flag. */ + sec->flags |= SEC_EXCLUDE; + return true; + } + + h = ((struct mips_elf_link_hash_entry *) + sym_hashes[r_symndx - extsymoff]); + + /* H is the symbol this stub is for. */ + + if (strncmp (name, CALL_FP_STUB, sizeof CALL_FP_STUB - 1) == 0) + loc = &h->call_fp_stub; + else + loc = &h->call_stub; + + /* If we already have an appropriate stub for this function, we + don't need another one, so we can discard this one. Since + this function is called before the linker maps input sections + to output sections, we can easily discard it by setting the + SEC_EXCLUDE flag. We can also discard this section if we + happen to already know that this is a mips16 function; it is + not necessary to check this here, as it is checked later, but + it is slightly faster to check now. */ + if (*loc != NULL || h->root.other == STO_MIPS16) + { + sec->flags |= SEC_EXCLUDE; + return true; + } + + *loc = sec; + mips_elf_hash_table (info)->mips16_stubs_seen = true; + } + + if (dynobj == NULL) + { + sgot = NULL; + g = NULL; + } + else + { + sgot = bfd_get_section_by_name (dynobj, ".got"); + if (sgot == NULL) + g = NULL; + else + { + BFD_ASSERT (elf_section_data (sgot) != NULL); + g = (struct mips_got_info *) elf_section_data (sgot)->tdata; + BFD_ASSERT (g != NULL); + } + } + + sreloc = NULL; + + rel_end = relocs + sec->reloc_count; + for (rel = relocs; rel < rel_end; rel++) + { + unsigned long r_symndx; + struct elf_link_hash_entry *h; + + r_symndx = ELF32_R_SYM (rel->r_info); + + if (r_symndx < extsymoff) + h = NULL; + else + { + h = sym_hashes[r_symndx - extsymoff]; + + /* This may be an indirect symbol created because of a version. */ + if (h != NULL) + { + while (h->root.type == bfd_link_hash_indirect) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + } + } + + /* Some relocs require a global offset table. */ + if (dynobj == NULL || sgot == NULL) + { + switch (ELF32_R_TYPE (rel->r_info)) + { + case R_MIPS_GOT16: + case R_MIPS_CALL16: + case R_MIPS_CALL_HI16: + case R_MIPS_CALL_LO16: + case R_MIPS_GOT_HI16: + case R_MIPS_GOT_LO16: + if (dynobj == NULL) + elf_hash_table (info)->dynobj = dynobj = abfd; + if (! mips_elf_create_got_section (dynobj, info)) + return false; + sgot = bfd_get_section_by_name (dynobj, ".got"); + BFD_ASSERT (sgot != NULL); + BFD_ASSERT (elf_section_data (sgot) != NULL); + g = (struct mips_got_info *) elf_section_data (sgot)->tdata; + BFD_ASSERT (g != NULL); + break; + + case R_MIPS_32: + case R_MIPS_REL32: + if (dynobj == NULL + && (info->shared || h != NULL) + && (sec->flags & SEC_ALLOC) != 0) + elf_hash_table (info)->dynobj = dynobj = abfd; + break; + + default: + break; + } + } + + switch (ELF32_R_TYPE (rel->r_info)) + { + case R_MIPS_CALL16: + case R_MIPS_CALL_HI16: + case R_MIPS_CALL_LO16: + /* This symbol requires a global offset table entry. */ + + if (h == NULL) + { + (*_bfd_error_handler) + ("%s: CALL16 reloc at 0x%lx not against global symbol", + bfd_get_filename (abfd), (unsigned long) rel->r_offset); + bfd_set_error (bfd_error_bad_value); + return false; + } + + /* Make sure this symbol is output as a dynamic symbol. */ + if (h->dynindx == -1) + { + if (! bfd_elf32_link_record_dynamic_symbol (info, h)) + return false; + } + + if (h->got_offset != (bfd_vma) -1) + { + /* We have already allocated space in the .got. */ + break; + } + + /* Note the index of the first global got symbol in .dynsym. */ + if (g->global_gotsym == 0 + || g->global_gotsym > (unsigned long) h->dynindx) + g->global_gotsym = h->dynindx; + + /* Make this symbol to have the corresponding got entry. */ + h->got_offset = 0; + + /* We need a stub, not a plt entry for the undefined + function. But we record it as if it needs plt. See + elf_adjust_dynamic_symbol in elflink.h. */ + h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT; + h->type = STT_FUNC; + + break; + + case R_MIPS_GOT16: + case R_MIPS_GOT_HI16: + case R_MIPS_GOT_LO16: + /* This symbol requires a global offset table entry. */ + + if (h != NULL) + { + /* Make sure this symbol is output as a dynamic symbol. */ + if (h->dynindx == -1) + { + if (! bfd_elf32_link_record_dynamic_symbol (info, h)) + return false; + } + + if (h->got_offset != (bfd_vma) -1) + { + /* We have already allocated space in the .got. */ + break; + } + /* Note the index of the first global got symbol in + .dynsym. */ + if (g->global_gotsym == 0 + || g->global_gotsym > (unsigned long) h->dynindx) + g->global_gotsym = h->dynindx; + + /* Make this symbol to be the global got symbol. */ + h->got_offset = 0; + } + + break; + + case R_MIPS_32: + case R_MIPS_REL32: + if ((info->shared || h != NULL) + && (sec->flags & SEC_ALLOC) != 0) + { + if (sreloc == NULL) + { + const char *name = ".rel.dyn"; + + sreloc = bfd_get_section_by_name (dynobj, name); + if (sreloc == NULL) + { + sreloc = bfd_make_section (dynobj, name); + if (sreloc == NULL + || ! bfd_set_section_flags (dynobj, sreloc, + (SEC_ALLOC + | SEC_LOAD + | SEC_HAS_CONTENTS + | SEC_IN_MEMORY + | SEC_LINKER_CREATED + | SEC_READONLY)) + || ! bfd_set_section_alignment (dynobj, sreloc, + 4)) + return false; + } + } + if (info->shared) + { + /* When creating a shared object, we must copy these + reloc types into the output file as R_MIPS_REL32 + relocs. We make room for this reloc in the + .rel.dyn reloc section */ + if (sreloc->_raw_size == 0) + { + /* Add a null element. */ + sreloc->_raw_size += sizeof (Elf32_External_Rel); + ++sreloc->reloc_count; + } + sreloc->_raw_size += sizeof (Elf32_External_Rel); + } + else + { + struct mips_elf_link_hash_entry *hmips; + + /* We only need to copy this reloc if the symbol is + defined in a dynamic object. */ + hmips = (struct mips_elf_link_hash_entry *) h; + ++hmips->mips_32_relocs; + } + } + + if (SGI_COMPAT (abfd)) + mips_elf_hash_table (info)->compact_rel_size += + sizeof (Elf32_External_crinfo); + + break; + + case R_MIPS_26: + case R_MIPS_GPREL16: + case R_MIPS_LITERAL: + case R_MIPS_GPREL32: + if (SGI_COMPAT (abfd)) + mips_elf_hash_table (info)->compact_rel_size += + sizeof (Elf32_External_crinfo); + break; + + default: + break; + } + + /* If this reloc is not a 16 bit call, and it has a global + symbol, then we will need the fn_stub if there is one. + References from a stub section do not count. */ + if (h != NULL + && ELF32_R_TYPE (rel->r_info) != R_MIPS16_26 + && strncmp (bfd_get_section_name (abfd, sec), FN_STUB, + sizeof FN_STUB - 1) != 0 + && strncmp (bfd_get_section_name (abfd, sec), CALL_STUB, + sizeof CALL_STUB - 1) != 0 + && strncmp (bfd_get_section_name (abfd, sec), CALL_FP_STUB, + sizeof CALL_FP_STUB - 1) != 0) + { + struct mips_elf_link_hash_entry *mh; + + mh = (struct mips_elf_link_hash_entry *) h; + mh->need_fn_stub = true; + } + } + + return true; +} + +/* Adjust a symbol defined by a dynamic object and referenced by a + regular object. The current definition is in some section of the + dynamic object, but we're not including those sections. We have to + change the definition to something the rest of the link can + understand. */ + +static boolean +mips_elf_adjust_dynamic_symbol (info, h) + struct bfd_link_info *info; + struct elf_link_hash_entry *h; +{ + bfd *dynobj; + struct mips_elf_link_hash_entry *hmips; + asection *s; + + dynobj = elf_hash_table (info)->dynobj; + + /* Make sure we know what is going on here. */ + BFD_ASSERT (dynobj != NULL + && ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) + || h->weakdef != NULL + || ((h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_DYNAMIC) != 0 + && (h->elf_link_hash_flags + & ELF_LINK_HASH_REF_REGULAR) != 0 + && (h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) == 0))); + + /* If this symbol is defined in a dynamic object, we need to copy + any R_MIPS_32 or R_MIPS_REL32 relocs against it into the output + file. */ + hmips = (struct mips_elf_link_hash_entry *) h; + if (! info->relocateable + && hmips->mips_32_relocs != 0 + && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0) + { + s = bfd_get_section_by_name (dynobj, ".rel.dyn"); + BFD_ASSERT (s != NULL); + + if (s->_raw_size == 0) + { + /* Make room for a null element. */ + s->_raw_size += sizeof (Elf32_External_Rel); + ++s->reloc_count; + } + s->_raw_size += hmips->mips_32_relocs * sizeof (Elf32_External_Rel); + } + + /* For a function, create a stub, if needed. */ + if (h->type == STT_FUNC + || (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0) + { + if (! elf_hash_table (info)->dynamic_sections_created) + return true; + + /* If this symbol is not defined in a regular file, then set + the symbol to the stub location. This is required to make + function pointers compare as equal between the normal + executable and the shared library. */ + if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0) + { + /* We need .stub section. */ + s = bfd_get_section_by_name (dynobj, ".stub"); + BFD_ASSERT (s != NULL); + + h->root.u.def.section = s; + h->root.u.def.value = s->_raw_size; + + /* XXX Write this stub address somewhere. */ + h->plt_offset = s->_raw_size; + + /* Make room for this stub code. */ + s->_raw_size += MIPS_FUNCTION_STUB_SIZE; + + /* The last half word of the stub will be filled with the index + of this symbol in .dynsym section. */ + return true; + } + } + + /* If this is a weak symbol, and there is a real definition, the + processor independent code will have arranged for us to see the + real definition first, and we can just use the same value. */ + if (h->weakdef != NULL) + { + BFD_ASSERT (h->weakdef->root.type == bfd_link_hash_defined + || h->weakdef->root.type == bfd_link_hash_defweak); + h->root.u.def.section = h->weakdef->root.u.def.section; + h->root.u.def.value = h->weakdef->root.u.def.value; + return true; + } + + /* This is a reference to a symbol defined by a dynamic object which + is not a function. */ + + return true; +} + +/* This function is called after all the input files have been read, + and the input sections have been assigned to output sections. We + check for any mips16 stub sections that we can discard. */ + +static boolean mips_elf_check_mips16_stubs + PARAMS ((struct mips_elf_link_hash_entry *, PTR)); + +static boolean +mips_elf_always_size_sections (output_bfd, info) + bfd *output_bfd; + struct bfd_link_info *info; +{ + if (info->relocateable + || ! mips_elf_hash_table (info)->mips16_stubs_seen) + return true; + + mips_elf_link_hash_traverse (mips_elf_hash_table (info), + mips_elf_check_mips16_stubs, + (PTR) NULL); + + return true; +} + +/* Check the mips16 stubs for a particular symbol, and see if we can + discard them. */ + +/*ARGSUSED*/ +static boolean +mips_elf_check_mips16_stubs (h, data) + struct mips_elf_link_hash_entry *h; + PTR data; +{ + if (h->fn_stub != NULL + && ! h->need_fn_stub) + { + /* We don't need the fn_stub; the only references to this symbol + are 16 bit calls. Clobber the size to 0 to prevent it from + being included in the link. */ + h->fn_stub->_raw_size = 0; + h->fn_stub->_cooked_size = 0; + h->fn_stub->flags &= ~ SEC_RELOC; + h->fn_stub->reloc_count = 0; + h->fn_stub->flags |= SEC_EXCLUDE; + } + + if (h->call_stub != NULL + && h->root.other == STO_MIPS16) + { + /* We don't need the call_stub; this is a 16 bit function, so + calls from other 16 bit functions are OK. Clobber the size + to 0 to prevent it from being included in the link. */ + h->call_stub->_raw_size = 0; + h->call_stub->_cooked_size = 0; + h->call_stub->flags &= ~ SEC_RELOC; + h->call_stub->reloc_count = 0; + h->call_stub->flags |= SEC_EXCLUDE; + } + + if (h->call_fp_stub != NULL + && h->root.other == STO_MIPS16) + { + /* We don't need the call_stub; this is a 16 bit function, so + calls from other 16 bit functions are OK. Clobber the size + to 0 to prevent it from being included in the link. */ + h->call_fp_stub->_raw_size = 0; + h->call_fp_stub->_cooked_size = 0; + h->call_fp_stub->flags &= ~ SEC_RELOC; + h->call_fp_stub->reloc_count = 0; + h->call_fp_stub->flags |= SEC_EXCLUDE; + } + + return true; +} + +/* Set the sizes of the dynamic sections. */ + +static boolean +mips_elf_size_dynamic_sections (output_bfd, info) + bfd *output_bfd; + struct bfd_link_info *info; +{ + bfd *dynobj; + asection *s; + boolean reltext; + asection *sgot; + struct mips_got_info *g; + + dynobj = elf_hash_table (info)->dynobj; + BFD_ASSERT (dynobj != NULL); + + if (elf_hash_table (info)->dynamic_sections_created) + { + /* Set the contents of the .interp section to the interpreter. */ + if (! info->shared) + { + s = bfd_get_section_by_name (dynobj, ".interp"); + BFD_ASSERT (s != NULL); + s->_raw_size = sizeof ELF_DYNAMIC_INTERPRETER; + s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER; + } + } + + /* Recompute the size of .got for local entires (reserved and + hipages) if needed. To estimate it, get the upper bound of total + size of loadable sections. */ + sgot = bfd_get_section_by_name (dynobj, ".got"); + + if (sgot != NULL) + { + bfd_size_type loadable_size = 0; + bfd_size_type local_gotno; + struct _bfd *sub; + + BFD_ASSERT (elf_section_data (sgot) != NULL); + g = (struct mips_got_info *) elf_section_data (sgot)->tdata; + BFD_ASSERT (g != NULL); + + for (sub = info->input_bfds; sub; sub = sub->link_next) + for (s = sub->sections; s != NULL; s = s->next) + { + if ((s->flags & SEC_ALLOC) == 0) + continue; + loadable_size += (s->_raw_size + 0xf) & ~0xf; + } + + loadable_size += MIPS_FUNCTION_STUB_SIZE; + + /* Assume there are two loadable segments consisting of + contiguous sections. Is 5 enough? */ + local_gotno = (loadable_size >> 16) + 5 + MIPS_RESERVED_GOTNO; + g->local_gotno = local_gotno; + sgot->_raw_size += local_gotno * 4; + } + + /* The check_relocs and adjust_dynamic_symbol entry points have + determined the sizes of the various dynamic sections. Allocate + memory for them. */ + reltext = false; + for (s = dynobj->sections; s != NULL; s = s->next) + { + const char *name; + boolean strip; + + /* It's OK to base decisions on the section name, because none + of the dynobj section names depend upon the input files. */ + name = bfd_get_section_name (dynobj, s); + + if ((s->flags & SEC_LINKER_CREATED) == 0) + continue; + + strip = false; + + if (strncmp (name, ".rel", 4) == 0) + { + if (s->_raw_size == 0) + strip = true; + else + { + const char *outname; + asection *target; + + /* If this relocation section applies to a read only + section, then we probably need a DT_TEXTREL entry. + If the relocation section is .rel.dyn, we always + assert a DT_TEXTREL entry rather than testing whether + there exists a relocation to a read only section or + not. */ + outname = bfd_get_section_name (output_bfd, + s->output_section); + target = bfd_get_section_by_name (output_bfd, outname + 4); + if ((target != NULL + && (target->flags & SEC_READONLY) != 0 + && (target->flags & SEC_ALLOC) != 0) + || strcmp (outname, ".rel.dyn") == 0) + reltext = true; + + /* We use the reloc_count field as a counter if we need + to copy relocs into the output file. */ + if (strcmp (name, ".rel.dyn") != 0) + s->reloc_count = 0; + } + } + else if (strncmp (name, ".got", 4) == 0) + { + int i; + + BFD_ASSERT (elf_section_data (s) != NULL); + g = (struct mips_got_info *) elf_section_data (s)->tdata; + BFD_ASSERT (g != NULL); + + /* Fix the size of .got section for the correspondence of + global symbols and got entries. This adds some useless + got entries. Is this required by ABI really? */ + i = elf_hash_table (info)->dynsymcount - g->global_gotsym; + s->_raw_size += i * 4; + } + else if (strncmp (name, ".stub", 5) == 0) + { + /* Irix rld assumes that the function stub isn't at the end + of .text section. So put a dummy. XXX */ + s->_raw_size += MIPS_FUNCTION_STUB_SIZE; + } + else if (! info->shared + && ! mips_elf_hash_table (info)->use_rld_obj_head + && strncmp (name, ".rld_map", 8) == 0) + { + /* We add a room for __rld_map. It will be filled in by the + rtld to contain a pointer to the _r_debug structure. */ + s->_raw_size += 4; + } + else if (SGI_COMPAT (output_bfd) + && strncmp (name, ".compact_rel", 12) == 0) + s->_raw_size += mips_elf_hash_table (info)->compact_rel_size; + else if (strncmp (name, ".init", 5) != 0) + { + /* It's not one of our sections, so don't allocate space. */ + continue; + } + + if (strip) + { + asection **spp; + + for (spp = &s->output_section->owner->sections; + *spp != s->output_section; + spp = &(*spp)->next) + ; + *spp = s->output_section->next; + --s->output_section->owner->section_count; + + continue; + } + + /* Allocate memory for the section contents. */ + s->contents = (bfd_byte *) bfd_alloc (dynobj, s->_raw_size); + if (s->contents == NULL && s->_raw_size != 0) + { + bfd_set_error (bfd_error_no_memory); + return false; + } + memset (s->contents, 0, s->_raw_size); + } + + if (elf_hash_table (info)->dynamic_sections_created) + { + /* Add some entries to the .dynamic section. We fill in the + values later, in elf_mips_finish_dynamic_sections, but we + must add the entries now so that we get the correct size for + the .dynamic section. The DT_DEBUG entry is filled in by the + dynamic linker and used by the debugger. */ + if (! info->shared) + { + if (SGI_COMPAT (output_bfd)) + { + /* SGI object has the equivalence of DT_DEBUG in the + DT_MIPS_RLD_MAP entry. */ + if (! bfd_elf32_add_dynamic_entry (info, DT_MIPS_RLD_MAP, 0)) + return false; + } + else + if (! bfd_elf32_add_dynamic_entry (info, DT_DEBUG, 0)) + return false; + } + + if (reltext) + { + if (! bfd_elf32_add_dynamic_entry (info, DT_TEXTREL, 0)) + return false; + } + + if (! bfd_elf32_add_dynamic_entry (info, DT_PLTGOT, 0)) + return false; + + if (bfd_get_section_by_name (dynobj, ".rel.dyn")) + { + if (! bfd_elf32_add_dynamic_entry (info, DT_REL, 0)) + return false; + + if (! bfd_elf32_add_dynamic_entry (info, DT_RELSZ, 0)) + return false; + + if (! bfd_elf32_add_dynamic_entry (info, DT_RELENT, 0)) + return false; + } + + if (! bfd_elf32_add_dynamic_entry (info, DT_MIPS_CONFLICTNO, 0)) + return false; + + if (! bfd_elf32_add_dynamic_entry (info, DT_MIPS_LIBLISTNO, 0)) + return false; + + if (bfd_get_section_by_name (dynobj, ".conflict") != NULL) + { + if (! bfd_elf32_add_dynamic_entry (info, DT_MIPS_CONFLICT, 0)) + return false; + + s = bfd_get_section_by_name (dynobj, ".liblist"); + BFD_ASSERT (s != NULL); + + if (! bfd_elf32_add_dynamic_entry (info, DT_MIPS_LIBLIST, 0)) + return false; + } + + if (! bfd_elf32_add_dynamic_entry (info, DT_MIPS_RLD_VERSION, 0)) + return false; + + if (! bfd_elf32_add_dynamic_entry (info, DT_MIPS_FLAGS, 0)) + return false; + +#if 0 + /* Time stamps in executable files are a bad idea. */ + if (! bfd_elf32_add_dynamic_entry (info, DT_MIPS_TIME_STAMP, 0)) + return false; +#endif + +#if 0 /* FIXME */ + if (! bfd_elf32_add_dynamic_entry (info, DT_MIPS_ICHECKSUM, 0)) + return false; +#endif + +#if 0 /* FIXME */ + if (! bfd_elf32_add_dynamic_entry (info, DT_MIPS_IVERSION, 0)) + return false; +#endif + + if (! bfd_elf32_add_dynamic_entry (info, DT_MIPS_BASE_ADDRESS, 0)) + return false; + + if (! bfd_elf32_add_dynamic_entry (info, DT_MIPS_LOCAL_GOTNO, 0)) + return false; + + if (! bfd_elf32_add_dynamic_entry (info, DT_MIPS_SYMTABNO, 0)) + return false; + + if (! bfd_elf32_add_dynamic_entry (info, DT_MIPS_UNREFEXTNO, 0)) + return false; + + if (! bfd_elf32_add_dynamic_entry (info, DT_MIPS_GOTSYM, 0)) + return false; + + if (! bfd_elf32_add_dynamic_entry (info, DT_MIPS_HIPAGENO, 0)) + return false; + +#if 0 /* (SGI_COMPAT) */ + if (! bfd_get_section_by_name (dynobj, ".init")) + if (! bfd_elf32_add_dynamic_entry (info, DT_INIT, 0)) + return false; + + if (! bfd_get_section_by_name (dynobj, ".fini")) + if (! bfd_elf32_add_dynamic_entry (info, DT_FINI, 0)) + return false; +#endif + } + + /* If we use dynamic linking, we generate a section symbol for each + output section. These are local symbols, which means that they + must come first in the dynamic symbol table. + That means we must increment the dynamic symbol index of every + other dynamic symbol. */ + { + const char * const *namep; + unsigned int c, i; + bfd_size_type strindex; + struct bfd_strtab_hash *dynstr; + struct mips_got_info *g; + + c = 0; + if (elf_hash_table (info)->dynamic_sections_created) + { + if (SGI_COMPAT (output_bfd)) + { + c = SIZEOF_MIPS_DYNSYM_SECNAMES - 1; + elf_link_hash_traverse (elf_hash_table (info), + mips_elf_adjust_dynindx, + (PTR) &c); + elf_hash_table (info)->dynsymcount += c; + + dynstr = elf_hash_table (info)->dynstr; + BFD_ASSERT (dynstr != NULL); + + for (i = 1, namep = mips_elf_dynsym_sec_names; + *namep != NULL; + i++, namep++) + { + s = bfd_get_section_by_name (output_bfd, *namep); + if (s != NULL) + elf_section_data (s)->dynindx = i; + + strindex = _bfd_stringtab_add (dynstr, *namep, true, false); + if (strindex == (bfd_size_type) -1) + return false; + + mips_elf_hash_table (info)->dynsym_sec_strindex[i] = strindex; + } + } + else + { + c = bfd_count_sections (output_bfd); + elf_link_hash_traverse (elf_hash_table (info), + mips_elf_adjust_dynindx, + (PTR) &c); + elf_hash_table (info)->dynsymcount += c; + + for (i = 1, s = output_bfd->sections; s != NULL; s = s->next, i++) + { + elf_section_data (s)->dynindx = i; + /* These symbols will have no names, so we don't need to + fiddle with dynstr_index. */ + } + } + } + + if (sgot != NULL) + { + BFD_ASSERT (elf_section_data (sgot) != NULL); + g = (struct mips_got_info *) elf_section_data (sgot)->tdata; + BFD_ASSERT (g != NULL); + + /* If there are no global got symbols, fake the last symbol so + for safety. */ + if (g->global_gotsym) + g->global_gotsym += c; + else + g->global_gotsym = elf_hash_table (info)->dynsymcount - 1; + } + } + + return true; +} + +/* Increment the index of a dynamic symbol by a given amount. Called + via elf_link_hash_traverse. */ + +static boolean +mips_elf_adjust_dynindx (h, cparg) + struct elf_link_hash_entry *h; + PTR cparg; +{ + unsigned int *cp = (unsigned int *) cparg; + + if (h->dynindx != -1) + h->dynindx += *cp; + return true; +} + +/* Finish up dynamic symbol handling. We set the contents of various + dynamic sections here. */ + +static boolean +mips_elf_finish_dynamic_symbol (output_bfd, info, h, sym) + bfd *output_bfd; + struct bfd_link_info *info; + struct elf_link_hash_entry *h; + Elf_Internal_Sym *sym; +{ + bfd *dynobj; + bfd_vma gval; + asection *sgot; + struct mips_got_info *g; + const char *name; + + dynobj = elf_hash_table (info)->dynobj; + gval = sym->st_value; + + if (h->plt_offset != (bfd_vma) -1) + { + asection *s; + bfd_byte *p; + bfd_byte stub[MIPS_FUNCTION_STUB_SIZE]; + + /* This symbol has a stub. Set it up. */ + + BFD_ASSERT (h->dynindx != -1); + + s = bfd_get_section_by_name (dynobj, ".stub"); + BFD_ASSERT (s != NULL); + + /* Fill the stub. */ + p = stub; + bfd_put_32 (output_bfd, STUB_LW(output_bfd), p); + p += 4; + bfd_put_32 (output_bfd, STUB_MOVE, p); + p += 4; + + /* FIXME: Can h->dynindex be more than 64K? */ + if (h->dynindx & 0xffff0000) + return false; + + bfd_put_32 (output_bfd, STUB_JALR, p); + p += 4; + bfd_put_32 (output_bfd, STUB_LI16 + h->dynindx, p); + + BFD_ASSERT (h->plt_offset <= s->_raw_size); + memcpy (s->contents + h->plt_offset, stub, MIPS_FUNCTION_STUB_SIZE); + + /* Mark the symbol as undefined. plt_offset != -1 occurs + only for the referenced symbol. */ + sym->st_shndx = SHN_UNDEF; + + /* The run-time linker uses the st_value field of the symbol + to reset the global offset table entry for this external + to its stub address when unlinking a shared object. */ + gval = s->output_section->vma + s->output_offset + h->plt_offset; + sym->st_value = gval; + } + + BFD_ASSERT (h->dynindx != -1); + + sgot = bfd_get_section_by_name (dynobj, ".got"); + BFD_ASSERT (sgot != NULL); + BFD_ASSERT (elf_section_data (sgot) != NULL); + g = (struct mips_got_info *) elf_section_data (sgot)->tdata; + BFD_ASSERT (g != NULL); + + if ((unsigned long) h->dynindx >= g->global_gotsym) + { + bfd_size_type offset; + + /* This symbol has an entry in the global offset table. Set its + value to the corresponding got entry, if needed. */ + if (h->got_offset == (bfd_vma) -1) + { + offset = (h->dynindx - g->global_gotsym + g->local_gotno) * 4; + BFD_ASSERT (g->local_gotno * 4 <= offset + && offset < sgot->_raw_size); + bfd_put_32 (output_bfd, gval, sgot->contents + offset); + } + } + + /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. */ + name = h->root.root.string; + if (strcmp (name, "_DYNAMIC") == 0 + || strcmp (name, "_GLOBAL_OFFSET_TABLE_") == 0) + sym->st_shndx = SHN_ABS; + else if (strcmp (name, "_DYNAMIC_LINK") == 0) + { + sym->st_shndx = SHN_ABS; + sym->st_info = ELF_ST_INFO (STB_GLOBAL, STT_SECTION); + sym->st_value = 1; + } + else if (SGI_COMPAT (output_bfd)) + { + if (strcmp (name, "_gp_disp") == 0) + { + sym->st_shndx = SHN_ABS; + sym->st_info = ELF_ST_INFO (STB_GLOBAL, STT_SECTION); + sym->st_value = elf_gp (output_bfd); + } + else if (strcmp (name, mips_elf_dynsym_rtproc_names[0]) == 0 + || strcmp (name, mips_elf_dynsym_rtproc_names[1]) == 0) + { + sym->st_info = ELF_ST_INFO (STB_GLOBAL, STT_SECTION); + sym->st_other = STO_PROTECTED; + sym->st_value = 0; + sym->st_shndx = SHN_MIPS_DATA; + } + else if (strcmp (name, mips_elf_dynsym_rtproc_names[2]) == 0) + { + sym->st_info = ELF_ST_INFO (STB_GLOBAL, STT_SECTION); + sym->st_other = STO_PROTECTED; + sym->st_value = mips_elf_hash_table (info)->procedure_count; + sym->st_shndx = SHN_ABS; + } + else if (sym->st_shndx != SHN_UNDEF && sym->st_shndx != SHN_ABS) + { + if (h->type == STT_FUNC) + sym->st_shndx = SHN_MIPS_TEXT; + else if (h->type == STT_OBJECT) + sym->st_shndx = SHN_MIPS_DATA; + } + } + + if (SGI_COMPAT (output_bfd) + && ! info->shared) + { + if (! mips_elf_hash_table (info)->use_rld_obj_head + && strcmp (name, "__rld_map") == 0) + { + asection *s = bfd_get_section_by_name (dynobj, ".rld_map"); + BFD_ASSERT (s != NULL); + sym->st_value = s->output_section->vma + s->output_offset; + bfd_put_32 (output_bfd, (bfd_vma) 0, s->contents); + if (mips_elf_hash_table (info)->rld_value == 0) + mips_elf_hash_table (info)->rld_value = sym->st_value; + } + else if (mips_elf_hash_table (info)->use_rld_obj_head + && strcmp (name, "__rld_obj_head") == 0) + { + asection *s = bfd_get_section_by_name (dynobj, ".rld_map"); + BFD_ASSERT (s != NULL); + mips_elf_hash_table (info)->rld_value = sym->st_value; + } + } + + /* If this is a mips16 symbol, force the value to be even. */ + if (sym->st_other == STO_MIPS16 + && (sym->st_value & 1) != 0) + --sym->st_value; + + return true; +} + +/* Finish up the dynamic sections. */ + +static boolean +mips_elf_finish_dynamic_sections (output_bfd, info) + bfd *output_bfd; + struct bfd_link_info *info; +{ + bfd *dynobj; + asection *sdyn; + asection *sgot; + struct mips_got_info *g; + + dynobj = elf_hash_table (info)->dynobj; + + sdyn = bfd_get_section_by_name (dynobj, ".dynamic"); + + sgot = bfd_get_section_by_name (dynobj, ".got"); + if (sgot == NULL) + g = NULL; + else + { + BFD_ASSERT (elf_section_data (sgot) != NULL); + g = (struct mips_got_info *) elf_section_data (sgot)->tdata; + BFD_ASSERT (g != NULL); + } + + if (elf_hash_table (info)->dynamic_sections_created) + { + Elf32_External_Dyn *dyncon, *dynconend; + + BFD_ASSERT (sdyn != NULL); + BFD_ASSERT (g != NULL); + + dyncon = (Elf32_External_Dyn *) sdyn->contents; + dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->_raw_size); + for (; dyncon < dynconend; dyncon++) + { + Elf_Internal_Dyn dyn; + const char *name; + size_t elemsize; + asection *s; + + bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn); + + switch (dyn.d_tag) + { + default: + break; + + case DT_RELENT: + s = bfd_get_section_by_name (dynobj, ".rel.dyn"); + BFD_ASSERT (s != NULL); + dyn.d_un.d_val = sizeof (Elf32_External_Rel); + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_STRSZ: + /* Rewrite DT_STRSZ. */ + dyn.d_un.d_val = + _bfd_stringtab_size (elf_hash_table (info)->dynstr); + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_PLTGOT: + name = ".got"; + goto get_vma; + case DT_MIPS_CONFLICT: + name = ".conflict"; + goto get_vma; + case DT_MIPS_LIBLIST: + name = ".liblist"; + get_vma: + s = bfd_get_section_by_name (output_bfd, name); + BFD_ASSERT (s != NULL); + dyn.d_un.d_ptr = s->vma; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_MIPS_RLD_VERSION: + dyn.d_un.d_val = 1; /* XXX */ + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_MIPS_FLAGS: + dyn.d_un.d_val = RHF_NOTPOT; /* XXX */ + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_MIPS_CONFLICTNO: + name = ".conflict"; + elemsize = sizeof (Elf32_Conflict); + goto set_elemno; + + case DT_MIPS_LIBLISTNO: + name = ".liblist"; + elemsize = sizeof (Elf32_Lib); + set_elemno: + s = bfd_get_section_by_name (output_bfd, name); + if (s != NULL) + { + if (s->_cooked_size != 0) + dyn.d_un.d_val = s->_cooked_size / elemsize; + else + dyn.d_un.d_val = s->_raw_size / elemsize; + } + else + dyn.d_un.d_val = 0; + + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_MIPS_TIME_STAMP: + time ((time_t *) &dyn.d_un.d_val); + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_MIPS_ICHECKSUM: + /* XXX FIXME: */ + break; + + case DT_MIPS_IVERSION: + /* XXX FIXME: */ + break; + + case DT_MIPS_BASE_ADDRESS: + s = output_bfd->sections; + BFD_ASSERT (s != NULL); + dyn.d_un.d_ptr = s->vma & ~(0xffff); + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_MIPS_LOCAL_GOTNO: + dyn.d_un.d_val = g->local_gotno; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_MIPS_SYMTABNO: + name = ".dynsym"; + elemsize = sizeof (Elf32_External_Sym); + s = bfd_get_section_by_name (output_bfd, name); + BFD_ASSERT (s != NULL); + + if (s->_cooked_size != 0) + dyn.d_un.d_val = s->_cooked_size / elemsize; + else + dyn.d_un.d_val = s->_raw_size / elemsize; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_MIPS_UNREFEXTNO: + /* XXX FIXME: */ + dyn.d_un.d_val = SIZEOF_MIPS_DYNSYM_SECNAMES; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_MIPS_GOTSYM: + dyn.d_un.d_val = g->global_gotsym; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_MIPS_HIPAGENO: + dyn.d_un.d_val = g->local_gotno - MIPS_RESERVED_GOTNO; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + case DT_MIPS_RLD_MAP: + dyn.d_un.d_ptr = mips_elf_hash_table (info)->rld_value; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; + + } + } + } + + /* The first entry of the global offset table will be filled at + runtime. The second entry will be used by some runtime loaders. + This isn't the case of Irix rld. */ + if (sgot != NULL && sgot->_raw_size > 0) + { + bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents); + bfd_put_32 (output_bfd, (bfd_vma) 0x80000000, sgot->contents + 4); + } + + if (sgot != NULL) + elf_section_data (sgot->output_section)->this_hdr.sh_entsize = 4; + + { + asection *sdynsym; + asection *s; + unsigned int i; + bfd_vma last; + Elf_Internal_Sym sym; + long dindx; + const char *name; + const char * const * namep = mips_elf_dynsym_sec_names; + Elf32_compact_rel cpt; + + /* Set up the section symbols for the output sections. SGI sets + the STT_NOTYPE attribute for these symbols. Should we do so? */ + + sdynsym = bfd_get_section_by_name (dynobj, ".dynsym"); + if (sdynsym != NULL) + { + if (SGI_COMPAT (output_bfd)) + { + sym.st_size = 0; + sym.st_name = 0; + sym.st_info = ELF_ST_INFO (STB_LOCAL, STT_NOTYPE); + sym.st_other = 0; + + i = 0; + last = 0; + dindx = 0; + while ((name = *namep++) != NULL) + { + s = bfd_get_section_by_name (output_bfd, name); + if (s != NULL) + { + sym.st_value = s->vma; + dindx = elf_section_data (s)->dynindx; + last = s->vma + s->_raw_size; + } + else + { + sym.st_value = last; + dindx++; + } + + sym.st_shndx = (i < MIPS_TEXT_DYNSYM_SECNO + ? SHN_MIPS_TEXT + : SHN_MIPS_DATA); + ++i; + sym.st_name = + mips_elf_hash_table (info)->dynsym_sec_strindex[dindx]; + + bfd_elf32_swap_symbol_out (output_bfd, &sym, + (((Elf32_External_Sym *) + sdynsym->contents) + + dindx)); + } + + /* Set the sh_info field of the output .dynsym section to + the index of the first global symbol. */ + elf_section_data (sdynsym->output_section)->this_hdr.sh_info = + SIZEOF_MIPS_DYNSYM_SECNAMES; + } + else + { + sym.st_size = 0; + sym.st_name = 0; + sym.st_info = ELF_ST_INFO (STB_LOCAL, STT_SECTION); + sym.st_other = 0; + + for (s = output_bfd->sections; s != NULL; s = s->next) + { + int indx; + + sym.st_value = s->vma; + + indx = elf_section_data (s)->this_idx; + BFD_ASSERT (indx > 0); + sym.st_shndx = indx; + + bfd_elf32_swap_symbol_out (output_bfd, &sym, + (((Elf32_External_Sym *) + sdynsym->contents) + + elf_section_data (s)->dynindx)); + } + + /* Set the sh_info field of the output .dynsym section to + the index of the first global symbol. */ + elf_section_data (sdynsym->output_section)->this_hdr.sh_info = + bfd_count_sections (output_bfd) + 1; + } + } + + if (SGI_COMPAT (output_bfd)) + { + /* Write .compact_rel section out. */ + s = bfd_get_section_by_name (dynobj, ".compact_rel"); + if (s != NULL) + { + cpt.id1 = 1; + cpt.num = s->reloc_count; + cpt.id2 = 2; + cpt.offset = (s->output_section->filepos + + sizeof (Elf32_External_compact_rel)); + cpt.reserved0 = 0; + cpt.reserved1 = 0; + bfd_elf32_swap_compact_rel_out (output_bfd, &cpt, + ((Elf32_External_compact_rel *) + s->contents)); + + /* Clean up a dummy stub function entry in .text. */ + s = bfd_get_section_by_name (dynobj, ".stub"); + if (s != NULL) + { + file_ptr dummy_offset; + + BFD_ASSERT (s->_raw_size >= MIPS_FUNCTION_STUB_SIZE); + dummy_offset = s->_raw_size - MIPS_FUNCTION_STUB_SIZE; + memset (s->contents + dummy_offset, 0, + MIPS_FUNCTION_STUB_SIZE); + } + } + } + + /* Clean up a first relocation in .rel.dyn. */ + s = bfd_get_section_by_name (dynobj, ".rel.dyn"); + if (s != NULL && s->_raw_size > 0) + memset (s->contents, 0, sizeof (Elf32_External_Rel)); + } + + return true; +} + +/* This is almost identical to bfd_generic_get_... except that some + MIPS relocations need to be handled specially. Sigh. */ + +static bfd_byte * +elf32_mips_get_relocated_section_contents (abfd, link_info, link_order, data, + relocateable, symbols) + bfd *abfd; + struct bfd_link_info *link_info; + struct bfd_link_order *link_order; + bfd_byte *data; + boolean relocateable; + asymbol **symbols; +{ + /* Get enough memory to hold the stuff */ + bfd *input_bfd = link_order->u.indirect.section->owner; + asection *input_section = link_order->u.indirect.section; + + long reloc_size = bfd_get_reloc_upper_bound (input_bfd, input_section); + arelent **reloc_vector = NULL; + long reloc_count; + + if (reloc_size < 0) + goto error_return; + + reloc_vector = (arelent **) bfd_malloc (reloc_size); + if (reloc_vector == NULL && reloc_size != 0) + goto error_return; + + /* read in the section */ + if (!bfd_get_section_contents (input_bfd, + input_section, + (PTR) data, + 0, + input_section->_raw_size)) + goto error_return; + + /* We're not relaxing the section, so just copy the size info */ + input_section->_cooked_size = input_section->_raw_size; + input_section->reloc_done = true; + + reloc_count = bfd_canonicalize_reloc (input_bfd, + input_section, + reloc_vector, + symbols); + if (reloc_count < 0) + goto error_return; + + if (reloc_count > 0) + { + arelent **parent; + /* for mips */ + int gp_found; + bfd_vma gp = 0x12345678; /* initialize just to shut gcc up */ + + { + struct bfd_hash_entry *h; + struct bfd_link_hash_entry *lh; + /* Skip all this stuff if we aren't mixing formats. */ + if (abfd && input_bfd + && abfd->xvec == input_bfd->xvec) + lh = 0; + else + { + h = bfd_hash_lookup (&link_info->hash->table, "_gp", false, false); + lh = (struct bfd_link_hash_entry *) h; + } + lookup: + if (lh) + { + switch (lh->type) + { + case bfd_link_hash_undefined: + case bfd_link_hash_undefweak: + case bfd_link_hash_common: + gp_found = 0; + break; + case bfd_link_hash_defined: + case bfd_link_hash_defweak: + gp_found = 1; + gp = lh->u.def.value; + break; + case bfd_link_hash_indirect: + case bfd_link_hash_warning: + lh = lh->u.i.link; + /* @@FIXME ignoring warning for now */ + goto lookup; + case bfd_link_hash_new: + default: + abort (); + } + } + else + gp_found = 0; + } + /* end mips */ + for (parent = reloc_vector; *parent != (arelent *) NULL; + parent++) + { + char *error_message = (char *) NULL; + bfd_reloc_status_type r; + + /* Specific to MIPS: Deal with relocation types that require + knowing the gp of the output bfd. */ + asymbol *sym = *(*parent)->sym_ptr_ptr; + if (bfd_is_abs_section (sym->section) && abfd) + { + /* The special_function wouldn't get called anyways. */ + } + else if (!gp_found) + { + /* The gp isn't there; let the special function code + fall over on its own. */ + } + else if ((*parent)->howto->special_function + == _bfd_mips_elf_gprel16_reloc) + { + /* bypass special_function call */ + r = gprel16_with_gp (input_bfd, sym, *parent, input_section, + relocateable, (PTR) data, gp); + goto skip_bfd_perform_relocation; + } + /* end mips specific stuff */ + + r = bfd_perform_relocation (input_bfd, + *parent, + (PTR) data, + input_section, + relocateable ? abfd : (bfd *) NULL, + &error_message); + skip_bfd_perform_relocation: + + if (relocateable) + { + asection *os = input_section->output_section; + + /* A partial link, so keep the relocs */ + os->orelocation[os->reloc_count] = *parent; + os->reloc_count++; + } + + if (r != bfd_reloc_ok) + { + switch (r) + { + case bfd_reloc_undefined: + if (!((*link_info->callbacks->undefined_symbol) + (link_info, bfd_asymbol_name (*(*parent)->sym_ptr_ptr), + input_bfd, input_section, (*parent)->address))) + goto error_return; + break; + case bfd_reloc_dangerous: + BFD_ASSERT (error_message != (char *) NULL); + if (!((*link_info->callbacks->reloc_dangerous) + (link_info, error_message, input_bfd, input_section, + (*parent)->address))) + goto error_return; + break; + case bfd_reloc_overflow: + if (!((*link_info->callbacks->reloc_overflow) + (link_info, bfd_asymbol_name (*(*parent)->sym_ptr_ptr), + (*parent)->howto->name, (*parent)->addend, + input_bfd, input_section, (*parent)->address))) + goto error_return; + break; + case bfd_reloc_outofrange: + default: + abort (); + break; + } + + } + } + } + if (reloc_vector != NULL) + free (reloc_vector); + return data; + +error_return: + if (reloc_vector != NULL) + free (reloc_vector); + return NULL; +} +#define bfd_elf32_bfd_get_relocated_section_contents \ + elf32_mips_get_relocated_section_contents + +/* 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 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" +#define ELF_ARCH bfd_arch_mips +#define ELF_MACHINE_CODE EM_MIPS + +/* 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 + +#define elf_backend_collect true +#define elf_backend_type_change_ok true +#define elf_info_to_howto 0 +#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_section_from_shdr mips_elf32_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_section_processing mips_elf32_section_processing +#define elf_backend_symbol_processing _bfd_mips_elf_symbol_processing +#define elf_backend_additional_program_headers \ + mips_elf_additional_program_headers +#define elf_backend_modify_segment_map mips_elf_modify_segment_map +#define elf_backend_final_write_processing \ + _bfd_mips_elf_final_write_processing +#define elf_backend_ecoff_debug_swap &mips_elf32_ecoff_debug_swap + +#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_set_section_contents _bfd_mips_elf_set_section_contents +#define bfd_elf32_bfd_link_hash_table_create \ + mips_elf_link_hash_table_create +#define bfd_elf32_bfd_final_link mips_elf_final_link +#define bfd_elf32_bfd_copy_private_bfd_data \ + _bfd_mips_elf_copy_private_bfd_data +#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 elf_backend_add_symbol_hook mips_elf_add_symbol_hook +#define elf_backend_create_dynamic_sections \ + mips_elf_create_dynamic_sections +#define elf_backend_check_relocs mips_elf_check_relocs +#define elf_backend_adjust_dynamic_symbol \ + mips_elf_adjust_dynamic_symbol +#define elf_backend_always_size_sections \ + mips_elf_always_size_sections +#define elf_backend_size_dynamic_sections \ + mips_elf_size_dynamic_sections +#define elf_backend_relocate_section mips_elf_relocate_section +#define elf_backend_link_output_symbol_hook \ + mips_elf_link_output_symbol_hook +#define elf_backend_finish_dynamic_symbol \ + mips_elf_finish_dynamic_symbol +#define elf_backend_finish_dynamic_sections \ + mips_elf_finish_dynamic_sections + +#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..c513dea --- /dev/null +++ b/contrib/binutils/bfd/elf64-mips.c @@ -0,0 +1,2172 @@ +/* MIPS-specific support for 64-bit ELF + Copyright 1996, 1997 Free Software Foundation, Inc. + Ian Lance Taylor, 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. */ + +/* 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. + + The MIPS 64-bit ELF ABI also uses an unusual archive map format. */ + +#include "bfd.h" +#include "sysdep.h" +#include "libbfd.h" +#include "aout/ar.h" +#include "bfdlink.h" +#include "genlink.h" +#include "elf-bfd.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_64 +#include "ecoffswap.h" + +static void mips_elf64_swap_reloc_in + PARAMS ((bfd *, const Elf64_Mips_External_Rel *, + Elf64_Mips_Internal_Rel *)); +static void mips_elf64_swap_reloca_in + PARAMS ((bfd *, const Elf64_Mips_External_Rela *, + Elf64_Mips_Internal_Rela *)); +#if 0 +static void mips_elf64_swap_reloc_out + PARAMS ((bfd *, const Elf64_Mips_Internal_Rel *, + Elf64_Mips_External_Rel *)); +#endif +static void mips_elf64_swap_reloca_out + PARAMS ((bfd *, const Elf64_Mips_Internal_Rela *, + Elf64_Mips_External_Rela *)); +static reloc_howto_type *mips_elf64_reloc_type_lookup + PARAMS ((bfd *, bfd_reloc_code_real_type)); +static long mips_elf64_get_reloc_upper_bound PARAMS ((bfd *, asection *)); +static boolean mips_elf64_slurp_one_reloc_table + PARAMS ((bfd *, asection *, asymbol **, const Elf_Internal_Shdr *)); +static boolean mips_elf64_slurp_reloc_table + PARAMS ((bfd *, asection *, asymbol **, boolean)); +static void mips_elf64_write_relocs PARAMS ((bfd *, asection *, PTR)); +static boolean mips_elf64_section_from_shdr + PARAMS ((bfd *, Elf_Internal_Shdr *, char *)); +static boolean mips_elf64_section_processing + PARAMS ((bfd *, Elf_Internal_Shdr *)); +static boolean mips_elf64_slurp_armap PARAMS ((bfd *)); +static boolean mips_elf64_write_armap + PARAMS ((bfd *, unsigned int, struct orl *, unsigned int, int)); + +/* The relocation types. */ + +enum mips_elf64_reloc_type +{ + R_MIPS_NONE = 0, + R_MIPS_16 = 1, + R_MIPS_32 = 2, + R_MIPS_ADD = 2, + R_MIPS_REL32 = 3, + R_MIPS_REL = 3, + R_MIPS_26 = 4, + R_MIPS_HI16 = 5, + R_MIPS_LO16 = 6, + R_MIPS_GPREL16 = 7, + R_MIPS_GPREL = 7, + R_MIPS_LITERAL = 8, + R_MIPS_GOT16 = 9, + R_MIPS_GOT = 9, + R_MIPS_PC16 = 10, + R_MIPS_CALL16 = 11, + R_MIPS_CALL = 11, + R_MIPS_GPREL32 = 12, + R_MIPS_SHIFT5 = 16, + R_MIPS_SHIFT6 = 17, + R_MIPS_64 = 18, + R_MIPS_GOT_DISP = 19, + R_MIPS_GOT_PAGE = 20, + R_MIPS_GOT_OFST = 21, + R_MIPS_GOT_HI16 = 22, + R_MIPS_GOT_LO16 = 23, + R_MIPS_SUB = 24, + R_MIPS_INSERT_A = 25, + R_MIPS_INSERT_B = 26, + R_MIPS_DELETE = 27, + R_MIPS_HIGHER = 28, + R_MIPS_HIGHEST = 29, + R_MIPS_CALL_HI16 = 30, + R_MIPS_CALL_LO16 = 31, + R_MIPS_SCN_DISP = 32, + R_MIPS_REL16 = 33, + R_MIPS_ADD_IMMEDIATE = 34, + R_MIPS_PJUMP = 35, + R_MIPS_RELGOT = 36 +}; + +/* 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 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_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 */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_16", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* 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_bitfield, /* complain_on_overflow */ + bfd_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_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_REL32", /* name */ + true, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* 26 bit branch 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. */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_26", /* name */ + true, /* partial_inplace */ + 0x3ffffff, /* src_mask */ + 0x3ffffff, /* 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_hi16_reloc, /* special_function */ + "R_MIPS_HI16", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* 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 */ + 0xffff, /* src_mask */ + 0xffff, /* 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_elf_gprel16_reloc, /* special_function */ + "R_MIPS_GPREL16", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* 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_elf_gprel16_reloc, /* special_function */ + "R_MIPS_LITERAL", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* 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 */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* 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_elf_generic_reloc, /* special_function */ + "R_MIPS_PC16", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* 16 bit call through global offset table. */ + /* FIXME: This is not handled correctly. */ + 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_elf_generic_reloc, /* special_function */ + "R_MIPS_CALL16", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* 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_bitfield, /* complain_on_overflow */ + _bfd_mips_elf_gprel32_reloc, /* special_function */ + "R_MIPS_GPREL32", /* name */ + true, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + false), /* pcrel_offset */ + + { 13 }, + { 14 }, + { 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_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_elf_generic_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_bitfield, /* complain_on_overflow */ + bfd_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. */ + /* FIXME: Not handled correctly. */ + 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_bitfield, /* complain_on_overflow */ + bfd_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. */ + /* FIXME: Not handled correctly. */ + 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_bitfield, /* complain_on_overflow */ + bfd_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. */ + /* FIXME: Not handled correctly. */ + 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_bitfield, /* complain_on_overflow */ + bfd_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. */ + /* FIXME: Not handled correctly. */ + 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_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. */ + /* FIXME: Not handled correctly. */ + 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_elf_generic_reloc, /* special_function */ + "R_MIPS_GOT_LO16", /* name */ + true, /* partial_inplace */ + 0x0000ffff, /* src_mask */ + 0x0000ffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* 64 bit substraction. */ + /* FIXME: Not handled correctly. */ + HOWTO (R_MIPS_SUB, /* type */ + 0, /* rightshift */ + 4, /* size (0 = byte, 1 = short, 2 = long) */ + 64, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + bfd_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 */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 0, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_INSERT_A", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0, /* 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 */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 0, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_INSERT_B", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + false), /* pcrel_offset */ + + /* Delete a 32 bit instruction. */ + /* FIXME: Not handled correctly. */ + HOWTO (R_MIPS_DELETE, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 0, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_DELETE", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + false), /* pcrel_offset */ + + /* Get the higher value of a 64 bit addend. */ + /* FIXME: Not handled correctly. */ + 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_elf_generic_reloc, /* special_function */ + "R_MIPS_HIGHER", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* Get the highest value of a 64 bit addend. */ + /* FIXME: Not handled correctly. */ + 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_elf_generic_reloc, /* special_function */ + "R_MIPS_HIGHEST", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* High 16 bits of displacement in global offset table. */ + /* FIXME: Not handled correctly. */ + 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_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. */ + /* FIXME: Not handled correctly. */ + 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_elf_generic_reloc, /* special_function */ + "R_MIPS_CALL_LO16", /* name */ + true, /* partial_inplace */ + 0x0000ffff, /* src_mask */ + 0x0000ffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* I'm not sure what the remaining relocs are, but they are defined + on Irix 6. */ + + HOWTO (R_MIPS_SCN_DISP, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 0, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_SCN_DISP", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + false), /* pcrel_offset */ + + HOWTO (R_MIPS_REL16, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 0, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_REL16", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + false), /* pcrel_offset */ + + HOWTO (R_MIPS_ADD_IMMEDIATE, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 0, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_ADD_IMMEDIATE", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + false), /* pcrel_offset */ + + HOWTO (R_MIPS_PJUMP, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 0, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_PJUMP", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + false), /* pcrel_offset */ + + HOWTO (R_MIPS_RELGOT, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 0, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_RELGOT", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0, /* 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_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 */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_16", /* name */ + true, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* 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_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_32", /* name */ + true, /* 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_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_REL32", /* name */ + true, /* partial_inplace */ + 0, /* src_mask */ + 0xffffffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* 26 bit branch 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. */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_26", /* name */ + true, /* partial_inplace */ + 0, /* src_mask */ + 0x3ffffff, /* 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_elf_generic_reloc, /* special_function */ + "R_MIPS_HI16", /* name */ + true, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* 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_elf_generic_reloc, /* special_function */ + "R_MIPS_LO16", /* name */ + true, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* 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_elf_gprel16_reloc, /* special_function */ + "R_MIPS_GPREL16", /* name */ + true, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* 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_elf_gprel16_reloc, /* special_function */ + "R_MIPS_LITERAL", /* name */ + true, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* Reference to global offset table. */ + /* FIXME: This is not handled correctly. */ + 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_elf_generic_reloc, /* special_function */ + "R_MIPS_GOT16", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* 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_elf_generic_reloc, /* special_function */ + "R_MIPS_PC16", /* name */ + true, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* 16 bit call through global offset table. */ + /* FIXME: This is not handled correctly. */ + 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_elf_generic_reloc, /* special_function */ + "R_MIPS_CALL16", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* 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_bitfield, /* complain_on_overflow */ + _bfd_mips_elf_gprel32_reloc, /* special_function */ + "R_MIPS_GPREL32", /* name */ + true, /* partial_inplace */ + 0, /* src_mask */ + 0xffffffff, /* dst_mask */ + false), /* pcrel_offset */ + + { 13 }, + { 14 }, + { 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_elf_generic_reloc, /* special_function */ + "R_MIPS_SHIFT5", /* name */ + true, /* partial_inplace */ + 0, /* 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_elf_generic_reloc, /* special_function */ + "R_MIPS_SHIFT6", /* name */ + true, /* 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_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_64", /* name */ + true, /* partial_inplace */ + 0, /* src_mask */ + MINUS_ONE, /* dst_mask */ + false), /* pcrel_offset */ + + /* Displacement in the global offset table. */ + /* FIXME: Not handled correctly. */ + 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_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_GOT_DISP", /* name */ + true, /* partial_inplace */ + 0, /* src_mask */ + 0x0000ffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* Displacement to page pointer in the global offset table. */ + /* FIXME: Not handled correctly. */ + 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_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_GOT_PAGE", /* name */ + true, /* partial_inplace */ + 0, /* src_mask */ + 0x0000ffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* Offset from page pointer in the global offset table. */ + /* FIXME: Not handled correctly. */ + 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_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_GOT_OFST", /* name */ + true, /* partial_inplace */ + 0, /* src_mask */ + 0x0000ffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* High 16 bits of displacement in global offset table. */ + /* FIXME: Not handled correctly. */ + 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_elf_generic_reloc, /* special_function */ + "R_MIPS_GOT_HI16", /* name */ + true, /* partial_inplace */ + 0, /* src_mask */ + 0x0000ffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* Low 16 bits of displacement in global offset table. */ + /* FIXME: Not handled correctly. */ + 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_elf_generic_reloc, /* special_function */ + "R_MIPS_GOT_LO16", /* name */ + true, /* partial_inplace */ + 0, /* src_mask */ + 0x0000ffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* 64 bit substraction. */ + /* FIXME: Not handled correctly. */ + HOWTO (R_MIPS_SUB, /* type */ + 0, /* rightshift */ + 4, /* size (0 = byte, 1 = short, 2 = long) */ + 64, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_SUB", /* name */ + true, /* 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 */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 0, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_INSERT_A", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0, /* 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 */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 0, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_INSERT_B", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + false), /* pcrel_offset */ + + /* Delete a 32 bit instruction. */ + /* FIXME: Not handled correctly. */ + HOWTO (R_MIPS_DELETE, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 0, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_DELETE", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + false), /* pcrel_offset */ + + /* Get the higher value of a 64 bit addend. */ + /* FIXME: Not handled correctly. */ + 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_elf_generic_reloc, /* special_function */ + "R_MIPS_HIGHER", /* name */ + true, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* Get the highest value of a 64 bit addend. */ + /* FIXME: Not handled correctly. */ + 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_elf_generic_reloc, /* special_function */ + "R_MIPS_HIGHEST", /* name */ + true, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* High 16 bits of displacement in global offset table. */ + /* FIXME: Not handled correctly. */ + 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_elf_generic_reloc, /* special_function */ + "R_MIPS_CALL_HI16", /* name */ + true, /* partial_inplace */ + 0, /* src_mask */ + 0x0000ffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* Low 16 bits of displacement in global offset table. */ + /* FIXME: Not handled correctly. */ + 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_elf_generic_reloc, /* special_function */ + "R_MIPS_CALL_LO16", /* name */ + true, /* partial_inplace */ + 0, /* src_mask */ + 0x0000ffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* I'm not sure what the remaining relocs are, but they are defined + on Irix 6. */ + + HOWTO (R_MIPS_SCN_DISP, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 0, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_SCN_DISP", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + false), /* pcrel_offset */ + + HOWTO (R_MIPS_REL16, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 0, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_REL16", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + false), /* pcrel_offset */ + + HOWTO (R_MIPS_ADD_IMMEDIATE, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 0, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_ADD_IMMEDIATE", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + false), /* pcrel_offset */ + + HOWTO (R_MIPS_PJUMP, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 0, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_PJUMP", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + false), /* pcrel_offset */ + + HOWTO (R_MIPS_RELGOT, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 0, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_RELGOT", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + false) /* pcrel_offset */ +}; + +/* Swap in a MIPS 64-bit Rel reloc. */ + +static void +mips_elf64_swap_reloc_in (abfd, src, dst) + bfd *abfd; + const Elf64_Mips_External_Rel *src; + Elf64_Mips_Internal_Rel *dst; +{ + dst->r_offset = bfd_h_get_64 (abfd, (bfd_byte *) src->r_offset); + dst->r_sym = bfd_h_get_32 (abfd, (bfd_byte *) src->r_sym); + dst->r_ssym = bfd_h_get_8 (abfd, (bfd_byte *) src->r_ssym); + dst->r_type3 = bfd_h_get_8 (abfd, (bfd_byte *) src->r_type3); + dst->r_type2 = bfd_h_get_8 (abfd, (bfd_byte *) src->r_type2); + dst->r_type = bfd_h_get_8 (abfd, (bfd_byte *) src->r_type); +} + +/* Swap in a MIPS 64-bit Rela reloc. */ + +static void +mips_elf64_swap_reloca_in (abfd, src, dst) + bfd *abfd; + const Elf64_Mips_External_Rela *src; + Elf64_Mips_Internal_Rela *dst; +{ + dst->r_offset = bfd_h_get_64 (abfd, (bfd_byte *) src->r_offset); + dst->r_sym = bfd_h_get_32 (abfd, (bfd_byte *) src->r_sym); + dst->r_ssym = bfd_h_get_8 (abfd, (bfd_byte *) src->r_ssym); + dst->r_type3 = bfd_h_get_8 (abfd, (bfd_byte *) src->r_type3); + dst->r_type2 = bfd_h_get_8 (abfd, (bfd_byte *) src->r_type2); + dst->r_type = bfd_h_get_8 (abfd, (bfd_byte *) src->r_type); + dst->r_addend = bfd_h_get_signed_64 (abfd, (bfd_byte *) src->r_addend); +} + +#if 0 + +/* This is not currently used. */ + +/* Swap out a MIPS 64-bit Rel reloc. */ + +static void +mips_elf64_swap_reloc_out (abfd, src, dst) + bfd *abfd; + const Elf64_Mips_Internal_Rel *src; + Elf64_Mips_External_Rel *dst; +{ + bfd_h_put_64 (abfd, src->r_offset, (bfd_byte *) dst->r_offset); + bfd_h_put_32 (abfd, src->r_sym, (bfd_byte *) dst->r_sym); + bfd_h_put_8 (abfd, src->r_ssym, (bfd_byte *) dst->r_ssym); + bfd_h_put_8 (abfd, src->r_type3, (bfd_byte *) dst->r_type3); + bfd_h_put_8 (abfd, src->r_type2, (bfd_byte *) dst->r_type2); + bfd_h_put_8 (abfd, src->r_type, (bfd_byte *) dst->r_type); +} + +#endif /* 0 */ + +/* Swap out a MIPS 64-bit Rela reloc. */ + +static void +mips_elf64_swap_reloca_out (abfd, src, dst) + bfd *abfd; + const Elf64_Mips_Internal_Rela *src; + Elf64_Mips_External_Rela *dst; +{ + bfd_h_put_64 (abfd, src->r_offset, (bfd_byte *) dst->r_offset); + bfd_h_put_32 (abfd, src->r_sym, (bfd_byte *) dst->r_sym); + bfd_h_put_8 (abfd, src->r_ssym, (bfd_byte *) dst->r_ssym); + bfd_h_put_8 (abfd, src->r_type3, (bfd_byte *) dst->r_type3); + bfd_h_put_8 (abfd, src->r_type2, (bfd_byte *) dst->r_type2); + bfd_h_put_8 (abfd, src->r_type, (bfd_byte *) dst->r_type); + bfd_h_put_64 (abfd, src->r_addend, (bfd_byte *) dst->r_addend); +} + +/* A mapping from BFD reloc types to MIPS ELF reloc types. */ + +struct elf_reloc_map +{ + bfd_reloc_code_real_type bfd_reloc_val; + enum mips_elf64_reloc_type elf_reloc_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 }, + { BFD_RELOC_64, R_MIPS_64 }, + { BFD_RELOC_CTOR, R_MIPS_64 }, + { BFD_RELOC_32_PCREL, R_MIPS_REL32 }, + { BFD_RELOC_MIPS_JMP, R_MIPS_26 }, + { BFD_RELOC_HI16_S, R_MIPS_HI16 }, + { BFD_RELOC_LO16, R_MIPS_LO16 }, + { BFD_RELOC_MIPS_GPREL, 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_MIPS_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 } +}; + +/* Given a BFD reloc type, return a howto structure. */ + +static reloc_howto_type * +mips_elf64_reloc_type_lookup (abfd, code) + bfd *abfd; + bfd_reloc_code_real_type code; +{ + unsigned int i; + + for (i = 0; i < sizeof (mips_reloc_map) / sizeof (struct elf_reloc_map); i++) + { + if (mips_reloc_map[i].bfd_reloc_val == code) + { + int v; + + v = (int) mips_reloc_map[i].elf_reloc_val; + return &mips_elf64_howto_table_rel[v]; + } + } + + return NULL; +} + +/* 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 (abfd, sec) + bfd *abfd; + asection *sec; +{ + return (sec->reloc_count * 3 + 1) * sizeof (arelent *); +} + +/* Read the relocations from one reloc section. */ + +static boolean +mips_elf64_slurp_one_reloc_table (abfd, asect, symbols, rel_hdr) + bfd *abfd; + asection *asect; + asymbol **symbols; + const Elf_Internal_Shdr *rel_hdr; +{ + PTR allocated = NULL; + bfd_byte *native_relocs; + arelent *relents; + arelent *relent; + unsigned int count; + unsigned int i; + int entsize; + reloc_howto_type *howto_table; + + allocated = (PTR) bfd_malloc (rel_hdr->sh_size); + if (allocated == NULL) + goto error_return; + + if (bfd_seek (abfd, rel_hdr->sh_offset, SEEK_SET) != 0 + || (bfd_read (allocated, 1, rel_hdr->sh_size, abfd) != rel_hdr->sh_size)) + goto error_return; + + native_relocs = (bfd_byte *) allocated; + + relents = asect->relocation + asect->reloc_count; + + entsize = rel_hdr->sh_entsize; + BFD_ASSERT (entsize == sizeof (Elf64_Mips_External_Rel) + || entsize == sizeof (Elf64_Mips_External_Rela)); + + count = rel_hdr->sh_size / entsize; + + if (entsize == sizeof (Elf64_Mips_External_Rel)) + howto_table = mips_elf64_howto_table_rel; + else + howto_table = mips_elf64_howto_table_rela; + + relent = relents; + for (i = 0; i < count; i++, native_relocs += entsize) + { + Elf64_Mips_Internal_Rela rela; + 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 + { + Elf64_Mips_Internal_Rel rel; + + mips_elf64_swap_reloc_in (abfd, + (Elf64_Mips_External_Rel *) native_relocs, + &rel); + rela.r_offset = rel.r_offset; + rela.r_sym = rel.r_sym; + rela.r_ssym = rel.r_ssym; + rela.r_type3 = rel.r_type3; + rela.r_type2 = rel.r_type2; + rela.r_type = rel.r_type; + rela.r_addend = 0; + } + + /* Each entry represents up to three actual relocations. */ + + used_sym = false; + used_ssym = false; + for (ir = 0; ir < 3; ir++) + { + enum mips_elf64_reloc_type type; + + switch (ir) + { + default: + abort (); + case 0: + type = (enum mips_elf64_reloc_type) rela.r_type; + break; + case 1: + type = (enum mips_elf64_reloc_type) rela.r_type2; + break; + case 2: + type = (enum mips_elf64_reloc_type) rela.r_type3; + break; + } + + if (type == R_MIPS_NONE) + { + /* There are no more relocations in this entry. If this + is the first entry, we need to generate a dummy + relocation so that the generic linker knows that + there has been a break in the sequence of relocations + applying to a particular address. */ + if (ir == 0) + { + relent->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr; + if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0) + relent->address = rela.r_offset; + else + relent->address = rela.r_offset - asect->vma; + relent->addend = 0; + relent->howto = &howto_table[(int) R_MIPS_NONE]; + ++relent; + } + 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) + relent->address = rela.r_offset; + else + relent->address = rela.r_offset - asect->vma; + + relent->addend = rela.r_addend; + + relent->howto = &howto_table[(int) type]; + + ++relent; + } + } + + asect->reloc_count += relent - relents; + + 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. */ + +static boolean +mips_elf64_slurp_reloc_table (abfd, asect, symbols, dynamic) + bfd *abfd; + asection *asect; + asymbol **symbols; + boolean dynamic; +{ + struct bfd_elf_section_data * const d = elf_section_data (asect); + + if (dynamic) + { + bfd_set_error (bfd_error_invalid_operation); + return false; + } + + if (asect->relocation != NULL + || (asect->flags & SEC_RELOC) == 0 + || asect->reloc_count == 0) + return true; + + /* Allocate space for 3 arelent structures for each Rel structure. */ + asect->relocation = ((arelent *) + bfd_alloc (abfd, + asect->reloc_count * 3 * sizeof (arelent))); + if (asect->relocation == 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, symbols, &d->rel_hdr)) + return false; + if (d->rel_hdr2 != NULL) + { + if (! mips_elf64_slurp_one_reloc_table (abfd, asect, symbols, + d->rel_hdr2)) + return false; + } + + return true; +} + +/* Write out the relocations. */ + +static void +mips_elf64_write_relocs (abfd, sec, data) + bfd *abfd; + asection *sec; + PTR data; +{ + boolean *failedp = (boolean *) data; + unsigned int count; + Elf_Internal_Shdr *rela_hdr; + Elf64_Mips_External_Rela *ext_rela; + unsigned int idx; + asymbol *last_sym = 0; + int last_sym_idx = 0; + + /* 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; + } + } + + rela_hdr = &elf_section_data (sec)->rel_hdr; + + rela_hdr->sh_size = rela_hdr->sh_entsize * count; + rela_hdr->contents = (PTR) 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); +} + +/* Handle a 64-bit MIPS ELF specific section. */ + +static boolean +mips_elf64_section_from_shdr (abfd, hdr, name) + bfd *abfd; + Elf_Internal_Shdr *hdr; + char *name; +{ + if (! _bfd_mips_elf_section_from_shdr (abfd, hdr, name)) + return false; + + /* For a SHT_MIPS_OPTIONS section, look for a ODK_REGINFO entry, and + set the gp value based on what we find. We may see both + SHT_MIPS_REGINFO and SHT_MIPS_OPTIONS/ODK_REGINFO; in that case, + they should agree. */ + if (hdr->sh_type == SHT_MIPS_OPTIONS) + { + bfd_byte *contents, *l, *lend; + + contents = (bfd_byte *) bfd_malloc (hdr->sh_size); + if (contents == NULL) + return false; + if (! bfd_get_section_contents (abfd, hdr->bfd_section, contents, + (file_ptr) 0, hdr->sh_size)) + { + free (contents); + return false; + } + l = contents; + lend = contents + hdr->sh_size; + while (l + sizeof (Elf_External_Options) <= lend) + { + Elf_Internal_Options intopt; + + bfd_mips_elf_swap_options_in (abfd, (Elf_External_Options *) l, + &intopt); + if (intopt.kind == ODK_REGINFO) + { + Elf64_Internal_RegInfo intreg; + + bfd_mips_elf64_swap_reginfo_in + (abfd, + ((Elf64_External_RegInfo *) + (l + sizeof (Elf_External_Options))), + &intreg); + elf_gp (abfd) = intreg.ri_gp_value; + } + l += intopt.size; + } + free (contents); + } + + return true; +} + +/* Work over a section just before writing it out. We update the GP + value in the SHT_MIPS_OPTIONS section based on the value we are + using. */ + +static boolean +mips_elf64_section_processing (abfd, hdr) + bfd *abfd; + Elf_Internal_Shdr *hdr; +{ + if (hdr->sh_type == SHT_MIPS_OPTIONS + && hdr->bfd_section != NULL + && elf_section_data (hdr->bfd_section) != NULL + && elf_section_data (hdr->bfd_section)->tdata != NULL) + { + bfd_byte *contents, *l, *lend; + + /* We stored the section contents in the elf_section_data tdata + field in the set_section_contents routine. We save the + section contents so that we don't have to read them again. + At this point we know that elf_gp is set, so we can look + through the section contents to see if there is an + ODK_REGINFO structure. */ + + contents = (bfd_byte *) elf_section_data (hdr->bfd_section)->tdata; + l = contents; + lend = contents + hdr->sh_size; + while (l + sizeof (Elf_External_Options) <= lend) + { + Elf_Internal_Options intopt; + + bfd_mips_elf_swap_options_in (abfd, (Elf_External_Options *) l, + &intopt); + if (intopt.kind == ODK_REGINFO) + { + bfd_byte buf[8]; + + if (bfd_seek (abfd, + (hdr->sh_offset + + (l - contents) + + sizeof (Elf_External_Options) + + (sizeof (Elf64_External_RegInfo) - 8)), + SEEK_SET) == -1) + return false; + bfd_h_put_64 (abfd, elf_gp (abfd), buf); + if (bfd_write (buf, 1, 8, abfd) != 8) + return false; + } + l += intopt.size; + } + } + + return _bfd_mips_elf_section_processing (abfd, hdr); +} + +/* Irix 6 defines a brand new archive map format, so that they can + have archives more than 4 GB in size. */ + +/* Read an Irix 6 armap. */ + +static boolean +mips_elf64_slurp_armap (abfd) + bfd *abfd; +{ + struct artdata *ardata = bfd_ardata (abfd); + char nextname[17]; + file_ptr arhdrpos; + bfd_size_type i, parsed_size, nsymz, stringsize, carsym_size, ptrsize; + struct areltdata *mapdata; + bfd_byte int_buf[8]; + char *stringbase; + bfd_byte *raw_armap = NULL; + carsym *carsyms; + + ardata->symdefs = NULL; + + /* Get the name of the first element. */ + arhdrpos = bfd_tell (abfd); + i = bfd_read ((PTR) nextname, 1, 16, abfd); + if (i == 0) + return true; + if (i != 16) + return false; + + if (bfd_seek (abfd, (file_ptr) - 16, SEEK_CUR) != 0) + return false; + + /* Archives with traditional armaps are still permitted. */ + if (strncmp (nextname, "/ ", 16) == 0) + return bfd_slurp_armap (abfd); + + if (strncmp (nextname, "/SYM64/ ", 16) != 0) + { + bfd_has_map (abfd) = false; + return true; + } + + mapdata = (struct areltdata *) _bfd_read_ar_hdr (abfd); + if (mapdata == NULL) + return false; + parsed_size = mapdata->parsed_size; + bfd_release (abfd, (PTR) mapdata); + + if (bfd_read (int_buf, 1, 8, abfd) != 8) + { + if (bfd_get_error () != bfd_error_system_call) + bfd_set_error (bfd_error_malformed_archive); + return false; + } + + nsymz = bfd_getb64 (int_buf); + stringsize = parsed_size - 8 * nsymz - 8; + + carsym_size = nsymz * sizeof (carsym); + ptrsize = 8 * nsymz; + + ardata->symdefs = (carsym *) bfd_zalloc (abfd, carsym_size + stringsize + 1); + if (ardata->symdefs == NULL) + return false; + carsyms = ardata->symdefs; + stringbase = ((char *) ardata->symdefs) + carsym_size; + + raw_armap = (bfd_byte *) bfd_alloc (abfd, ptrsize); + if (raw_armap == NULL) + goto error_return; + + if (bfd_read (raw_armap, 1, ptrsize, abfd) != ptrsize + || bfd_read (stringbase, 1, stringsize, abfd) != stringsize) + { + if (bfd_get_error () != bfd_error_system_call) + bfd_set_error (bfd_error_malformed_archive); + goto error_return; + } + + for (i = 0; i < nsymz; i++) + { + carsyms->file_offset = bfd_getb64 (raw_armap + i * 8); + carsyms->name = stringbase; + stringbase += strlen (stringbase) + 1; + ++carsyms; + } + *stringbase = '\0'; + + ardata->symdef_count = nsymz; + ardata->first_file_filepos = arhdrpos + sizeof (struct ar_hdr) + parsed_size; + + bfd_has_map (abfd) = true; + bfd_release (abfd, raw_armap); + + return true; + + error_return: + if (raw_armap != NULL) + bfd_release (abfd, raw_armap); + if (ardata->symdefs != NULL) + bfd_release (abfd, ardata->symdefs); + return false; +} + +/* Write out an Irix 6 armap. The Irix 6 tools are supposed to be + able to handle ordinary ELF armaps, but at least on Irix 6.2 the + linker crashes. */ + +static boolean +mips_elf64_write_armap (arch, elength, map, symbol_count, stridx) + bfd *arch; + unsigned int elength; + struct orl *map; + unsigned int symbol_count; + int stridx; +{ + unsigned int ranlibsize = (symbol_count * 8) + 8; + unsigned int stringsize = stridx; + unsigned int mapsize = stringsize + ranlibsize; + file_ptr archive_member_file_ptr; + bfd *current = arch->archive_head; + unsigned int count; + struct ar_hdr hdr; + unsigned int i; + int padding; + bfd_byte buf[8]; + + padding = BFD_ALIGN (mapsize, 8) - mapsize; + mapsize += padding; + + /* work out where the first object file will go in the archive */ + archive_member_file_ptr = (mapsize + + elength + + sizeof (struct ar_hdr) + + SARMAG); + + memset ((char *) (&hdr), 0, sizeof (struct ar_hdr)); + strcpy (hdr.ar_name, "/SYM64/"); + sprintf (hdr.ar_size, "%-10d", (int) mapsize); + sprintf (hdr.ar_date, "%ld", (long) time (NULL)); + /* This, at least, is what Intel coff sets the values to.: */ + sprintf ((hdr.ar_uid), "%d", 0); + sprintf ((hdr.ar_gid), "%d", 0); + sprintf ((hdr.ar_mode), "%-7o", (unsigned) 0); + strncpy (hdr.ar_fmag, ARFMAG, 2); + + for (i = 0; i < sizeof (struct ar_hdr); i++) + if (((char *) (&hdr))[i] == '\0') + (((char *) (&hdr))[i]) = ' '; + + /* Write the ar header for this item and the number of symbols */ + + if (bfd_write ((PTR) &hdr, 1, sizeof (struct ar_hdr), arch) + != sizeof (struct ar_hdr)) + return false; + + bfd_putb64 (symbol_count, buf); + if (bfd_write (buf, 1, 8, arch) != 8) + return false; + + /* Two passes, first write the file offsets for each symbol - + remembering that each offset is on a two byte boundary. */ + + /* Write out the file offset for the file associated with each + symbol, and remember to keep the offsets padded out. */ + + current = arch->archive_head; + count = 0; + while (current != (bfd *) NULL && count < symbol_count) + { + /* For each symbol which is used defined in this object, write out + the object file's address in the archive */ + + while (((bfd *) (map[count]).pos) == current) + { + bfd_putb64 (archive_member_file_ptr, buf); + if (bfd_write (buf, 1, 8, arch) != 8) + return false; + count++; + } + /* Add size of this archive entry */ + archive_member_file_ptr += (arelt_size (current) + + sizeof (struct ar_hdr)); + /* remember about the even alignment */ + archive_member_file_ptr += archive_member_file_ptr % 2; + current = current->next; + } + + /* now write the strings themselves */ + for (count = 0; count < symbol_count; count++) + { + size_t len = strlen (*map[count].name) + 1; + + if (bfd_write (*map[count].name, 1, len, arch) != len) + return false; + } + + /* The spec says that this should be padded to an 8 byte boundary. + However, the Irix 6.2 tools do not appear to do this. */ + while (padding != 0) + { + if (bfd_write ("", 1, 1, arch) != 1) + return false; + --padding; + } + + 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), + 64, /* arch_size */ + 8, /* file_align */ + ELFCLASS64, + EV_CURRENT, + bfd_elf64_write_out_phdrs, + bfd_elf64_write_shdrs_and_ehdr, + mips_elf64_write_relocs, + bfd_elf64_swap_symbol_out, + mips_elf64_slurp_reloc_table, + bfd_elf64_slurp_symbol_table, + bfd_elf64_swap_dyn_in +}; + +#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" +#define ELF_ARCH bfd_arch_mips +#define ELF_MACHINE_CODE EM_MIPS +#define ELF_MAXPAGESIZE 0x1000 +#define elf_backend_size_info mips_elf64_size_info +#define elf_backend_object_p _bfd_mips_elf_object_p +#define elf_backend_section_from_shdr mips_elf64_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_section_processing mips_elf64_section_processing +#define elf_backend_symbol_processing _bfd_mips_elf_symbol_processing +#define elf_backend_final_write_processing \ + _bfd_mips_elf_final_write_processing +#define elf_backend_ecoff_debug_swap &mips_elf64_ecoff_debug_swap + +#define bfd_elf64_find_nearest_line _bfd_mips_elf_find_nearest_line +#define bfd_elf64_get_reloc_upper_bound mips_elf64_get_reloc_upper_bound +#define bfd_elf64_bfd_reloc_type_lookup mips_elf64_reloc_type_lookup +#define bfd_elf64_set_section_contents _bfd_mips_elf_set_section_contents +#define bfd_elf64_bfd_copy_private_bfd_data \ + _bfd_mips_elf_copy_private_bfd_data +#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_archive_functions +#define bfd_elf64_archive_slurp_armap mips_elf64_slurp_armap +#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_write_armap mips_elf64_write_armap +#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 + +#include "elf64-target.h" diff --git a/contrib/binutils/bfd/hosts/decstation.h b/contrib/binutils/bfd/hosts/decstation.h new file mode 100644 index 0000000..a80c143 --- /dev/null +++ b/contrib/binutils/bfd/hosts/decstation.h @@ -0,0 +1,17 @@ +/* Hopefully this should include either machine/param.h (Ultrix) or + machine/machparam.h (Mach), whichever is its name on this system. */ +#include <sys/param.h> + +#include <machine/vmparam.h> + +#define HOST_PAGE_SIZE NBPG +/* #define HOST_SEGMENT_SIZE NBPG -- we use HOST_DATA_START_ADDR */ +#define HOST_MACHINE_ARCH bfd_arch_mips +/* #define HOST_MACHINE_MACHINE */ + +#define HOST_TEXT_START_ADDR USRTEXT +#define HOST_DATA_START_ADDR USRDATA +#define HOST_STACK_END_ADDR USRSTACK + +#define TRAD_UNIX_CORE_FILE_FAILING_SIGNAL(core_bfd) \ + ((core_bfd)->tdata.trad_core_data->u.u_arg[0]) diff --git a/contrib/binutils/bfd/mipsbsd.c b/contrib/binutils/bfd/mipsbsd.c new file mode 100644 index 0000000..c5ec9e1 --- /dev/null +++ b/contrib/binutils/bfd/mipsbsd.c @@ -0,0 +1,468 @@ +/* BFD backend for MIPS BSD (a.out) binaries. + Copyright (C) 1993, 94, 95, 97, 1998 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 BYTES_IN_WORD 4 +/* #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' + +#define MY(OP) CAT(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); +void MY(set_arch_mach) PARAMS ((bfd *abfd, int machtype)); +static void MY(choose_reloc_size) PARAMS ((bfd *abfd)); + +#define MY_write_object_contents MY(write_object_contents) +static boolean MY(write_object_contents) PARAMS ((bfd *abfd)); + +/* We can't use MY(x) here because it leads to a recursive call to CAT + 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" + +void +MY(set_arch_mach) (abfd, machtype) + bfd *abfd; + int machtype; +{ + enum bfd_architecture arch; + long machine; + + /* Determine the architecture and machine type of the object file. */ + switch (machtype) { + + case M_MIPS1: + arch = bfd_arch_mips; + machine = 3000; + break; + + case M_MIPS2: + arch = bfd_arch_mips; + machine = 4000; + 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 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 4000: + case 6000: + 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) + bfd *abfd; + arelent *reloc_entry; + struct symbol_cache_entry *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; +{ + 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 targetted 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; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message; +{ + 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 targetted 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 */ +}; + +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), + + (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), + + (PTR) MY_backend_data, +}; diff --git a/contrib/binutils/config/mh-decstation b/contrib/binutils/config/mh-decstation new file mode 100644 index 0000000..3720192 --- /dev/null +++ b/contrib/binutils/config/mh-decstation @@ -0,0 +1,5 @@ +CC = cc -Wf,-XNg1000 + +# for X11, since the native DECwindows include files are really broken when +# it comes to function prototypes. +X11_EXTRA_CFLAGS = "-DNeedFunctionPrototypes=0" diff --git a/contrib/binutils/include/elf/mips.h b/contrib/binutils/include/elf/mips.h new file mode 100644 index 0000000..f6efad3 --- /dev/null +++ b/contrib/binutils/include/elf/mips.h @@ -0,0 +1,509 @@ +/* MIPS ELF support for BFD. + Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc. + + By Ian Lance Taylor, Cygnus Support, <ian@cygnus.com>, from + information in the System V Application Binary Interface, MIPS + Processor Supplement. + +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 holds definitions specific to the MIPS ELF ABI. Note + that most of this is not actually implemented by BFD. */ + +#ifndef _ELF_MIPS_H +#define _ELF_MIPS_H + +/* Processor specific flags for the ELF header e_flags field. */ + +/* At least one .noreorder directive appears in the source. */ +#define EF_MIPS_NOREORDER 0x00000001 + +/* File contains position independent code. */ +#define EF_MIPS_PIC 0x00000002 + +/* Code in file uses the standard calling sequence for calling + position independent code. */ +#define EF_MIPS_CPIC 0x00000004 + +/* Code in file uses new ABI (-n32 on Irix 6). */ +#define EF_MIPS_ABI2 0x00000020 + +/* Four bit MIPS architecture field. */ +#define EF_MIPS_ARCH 0xf0000000 + +/* -mips1 code. */ +#define E_MIPS_ARCH_1 0x00000000 + +/* -mips2 code. */ +#define E_MIPS_ARCH_2 0x10000000 + +/* -mips3 code. */ +#define E_MIPS_ARCH_3 0x20000000 + +/* -mips4 code. */ +#define E_MIPS_ARCH_4 0x30000000 + +/* Processor specific section indices. These sections do not actually + exist. Symbols with a st_shndx field corresponding to one of these + values have a special meaning. */ + +/* Defined and allocated common symbol. Value is virtual address. If + relocated, alignment must be preserved. */ +#define SHN_MIPS_ACOMMON 0xff00 + +/* Defined and allocated text symbol. Value is virtual address. + Occur in the dynamic symbol table of Alpha OSF/1 and Irix 5 executables. */ +#define SHN_MIPS_TEXT 0xff01 + +/* Defined and allocated data symbol. Value is virtual address. + Occur in the dynamic symbol table of Alpha OSF/1 and Irix 5 executables. */ +#define SHN_MIPS_DATA 0xff02 + +/* Small common symbol. */ +#define SHN_MIPS_SCOMMON 0xff03 + +/* Small undefined symbol. */ +#define SHN_MIPS_SUNDEFINED 0xff04 + +/* Processor specific section types. */ + +/* Section contains the set of dynamic shared objects used when + statically linking. */ +#define SHT_MIPS_LIBLIST 0x70000000 + +/* I'm not sure what this is, but it's used on Irix 5. */ +#define SHT_MIPS_MSYM 0x70000001 + +/* Section contains list of symbols whose definitions conflict with + symbols defined in shared objects. */ +#define SHT_MIPS_CONFLICT 0x70000002 + +/* Section contains the global pointer table. */ +#define SHT_MIPS_GPTAB 0x70000003 + +/* Section contains microcode information. The exact format is + unspecified. */ +#define SHT_MIPS_UCODE 0x70000004 + +/* Section contains some sort of debugging information. The exact + format is unspecified. It's probably ECOFF symbols. */ +#define SHT_MIPS_DEBUG 0x70000005 + +/* Section contains register usage information. */ +#define SHT_MIPS_REGINFO 0x70000006 + +/* Section contains interface information. */ +#define SHT_MIPS_IFACE 0x7000000b + +/* Section contains description of contents of another section. */ +#define SHT_MIPS_CONTENT 0x7000000c + +/* Section contains miscellaneous options. */ +#define SHT_MIPS_OPTIONS 0x7000000d + +/* DWARF debugging section. */ +#define SHT_MIPS_DWARF 0x7000001e + +/* I'm not sure what this is, but it appears on Irix 6. */ +#define SHT_MIPS_SYMBOL_LIB 0x70000020 + +/* Events section. */ +#define SHT_MIPS_EVENTS 0x70000021 + +/* A section of type SHT_MIPS_LIBLIST contains an array of the + following structure. The sh_link field is the section index of the + string table. The sh_info field is the number of entries in the + section. */ +typedef struct +{ + /* String table index for name of shared object. */ + unsigned long l_name; + /* Time stamp. */ + unsigned long l_time_stamp; + /* Checksum of symbol names and common sizes. */ + unsigned long l_checksum; + /* String table index for version. */ + unsigned long l_version; + /* Flags. */ + unsigned long l_flags; +} Elf32_Lib; + +/* The l_flags field of an Elf32_Lib structure may contain the + following flags. */ + +/* Require an exact match at runtime. */ +#define LL_EXACT_MATCH 0x00000001 + +/* Ignore version incompatibilities at runtime. */ +#define LL_IGNORE_INT_VER 0x00000002 + +/* A section of type SHT_MIPS_CONFLICT is an array of indices into the + .dynsym section. Each element has the following type. */ +typedef unsigned long Elf32_Conflict; + +/* A section of type SHT_MIPS_GPTAB contains information about how + much GP space would be required for different -G arguments. This + information is only used so that the linker can provide informative + suggestions as to the best -G value to use. The sh_info field is + the index of the section for which this information applies. The + contents of the section are an array of the following union. The + first element uses the gt_header field. The remaining elements use + the gt_entry field. */ +typedef union +{ + struct + { + /* -G value actually used for this object file. */ + unsigned long gt_current_g_value; + /* Unused. */ + unsigned long gt_unused; + } gt_header; + struct + { + /* If this -G argument has been used... */ + unsigned long gt_g_value; + /* ...this many GP section bytes would be required. */ + unsigned long gt_bytes; + } gt_entry; +} Elf32_gptab; + +/* The external version of Elf32_gptab. */ + +typedef union +{ + struct + { + unsigned char gt_current_g_value[4]; + unsigned char gt_unused[4]; + } gt_header; + struct + { + unsigned char gt_g_value[4]; + unsigned char gt_bytes[4]; + } gt_entry; +} Elf32_External_gptab; + +/* A section of type SHT_MIPS_REGINFO contains the following + structure. */ +typedef struct +{ + /* Mask of general purpose registers used. */ + unsigned long ri_gprmask; + /* Mask of co-processor registers used. */ + unsigned long ri_cprmask[4]; + /* GP register value for this object file. */ + long ri_gp_value; +} Elf32_RegInfo; + +/* The external version of the Elf_RegInfo structure. */ +typedef struct +{ + unsigned char ri_gprmask[4]; + unsigned char ri_cprmask[4][4]; + unsigned char ri_gp_value[4]; +} Elf32_External_RegInfo; + +/* MIPS ELF .reginfo swapping routines. */ +extern void bfd_mips_elf32_swap_reginfo_in + PARAMS ((bfd *, const Elf32_External_RegInfo *, Elf32_RegInfo *)); +extern void bfd_mips_elf32_swap_reginfo_out + PARAMS ((bfd *, const Elf32_RegInfo *, Elf32_External_RegInfo *)); + +/* Processor specific section flags. */ + +/* This section must be in the global data area. */ +#define SHF_MIPS_GPREL 0x10000000 + +/* This section should be merged. */ +#define SHF_MIPS_MERGE 0x20000000 + +/* This section contains 32 bit addresses. */ +#define SHF_MIPS_ADDR32 0x40000000 + +/* This section contains 64 bit addresses. */ +#define SHF_MIPS_ADDR64 0x80000000 + +/* This section may not be stripped. */ +#define SHF_MIPS_NOSTRIP 0x08000000 + +/* This section is local to threads. */ +#define SHF_MIPS_LOCAL 0x04000000 + +/* Linker should generate implicit weak names for this section. */ +#define SHF_MIPS_NAMES 0x02000000 + +/* Processor specific program header types. */ + +/* Register usage information. Identifies one .reginfo section. */ +#define PT_MIPS_REGINFO 0x70000000 + +/* Runtime procedure table. */ +#define PT_MIPS_RTPROC 0x70000001 + +/* Processor specific dynamic array tags. */ + +/* 32 bit version number for runtime linker interface. */ +#define DT_MIPS_RLD_VERSION 0x70000001 + +/* Time stamp. */ +#define DT_MIPS_TIME_STAMP 0x70000002 + +/* Checksum of external strings and common sizes. */ +#define DT_MIPS_ICHECKSUM 0x70000003 + +/* Index of version string in string table. */ +#define DT_MIPS_IVERSION 0x70000004 + +/* 32 bits of flags. */ +#define DT_MIPS_FLAGS 0x70000005 + +/* Base address of the segment. */ +#define DT_MIPS_BASE_ADDRESS 0x70000006 + +/* Address of .conflict section. */ +#define DT_MIPS_CONFLICT 0x70000008 + +/* Address of .liblist section. */ +#define DT_MIPS_LIBLIST 0x70000009 + +/* Number of local global offset table entries. */ +#define DT_MIPS_LOCAL_GOTNO 0x7000000a + +/* Number of entries in the .conflict section. */ +#define DT_MIPS_CONFLICTNO 0x7000000b + +/* Number of entries in the .liblist section. */ +#define DT_MIPS_LIBLISTNO 0x70000010 + +/* Number of entries in the .dynsym section. */ +#define DT_MIPS_SYMTABNO 0x70000011 + +/* Index of first external dynamic symbol not referenced locally. */ +#define DT_MIPS_UNREFEXTNO 0x70000012 + +/* Index of first dynamic symbol in global offset table. */ +#define DT_MIPS_GOTSYM 0x70000013 + +/* Number of page table entries in global offset table. */ +#define DT_MIPS_HIPAGENO 0x70000014 + +/* Address of run time loader map, used for debugging. */ +#define DT_MIPS_RLD_MAP 0x70000016 + +/* Flags which may appear in a DT_MIPS_FLAGS entry. */ + +/* No flags. */ +#define RHF_NONE 0x00000000 + +/* Uses shortcut pointers. */ +#define RHF_QUICKSTART 0x00000001 + +/* Hash size is not a power of two. */ +#define RHF_NOTPOT 0x00000002 + +/* Ignore LD_LIBRARY_PATH. */ +#define RHS_NO_LIBRARY_REPLACEMENT \ + 0x00000004 + +/* Special values for the st_other field in the symbol table. These + are used in an Irix 5 dynamic symbol table. */ + +#define STO_DEFAULT 0x00 +#define STO_INTERNAL 0x01 +#define STO_HIDDEN 0x02 +#define STO_PROTECTED 0x03 + +/* This value is used for a mips16 .text symbol. */ +#define STO_MIPS16 0xf0 + +/* The 64-bit MIPS ELF ABI uses an unusual reloc format. Each + relocation entry specifies up to three actual relocations, all at + the same address. The first relocation which required a symbol + uses the symbol in the r_sym field. The second relocation which + requires a symbol uses the symbol in the r_ssym field. If all + three relocations require a symbol, the third one uses a zero + value. */ + +/* An entry in a 64 bit SHT_REL section. */ + +typedef struct +{ + /* Address of relocation. */ + unsigned char r_offset[8]; + /* Symbol index. */ + unsigned char r_sym[4]; + /* Special symbol. */ + unsigned char r_ssym[1]; + /* Third relocation. */ + unsigned char r_type3[1]; + /* Second relocation. */ + unsigned char r_type2[1]; + /* First relocation. */ + unsigned char r_type[1]; +} Elf64_Mips_External_Rel; + +typedef struct +{ + /* Address of relocation. */ + bfd_vma r_offset; + /* Symbol index. */ + unsigned long r_sym; + /* Special symbol. */ + unsigned char r_ssym; + /* Third relocation. */ + unsigned char r_type3; + /* Second relocation. */ + unsigned char r_type2; + /* First relocation. */ + unsigned char r_type; +} Elf64_Mips_Internal_Rel; + +/* An entry in a 64 bit SHT_RELA section. */ + +typedef struct +{ + /* Address of relocation. */ + unsigned char r_offset[8]; + /* Symbol index. */ + unsigned char r_sym[4]; + /* Special symbol. */ + unsigned char r_ssym[1]; + /* Third relocation. */ + unsigned char r_type3[1]; + /* Second relocation. */ + unsigned char r_type2[1]; + /* First relocation. */ + unsigned char r_type[1]; + /* Addend. */ + unsigned char r_addend[8]; +} Elf64_Mips_External_Rela; + +typedef struct +{ + /* Address of relocation. */ + bfd_vma r_offset; + /* Symbol index. */ + unsigned long r_sym; + /* Special symbol. */ + unsigned char r_ssym; + /* Third relocation. */ + unsigned char r_type3; + /* Second relocation. */ + unsigned char r_type2; + /* First relocation. */ + unsigned char r_type; + /* Addend. */ + bfd_signed_vma r_addend; +} Elf64_Mips_Internal_Rela; + +/* Values found in the r_ssym field of a relocation entry. */ + +/* No relocation. */ +#define RSS_UNDEF 0 + +/* Value of GP. */ +#define RSS_GP 1 + +/* Value of GP in object being relocated. */ +#define RSS_GP0 2 + +/* Address of location being relocated. */ +#define RSS_LOC 3 + +/* A SHT_MIPS_OPTIONS section contains a series of options, each of + which starts with this header. */ + +typedef struct +{ + /* Type of option. */ + unsigned char kind[1]; + /* Size of option descriptor, including header. */ + unsigned char size[1]; + /* Section index of affected section, or 0 for global option. */ + unsigned char section[2]; + /* Information specific to this kind of option. */ + unsigned char info[4]; +} Elf_External_Options; + +typedef struct +{ + /* Type of option. */ + unsigned char kind; + /* Size of option descriptor, including header. */ + unsigned char size; + /* Section index of affected section, or 0 for global option. */ + unsigned short section; + /* Information specific to this kind of option. */ + unsigned long info; +} Elf_Internal_Options; + +/* MIPS ELF option header swapping routines. */ +extern void bfd_mips_elf_swap_options_in + PARAMS ((bfd *, const Elf_External_Options *, Elf_Internal_Options *)); +extern void bfd_mips_elf_swap_options_out + PARAMS ((bfd *, const Elf_Internal_Options *, Elf_External_Options *)); + +/* Values which may appear in the kind field of an Elf_Options + structure. */ + +/* Undefined. */ +#define ODK_NULL 0 + +/* Register usage and GP value. */ +#define ODK_REGINFO 1 + +/* Exception processing information. */ +#define ODK_EXCEPTIONS 2 + +/* Section padding information. */ +#define ODK_PAD 3 + +/* In the 32 bit ABI, an ODK_REGINFO option is just a Elf32_Reginfo + structure. In the 64 bit ABI, it is the following structure. The + info field of the options header is not used. */ + +typedef struct +{ + /* Mask of general purpose registers used. */ + unsigned char ri_gprmask[4]; + /* Padding. */ + unsigned char ri_pad[4]; + /* Mask of co-processor registers used. */ + unsigned char ri_cprmask[4][4]; + /* GP register value for this object file. */ + unsigned char ri_gp_value[8]; +} Elf64_External_RegInfo; + +typedef struct +{ + /* Mask of general purpose registers used. */ + unsigned long ri_gprmask; + /* Padding. */ + unsigned long ri_pad; + /* Mask of co-processor registers used. */ + unsigned long ri_cprmask[4]; + /* GP register value for this object file. */ + bfd_vma ri_gp_value; +} Elf64_Internal_RegInfo; + +/* MIPS ELF reginfo swapping routines. */ +extern void bfd_mips_elf64_swap_reginfo_in + PARAMS ((bfd *, const Elf64_External_RegInfo *, Elf64_Internal_RegInfo *)); +extern void bfd_mips_elf64_swap_reginfo_out + PARAMS ((bfd *, const Elf64_Internal_RegInfo *, Elf64_External_RegInfo *)); + +#endif /* _ELF_MIPS_H */ diff --git a/contrib/binutils/include/opcode/mips.h b/contrib/binutils/include/opcode/mips.h new file mode 100644 index 0000000..ee5ee82 --- /dev/null +++ b/contrib/binutils/include/opcode/mips.h @@ -0,0 +1,715 @@ +/* mips.h. Mips opcode list for GDB, the GNU debugger. + Copyright 1993, 94, 95, 96, 1997 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). + + The syscall instruction uses SYSCALL. + + 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_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_SYSCALL 0xfffff +#define OP_SH_SYSCALL 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 + +/* 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 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. + + 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) + "o" 16 bit signed offset (OP_*_DELTA) + "p" 16 bit PC relative branch target address (OP_*_DELTA) + "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) + "C" 25 bit coprocessor function code (OP_*_COPZ) + "B" 20 bit syscall function code (OP_*_SYSCALL) + "x" accept and ignore register name + "z" must be zero register + + 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) + "P" 5 bit performance-monitor register (OP_*_PERFREG) + + Macro instructions: + "A" General 32 bit expression + "I" 32 bit immediate + "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 + + Other: + "()" parens surrounding optional value + "," separates operands + + Characters used so far, for quick reference when adding more: + "<>()," + "ABCDEFGILMNSTRVW" + "abcdfhijkloprstuvwxz" +*/ + +/* 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 + +/* As yet unused bits: 0x40000000 */ + +/* Instruction is actually a macro. It should be ignored by the + disassembler, and requires special treatment by the assembler. */ +#define INSN_MACRO 0xffffffff + + + + + +/* MIPS ISA field--CPU level at which insn is supported. */ +#define INSN_ISA 0x0000000F +/* An instruction which is not part of any basic MIPS ISA. + (ie it is a chip specific instruction) */ +#define INSN_NO_ISA 0x00000000 +/* MIPS ISA 1 instruction. */ +#define INSN_ISA1 0x00000001 +/* MIPS ISA 2 instruction (R6000 or R4000). */ +#define INSN_ISA2 0x00000002 +/* MIPS ISA 3 instruction (R4000). */ +#define INSN_ISA3 0x00000003 +/* MIPS ISA 4 instruction (R8000). */ +#define INSN_ISA4 0x00000004 + +/* Chip specific instructions. These are bitmasks. */ +/* MIPS R4650 instruction. */ +#define INSN_4650 0x00000010 +/* LSI R4010 instruction. */ +#define INSN_4010 0x00000020 +/* NEC VR4100 instruction. */ +#define INSN_4100 0x00000040 +/* Toshiba R3900 instruction. */ +#define INSN_3900 0x00000080 + + +/* 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_DIV_3, + M_DIV_3I, + M_DIVU_3, + M_DIVU_3I, + M_DLA_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_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_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_ROL, + M_ROL_I, + M_ROR, + 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 + +/* The following flags have the same value for the mips16 opcode + table: + INSN_UNCOND_BRANCH_DELAY + INSN_COND_BRANCH_DELAY + INSN_COND_BRANCH_LIKELY (never used) + INSN_READ_HI + INSN_READ_LO + INSN_WRITE_HI + INSN_WRITE_LO + INSN_TRAP + INSN_ISA3 + */ + +extern const struct mips_opcode mips16_opcodes[]; +extern const int bfd_mips16_num_opcodes; + +#endif /* _MIPS_H_ */ diff --git a/contrib/binutils/ld/emulparams/elf32bmip.sh b/contrib/binutils/ld/emulparams/elf32bmip.sh new file mode 100644 index 0000000..473c411 --- /dev/null +++ b/contrib/binutils/ld/emulparams/elf32bmip.sh @@ -0,0 +1,30 @@ +SCRIPT_NAME=elf +OUTPUT_FORMAT="elf32-bigmips" +BIG_OUTPUT_FORMAT="elf32-bigmips" +LITTLE_OUTPUT_FORMAT="elf32-littlemips" +TEXT_START_ADDR=0x0400000 +DATA_ADDR=0x10000000 +MAXPAGESIZE=0x40000 +NONPAGED_TEXT_START_ADDR=0x0400000 +SHLIB_TEXT_START_ADDR=0x5ffe0000 +TEXT_DYNAMIC= +INITIAL_READONLY_SECTIONS='.reginfo : { *(.reginfo) }' +OTHER_TEXT_SECTIONS='*(.mips16.fn.*) *(.mips16.call.*)' +OTHER_GOT_SYMBOLS=' + _gp = ALIGN(16) + 0x7ff0; +' +OTHER_GOT_SECTIONS=' + .lit8 : { *(.lit8) } + .lit4 : { *(.lit4) } +' +TEXT_START_SYMBOLS='_ftext = . ;' +DATA_START_SYMBOLS='_fdata = . ;' +OTHER_BSS_SYMBOLS='_fbss = .;' +OTHER_SECTIONS=' + .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) } + .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) } +' +ARCH=mips +MACHINE= +TEMPLATE_NAME=elf32 +GENERATE_SHLIB_SCRIPT=yes diff --git a/contrib/binutils/ld/emulparams/elf32bsmip.sh b/contrib/binutils/ld/emulparams/elf32bsmip.sh new file mode 100755 index 0000000..09f1307 --- /dev/null +++ b/contrib/binutils/ld/emulparams/elf32bsmip.sh @@ -0,0 +1,31 @@ +SCRIPT_NAME=elf +OUTPUT_FORMAT="elf32-bigmips" +BIG_OUTPUT_FORMAT="elf32-bigmips" +LITTLE_OUTPUT_FORMAT="elf32-littlemips" +TEXT_START_ADDR=0x0400000 +DATA_ADDR=0x10000000 +MAXPAGESIZE=0x40000 +NONPAGED_TEXT_START_ADDR=0x0400000 +SHLIB_TEXT_START_ADDR=0x5ffe0000 +TEXT_DYNAMIC= +INITIAL_READONLY_SECTIONS='.reginfo : { *(.reginfo) }' +OTHER_TEXT_SECTIONS='*(.mips16.fn.*) *(.mips16.call.*)' +OTHER_GOT_SYMBOLS=' + _gp = ALIGN(16) + 0x7ff0; +' +OTHER_GOT_SECTIONS=' + .lit8 : { *(.lit8) } + .lit4 : { *(.lit4) } +' +TEXT_START_SYMBOLS='_ftext = . ;' +DATA_START_SYMBOLS='_fdata = . ;' +OTHER_BSS_SYMBOLS='_fbss = .;' +OTHER_SECTIONS=' + .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) } + .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) } +' +ARCH=mips +MACHINE= +TEMPLATE_NAME=elf32 +GENERATE_SHLIB_SCRIPT=yes +ENTRY=__start diff --git a/contrib/binutils/ld/emulparams/elf32ebmip.sh b/contrib/binutils/ld/emulparams/elf32ebmip.sh new file mode 100644 index 0000000..a6b80a9 --- /dev/null +++ b/contrib/binutils/ld/emulparams/elf32ebmip.sh @@ -0,0 +1,29 @@ +SCRIPT_NAME=elf +OUTPUT_FORMAT="elf32-bigmips" +BIG_OUTPUT_FORMAT="elf32-bigmips" +LITTLE_OUTPUT_FORMAT="elf32-littlemips" +TEXT_START_ADDR=0x0400000 +MAXPAGESIZE=0x40000 +NONPAGED_TEXT_START_ADDR=0x0400000 +SHLIB_TEXT_START_ADDR=0x5ffe0000 +INITIAL_READONLY_SECTIONS='.reginfo : { *(.reginfo) }' +OTHER_TEXT_SECTIONS='*(.mips16.fn.*) *(.mips16.call.*)' +OTHER_GOT_SYMBOLS=' + _gp = ALIGN(16) + 0x7ff0; +' +OTHER_GOT_SECTIONS=' + .lit8 : { *(.lit8) } + .lit4 : { *(.lit4) } +' +TEXT_START_SYMBOLS='_ftext = . ;' +DATA_START_SYMBOLS='_fdata = . ;' +OTHER_BSS_SYMBOLS='_fbss = .;' +OTHER_SECTIONS=' + .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) } + .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) } +' +ARCH=mips +MACHINE= +TEMPLATE_NAME=elf32 +GENERATE_SHLIB_SCRIPT=yes +EMBEDDED=yes diff --git a/contrib/binutils/ld/emulparams/elf32elmip.sh b/contrib/binutils/ld/emulparams/elf32elmip.sh new file mode 100644 index 0000000..313d74a --- /dev/null +++ b/contrib/binutils/ld/emulparams/elf32elmip.sh @@ -0,0 +1,29 @@ +SCRIPT_NAME=elf +OUTPUT_FORMAT="elf32-littlemips" +BIG_OUTPUT_FORMAT="elf32-bigmips" +LITTLE_OUTPUT_FORMAT="elf32-littlemips" +TEXT_START_ADDR=0x0400000 +MAXPAGESIZE=0x40000 +NONPAGED_TEXT_START_ADDR=0x0400000 +SHLIB_TEXT_START_ADDR=0x5ffe0000 +INITIAL_READONLY_SECTIONS='.reginfo : { *(.reginfo) }' +OTHER_TEXT_SECTIONS='*(.mips16.fn.*) *(.mips16.call.*)' +OTHER_GOT_SYMBOLS=' + _gp = ALIGN(16) + 0x7ff0; +' +OTHER_GOT_SECTIONS=' + .lit8 : { *(.lit8) } + .lit4 : { *(.lit4) } +' +TEXT_START_SYMBOLS='_ftext = . ;' +DATA_START_SYMBOLS='_fdata = . ;' +OTHER_BSS_SYMBOLS='_fbss = .;' +OTHER_SECTIONS=' + .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) } + .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) } +' +ARCH=mips +MACHINE= +TEMPLATE_NAME=elf32 +GENERATE_SHLIB_SCRIPT=yes +EMBEDDED=yes diff --git a/contrib/binutils/ld/emulparams/elf32lmip.sh b/contrib/binutils/ld/emulparams/elf32lmip.sh new file mode 100644 index 0000000..23312f4 --- /dev/null +++ b/contrib/binutils/ld/emulparams/elf32lmip.sh @@ -0,0 +1,30 @@ +SCRIPT_NAME=elf +OUTPUT_FORMAT="elf32-littlemips" +BIG_OUTPUT_FORMAT="elf32-bigmips" +LITTLE_OUTPUT_FORMAT="elf32-littlemips" +TEXT_START_ADDR=0x0400000 +DATA_ADDR=0x10000000 +MAXPAGESIZE=0x40000 +NONPAGED_TEXT_START_ADDR=0x0400000 +SHLIB_TEXT_START_ADDR=0x5ffe0000 +TEXT_DYNAMIC= +INITIAL_READONLY_SECTIONS='.reginfo : { *(.reginfo) }' +OTHER_TEXT_SECTIONS='*(.mips16.fn.*) *(.mips16.call.*)' +OTHER_GOT_SYMBOLS=' + _gp = ALIGN(16) + 0x7ff0; +' +OTHER_GOT_SECTIONS=' + .lit8 : { *(.lit8) } + .lit4 : { *(.lit4) } +' +TEXT_START_SYMBOLS='_ftext = . ;' +DATA_START_SYMBOLS='_fdata = . ;' +OTHER_BSS_SYMBOLS='_fbss = .;' +OTHER_SECTIONS=' + .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) } + .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) } +' +ARCH=mips +MACHINE= +TEMPLATE_NAME=elf32 +GENERATE_SHLIB_SCRIPT=yes diff --git a/contrib/binutils/ld/emulparams/elf32lsmip.sh b/contrib/binutils/ld/emulparams/elf32lsmip.sh new file mode 100755 index 0000000..4bdc8a1 --- /dev/null +++ b/contrib/binutils/ld/emulparams/elf32lsmip.sh @@ -0,0 +1,31 @@ +SCRIPT_NAME=elf +OUTPUT_FORMAT="elf32-littlemips" +BIG_OUTPUT_FORMAT="elf32-bigmips" +LITTLE_OUTPUT_FORMAT="elf32-littlemips" +TEXT_START_ADDR=0x0400000 +DATA_ADDR=0x10000000 +MAXPAGESIZE=0x40000 +NONPAGED_TEXT_START_ADDR=0x0400000 +SHLIB_TEXT_START_ADDR=0x5ffe0000 +TEXT_DYNAMIC= +INITIAL_READONLY_SECTIONS='.reginfo : { *(.reginfo) }' +OTHER_TEXT_SECTIONS='*(.mips16.fn.*) *(.mips16.call.*)' +OTHER_GOT_SYMBOLS=' + _gp = ALIGN(16) + 0x7ff0; +' +OTHER_GOT_SECTIONS=' + .lit8 : { *(.lit8) } + .lit4 : { *(.lit4) } +' +TEXT_START_SYMBOLS='_ftext = . ;' +DATA_START_SYMBOLS='_fdata = . ;' +OTHER_BSS_SYMBOLS='_fbss = .;' +OTHER_SECTIONS=' + .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) } + .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) } +' +ARCH=mips +MACHINE= +TEMPLATE_NAME=elf32 +GENERATE_SHLIB_SCRIPT=yes +ENTRY=__start |