X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=bfd%2Fcoff-mips.c;h=dd7ace5ecdd3f545eb46b4b944aa30cfd93dece4;hb=60bcf0fa8c115b4e71d7b1372aca3efccffc9607;hp=cddc4159b876efb9fcc8aa5e8021856376315afe;hpb=515c4292110048518ddacfaaece66c7829f0ca58;p=binutils-gdb.git diff --git a/bfd/coff-mips.c b/bfd/coff-mips.c index cddc4159b87..dd7ace5ecdd 100644 --- a/bfd/coff-mips.c +++ b/bfd/coff-mips.c @@ -1,7 +1,8 @@ /* BFD back-end for MIPS Extended-Coff files. - Copyright 1990, 1991, 1992 Free Software Foundation, Inc. - Written by Per Bothner. - Symbol and line number support added by Ian Lance Taylor. + Copyright 1990, 91, 92, 93, 94, 95, 96, 97, 98, 99, 2000 + 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. @@ -17,1350 +18,2718 @@ 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ +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/mips.h" #include "coff/internal.h" #include "coff/sym.h" #include "coff/symconst.h" -#include "coff/ecoff-ext.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)); -/* `Tdata' information kept for ECOFF files. */ + +/* 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" -#define ecoff_data(abfd) ((abfd)->tdata.ecoff_obj_data) +/* Get the ECOFF swapping routines. */ +#define ECOFF_32 +#include "ecoffswap.h" + +/* How to process the various relocs types. */ -typedef struct ecoff_tdata +static reloc_howto_type mips_howto_table[] = { - /* The symbol table file position. */ - file_ptr sym_filepos; + /* Reloc type 0 is ignored. The reloc reading code ensures that + this is a reference to the .abs section, which will cause + bfd_perform_relocation to do nothing. */ + HOWTO (MIPS_R_IGNORE, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 8, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + 0, /* special_function */ + "IGNORE", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + false), /* pcrel_offset */ + + /* A 16 bit reference to a symbol, normally from a data section. */ + HOWTO (MIPS_R_REFHALF, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + mips_generic_reloc, /* special_function */ + "REFHALF", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* A 32 bit reference to a symbol, normally from a data section. */ + HOWTO (MIPS_R_REFWORD, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + mips_generic_reloc, /* special_function */ + "REFWORD", /* name */ + true, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* A 26 bit absolute jump address. */ + HOWTO (MIPS_R_JMPADDR, /* type */ + 2, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 26, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + /* This needs complex overflow + detection, because the upper four + bits must match the PC. */ + mips_generic_reloc, /* special_function */ + "JMPADDR", /* name */ + true, /* partial_inplace */ + 0x3ffffff, /* src_mask */ + 0x3ffffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* The high 16 bits of a symbol value. Handled by the function + mips_refhi_reloc. */ + HOWTO (MIPS_R_REFHI, /* type */ + 16, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + mips_refhi_reloc, /* special_function */ + "REFHI", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* The low 16 bits of a symbol value. */ + HOWTO (MIPS_R_REFLO, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + mips_reflo_reloc, /* special_function */ + "REFLO", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* A reference to an offset from the gp register. Handled by the + function mips_gprel_reloc. */ + HOWTO (MIPS_R_GPREL, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + mips_gprel_reloc, /* special_function */ + "GPREL", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* A reference to a literal using an offset from the gp register. + Handled by the function mips_gprel_reloc. */ + HOWTO (MIPS_R_LITERAL, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + mips_gprel_reloc, /* special_function */ + "LITERAL", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + EMPTY_HOWTO (8), + EMPTY_HOWTO (9), + EMPTY_HOWTO (10), + EMPTY_HOWTO (11), + + /* This reloc is a Cygnus extension used when generating position + independent code for embedded systems. It represents a 16 bit PC + relative reloc rightshifted twice as used in the MIPS branch + instructions. */ + HOWTO (MIPS_R_PCREL16, /* type */ + 2, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + true, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + mips_generic_reloc, /* special_function */ + "PCREL16", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + true), /* pcrel_offset */ + + /* This reloc is a Cygnus extension used when generating position + independent code for embedded systems. It represents the high 16 + bits of a PC relative reloc. The next reloc must be + MIPS_R_RELLO, and the addend is formed from the addends of the + two instructions, just as in MIPS_R_REFHI and MIPS_R_REFLO. The + final value is actually PC relative to the location of the + MIPS_R_RELLO reloc, not the MIPS_R_RELHI reloc. */ + HOWTO (MIPS_R_RELHI, /* type */ + 16, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + true, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + mips_relhi_reloc, /* special_function */ + "RELHI", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + true), /* pcrel_offset */ + + /* This reloc is a Cygnus extension used when generating position + independent code for embedded systems. It represents the low 16 + bits of a PC relative reloc. */ + HOWTO (MIPS_R_RELLO, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + true, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + mips_rello_reloc, /* special_function */ + "RELLO", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + true), /* pcrel_offset */ + + EMPTY_HOWTO (15), + EMPTY_HOWTO (16), + EMPTY_HOWTO (17), + EMPTY_HOWTO (18), + EMPTY_HOWTO (19), + EMPTY_HOWTO (20), + EMPTY_HOWTO (21), + + /* This reloc is a Cygnus extension used when generating position + independent code for embedded systems. It represents an entry in + a switch table, which is the difference between two symbols in + the .text section. The symndx is actually the offset from the + reloc address to the subtrahend. See include/coff/mips.h for + more details. */ + HOWTO (MIPS_R_SWITCH, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + true, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + mips_switch_reloc, /* special_function */ + "SWITCH", /* name */ + true, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + true) /* pcrel_offset */ +}; - /* The unswapped ECOFF symbolic information. */ - PTR raw_syments; +#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. */ - /* The swapped ECOFF symbolic header. */ - HDRR symbolic_header; +static boolean +mips_ecoff_bad_format_hook (abfd, filehdr) + bfd *abfd; + PTR filehdr; +{ + struct internal_filehdr *internal_f = (struct internal_filehdr *) filehdr; - /* Pointers to the unswapped symbolic information. */ - unsigned char *line; - struct dnr_ext *external_dnr; - struct pdr_ext *external_pdr; - struct sym_ext *external_sym; - struct opt_ext *external_opt; - union aux_ext *external_aux; - char *ss; - char *ssext; - struct fdr_ext *external_fdr; - struct rfd_ext *external_rfd; - struct ext_ext *external_ext; + switch (internal_f->f_magic) + { + case MIPS_MAGIC_1: + /* I don't know what endianness this implies. */ + return true; - /* The swapped fdr information. */ - FDR *fdr; + case MIPS_MAGIC_BIG: + case MIPS_MAGIC_BIG2: + case MIPS_MAGIC_BIG3: + return bfd_big_endian (abfd); - /* The canonical BFD symbols. */ - struct ecoff_symbol_struct *canonical_symbols; + case MIPS_MAGIC_LITTLE: + case MIPS_MAGIC_LITTLE2: + case MIPS_MAGIC_LITTLE3: + return bfd_little_endian (abfd); -} ecoff_data_type; + 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. */ -/* Each canonical asymbol really looks like this. */ +/* Swap a reloc in. */ -typedef struct ecoff_symbol_struct +static void +mips_ecoff_swap_reloc_in (abfd, ext_ptr, intern) + bfd *abfd; + PTR ext_ptr; + struct internal_reloc *intern; { - /* The actual symbol which the rest of BFD works with */ - asymbol symbol; + const RELOC *ext = (RELOC *) ext_ptr; - /* The fdr for this symbol. */ - FDR *fdr; - - /* true if this is a local symbol rather than an external one. */ - boolean local; - - /* A pointer to the unswapped hidden information for this symbol */ - union + intern->r_vaddr = bfd_h_get_32 (abfd, (bfd_byte *) ext->r_vaddr); + if (bfd_header_big_endian (abfd)) { - struct sym_ext *lnative; - struct ext_ext *enative; + 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; } - native; -} ecoff_symbol_type; - -/* We take the address of the first element of a asymbol to ensure that the - macro is only ever applied to an asymbol. */ -#define ecoffsymbol(asymbol) ((ecoff_symbol_type *) (&((asymbol)->the_bfd))) - -/* MIPS ECOFF has COFF sections, but the debugging information is - stored in a completely different format. This files uses the some - of the swapping routines from coffswap.h, and some of the generic - COFF routines in coffgen.c, but, unlike the real COFF targets, does - not use coffcode.h itself. */ - -/* Get the generic COFF swapping routines, except for the symbol and - lineno ones. Give them ecoff names. */ -#define NO_COFF_SYMBOLS -#define NO_COFF_LINENOS -#define coff_swap_reloc_in ecoff_swap_reloc_in -#define coff_swap_reloc_out ecoff_swap_reloc_out -#define coff_swap_filehdr_in ecoff_swap_filehdr_in -#define coff_swap_filehdr_out ecoff_swap_filehdr_out -#define coff_swap_aouthdr_in ecoff_swap_aouthdr_in -#define coff_swap_aouthdr_out ecoff_swap_aouthdr_out -#define coff_swap_scnhdr_in ecoff_swap_scnhdr_in -#define coff_swap_scnhdr_out ecoff_swap_scnhdr_out -#include "coffswap.h" - -/* This stuff is somewhat copied from coffcode.h. */ -static asection bfd_debug_section = { "*DEBUG*" }; + /* 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; + } +} -/* See whether the magic number matches. */ +/* Swap a reloc out. */ -static boolean -DEFUN(ecoff_bad_format_hook, (abfd, filehdr), - bfd *abfd AND - PTR filehdr) +static void +mips_ecoff_swap_reloc_out (abfd, intern, dst) + bfd *abfd; + const struct internal_reloc *intern; + PTR dst; { - struct internal_filehdr *internal_f = (struct internal_filehdr *) filehdr; - - if (ECOFFBADMAG (*internal_f)) - return false; + 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; + } - return true; + 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)); + } } -/* This is a hook needed by SCO COFF, but we have nothing to do. */ +/* 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 asection * -DEFUN (ecoff_make_section_hook, (abfd, name), - bfd *abfd AND - char *name) +static void +mips_adjust_reloc_in (abfd, intern, rptr) + bfd *abfd; + const struct internal_reloc *intern; + arelent *rptr; { - return (asection *) NULL; + 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]; } -/* Initialize a new section. */ +/* Make any adjustments needed to a reloc before writing it out. None + are needed for MIPS. */ -static boolean -DEFUN (ecoff_new_section_hook, (abfd, section), - bfd *abfd AND - asection *section) +static void +mips_adjust_reloc_out (abfd, rel, intern) + bfd *abfd ATTRIBUTE_UNUSED; + const arelent *rel; + struct internal_reloc *intern; { - section->alignment_power = abfd->xvec->align_power_min; - return true; + /* 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; } -#define ecoff_set_alignment_hook \ - ((void (*) PARAMS ((bfd *, asection *, PTR))) bfd_void) - -static boolean -DEFUN (ecoff_mkobject, (abfd), - bfd *abfd) +/* 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 ATTRIBUTE_UNUSED; + arelent *reloc_entry; + asymbol *symbol; + PTR data ATTRIBUTE_UNUSED; + asection *input_section; + bfd *output_bfd; + char **error_message ATTRIBUTE_UNUSED; { - abfd->tdata.ecoff_obj_data = ((struct ecoff_tdata *) - bfd_zalloc (abfd, sizeof(ecoff_data_type))); - if (abfd->tdata.ecoff_obj_data == NULL) + if (output_bfd != (bfd *) NULL + && (symbol->flags & BSF_SECTION_SYM) == 0 + && reloc_entry->addend == 0) { - bfd_error = no_memory; - return false; + reloc_entry->address += input_section->output_offset; + return bfd_reloc_ok; } - return true; + return bfd_reloc_continue; } -/* Create the COFF backend specific information. */ +/* 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. */ -static PTR -DEFUN(ecoff_mkobject_hook,(abfd, filehdr), - bfd *abfd AND - PTR filehdr) +struct mips_hi { - struct internal_filehdr *internal_f = (struct internal_filehdr *) filehdr; - ecoff_data_type *ecoff; + struct mips_hi *next; + bfd_byte *addr; + bfd_vma addend; +}; + +/* FIXME: This should not be a static variable. */ + +static struct mips_hi *mips_refhi_list; + +static bfd_reloc_status_type +mips_refhi_reloc (abfd, + reloc_entry, + symbol, + data, + input_section, + output_bfd, + error_message) + bfd *abfd ATTRIBUTE_UNUSED; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message ATTRIBUTE_UNUSED; +{ + bfd_reloc_status_type ret; + bfd_vma relocation; + struct mips_hi *n; + + /* If we're relocating, and this an external symbol, we don't want + to change anything. */ + if (output_bfd != (bfd *) NULL + && (symbol->flags & BSF_SECTION_SYM) == 0 + && reloc_entry->addend == 0) + { + reloc_entry->address += input_section->output_offset; + return bfd_reloc_ok; + } + + ret = bfd_reloc_ok; + if (bfd_is_und_section (symbol->section) + && output_bfd == (bfd *) NULL) + ret = bfd_reloc_undefined; + + if (bfd_is_com_section (symbol->section)) + relocation = 0; + else + relocation = symbol->value; - if (ecoff_mkobject (abfd) == false) - return NULL; + relocation += symbol->section->output_section->vma; + relocation += symbol->section->output_offset; + relocation += reloc_entry->addend; - ecoff = ecoff_data (abfd); - ecoff->sym_filepos = internal_f->f_symptr; - return (PTR) ecoff; + 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; } -/* Determine the machine architecture and type. */ -static boolean -DEFUN (ecoff_set_arch_mach_hook, (abfd, filehdr), - bfd *abfd AND - PTR filehdr) +/* 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; { - long machine; - enum bfd_architecture arch; - struct internal_filehdr *internal_f = (struct internal_filehdr *) filehdr; + if (mips_refhi_list != NULL) + { + struct mips_hi *l; - machine = 0; - switch (internal_f->f_magic) { - case MIPS_MAGIC_1: - case MIPS_MAGIC_2: - case MIPS_MAGIC_3: - arch = bfd_arch_mips; - machine = 0; - break; - - default: /* Unreadable input file type */ - arch = bfd_arch_obscure; - break; - } - - bfd_default_set_arch_mach(abfd, arch, machine); - return true; -} + 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; + } -/* Get the BFD flags to use for a section. */ + 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); +} -static flagword -DEFUN(styp_to_sec_flags, (abfd, hdr), - bfd *abfd AND - PTR hdr) +/* 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; { - struct internal_scnhdr *internal_s = (struct internal_scnhdr *) hdr; - long styp_flags = internal_s->s_flags; - flagword sec_flags=0; + 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 (styp_flags & STYP_NOLOAD) - sec_flags |= SEC_NEVER_LOAD; + if (output_bfd != (bfd *) NULL) + relocateable = true; + else + { + relocateable = false; + output_bfd = symbol->section->output_section->owner; + } - /* For 386 COFF, at least, an unloadable text or data section is - actually a shared library section. */ - if (styp_flags & STYP_TEXT) + 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 (sec_flags & SEC_NEVER_LOAD) - sec_flags |= SEC_CODE | SEC_SHARED_LIBRARY; + if (relocateable != false) + { + /* Make up a value. */ + gp = symbol->section->output_section->vma + 0x4000; + _bfd_set_gp_value (output_bfd, gp); + } else - sec_flags |= SEC_CODE | SEC_LOAD | SEC_ALLOC; + { + 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; + } + } } - else if ((styp_flags & STYP_DATA) - || (styp_flags & STYP_RDATA) - || (styp_flags & STYP_SDATA)) + + 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 ((long) val >= 0x8000 || (long) val < -0x8000) + return bfd_reloc_overflow; + + return bfd_reloc_ok; +} + +/* Do a RELHI relocation. We do this in conjunction with a RELLO + reloc, just as REFHI and REFLO are done together. RELHI and RELLO + are Cygnus extensions used when generating position independent + code for embedded systems. */ + +/* FIXME: This should not be a static variable. */ + +static struct mips_hi *mips_relhi_list; + +static bfd_reloc_status_type +mips_relhi_reloc (abfd, + reloc_entry, + symbol, + data, + input_section, + output_bfd, + error_message) + bfd *abfd ATTRIBUTE_UNUSED; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message ATTRIBUTE_UNUSED; +{ + bfd_reloc_status_type ret; + bfd_vma relocation; + struct mips_hi *n; + + /* If this is a reloc against a section symbol, then it is correct + in the object file. The only time we want to change this case is + when we are relaxing, and that is handled entirely by + mips_relocate_section and never calls this function. */ + if ((symbol->flags & BSF_SECTION_SYM) != 0) { - if (sec_flags & SEC_NEVER_LOAD) - sec_flags |= SEC_DATA | SEC_SHARED_LIBRARY; - else - sec_flags |= SEC_DATA | SEC_LOAD | SEC_ALLOC; - if (styp_flags & STYP_RDATA) - sec_flags |= SEC_READONLY; + if (output_bfd != (bfd *) NULL) + reloc_entry->address += input_section->output_offset; + return bfd_reloc_ok; } - else if ((styp_flags & STYP_BSS) - || (styp_flags & STYP_SBSS)) + + /* This is an external symbol. If we're relocating, we don't want + to change anything. */ + if (output_bfd != (bfd *) NULL) { - sec_flags |= SEC_ALLOC; + reloc_entry->address += input_section->output_offset; + return bfd_reloc_ok; } - else if (styp_flags & STYP_INFO) + + 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) { - sec_flags |= SEC_NEVER_LOAD; + 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; } - else if ((styp_flags & STYP_LIT8) - || (styp_flags & STYP_LIT4)) + + /* 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) { - sec_flags |= SEC_DATA | SEC_LOAD | SEC_ALLOC | SEC_READONLY; + if (output_bfd != (bfd *) NULL) + reloc_entry->address += input_section->output_offset; + return bfd_reloc_ok; } - else + + /* 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) { - sec_flags |= SEC_ALLOC | SEC_LOAD; + reloc_entry->address += input_section->output_offset; + return bfd_reloc_ok; } - return sec_flags; + /* Now do the RELLO reloc in the usual way. */ + return mips_generic_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message); } - -/* ECOFF symbol table routines. The ECOFF symbol table is described - in gcc/mips-tfile.c. */ -/* Create an empty symbol. */ - -static asymbol * -DEFUN (ecoff_make_empty_symbol, (abfd), - bfd *abfd) +/* 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 ATTRIBUTE_UNUSED; + arelent *reloc_entry ATTRIBUTE_UNUSED; + asymbol *symbol ATTRIBUTE_UNUSED; + PTR data ATTRIBUTE_UNUSED; + asection *input_section ATTRIBUTE_UNUSED; + bfd *output_bfd ATTRIBUTE_UNUSED; + char **error_message ATTRIBUTE_UNUSED; { - ecoff_symbol_type *new; - - new = (ecoff_symbol_type *) bfd_alloc (abfd, sizeof (ecoff_symbol_type)); - if (new == (ecoff_symbol_type *) NULL) - { - bfd_error = no_memory; - return (asymbol *) NULL; - } - new->symbol.section = (asection *) NULL; - new->fdr = (FDR *) NULL; - new->local = false; - new->native.lnative = (struct sym_ext *) NULL; - new->symbol.the_bfd = abfd; - return &new->symbol; + return bfd_reloc_ok; } -/* Set the BFD flags and section for an ECOFF symbol. */ +/* Get the howto structure for a generic reloc type. */ -static void -DEFUN (ecoff_set_symbol_info, (abfd, ecoff_sym, asym, ext), - bfd *abfd AND - SYMR *ecoff_sym AND - asymbol *asym AND - int ext) +static reloc_howto_type * +mips_bfd_reloc_type_lookup (abfd, code) + bfd *abfd ATTRIBUTE_UNUSED; + bfd_reloc_code_real_type code; { - asym->the_bfd = abfd; - asym->value = ecoff_sym->value; - asym->section = &bfd_debug_section; - asym->udata = NULL; - if (ext) - asym->flags = BSF_EXPORT | BSF_GLOBAL; - else - asym->flags = BSF_LOCAL; - switch (ecoff_sym->sc) + int mips_type; + + switch (code) { - case scNil: - asym->flags = 0; - break; - case scText: - asym->section = bfd_make_section_old_way (abfd, ".text"); - asym->value -= asym->section->vma; - break; - case scData: - asym->section = bfd_make_section_old_way (abfd, ".data"); - asym->value -= asym->section->vma; - break; - case scBss: - asym->section = &bfd_com_section; - asym->flags = 0; + case BFD_RELOC_16: + mips_type = MIPS_R_REFHALF; break; - case scRegister: - asym->flags = BSF_DEBUGGING; + case BFD_RELOC_32: + case BFD_RELOC_CTOR: + mips_type = MIPS_R_REFWORD; break; - case scAbs: - asym->section = &bfd_abs_section; - asym->flags = 0; + case BFD_RELOC_MIPS_JMP: + mips_type = MIPS_R_JMPADDR; break; - case scUndefined: - asym->section = &bfd_und_section; - asym->flags = 0; + case BFD_RELOC_HI16_S: + mips_type = MIPS_R_REFHI; break; - case scCdbLocal: - case scBits: - case scCdbSystem: - case scRegImage: - case scInfo: - case scUserStruct: - asym->flags = BSF_DEBUGGING; + case BFD_RELOC_LO16: + mips_type = MIPS_R_REFLO; break; - case scSData: - asym->section = bfd_make_section_old_way (abfd, ".sdata"); - asym->value -= asym->section->vma; + case BFD_RELOC_MIPS_GPREL: + mips_type = MIPS_R_GPREL; break; - case scSBss: - asym->section = &bfd_com_section; - asym->flags = 0; + case BFD_RELOC_MIPS_LITERAL: + mips_type = MIPS_R_LITERAL; break; - case scRData: - asym->section = bfd_make_section_old_way (abfd, ".rdata"); - asym->value -= asym->section->vma; + case BFD_RELOC_16_PCREL_S2: + mips_type = MIPS_R_PCREL16; break; - case scVar: - asym->flags = BSF_DEBUGGING; + case BFD_RELOC_PCREL_HI16_S: + mips_type = MIPS_R_RELHI; break; - case scCommon: - case scSCommon: - asym->section = &bfd_com_section; - asym->flags = 0; + case BFD_RELOC_PCREL_LO16: + mips_type = MIPS_R_RELLO; break; - case scVarRegister: - case scVariant: - asym->flags = BSF_DEBUGGING; - break; - case scSUndefined: - asym->section = &bfd_und_section; - asym->flags = 0; - break; - case scInit: - asym->section = bfd_make_section_old_way (abfd, ".init"); - asym->value -= asym->section->vma; - break; - case scBasedVar: - case scXData: - case scPData: - asym->flags = BSF_DEBUGGING; - break; - case scFini: - asym->section = bfd_make_section_old_way (abfd, ".fini"); - asym->value -= asym->section->vma; + case BFD_RELOC_GPREL32: + mips_type = MIPS_R_SWITCH; break; default: - asym->flags = 0; - break; + 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. */ -/* Read an ECOFF symbol table. */ +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; + + if (refhi == NULL) + return; + + insn = bfd_get_32 (input_bfd, + contents + adjust + refhi->r_vaddr - input_section->vma); + if (reflo == NULL) + vallo = 0; + else + vallo = (bfd_get_32 (input_bfd, + contents + adjust + reflo->r_vaddr - input_section->vma) + & 0xffff); + + val = ((insn & 0xffff) << 16) + vallo; + val += relocation; + + /* The low order 16 bits are always treated as a signed value. + Therefore, a negative value in the low order bits requires an + adjustment in the high order bits. We need to make this + adjustment in two ways: once for the bits we took from the data, + and once for the bits we are putting back in to the data. */ + if ((vallo & 0x8000) != 0) + val -= 0x10000; + + if (pcrel) + val -= (input_section->output_section->vma + + input_section->output_offset + + (reflo->r_vaddr - input_section->vma + adjust)); + + if ((val & 0x8000) != 0) + val += 0x10000; + + insn = (insn &~ 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 -DEFUN (ecoff_slurp_symbol_table, (abfd), - bfd *abfd) +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; { - struct hdr_ext external_symhdr; - HDRR *internal_symhdr; - bfd_size_type raw_base; - bfd_size_type raw_size; - PTR raw; - bfd_size_type internal_size; - struct fdr_ext *fraw_src; - struct fdr_ext *fraw_end; - struct fdr *fdr_ptr; - struct fdr *fdr_end; - ecoff_symbol_type *internal; - ecoff_symbol_type *internal_ptr; - struct ext_ext *eraw_src; - struct ext_ext *eraw_end; - - /* If we've already read in the symbol table, do nothing. */ - if (ecoff_data (abfd)->canonical_symbols != NULL) - return true; - - /* At this point bfd_get_symcount (abfd) holds the number of symbols - as read from the file header, but on ECOFF this is always the - size of the symbolic information header. It would be cleaner to - handle this when we first read the file in coffgen.c. */ - if (bfd_get_symcount (abfd) != sizeof (external_symhdr)) + 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) { - bfd_error = bad_value; - return false; + 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; } - /* Read the symbolic information header. */ - if (bfd_seek (abfd, ecoff_data (abfd)->sym_filepos, SEEK_SET) == -1 - || (bfd_read ((PTR) &external_symhdr, sizeof (external_symhdr), 1, abfd) - != sizeof (external_symhdr))) - { - bfd_error = system_call_error; - return false; - } - internal_symhdr = &ecoff_data (abfd)->symbolic_header; - ecoff_swap_hdr_in (abfd, &external_symhdr, internal_symhdr); + sym_hashes = ecoff_data (input_bfd)->sym_hashes; - if (internal_symhdr->magic != magicSym) - { - bfd_error = bad_value; - return false; - } + gp = _bfd_get_gp_value (output_bfd); + if (gp == 0) + gp_undefined = true; + else + gp_undefined = false; - /* Now we can get the correct number of symbols. */ - bfd_get_symcount (abfd) = (internal_symhdr->isymMax - + internal_symhdr->iextMax); - - /* Read the entire symbol table at once. This expression assumes - that the external symbols are always the last item. */ - raw_base = ecoff_data (abfd)->sym_filepos + sizeof (external_symhdr); - raw_size = (internal_symhdr->cbExtOffset - raw_base - + internal_symhdr->iextMax * sizeof (struct ext_ext)); - raw = (PTR) bfd_alloc (abfd, raw_size); - if (raw == NULL) - { - bfd_error = no_memory; - return false; - } - if (bfd_read (raw, raw_size, 1, abfd) != raw_size) - { - bfd_error = system_call_error; - bfd_release (abfd, raw); - return false; - } + got_lo = false; - ecoff_data (abfd)->raw_syments = raw; - - /* Get pointers for the numeric offsets in the HDRR structure. */ -#define FIX(off1, off2, type) \ - if (internal_symhdr->off1 == 0) \ - ecoff_data (abfd)->off2 = (type *) NULL; \ - else \ - ecoff_data (abfd)->off2 = (type *) ((char *) raw \ - + internal_symhdr->off1 \ - - raw_base) - FIX (cbLineOffset, line, unsigned char); - FIX (cbDnOffset, external_dnr, struct dnr_ext); - FIX (cbPdOffset, external_pdr, struct pdr_ext); - FIX (cbSymOffset, external_sym, struct sym_ext); - FIX (cbOptOffset, external_opt, struct opt_ext); - FIX (cbAuxOffset, external_aux, union aux_ext); - FIX (cbSsOffset, ss, char); - FIX (cbSsExtOffset, ssext, char); - FIX (cbFdOffset, external_fdr, struct fdr_ext); - FIX (cbRfdOffset, external_rfd, struct rfd_ext); - FIX (cbExtOffset, external_ext, struct ext_ext); -#undef FIX - - /* I don't want to always swap all the data, because it will just - waste time and most programs will never look at this data. The - only time the linker needs most of the debugging information - swapped is when linking big-endian and little-endian MIPS object - files together, which is not a common occurrence. - - We need to look at the fdr to deal with a lot of information in - the symbols, so we swap them here. We also canonicalize the - symbols. */ - ecoff_data (abfd)->fdr = (struct fdr *) bfd_alloc (abfd, - (internal_symhdr->ifdMax * - sizeof (struct fdr))); - if (ecoff_data (abfd)->fdr == NULL) - { - bfd_error = no_memory; - return false; - } - fdr_ptr = ecoff_data (abfd)->fdr; - fraw_src = ecoff_data (abfd)->external_fdr; - fraw_end = fraw_src + internal_symhdr->ifdMax; - for (; fraw_src < fraw_end; fraw_src++, fdr_ptr++) - ecoff_swap_fdr_in (abfd, fraw_src, fdr_ptr); - - internal_size = bfd_get_symcount (abfd) * sizeof (ecoff_symbol_type); - internal = (ecoff_symbol_type *) bfd_alloc (abfd, internal_size); - if (internal == NULL) - { - bfd_error = no_memory; - return false; - } + adjust = 0; - internal_ptr = internal; - eraw_src = ecoff_data (abfd)->external_ext; - eraw_end = eraw_src + internal_symhdr->iextMax; - for (; eraw_src < eraw_end; eraw_src++, internal_ptr++) - { - EXTR internal_esym; - - ecoff_swap_ext_in (abfd, eraw_src, &internal_esym); - internal_ptr->symbol.name = (ecoff_data (abfd)->ssext - + internal_esym.asym.iss); - ecoff_set_symbol_info (abfd, &internal_esym.asym, - &internal_ptr->symbol, 1); - internal_ptr->fdr = ecoff_data (abfd)->fdr + internal_esym.ifd; - internal_ptr->local = false; - internal_ptr->native.enative = eraw_src; - } + if (ecoff_section_data (input_bfd, input_section) == NULL) + offsets = NULL; + else + offsets = ecoff_section_data (input_bfd, input_section)->offsets; - /* The local symbols must be accessed via the fdr's, because the - string and aux indices are relative to the fdr information. */ - fdr_ptr = ecoff_data (abfd)->fdr; - fdr_end = fdr_ptr + internal_symhdr->ifdMax; - for (; fdr_ptr < fdr_end; fdr_ptr++) + 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 sym_ext *lraw_src; - struct sym_ext *lraw_end; + 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]); - lraw_src = ecoff_data (abfd)->external_sym + fdr_ptr->isymBase; - lraw_end = lraw_src + fdr_ptr->csym; - for (; lraw_src < lraw_end; lraw_src++, internal_ptr++) + /* 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) { - SYMR internal_sym; - - ecoff_swap_sym_in (abfd, lraw_src, &internal_sym); - internal_ptr->symbol.name = (ecoff_data (abfd)->ss - + fdr_ptr->issBase - + internal_sym.iss); - ecoff_set_symbol_info (abfd, &internal_sym, - &internal_ptr->symbol, 0); - internal_ptr->fdr = fdr_ptr; - internal_ptr->local = true; - internal_ptr->native.lnative = lraw_src; + 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; + } } - } - ecoff_data (abfd)->canonical_symbols = internal; + 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); + } - return true; -} + continue; + } -static unsigned int -DEFUN (ecoff_get_symtab_upper_bound, (abfd), - bfd *abfd) -{ - if (! ecoff_slurp_symbol_table (abfd)) - return 0; + 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]; - return (bfd_get_symcount (abfd) + 1) * (sizeof (ecoff_symbol_type *)); -} + if (s == (asection *) NULL) + abort (); + } -static unsigned int -DEFUN (ecoff_get_symtab, (abfd, alocation), - bfd *abfd AND - asymbol **alocation) -{ - unsigned int counter = 0; - ecoff_symbol_type *symbase; - ecoff_symbol_type **location = (ecoff_symbol_type **) alocation; + /* 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 (! ecoff_slurp_symbol_table (abfd)) - return 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; + } + } - symbase = ecoff_data (abfd)->canonical_symbols; - while (counter < bfd_get_symcount (abfd)) - { - *(location++) = symbase++; - counter++; - } - *location++ = (ecoff_symbol_type *) NULL; - return bfd_get_symcount (abfd); -} + /* 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; + } + } -/* Turn ECOFF type information into a printable string. - emit_aggregate and type_to_string are from gcc/mips-tdump.c, with - swapping added and used_ptr removed. */ + 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); + } -/* Write aggregate information to a string. */ + 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; + } + } -static void -DEFUN (emit_aggregate, (abfd, string, rndx, isym, which), - bfd *abfd AND - char *string AND - RNDXR *rndx AND - long isym AND - CONST char *which) -{ - int ifd = rndx->rfd; - int indx = rndx->index; - int sym_base, ss_base; - CONST char *name; - - if (ifd == 0xfff) - ifd = isym; + /* Adjust the reloc address. */ + int_rel.r_vaddr += (input_section->output_section->vma + + input_section->output_offset + - input_section->vma); - sym_base = ecoff_data (abfd)->fdr[ifd].isymBase; - ss_base = ecoff_data (abfd)->fdr[ifd].issBase; - - if (indx == indexNil) - name = "/* no name */"; - else - { - SYMR sym; + /* Save the changed reloc information. */ + mips_ecoff_swap_reloc_out (input_bfd, &int_rel, (PTR) ext_rel); + } + else + { + /* We are producing a final executable. */ + if (int_rel.r_extern) + { + /* This is a reloc against a symbol. */ + if (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + { + asection *hsec; + + hsec = h->root.u.def.section; + relocation = (h->root.u.def.value + + hsec->output_section->vma + + hsec->output_offset); + } + else + { + if (! ((*info->callbacks->undefined_symbol) + (info, h->root.root.string, input_bfd, + input_section, + int_rel.r_vaddr - input_section->vma, true))) + return false; + relocation = 0; + } + } + else + { + /* This is a reloc against a section. */ + relocation = (s->output_section->vma + + s->output_offset + - s->vma); + + /* A PC relative reloc is already correct in the object + file. Make it look like a pcrel_offset relocation by + adding in the start address. */ + if (howto->pc_relative) + { + if (int_rel.r_type != MIPS_R_RELHI || ! use_lo) + relocation += int_rel.r_vaddr + adjust; + else + relocation += lo_int_rel.r_vaddr + adjust; + } + } + + if (int_rel.r_type != MIPS_R_REFHI + && int_rel.r_type != MIPS_R_RELHI) + r = _bfd_final_link_relocate (howto, + input_bfd, + input_section, + contents, + (int_rel.r_vaddr + - input_section->vma + + adjust), + relocation, + addend); + else + { + mips_relocate_hi (&int_rel, + use_lo ? &lo_int_rel : NULL, + input_bfd, input_section, contents, adjust, + relocation, + int_rel.r_type == MIPS_R_RELHI); + r = bfd_reloc_ok; + } + } + + /* MIPS_R_JMPADDR requires peculiar overflow detection. The + instruction provides a 28 bit address (the two lower bits are + implicit zeroes) which is combined with the upper four bits + of the instruction address. */ + if (r == bfd_reloc_ok + && int_rel.r_type == MIPS_R_JMPADDR + && (((relocation + + addend + + (int_rel.r_extern ? 0 : s->vma)) + & 0xf0000000) + != ((input_section->output_section->vma + + input_section->output_offset + + (int_rel.r_vaddr - input_section->vma) + + adjust) + & 0xf0000000))) + r = bfd_reloc_overflow; + + if (r != bfd_reloc_ok) + { + switch (r) + { + default: + case bfd_reloc_outofrange: + abort (); + case bfd_reloc_overflow: + { + const char *name; - indx += sym_base; - ecoff_swap_sym_in (abfd, - ecoff_data (abfd)->external_sym + indx, - &sym); - name = ecoff_data (abfd)->ss + ss_base + sym.iss; + 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; + } + } } - sprintf (string, - "%s %s { ifd = %d, index = %d }", - which, name, ifd, - indx + ecoff_data (abfd)->symbolic_header.iextMax); + return true; } + +/* Read in the relocs for a section. */ -/* Convert the type information to string format. */ - -static char * -DEFUN (type_to_string, (abfd, aux_ptr, indx, bigendian), - bfd *abfd AND - union aux_ext *aux_ptr AND - int indx AND - int bigendian) +static boolean +mips_read_relocs (abfd, sec) + bfd *abfd; + asection *sec; { - AUXU u; - struct qual { - unsigned int type; - int low_bound; - int high_bound; - int stride; - } qualifiers[7]; - - unsigned int basic_type; - int i; - static char buffer1[1024]; - static char buffer2[1024]; - char *p1 = buffer1; - char *p2 = buffer2; - RNDXR rndx; - - for (i = 0; i < 7; i++) + struct ecoff_section_tdata *section_tdata; + + section_tdata = ecoff_section_data (abfd, sec); + if (section_tdata == (struct ecoff_section_tdata *) NULL) { - qualifiers[i].low_bound = 0; - qualifiers[i].high_bound = 0; - qualifiers[i].stride = 0; + 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 (AUX_GET_ISYM (bigendian, &aux_ptr[indx]) == -1) - return "-1 (no type)"; - ecoff_swap_tir_in (bigendian, &aux_ptr[indx++].a_ti, &u.ti); - - basic_type = u.ti.bt; - qualifiers[0].type = u.ti.tq0; - qualifiers[1].type = u.ti.tq1; - qualifiers[2].type = u.ti.tq2; - qualifiers[3].type = u.ti.tq3; - qualifiers[4].type = u.ti.tq4; - qualifiers[5].type = u.ti.tq5; - qualifiers[6].type = tqNil; - - /* - * Go get the basic type. - */ - switch (basic_type) + if (section_tdata->external_relocs == NULL) { - case btNil: /* undefined */ - strcpy (p1, "nil"); - break; + bfd_size_type external_relocs_size; - case btAdr: /* address - integer same size as pointer */ - strcpy (p1, "address"); - break; + external_relocs_size = (ecoff_backend (abfd)->external_reloc_size + * sec->reloc_count); - case btChar: /* character */ - strcpy (p1, "char"); - break; + section_tdata->external_relocs = + (PTR) bfd_alloc (abfd, external_relocs_size); + if (section_tdata->external_relocs == NULL && external_relocs_size != 0) + return false; - case btUChar: /* unsigned character */ - strcpy (p1, "unsigned char"); - break; + 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; + } - case btShort: /* short */ - strcpy (p1, "short"); - break; + return true; +} - case btUShort: /* unsigned short */ - strcpy (p1, "unsigned short"); - break; +/* 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. */ - case btInt: /* int */ - strcpy (p1, "int"); - break; +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; - case btUInt: /* unsigned int */ - strcpy (p1, "unsigned int"); - break; + /* If there are no relocs, there is nothing to do. */ + if (sec->reloc_count == 0) + return true; - case btLong: /* long */ - strcpy (p1, "long"); - break; + /* 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; - case btULong: /* unsigned long */ - strcpy (p1, "unsigned long"); - break; + /* 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); + } - case btFloat: /* float (real) */ - strcpy (p1, "float"); - break; + if (sec->_cooked_size == 0) + { + /* We must initialize _cooked_size only the first time we are + called. */ + sec->_cooked_size = sec->_raw_size; + } - case btDouble: /* Double (real) */ - strcpy (p1, "double"); - break; + contents = section_tdata->contents; + offsets = section_tdata->offsets; - /* Structures add 1-2 aux words: - 1st word is [ST_RFDESCAPE, offset] pointer to struct def; - 2nd word is file index if 1st word rfd is ST_RFDESCAPE. */ + /* 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; + } - case btStruct: /* Structure (Record) */ - ecoff_swap_rndx_in (bigendian, &aux_ptr[indx].a_rndx, &rndx); - emit_aggregate (abfd, p1, &rndx, - AUX_GET_ISYM (bigendian, &aux_ptr[indx+1]), - "struct"); - indx++; /* skip aux words */ - break; + mips_ecoff_swap_reloc_in (abfd, (PTR) ext_rel, &int_rel); - /* Unions add 1-2 aux words: - 1st word is [ST_RFDESCAPE, offset] pointer to union def; - 2nd word is file index if 1st word rfd is ST_RFDESCAPE. */ + h = ecoff_data (abfd)->sym_hashes[int_rel.r_symndx]; + if (h == (struct ecoff_link_hash_entry *) NULL) + abort (); - case btUnion: /* Union */ - ecoff_swap_rndx_in (bigendian, &aux_ptr[indx].a_rndx, &rndx); - emit_aggregate (abfd, p1, &rndx, - AUX_GET_ISYM (bigendian, &aux_ptr[indx+1]), - "union"); - indx++; /* skip aux words */ - break; + 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; + } - /* Enumerations add 1-2 aux words: - 1st word is [ST_RFDESCAPE, offset] pointer to enum def; - 2nd word is file index if 1st word rfd is ST_RFDESCAPE. */ + /* 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; + } - case btEnum: /* Enumeration */ - ecoff_swap_rndx_in (bigendian, &aux_ptr[indx].a_rndx, &rndx); - emit_aggregate (abfd, p1, &rndx, - AUX_GET_ISYM (bigendian, &aux_ptr[indx+1]), - "enum"); - indx++; /* skip aux words */ - break; + /* 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; + } - case btTypedef: /* defined via a typedef, isymRef points */ - strcpy (p1, "typedef"); - break; + offsets[i] = 1; - case btRange: /* subrange of int */ - strcpy (p1, "subrange"); - break; + /* 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; - case btSet: /* pascal sets */ - strcpy (p1, "set"); - break; + mips_ecoff_swap_reloc_in (abfd, (PTR) adj_ext_rel, &adj_int_rel); - case btComplex: /* fortran complex */ - strcpy (p1, "complex"); - break; + if (adj_int_rel.r_type == MIPS_R_PCREL16) + { + unsigned long insn; - case btDComplex: /* fortran double complex */ - strcpy (p1, "double complex"); - break; + /* We only care about local references. External ones + will be relocated correctly anyhow. */ + if (adj_int_rel.r_extern) + continue; - case btIndirect: /* forward or unnamed typedef */ - strcpy (p1, "forward/unamed typedef"); - break; + /* 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; - case btFixedDec: /* Fixed Decimal */ - strcpy (p1, "fixed decimal"); - break; + start = adj_int_rel.r_vaddr; - case btFloatDec: /* Float Decimal */ - strcpy (p1, "float decimal"); - break; + insn = bfd_get_32 (abfd, + contents + adj_int_rel.r_vaddr - sec->vma); - case btString: /* Varying Length Character String */ - strcpy (p1, "string"); - break; + 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; + } + } - case btBit: /* Aligned Bit String */ - strcpy (p1, "bit"); - break; + /* 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; + } - case btPicture: /* Picture */ - strcpy (p1, "picture"); - break; + /* 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; - case btVoid: /* Void */ - strcpy (p1, "void"); - break; + adjust->start = int_rel.r_vaddr; + adjust->end = sec->vma + sec->_raw_size; + adjust->adjust = PCREL16_EXPANSION_ADJUSTMENT; - default: - sprintf (p1, "Unknown basic type %d", (int) basic_type); - break; + adjust->next = ecoff_data (abfd)->debug_info.adjust; + ecoff_data (abfd)->debug_info.adjust = adjust; } - p1 += strlen (buffer1); + if (contents != (bfd_byte *) NULL && ! info->keep_memory) + free (contents); - /* - * If this is a bitfield, get the bitsize. - */ - if (u.ti.fBitfield) - { - int bitsize; + return true; - bitsize = AUX_GET_WIDTH (bigendian, &aux_ptr[indx++]); - sprintf (p1, " : %d", bitsize); - p1 += strlen (buffer1); - } + 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. */ - /* - * Deal with any qualifiers. - */ - if (qualifiers[0].type != tqNil) - { - /* - * Snarf up any array bounds in the correct order. Arrays - * store 5 successive words in the aux. table: - * word 0 RNDXR to type of the bounds (ie, int) - * word 1 Current file descriptor index - * word 2 low bound - * word 3 high bound (or -1 if []) - * word 4 stride size in bits - */ - for (i = 0; i < 7; i++) - { - if (qualifiers[i].type == tqArray) - { - qualifiers[i].low_bound = - AUX_GET_DNLOW (bigendian, &aux_ptr[indx+2]); - qualifiers[i].high_bound = - AUX_GET_DNHIGH (bigendian, &aux_ptr[indx+3]); - qualifiers[i].stride = - AUX_GET_WIDTH (bigendian, &aux_ptr[indx+4]); - indx += 5; - } - } +static boolean +mips_relax_pcrel16 (info, input_bfd, input_section, h, location, address) + struct bfd_link_info *info ATTRIBUTE_UNUSED; + bfd *input_bfd; + asection *input_section ATTRIBUTE_UNUSED; + struct ecoff_link_hash_entry *h; + bfd_byte *location; + bfd_vma address; +{ + bfd_vma relocation; + + /* 0x0411ffff is bgezal $0,. == bal . */ + BFD_ASSERT (bfd_get_32 (input_bfd, location) == 0x0411ffff); + + /* We need to compute the distance between the symbol and the + current address plus eight. */ + relocation = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + relocation -= address + 8; + + /* If the lower half is negative, increment the upper 16 half. */ + if ((relocation & 0x8000) != 0) + relocation += 0x10000; + + bfd_put_32 (input_bfd, 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 */ - /* - * Now print out the qualifiers. - */ - for (i = 0; i < 6; i++) - { - switch (qualifiers[i].type) - { - case tqNil: - case tqMax: - break; + return true; +} - case tqPtr: - strcpy (p2, "ptr to "); - p2 += sizeof ("ptr to ")-1; - break; +/* 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; - case tqVol: - strcpy (p2, "volatile "); - p2 += sizeof ("volatile ")-1; - break; + BFD_ASSERT (! info->relocateable); - case tqFar: - strcpy (p2, "far "); - p2 += sizeof ("far ")-1; - break; + *errmsg = NULL; - case tqProc: - strcpy (p2, "func. ret. "); - p2 += sizeof ("func. ret. "); - break; + if (datasec->reloc_count == 0) + return true; - case tqArray: - { - int first_array = i; - int j; - - /* Print array bounds reversed (ie, in the order the C - programmer writes them). C is such a fun language.... */ - - while (i < 5 && qualifiers[i+1].type == tqArray) - i++; - - for (j = i; j >= first_array; j--) - { - strcpy (p2, "array ["); - p2 += sizeof ("array [")-1; - if (qualifiers[j].low_bound != 0) - sprintf (p2, - "%ld:%ld {%ld bits}", - (long) qualifiers[j].low_bound, - (long) qualifiers[j].high_bound, - (long) qualifiers[j].stride); - - else if (qualifiers[j].high_bound != -1) - sprintf (p2, - "%ld {%ld bits}", - (long) (qualifiers[j].high_bound + 1), - (long) (qualifiers[j].stride)); - - else - sprintf (p2, " {%ld bits}", (long) (qualifiers[j].stride)); - - p2 += strlen (p2); - strcpy (p2, "] of "); - p2 += sizeof ("] of ")-1; - } - } - break; - } - } - } + sym_hashes = ecoff_data (abfd)->sym_hashes; - strcpy (p2, buffer1); - return buffer2; -} + if (! mips_read_relocs (abfd, datasec)) + return false; -/* Print information about an ECOFF symbol. */ + relsec->contents = (bfd_byte *) bfd_alloc (abfd, datasec->reloc_count * 4); + if (relsec->contents == NULL) + return false; -static void -DEFUN (ecoff_print_symbol, (abfd, filep, symbol, how), - bfd *abfd AND - PTR filep AND - asymbol *symbol AND - bfd_print_symbol_type how) -{ - FILE *file = (FILE *)filep; + p = relsec->contents; - switch (how) + 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) { - case bfd_print_symbol_name: - fprintf (file, "%s", symbol->name); - break; - case bfd_print_symbol_more: - if (ecoffsymbol (symbol)->local) + 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) { - SYMR ecoff_sym; - - ecoff_swap_sym_in (abfd, ecoffsymbol (symbol)->native.lnative, - &ecoff_sym); - fprintf (file, "ecoff local %lx %x %x", - (unsigned long) ecoff_sym.value, - (unsigned) ecoff_sym.st, (unsigned) ecoff_sym.sc); + 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 { - EXTR ecoff_ext; - - ecoff_swap_ext_in (abfd, ecoffsymbol (symbol)->native.enative, - &ecoff_ext); - fprintf (file, "ecoff extern %lx %x %x", - (unsigned long) ecoff_ext.asym.value, - (unsigned) ecoff_ext.asym.st, - (unsigned) ecoff_ext.asym.sc); + 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; + } } - break; - case bfd_print_symbol_nm: - { - CONST char *section_name = symbol->section->name; - - bfd_print_symbol_vandf ((PTR) file, symbol); - fprintf (file, " %-5s %s %s", - section_name, - ecoffsymbol (symbol)->local ? "l" : "e", - symbol->name); - } - break; - case bfd_print_symbol_all: - /* Print out the symbols in a reasonable way */ - { - CONST char *section_name = symbol->section->name; - char type; - int pos; - EXTR ecoff_ext; - char jmptbl; - char cobol_main; - char weakext; - - if (ecoffsymbol (symbol)->local) - { - ecoff_swap_sym_in (abfd, ecoffsymbol (symbol)->native.lnative, - &ecoff_ext.asym); - type = 'l'; - pos = (ecoffsymbol (symbol)->native.lnative - - ecoff_data (abfd)->external_sym - + ecoff_data (abfd)->symbolic_header.iextMax); - jmptbl = ' '; - cobol_main = ' '; - weakext = ' '; - } - else - { - ecoff_swap_ext_in (abfd, ecoffsymbol (symbol)->native.enative, - &ecoff_ext); - type = 'e'; - pos = (ecoffsymbol (symbol)->native.enative - - ecoff_data (abfd)->external_ext); - jmptbl = ecoff_ext.jmptbl ? 'j' : ' '; - cobol_main = ecoff_ext.cobol_main ? 'c' : ' '; - weakext = ecoff_ext.weakext ? 'w' : ' '; - } - - fprintf (file, "[%3d] %c %lx st %x sc %x indx %x %c%c%c %s", - pos, type, (unsigned long) ecoff_ext.asym.value, - (unsigned) ecoff_ext.asym.st, - (unsigned) ecoff_ext.asym.sc, - (unsigned) ecoff_ext.asym.index, - jmptbl, cobol_main, weakext, - symbol->name); - - if (ecoffsymbol (symbol)->fdr != NULL - && ecoff_ext.asym.index != indexNil) - { - unsigned indx; - int bigendian; - long sym_base; - union aux_ext *aux_base; - - indx = ecoff_ext.asym.index; - - /* sym_base is used to map the fdr relative indices which - appear in the file to the position number which we are - using. */ - sym_base = ecoffsymbol (symbol)->fdr->isymBase; - if (ecoffsymbol (symbol)->local) - sym_base += ecoff_data (abfd)->symbolic_header.iextMax; - - /* aux_base is the start of the aux entries for this file; - asym.index is an offset from this. */ - aux_base = (ecoff_data (abfd)->external_aux - + ecoffsymbol (symbol)->fdr->iauxBase); - - /* The aux entries are stored in host byte order; the - order is indicated by a bit in the fdr. */ - bigendian = ecoffsymbol (symbol)->fdr->fBigendian; - - /* This switch is basically from gcc/mips-tdump.c */ - switch (ecoff_ext.asym.st) - { - case stNil: - case stLabel: - break; - case stFile: - case stBlock: - printf ("\n End+1 symbol: %ld", indx + sym_base); - break; - - case stEnd: - if (ecoff_ext.asym.sc == scText - || ecoff_ext.asym.sc == scInfo) - printf ("\n First symbol: %ld", indx + sym_base); - else - printf ("\n First symbol: %ld", - (AUX_GET_ISYM (bigendian, - &aux_base[ecoff_ext.asym.index]) - + sym_base)); - break; - - case stProc: - case stStaticProc: - if (MIPS_IS_STAB (&ecoff_ext.asym)) - ; - else if (ecoffsymbol (symbol)->local) - printf ("\n End+1 symbol: %-7ld Type: %s", - (AUX_GET_ISYM (bigendian, - &aux_base[ecoff_ext.asym.index]) - + sym_base), - type_to_string (abfd, aux_base, indx + 1, - bigendian)); - else - printf ("\n Type: %s", - type_to_string (abfd, aux_base, indx, bigendian)); - - break; + if ((int_rel.r_offset & 3) != 0) + { + *errmsg = _("reloc not properly aligned"); + bfd_set_error (bfd_error_bad_value); + return false; + } - default: - if (!MIPS_IS_STAB (&ecoff_ext.asym)) - printf ("\n Type: %s", - type_to_string (abfd, aux_base, indx, bigendian)); - break; - } - } - } - break; + bfd_put_32 (abfd, + (int_rel.r_vaddr - datasec->vma + datasec->output_offset + + (text_relative ? 0 : 1)), + p); } + + return true; } -/* Provided a BFD, a section and an offset into the section, calculate - and return the name of the source file and the line nearest to the - wanted location. */ +/* This is the ECOFF backend structure. The backend field of the + target vector points to this. */ -static boolean -DEFUN (ecoff_find_nearest_line, (abfd, - section, - ignore_symbols, - offset, - filename_ptr, - functionname_ptr, - retline_ptr), - bfd *abfd AND - asection *section AND - asymbol **ignore_symbols AND - bfd_vma offset AND - CONST char **filename_ptr AND - CONST char **functionname_ptr AND - unsigned int *retline_ptr) +static const struct ecoff_backend_data mips_ecoff_backend_data = { - FDR *fdr_ptr; - FDR *fdr_start; - FDR *fdr_end; - FDR *fdr_hold; - struct pdr_ext *pdr_ptr; - struct pdr_ext *pdr_end; - PDR pdr; - unsigned char *line_ptr; - unsigned char *line_end; - int lineno; - SYMR proc_sym; - - /* If we're not in the .text section, we don't have any line - numbers. */ - if (strcmp (section->name, _TEXT) != 0) - return false; + /* COFF backend structure. */ + { + (void (*) PARAMS ((bfd *,PTR,int,int,int,int,PTR))) bfd_void, /* aux_in */ + (void (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* sym_in */ + (void (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* lineno_in */ + (unsigned (*) PARAMS ((bfd *,PTR,int,int,int,int,PTR)))bfd_void,/*aux_out*/ + (unsigned (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* sym_out */ + (unsigned (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* lineno_out */ + (unsigned (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* reloc_out */ + mips_ecoff_swap_filehdr_out, mips_ecoff_swap_aouthdr_out, + mips_ecoff_swap_scnhdr_out, + FILHSZ, AOUTSZ, SCNHSZ, 0, 0, 0, 0, FILNMLEN, true, false, 4, false, 2, + mips_ecoff_swap_filehdr_in, mips_ecoff_swap_aouthdr_in, + mips_ecoff_swap_scnhdr_in, NULL, + mips_ecoff_bad_format_hook, _bfd_ecoff_set_arch_mach_hook, + _bfd_ecoff_mkobject_hook, _bfd_ecoff_styp_to_sec_flags, + _bfd_ecoff_set_alignment_hook, _bfd_ecoff_slurp_symbol_table, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL + }, + /* Supported architecture. */ + bfd_arch_mips, + /* Initial portion of armap string. */ + "__________", + /* The page boundary used to align sections in a demand-paged + executable file. E.g., 0x1000. */ + 0x1000, + /* True if the .rdata section is part of the text segment, as on the + Alpha. False if .rdata is part of the data segment, as on the + MIPS. */ + false, + /* Bitsize of constructor entries. */ + 32, + /* Reloc to use for constructor entries. */ + &mips_howto_table[MIPS_R_REFWORD], + { + /* Symbol table magic number. */ + magicSym, + /* Alignment of debugging information. E.g., 4. */ + 4, + /* Sizes of external symbolic information. */ + sizeof (struct hdr_ext), + sizeof (struct dnr_ext), + sizeof (struct pdr_ext), + sizeof (struct sym_ext), + sizeof (struct opt_ext), + sizeof (struct fdr_ext), + sizeof (struct rfd_ext), + sizeof (struct ext_ext), + /* Functions to swap in external symbolic data. */ + ecoff_swap_hdr_in, + ecoff_swap_dnr_in, + ecoff_swap_pdr_in, + ecoff_swap_sym_in, + ecoff_swap_opt_in, + ecoff_swap_fdr_in, + ecoff_swap_rfd_in, + ecoff_swap_ext_in, + _bfd_ecoff_swap_tir_in, + _bfd_ecoff_swap_rndx_in, + /* Functions to swap out external symbolic data. */ + ecoff_swap_hdr_out, + ecoff_swap_dnr_out, + ecoff_swap_pdr_out, + ecoff_swap_sym_out, + ecoff_swap_opt_out, + ecoff_swap_fdr_out, + ecoff_swap_rfd_out, + ecoff_swap_ext_out, + _bfd_ecoff_swap_tir_out, + _bfd_ecoff_swap_rndx_out, + /* Function to read in symbolic data. */ + _bfd_ecoff_slurp_symbolic_info + }, + /* External reloc size. */ + RELSZ, + /* Reloc swapping functions. */ + mips_ecoff_swap_reloc_in, + mips_ecoff_swap_reloc_out, + /* Backend reloc tweaking. */ + mips_adjust_reloc_in, + mips_adjust_reloc_out, + /* Relocate section contents while linking. */ + mips_relocate_section, + /* Do final adjustments to filehdr and aouthdr. */ + NULL, + /* Read an element from an archive at a given file position. */ + _bfd_get_elt_at_filepos +}; - /* Each file descriptor (FDR) has a memory address. Here we track - down which FDR we want. The FDR's are stored in increasing - memory order. If speed is ever important, this can become a - binary search. We must ignore FDR's with no PDR entries; they - will have the adr of the FDR before or after them. */ - fdr_start = ecoff_data (abfd)->fdr; - fdr_end = fdr_start + ecoff_data (abfd)->symbolic_header.ifdMax; - fdr_hold = (FDR *) NULL; - for (fdr_ptr = fdr_start; fdr_ptr < fdr_end; fdr_ptr++) - { - if (offset < fdr_ptr->adr) - break; - if (fdr_ptr->cpd > 0) - fdr_hold = fdr_ptr; - } - if (fdr_hold == (FDR *) NULL) - return false; - fdr_ptr = fdr_hold; - - /* Each FDR has a list of procedure descriptors (PDR). PDR's also - have an address, which is relative to the FDR address, and are - also stored in increasing memory order. */ - offset -= fdr_ptr->adr; - pdr_ptr = ecoff_data (abfd)->external_pdr + fdr_ptr->ipdFirst; - pdr_end = pdr_ptr + fdr_ptr->cpd; - ecoff_swap_pdr_in (abfd, pdr_ptr, &pdr); - if (offset < pdr.adr) - return false; - for (pdr_ptr++; pdr_ptr < pdr_end; pdr_ptr++) - { - ecoff_swap_pdr_in (abfd, pdr_ptr, &pdr); - if (offset < pdr.adr) - break; - } +/* Looking up a reloc type is MIPS specific. */ +#define _bfd_ecoff_bfd_reloc_type_lookup mips_bfd_reloc_type_lookup - /* Now we can look for the actual line number. The line numbers are - stored in a very funky format, which I won't try to describe. - Note that right here pdr_ptr and pdr hold the PDR *after* the one - we want; we need this to compute line_end. */ - line_end = ecoff_data (abfd)->line; - if (pdr_ptr == pdr_end) - line_end += fdr_ptr->cbLineOffset + fdr_ptr->cbLine; - else - line_end += fdr_ptr->cbLineOffset + pdr.cbLineOffset; - - /* Now change pdr and pdr_ptr to the one we want. */ - pdr_ptr--; - ecoff_swap_pdr_in (abfd, pdr_ptr, &pdr); - - offset -= pdr.adr; - lineno = pdr.lnLow; - line_ptr = (ecoff_data (abfd)->line - + fdr_ptr->cbLineOffset - + pdr.cbLineOffset); - while (line_ptr < line_end) - { - int delta; - int count; - - delta = *line_ptr >> 4; - if (delta >= 0x8) - delta -= 0x10; - count = (*line_ptr & 0xf) + 1; - ++line_ptr; - if (delta == -8) - { - delta = (((line_ptr[0]) & 0xff) << 8) + ((line_ptr[1]) & 0xff); - if (delta >= 0x8000) - delta -= 0x10000; - line_ptr += 2; - } - lineno += delta; - if (offset < count * 4) - break; - offset -= count * 4; - } +/* Getting relocated section contents is generic. */ +#define _bfd_ecoff_bfd_get_relocated_section_contents \ + bfd_generic_get_relocated_section_contents - /* If offset is too large, this line is not interesting. */ - if (offset > 100) - return false; +/* Handling file windows is generic. */ +#define _bfd_ecoff_get_section_contents_in_window \ + _bfd_generic_get_section_contents_in_window - *filename_ptr = ecoff_data (abfd)->ss + fdr_ptr->issBase + fdr_ptr->rss; - ecoff_swap_sym_in (abfd, - (ecoff_data (abfd)->external_sym - + fdr_ptr->isymBase - + pdr.isym), - &proc_sym); - *functionname_ptr = ecoff_data (abfd)->ss + proc_sym.iss; - *retline_ptr = lineno; - return true; -} - -static CONST bfd_coff_backend_data bfd_ecoff_std_swap_table = { - (void (*) PARAMS ((bfd *,PTR,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,PTR))) bfd_void, /* aux_out */ - (unsigned (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* sym_out */ - (unsigned (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* lineno_out */ - ecoff_swap_reloc_out, ecoff_swap_filehdr_out, ecoff_swap_aouthdr_out, - ecoff_swap_scnhdr_out, - FILHSZ, AOUTSZ, SCNHSZ, 0, 0, 0, true, - ecoff_swap_filehdr_in, ecoff_swap_aouthdr_in, ecoff_swap_scnhdr_in, - ecoff_bad_format_hook, ecoff_set_arch_mach_hook, ecoff_mkobject_hook, - styp_to_sec_flags, ecoff_make_section_hook, ecoff_set_alignment_hook, - ecoff_slurp_symbol_table -}; +/* Relaxing sections is MIPS specific. */ +#define _bfd_ecoff_bfd_relax_section mips_relax_section + +/* GC of sections is not done. */ +#define _bfd_ecoff_bfd_gc_sections bfd_generic_gc_sections -/* Routines that need to be written. */ -#define ecoff_write_object_contents (boolean (*) PARAMS ((bfd *))) bfd_false -#define ecoff_set_section_contents (boolean (*) PARAMS ((bfd *, sec_ptr, PTR, file_ptr, bfd_size_type))) bfd_false -#define ecoff_get_reloc_upper_bound (unsigned int (*) PARAMS ((bfd *, sec_ptr))) bfd_0 -#define ecoff_canonicalize_reloc (unsigned int (*) PARAMS ((bfd *, sec_ptr, arelent **, struct symbol_cache_entry **))) bfd_0 -#define ecoff_set_arch_mach (boolean (*) PARAMS ((bfd *, enum bfd_architecture, unsigned long))) bfd_false -#define ecoff_sizeof_headers (int (*) PARAMS ((bfd *, boolean))) bfd_0 - -/* get_lineno could be written for ECOFF, but it would currently only - be useful for linking ECOFF and COFF files together, which doesn't - seem too likely. */ -#define ecoff_get_lineno (struct lineno_cache_entry *(*)()) bfd_nullvoidptr - -#define ecoff_core_file_failing_command _bfd_dummy_core_file_failing_command -#define ecoff_core_file_failing_signal _bfd_dummy_core_file_failing_signal -#define ecoff_core_file_matches_executable_p _bfd_dummy_core_file_matches_executable_p -#define ecoff_slurp_armap bfd_slurp_coff_armap -#define ecoff_slurp_extended_name_table _bfd_slurp_extended_name_table -#define ecoff_write_armap coff_write_armap -#define ecoff_truncate_arname bfd_dont_truncate_arname -#define ecoff_openr_next_archived_file bfd_generic_openr_next_archived_file -#define ecoff_generic_stat_arch_elt bfd_generic_stat_arch_elt -#define ecoff_get_section_contents bfd_generic_get_section_contents -#define ecoff_close_and_cleanup bfd_generic_close_and_cleanup - -#define ecoff_bfd_debug_info_start bfd_void -#define ecoff_bfd_debug_info_end bfd_void -#define ecoff_bfd_debug_info_accumulate \ - (void (*) PARAMS ((bfd *, struct sec *))) bfd_void -#define ecoff_bfd_get_relocated_section_contents bfd_generic_get_relocated_section_contents -#define ecoff_bfd_relax_section bfd_generic_relax_section - -bfd_target ecoff_little_vec = +extern const bfd_target ecoff_big_vec; + +const bfd_target ecoff_little_vec = { "ecoff-littlemips", /* name */ bfd_target_ecoff_flavour, - false, /* data byte order is little */ - false, /* header byte order is little */ + 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 | DYNAMIC | WP_TEXT), + HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED), - (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC), /* sect - flags */ + (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_CODE | SEC_DATA), 0, /* leading underscore */ - '/', /* ar_pad_char */ + ' ', /* ar_pad_char */ 15, /* ar_max_namelen */ - 3, /* minimum alignment power */ - _do_getl64, _do_putl64, _do_getl32, _do_putl32, _do_getl16, _do_putl16, /* data */ - _do_getl64, _do_putl64, _do_getl32, _do_putl32, _do_getl16, _do_putl16, /* hdrs */ + 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_generic_archive_p, _bfd_dummy_target}, - {bfd_false, ecoff_mkobject, bfd_false, /* bfd_set_format */ - bfd_false}, - {bfd_false, ecoff_write_object_contents, bfd_false, bfd_false}, - JUMP_TABLE (ecoff), - 0, 0, - (PTR) &bfd_ecoff_std_swap_table + _bfd_ecoff_archive_p, _bfd_dummy_target}, + {bfd_false, _bfd_ecoff_mkobject, /* bfd_set_format */ + _bfd_generic_mkarchive, bfd_false}, + {bfd_false, _bfd_ecoff_write_object_contents, /* bfd_write_contents */ + _bfd_write_archive_contents, bfd_false}, + + BFD_JUMP_TABLE_GENERIC (_bfd_ecoff), + BFD_JUMP_TABLE_COPY (_bfd_ecoff), + BFD_JUMP_TABLE_CORE (_bfd_nocore), + BFD_JUMP_TABLE_ARCHIVE (_bfd_ecoff), + BFD_JUMP_TABLE_SYMBOLS (_bfd_ecoff), + BFD_JUMP_TABLE_RELOCS (_bfd_ecoff), + BFD_JUMP_TABLE_WRITE (_bfd_ecoff), + BFD_JUMP_TABLE_LINK (_bfd_ecoff), + BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic), + + & ecoff_big_vec, + + (PTR) &mips_ecoff_backend_data }; -bfd_target ecoff_big_vec = +const bfd_target ecoff_big_vec = { "ecoff-bigmips", /* name */ bfd_target_ecoff_flavour, - true, /* data byte order is big */ - true, /* header byte order is big */ + 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 | DYNAMIC | WP_TEXT), + HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED), - (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC), /* sect flags */ + (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_CODE | SEC_DATA), 0, /* leading underscore */ ' ', /* ar_pad_char */ - 16, /* ar_max_namelen */ - 3, /* minimum alignment power */ - _do_getb64, _do_putb64, _do_getb32, _do_putb32, _do_getb16, _do_putb16, - _do_getb64, _do_putb64, _do_getb32, _do_putb32, _do_getb16, _do_putb16, + 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_generic_archive_p, _bfd_dummy_target}, - {bfd_false, ecoff_mkobject, bfd_false, /* bfd_set_format */ - bfd_false}, - {bfd_false, ecoff_write_object_contents, /* bfd_write_contents */ - bfd_false, bfd_false}, - JUMP_TABLE(ecoff), - 0, 0, - (PTR) &bfd_ecoff_std_swap_table - /* Note that there is another bfd_target just above this one. If - you are adding initializers here, you should be adding them there - as well. */ + _bfd_ecoff_archive_p, _bfd_dummy_target}, + {bfd_false, _bfd_ecoff_mkobject, /* bfd_set_format */ + _bfd_generic_mkarchive, bfd_false}, + {bfd_false, _bfd_ecoff_write_object_contents, /* bfd_write_contents */ + _bfd_write_archive_contents, bfd_false}, + + BFD_JUMP_TABLE_GENERIC (_bfd_ecoff), + BFD_JUMP_TABLE_COPY (_bfd_ecoff), + BFD_JUMP_TABLE_CORE (_bfd_nocore), + BFD_JUMP_TABLE_ARCHIVE (_bfd_ecoff), + BFD_JUMP_TABLE_SYMBOLS (_bfd_ecoff), + BFD_JUMP_TABLE_RELOCS (_bfd_ecoff), + BFD_JUMP_TABLE_WRITE (_bfd_ecoff), + BFD_JUMP_TABLE_LINK (_bfd_ecoff), + BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic), + + & ecoff_little_vec, + + (PTR) &mips_ecoff_backend_data +}; + +const bfd_target ecoff_biglittle_vec = +{ + "ecoff-biglittlemips", /* name */ + bfd_target_ecoff_flavour, + BFD_ENDIAN_LITTLE, /* data byte order is little */ + BFD_ENDIAN_BIG, /* header byte order is big */ + + (HAS_RELOC | EXEC_P | /* object flags */ + HAS_LINENO | HAS_DEBUG | + HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED), + + (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_CODE | SEC_DATA), + 0, /* leading underscore */ + ' ', /* ar_pad_char */ + 15, /* ar_max_namelen */ + bfd_getl64, bfd_getl_signed_64, bfd_putl64, + bfd_getl32, bfd_getl_signed_32, bfd_putl32, + bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* data */ + bfd_getb64, bfd_getb_signed_64, bfd_putb64, + bfd_getb32, bfd_getb_signed_32, bfd_putb32, + bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* hdrs */ + + {_bfd_dummy_target, coff_object_p, /* bfd_check_format */ + _bfd_ecoff_archive_p, _bfd_dummy_target}, + {bfd_false, _bfd_ecoff_mkobject, /* bfd_set_format */ + _bfd_generic_mkarchive, bfd_false}, + {bfd_false, _bfd_ecoff_write_object_contents, /* bfd_write_contents */ + _bfd_write_archive_contents, bfd_false}, + + BFD_JUMP_TABLE_GENERIC (_bfd_ecoff), + BFD_JUMP_TABLE_COPY (_bfd_ecoff), + BFD_JUMP_TABLE_CORE (_bfd_nocore), + BFD_JUMP_TABLE_ARCHIVE (_bfd_ecoff), + BFD_JUMP_TABLE_SYMBOLS (_bfd_ecoff), + BFD_JUMP_TABLE_RELOCS (_bfd_ecoff), + BFD_JUMP_TABLE_WRITE (_bfd_ecoff), + BFD_JUMP_TABLE_LINK (_bfd_ecoff), + BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic), + + NULL, + + (PTR) &mips_ecoff_backend_data };