binutils-gdb/bfd/elf64-mips.c

1973 lines
58 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* MIPS-specific support for 64-bit ELF
Copyright 1996 Free Software Foundation, Inc.
Ian Lance Taylor, Cygnus Support
This file is part of BFD, the Binary File Descriptor library.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/* This file supports the 64-bit MIPS ELF ABI.
The MIPS 64-bit ELF ABI uses an unusual reloc format. This file
overrides the usual ELF reloc handling, and handles reading and
writing the relocations here. */
#include "bfd.h"
#include "sysdep.h"
#include "libbfd.h"
#include "bfdlink.h"
#include "genlink.h"
#include "elf-bfd.h"
#include "elf/mips.h"
/* Get the ECOFF swapping routines. The 64-bit ABI is not supposed to
use ECOFF. However, we support it anyhow for an easier changeover. */
#include "coff/sym.h"
#include "coff/symconst.h"
#include "coff/internal.h"
#include "coff/ecoff.h"
/* The 64 bit versions of the mdebug data structures are in alpha.h. */
#include "coff/alpha.h"
#define ECOFF_64
#include "ecoffswap.h"
static void mips_elf64_swap_reloc_in
PARAMS ((bfd *, const Elf64_Mips_External_Rel *,
Elf64_Mips_Internal_Rel *));
static void mips_elf64_swap_reloca_in
PARAMS ((bfd *, const Elf64_Mips_External_Rela *,
Elf64_Mips_Internal_Rela *));
#if 0
static void mips_elf64_swap_reloc_out
PARAMS ((bfd *, const Elf64_Mips_Internal_Rel *,
Elf64_Mips_External_Rel *));
#endif
static void mips_elf64_swap_reloca_out
PARAMS ((bfd *, const Elf64_Mips_Internal_Rela *,
Elf64_Mips_External_Rela *));
static reloc_howto_type *mips_elf64_reloc_type_lookup
PARAMS ((bfd *, bfd_reloc_code_real_type));
static long mips_elf64_get_reloc_upper_bound PARAMS ((bfd *, asection *));
static boolean mips_elf64_slurp_one_reloc_table
PARAMS ((bfd *, asection *, asymbol **, const Elf_Internal_Shdr *));
static boolean mips_elf64_slurp_reloc_table
PARAMS ((bfd *, asection *, asymbol **));
static void mips_elf64_write_relocs PARAMS ((bfd *, asection *, PTR));
static boolean mips_elf64_section_from_shdr
PARAMS ((bfd *, Elf_Internal_Shdr *, char *));
static boolean mips_elf64_section_processing
PARAMS ((bfd *, Elf_Internal_Shdr *));
/* The relocation types. */
enum mips_elf64_reloc_type
{
R_MIPS_NONE = 0,
R_MIPS_16 = 1,
R_MIPS_32 = 2,
R_MIPS_ADD = 2,
R_MIPS_REL32 = 3,
R_MIPS_REL = 3,
R_MIPS_26 = 4,
R_MIPS_HI16 = 5,
R_MIPS_LO16 = 6,
R_MIPS_GPREL16 = 7,
R_MIPS_GPREL = 7,
R_MIPS_LITERAL = 8,
R_MIPS_GOT16 = 9,
R_MIPS_GOT = 9,
R_MIPS_PC16 = 10,
R_MIPS_CALL16 = 11,
R_MIPS_CALL = 11,
R_MIPS_GPREL32 = 12,
R_MIPS_SHIFT5 = 16,
R_MIPS_SHIFT6 = 17,
R_MIPS_64 = 18,
R_MIPS_GOT_DISP = 19,
R_MIPS_GOT_PAGE = 20,
R_MIPS_GOT_OFST = 21,
R_MIPS_GOT_HI16 = 22,
R_MIPS_GOT_LO16 = 23,
R_MIPS_SUB = 24,
R_MIPS_INSERT_A = 25,
R_MIPS_INSERT_B = 26,
R_MIPS_DELETE = 27,
R_MIPS_HIGHER = 28,
R_MIPS_HIGHEST = 29,
R_MIPS_CALL_HI16 = 30,
R_MIPS_CALL_LO16 = 31,
R_MIPS_SCN_DISP = 32,
R_MIPS_REL16 = 33,
R_MIPS_ADD_IMMEDIATE = 34,
R_MIPS_PJUMP = 35,
R_MIPS_RELGOT = 36
};
/* In case we're on a 32-bit machine, construct a 64-bit "-1" value
from smaller values. Start with zero, widen, *then* decrement. */
#define MINUS_ONE (((bfd_vma)0) - 1)
/* The relocation table used for SHT_REL sections. */
static reloc_howto_type mips_elf64_howto_table_rel[] =
{
/* No relocation. */
HOWTO (R_MIPS_NONE, /* type */
0, /* rightshift */
0, /* size (0 = byte, 1 = short, 2 = long) */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_NONE", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */
/* 16 bit relocation. */
HOWTO (R_MIPS_16, /* type */
0, /* rightshift */
1, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_16", /* name */
true, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* 32 bit relocation. */
HOWTO (R_MIPS_32, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_32", /* name */
true, /* partial_inplace */
0xffffffff, /* src_mask */
0xffffffff, /* dst_mask */
false), /* pcrel_offset */
/* 32 bit symbol relative relocation. */
HOWTO (R_MIPS_REL32, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_REL32", /* name */
true, /* partial_inplace */
0xffffffff, /* src_mask */
0xffffffff, /* dst_mask */
false), /* pcrel_offset */
/* 26 bit branch address. */
HOWTO (R_MIPS_26, /* type */
2, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
26, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
/* This needs complex overflow
detection, because the upper four
bits must match the PC. */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_26", /* name */
true, /* partial_inplace */
0x3ffffff, /* src_mask */
0x3ffffff, /* dst_mask */
false), /* pcrel_offset */
/* High 16 bits of symbol value. */
HOWTO (R_MIPS_HI16, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
_bfd_mips_elf_hi16_reloc, /* special_function */
"R_MIPS_HI16", /* name */
true, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* Low 16 bits of symbol value. */
HOWTO (R_MIPS_LO16, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
_bfd_mips_elf_lo16_reloc, /* special_function */
"R_MIPS_LO16", /* name */
true, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* GP relative reference. */
HOWTO (R_MIPS_GPREL16, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
_bfd_mips_elf_gprel16_reloc, /* special_function */
"R_MIPS_GPREL16", /* name */
true, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* Reference to literal section. */
HOWTO (R_MIPS_LITERAL, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
_bfd_mips_elf_gprel16_reloc, /* special_function */
"R_MIPS_LITERAL", /* name */
true, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* Reference to global offset table. */
HOWTO (R_MIPS_GOT16, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
_bfd_mips_elf_got16_reloc, /* special_function */
"R_MIPS_GOT16", /* name */
false, /* partial_inplace */
0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* 16 bit PC relative reference. */
HOWTO (R_MIPS_PC16, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_PC16", /* name */
true, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* 16 bit call through global offset table. */
/* FIXME: This is not handled correctly. */
HOWTO (R_MIPS_CALL16, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_CALL16", /* name */
false, /* partial_inplace */
0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* 32 bit GP relative reference. */
HOWTO (R_MIPS_GPREL32, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
_bfd_mips_elf_gprel32_reloc, /* special_function */
"R_MIPS_GPREL32", /* name */
true, /* partial_inplace */
0xffffffff, /* src_mask */
0xffffffff, /* dst_mask */
false), /* pcrel_offset */
{ 13 },
{ 14 },
{ 15 },
/* A 5 bit shift field. */
HOWTO (R_MIPS_SHIFT5, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
5, /* bitsize */
false, /* pc_relative */
6, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_SHIFT5", /* name */
true, /* partial_inplace */
0x000007c0, /* src_mask */
0x000007c0, /* dst_mask */
false), /* pcrel_offset */
/* A 6 bit shift field. */
/* FIXME: This is not handled correctly; a special function is
needed to put the most significant bit in the right place. */
HOWTO (R_MIPS_SHIFT6, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
6, /* bitsize */
false, /* pc_relative */
6, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_SHIFT6", /* name */
true, /* partial_inplace */
0x000007c4, /* src_mask */
0x000007c4, /* dst_mask */
false), /* pcrel_offset */
/* 64 bit relocation. */
HOWTO (R_MIPS_64, /* type */
0, /* rightshift */
4, /* size (0 = byte, 1 = short, 2 = long) */
64, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_64", /* name */
true, /* partial_inplace */
MINUS_ONE, /* src_mask */
MINUS_ONE, /* dst_mask */
false), /* pcrel_offset */
/* Displacement in the global offset table. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_GOT_DISP, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_GOT_DISP", /* name */
true, /* partial_inplace */
0x0000ffff, /* src_mask */
0x0000ffff, /* dst_mask */
false), /* pcrel_offset */
/* Displacement to page pointer in the global offset table. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_GOT_PAGE, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_GOT_PAGE", /* name */
true, /* partial_inplace */
0x0000ffff, /* src_mask */
0x0000ffff, /* dst_mask */
false), /* pcrel_offset */
/* Offset from page pointer in the global offset table. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_GOT_OFST, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_GOT_OFST", /* name */
true, /* partial_inplace */
0x0000ffff, /* src_mask */
0x0000ffff, /* dst_mask */
false), /* pcrel_offset */
/* High 16 bits of displacement in global offset table. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_GOT_HI16, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_GOT_HI16", /* name */
true, /* partial_inplace */
0x0000ffff, /* src_mask */
0x0000ffff, /* dst_mask */
false), /* pcrel_offset */
/* Low 16 bits of displacement in global offset table. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_GOT_LO16, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_GOT_LO16", /* name */
true, /* partial_inplace */
0x0000ffff, /* src_mask */
0x0000ffff, /* dst_mask */
false), /* pcrel_offset */
/* 64 bit substraction. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_SUB, /* type */
0, /* rightshift */
4, /* size (0 = byte, 1 = short, 2 = long) */
64, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_SUB", /* name */
true, /* partial_inplace */
MINUS_ONE, /* src_mask */
MINUS_ONE, /* dst_mask */
false), /* pcrel_offset */
/* Insert the addend as an instruction. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_INSERT_A, /* type */
0, /* rightshift */
0, /* size (0 = byte, 1 = short, 2 = long) */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_INSERT_A", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */
/* Insert the addend as an instruction, and change all relocations
to refer to the old instruction at the address. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_INSERT_B, /* type */
0, /* rightshift */
0, /* size (0 = byte, 1 = short, 2 = long) */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_INSERT_B", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */
/* Delete a 32 bit instruction. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_DELETE, /* type */
0, /* rightshift */
0, /* size (0 = byte, 1 = short, 2 = long) */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_DELETE", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */
/* Get the higher value of a 64 bit addend. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_HIGHER, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_HIGHER", /* name */
true, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* Get the highest value of a 64 bit addend. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_HIGHEST, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_HIGHEST", /* name */
true, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* High 16 bits of displacement in global offset table. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_CALL_HI16, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_CALL_HI16", /* name */
true, /* partial_inplace */
0x0000ffff, /* src_mask */
0x0000ffff, /* dst_mask */
false), /* pcrel_offset */
/* Low 16 bits of displacement in global offset table. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_CALL_LO16, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_CALL_LO16", /* name */
true, /* partial_inplace */
0x0000ffff, /* src_mask */
0x0000ffff, /* dst_mask */
false), /* pcrel_offset */
/* I'm not sure what the remaining relocs are, but they are defined
on Irix 6. */
HOWTO (R_MIPS_SCN_DISP, /* type */
0, /* rightshift */
0, /* size (0 = byte, 1 = short, 2 = long) */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_SCN_DISP", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */
HOWTO (R_MIPS_REL16, /* type */
0, /* rightshift */
0, /* size (0 = byte, 1 = short, 2 = long) */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_REL16", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */
HOWTO (R_MIPS_ADD_IMMEDIATE, /* type */
0, /* rightshift */
0, /* size (0 = byte, 1 = short, 2 = long) */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_ADD_IMMEDIATE", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */
HOWTO (R_MIPS_PJUMP, /* type */
0, /* rightshift */
0, /* size (0 = byte, 1 = short, 2 = long) */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_PJUMP", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */
HOWTO (R_MIPS_RELGOT, /* type */
0, /* rightshift */
0, /* size (0 = byte, 1 = short, 2 = long) */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_RELGOT", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false) /* pcrel_offset */
};
/* The relocation table used for SHT_RELA sections. */
static reloc_howto_type mips_elf64_howto_table_rela[] =
{
/* No relocation. */
HOWTO (R_MIPS_NONE, /* type */
0, /* rightshift */
0, /* size (0 = byte, 1 = short, 2 = long) */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_NONE", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */
/* 16 bit relocation. */
HOWTO (R_MIPS_16, /* type */
0, /* rightshift */
1, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_16", /* name */
true, /* partial_inplace */
0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* 32 bit relocation. */
HOWTO (R_MIPS_32, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_32", /* name */
true, /* partial_inplace */
0, /* src_mask */
0xffffffff, /* dst_mask */
false), /* pcrel_offset */
/* 32 bit symbol relative relocation. */
HOWTO (R_MIPS_REL32, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_REL32", /* name */
true, /* partial_inplace */
0, /* src_mask */
0xffffffff, /* dst_mask */
false), /* pcrel_offset */
/* 26 bit branch address. */
HOWTO (R_MIPS_26, /* type */
2, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
26, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
/* This needs complex overflow
detection, because the upper four
bits must match the PC. */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_26", /* name */
true, /* partial_inplace */
0, /* src_mask */
0x3ffffff, /* dst_mask */
false), /* pcrel_offset */
/* High 16 bits of symbol value. */
HOWTO (R_MIPS_HI16, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_HI16", /* name */
true, /* partial_inplace */
0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* Low 16 bits of symbol value. */
HOWTO (R_MIPS_LO16, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_LO16", /* name */
true, /* partial_inplace */
0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* GP relative reference. */
HOWTO (R_MIPS_GPREL16, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
_bfd_mips_elf_gprel16_reloc, /* special_function */
"R_MIPS_GPREL16", /* name */
true, /* partial_inplace */
0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* Reference to literal section. */
HOWTO (R_MIPS_LITERAL, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
_bfd_mips_elf_gprel16_reloc, /* special_function */
"R_MIPS_LITERAL", /* name */
true, /* partial_inplace */
0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* Reference to global offset table. */
/* FIXME: This is not handled correctly. */
HOWTO (R_MIPS_GOT16, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_GOT16", /* name */
false, /* partial_inplace */
0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* 16 bit PC relative reference. */
HOWTO (R_MIPS_PC16, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_PC16", /* name */
true, /* partial_inplace */
0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* 16 bit call through global offset table. */
/* FIXME: This is not handled correctly. */
HOWTO (R_MIPS_CALL16, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_CALL16", /* name */
false, /* partial_inplace */
0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* 32 bit GP relative reference. */
HOWTO (R_MIPS_GPREL32, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
32, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
_bfd_mips_elf_gprel32_reloc, /* special_function */
"R_MIPS_GPREL32", /* name */
true, /* partial_inplace */
0, /* src_mask */
0xffffffff, /* dst_mask */
false), /* pcrel_offset */
{ 13 },
{ 14 },
{ 15 },
/* A 5 bit shift field. */
HOWTO (R_MIPS_SHIFT5, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
5, /* bitsize */
false, /* pc_relative */
6, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_SHIFT5", /* name */
true, /* partial_inplace */
0, /* src_mask */
0x000007c0, /* dst_mask */
false), /* pcrel_offset */
/* A 6 bit shift field. */
/* FIXME: This is not handled correctly; a special function is
needed to put the most significant bit in the right place. */
HOWTO (R_MIPS_SHIFT6, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
6, /* bitsize */
false, /* pc_relative */
6, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_SHIFT6", /* name */
true, /* partial_inplace */
0, /* src_mask */
0x000007c4, /* dst_mask */
false), /* pcrel_offset */
/* 64 bit relocation. */
HOWTO (R_MIPS_64, /* type */
0, /* rightshift */
4, /* size (0 = byte, 1 = short, 2 = long) */
64, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_64", /* name */
true, /* partial_inplace */
0, /* src_mask */
MINUS_ONE, /* dst_mask */
false), /* pcrel_offset */
/* Displacement in the global offset table. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_GOT_DISP, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_GOT_DISP", /* name */
true, /* partial_inplace */
0, /* src_mask */
0x0000ffff, /* dst_mask */
false), /* pcrel_offset */
/* Displacement to page pointer in the global offset table. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_GOT_PAGE, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_GOT_PAGE", /* name */
true, /* partial_inplace */
0, /* src_mask */
0x0000ffff, /* dst_mask */
false), /* pcrel_offset */
/* Offset from page pointer in the global offset table. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_GOT_OFST, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_GOT_OFST", /* name */
true, /* partial_inplace */
0, /* src_mask */
0x0000ffff, /* dst_mask */
false), /* pcrel_offset */
/* High 16 bits of displacement in global offset table. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_GOT_HI16, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_GOT_HI16", /* name */
true, /* partial_inplace */
0, /* src_mask */
0x0000ffff, /* dst_mask */
false), /* pcrel_offset */
/* Low 16 bits of displacement in global offset table. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_GOT_LO16, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_GOT_LO16", /* name */
true, /* partial_inplace */
0, /* src_mask */
0x0000ffff, /* dst_mask */
false), /* pcrel_offset */
/* 64 bit substraction. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_SUB, /* type */
0, /* rightshift */
4, /* size (0 = byte, 1 = short, 2 = long) */
64, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_SUB", /* name */
true, /* partial_inplace */
0, /* src_mask */
MINUS_ONE, /* dst_mask */
false), /* pcrel_offset */
/* Insert the addend as an instruction. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_INSERT_A, /* type */
0, /* rightshift */
0, /* size (0 = byte, 1 = short, 2 = long) */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_INSERT_A", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */
/* Insert the addend as an instruction, and change all relocations
to refer to the old instruction at the address. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_INSERT_B, /* type */
0, /* rightshift */
0, /* size (0 = byte, 1 = short, 2 = long) */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_INSERT_B", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */
/* Delete a 32 bit instruction. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_DELETE, /* type */
0, /* rightshift */
0, /* size (0 = byte, 1 = short, 2 = long) */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_DELETE", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */
/* Get the higher value of a 64 bit addend. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_HIGHER, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_HIGHER", /* name */
true, /* partial_inplace */
0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* Get the highest value of a 64 bit addend. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_HIGHEST, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_HIGHEST", /* name */
true, /* partial_inplace */
0, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
/* High 16 bits of displacement in global offset table. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_CALL_HI16, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_CALL_HI16", /* name */
true, /* partial_inplace */
0, /* src_mask */
0x0000ffff, /* dst_mask */
false), /* pcrel_offset */
/* Low 16 bits of displacement in global offset table. */
/* FIXME: Not handled correctly. */
HOWTO (R_MIPS_CALL_LO16, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_CALL_LO16", /* name */
true, /* partial_inplace */
0, /* src_mask */
0x0000ffff, /* dst_mask */
false), /* pcrel_offset */
/* I'm not sure what the remaining relocs are, but they are defined
on Irix 6. */
HOWTO (R_MIPS_SCN_DISP, /* type */
0, /* rightshift */
0, /* size (0 = byte, 1 = short, 2 = long) */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_SCN_DISP", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */
HOWTO (R_MIPS_REL16, /* type */
0, /* rightshift */
0, /* size (0 = byte, 1 = short, 2 = long) */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_REL16", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */
HOWTO (R_MIPS_ADD_IMMEDIATE, /* type */
0, /* rightshift */
0, /* size (0 = byte, 1 = short, 2 = long) */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_ADD_IMMEDIATE", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */
HOWTO (R_MIPS_PJUMP, /* type */
0, /* rightshift */
0, /* size (0 = byte, 1 = short, 2 = long) */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_PJUMP", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false), /* pcrel_offset */
HOWTO (R_MIPS_RELGOT, /* type */
0, /* rightshift */
0, /* size (0 = byte, 1 = short, 2 = long) */
0, /* bitsize */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MIPS_RELGOT", /* name */
false, /* partial_inplace */
0, /* src_mask */
0, /* dst_mask */
false) /* pcrel_offset */
};
/* Swap in a MIPS 64-bit Rel reloc. */
static void
mips_elf64_swap_reloc_in (abfd, src, dst)
bfd *abfd;
const Elf64_Mips_External_Rel *src;
Elf64_Mips_Internal_Rel *dst;
{
dst->r_offset = bfd_h_get_64 (abfd, (bfd_byte *) src->r_offset);
dst->r_sym = bfd_h_get_32 (abfd, (bfd_byte *) src->r_sym);
dst->r_ssym = bfd_h_get_8 (abfd, (bfd_byte *) src->r_ssym);
dst->r_type3 = bfd_h_get_8 (abfd, (bfd_byte *) src->r_type3);
dst->r_type2 = bfd_h_get_8 (abfd, (bfd_byte *) src->r_type2);
dst->r_type = bfd_h_get_8 (abfd, (bfd_byte *) src->r_type);
}
/* Swap in a MIPS 64-bit Rela reloc. */
static void
mips_elf64_swap_reloca_in (abfd, src, dst)
bfd *abfd;
const Elf64_Mips_External_Rela *src;
Elf64_Mips_Internal_Rela *dst;
{
dst->r_offset = bfd_h_get_64 (abfd, (bfd_byte *) src->r_offset);
dst->r_sym = bfd_h_get_32 (abfd, (bfd_byte *) src->r_sym);
dst->r_ssym = bfd_h_get_8 (abfd, (bfd_byte *) src->r_ssym);
dst->r_type3 = bfd_h_get_8 (abfd, (bfd_byte *) src->r_type3);
dst->r_type2 = bfd_h_get_8 (abfd, (bfd_byte *) src->r_type2);
dst->r_type = bfd_h_get_8 (abfd, (bfd_byte *) src->r_type);
dst->r_addend = bfd_h_get_64 (abfd, (bfd_byte *) src->r_addend);
}
#if 0
/* This is not currently used. */
/* Swap out a MIPS 64-bit Rel reloc. */
static void
mips_elf64_swap_reloc_out (abfd, src, dst)
bfd *abfd;
const Elf64_Mips_Internal_Rel *src;
Elf64_Mips_External_Rel *dst;
{
bfd_h_put_64 (abfd, src->r_offset, (bfd_byte *) dst->r_offset);
bfd_h_put_32 (abfd, src->r_sym, (bfd_byte *) dst->r_sym);
bfd_h_put_8 (abfd, src->r_ssym, (bfd_byte *) dst->r_ssym);
bfd_h_put_8 (abfd, src->r_type3, (bfd_byte *) dst->r_type3);
bfd_h_put_8 (abfd, src->r_type2, (bfd_byte *) dst->r_type2);
bfd_h_put_8 (abfd, src->r_type, (bfd_byte *) dst->r_type);
}
#endif /* 0 */
/* Swap out a MIPS 64-bit Rela reloc. */
static void
mips_elf64_swap_reloca_out (abfd, src, dst)
bfd *abfd;
const Elf64_Mips_Internal_Rela *src;
Elf64_Mips_External_Rela *dst;
{
bfd_h_put_64 (abfd, src->r_offset, (bfd_byte *) dst->r_offset);
bfd_h_put_32 (abfd, src->r_sym, (bfd_byte *) dst->r_sym);
bfd_h_put_8 (abfd, src->r_ssym, (bfd_byte *) dst->r_ssym);
bfd_h_put_8 (abfd, src->r_type3, (bfd_byte *) dst->r_type3);
bfd_h_put_8 (abfd, src->r_type2, (bfd_byte *) dst->r_type2);
bfd_h_put_8 (abfd, src->r_type, (bfd_byte *) dst->r_type);
bfd_h_put_64 (abfd, src->r_offset, (bfd_byte *) dst->r_offset);
}
/* A mapping from BFD reloc types to MIPS ELF reloc types. */
struct elf_reloc_map
{
bfd_reloc_code_real_type bfd_reloc_val;
enum mips_elf64_reloc_type elf_reloc_val;
};
static CONST struct elf_reloc_map mips_reloc_map[] =
{
{ BFD_RELOC_NONE, R_MIPS_NONE, },
{ BFD_RELOC_16, R_MIPS_16 },
{ BFD_RELOC_32, R_MIPS_32 },
{ BFD_RELOC_64, R_MIPS_64 },
{ BFD_RELOC_CTOR, R_MIPS_64 },
{ BFD_RELOC_32_PCREL, R_MIPS_REL32 },
{ BFD_RELOC_MIPS_JMP, R_MIPS_26 },
{ BFD_RELOC_HI16_S, R_MIPS_HI16 },
{ BFD_RELOC_LO16, R_MIPS_LO16 },
{ BFD_RELOC_MIPS_GPREL, R_MIPS_GPREL16 },
{ BFD_RELOC_MIPS_LITERAL, R_MIPS_LITERAL },
{ BFD_RELOC_MIPS_GOT16, R_MIPS_GOT16 },
{ BFD_RELOC_16_PCREL, R_MIPS_PC16 },
{ BFD_RELOC_MIPS_CALL16, R_MIPS_CALL16 },
{ BFD_RELOC_MIPS_GPREL32, R_MIPS_GPREL32 },
{ BFD_RELOC_MIPS_GOT_HI16, R_MIPS_GOT_HI16 },
{ BFD_RELOC_MIPS_GOT_LO16, R_MIPS_GOT_LO16 },
{ BFD_RELOC_MIPS_CALL_HI16, R_MIPS_CALL_HI16 },
{ BFD_RELOC_MIPS_CALL_LO16, R_MIPS_CALL_LO16 }
};
/* Given a BFD reloc type, return a howto structure. */
static reloc_howto_type *
mips_elf64_reloc_type_lookup (abfd, code)
bfd *abfd;
bfd_reloc_code_real_type code;
{
unsigned int i;
for (i = 0; i < sizeof (mips_reloc_map) / sizeof (struct elf_reloc_map); i++)
{
if (mips_reloc_map[i].bfd_reloc_val == code)
{
int v;
v = (int) mips_reloc_map[i].elf_reloc_val;
return &mips_elf64_howto_table_rel[v];
}
}
return NULL;
}
/* Since each entry in an SHT_REL or SHT_RELA section can represent up
to three relocs, we must tell the user to allocate more space. */
static long
mips_elf64_get_reloc_upper_bound (abfd, sec)
bfd *abfd;
asection *sec;
{
return (sec->reloc_count * 3 + 1) * sizeof (arelent *);
}
/* Read the relocations from one reloc section. */
static boolean
mips_elf64_slurp_one_reloc_table (abfd, asect, symbols, rel_hdr)
bfd *abfd;
asection *asect;
asymbol **symbols;
const Elf_Internal_Shdr *rel_hdr;
{
PTR allocated = NULL;
bfd_byte *native_relocs;
arelent *relents;
arelent *relent;
unsigned int count;
unsigned int i;
int entsize;
reloc_howto_type *howto_table;
allocated = (PTR) bfd_malloc (rel_hdr->sh_size);
if (allocated == NULL)
goto error_return;
if (bfd_seek (abfd, rel_hdr->sh_offset, SEEK_SET) != 0
|| (bfd_read (allocated, 1, rel_hdr->sh_size, abfd) != rel_hdr->sh_size))
goto error_return;
native_relocs = (bfd_byte *) allocated;
relents = asect->relocation + asect->reloc_count;
entsize = rel_hdr->sh_entsize;
BFD_ASSERT (entsize == sizeof (Elf64_Mips_External_Rel)
|| entsize == sizeof (Elf64_Mips_External_Rela));
count = rel_hdr->sh_size / entsize;
if (entsize == sizeof (Elf64_Mips_External_Rel))
howto_table = mips_elf64_howto_table_rel;
else
howto_table = mips_elf64_howto_table_rela;
relent = relents;
for (i = 0; i < count; i++, native_relocs += entsize)
{
Elf64_Mips_Internal_Rela rela;
boolean used_sym, used_ssym;
int ir;
if (entsize == sizeof (Elf64_Mips_External_Rela))
mips_elf64_swap_reloca_in (abfd,
(Elf64_Mips_External_Rela *) native_relocs,
&rela);
else
{
Elf64_Mips_Internal_Rel rel;
mips_elf64_swap_reloc_in (abfd,
(Elf64_Mips_External_Rel *) native_relocs,
&rel);
rela.r_offset = rel.r_offset;
rela.r_sym = rel.r_sym;
rela.r_ssym = rel.r_ssym;
rela.r_type3 = rel.r_type3;
rela.r_type2 = rel.r_type2;
rela.r_type = rel.r_type;
rela.r_addend = 0;
}
/* Each entry represents up to three actual relocations. */
used_sym = false;
used_ssym = false;
for (ir = 0; ir < 3; ir++)
{
enum mips_elf64_reloc_type type;
switch (ir)
{
default:
abort ();
case 0:
type = (enum mips_elf64_reloc_type) rela.r_type;
break;
case 1:
type = (enum mips_elf64_reloc_type) rela.r_type2;
break;
case 2:
type = (enum mips_elf64_reloc_type) rela.r_type3;
break;
}
if (type == R_MIPS_NONE)
{
/* There are no more relocations in this entry. If this
is the first entry, we need to generate a dummy
relocation so that the generic linker knows that
there has been a break in the sequence of relocations
applying to a particular address. */
if (ir == 0)
{
relent->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0)
relent->address = rela.r_offset;
else
relent->address = rela.r_offset - asect->vma;
relent->addend = 0;
relent->howto = &howto_table[(int) R_MIPS_NONE];
++relent;
}
break;
}
/* Some types require symbols, whereas some do not. */
switch (type)
{
case R_MIPS_NONE:
case R_MIPS_LITERAL:
case R_MIPS_INSERT_A:
case R_MIPS_INSERT_B:
case R_MIPS_DELETE:
relent->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
break;
default:
if (! used_sym)
{
if (rela.r_sym == 0)
relent->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
else
{
asymbol **ps, *s;
ps = symbols + rela.r_sym - 1;
s = *ps;
if ((s->flags & BSF_SECTION_SYM) == 0)
relent->sym_ptr_ptr = ps;
else
relent->sym_ptr_ptr = s->section->symbol_ptr_ptr;
}
used_sym = true;
}
else if (! used_ssym)
{
switch (rela.r_ssym)
{
case RSS_UNDEF:
relent->sym_ptr_ptr =
bfd_abs_section_ptr->symbol_ptr_ptr;
break;
case RSS_GP:
case RSS_GP0:
case RSS_LOC:
/* FIXME: I think these need to be handled using
special howto structures. */
BFD_ASSERT (0);
break;
default:
BFD_ASSERT (0);
break;
}
used_ssym = true;
}
else
relent->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
break;
}
/* The address of an ELF reloc is section relative for an
object file, and absolute for an executable file or
shared library. The address of a BFD reloc is always
section relative. */
if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0)
relent->address = rela.r_offset;
else
relent->address = rela.r_offset - asect->vma;
relent->addend = rela.r_addend;
relent->howto = &howto_table[(int) type];
++relent;
}
}
asect->reloc_count += relent - relents;
if (allocated != NULL)
free (allocated);
return true;
error_return:
if (allocated != NULL)
free (allocated);
return false;
}
/* Read the relocations. On Irix 6, there can be two reloc sections
associated with a single data section. */
static boolean
mips_elf64_slurp_reloc_table (abfd, asect, symbols)
bfd *abfd;
asection *asect;
asymbol **symbols;
{
struct bfd_elf_section_data * const d = elf_section_data (asect);
if (asect->relocation != NULL
|| (asect->flags & SEC_RELOC) == 0
|| asect->reloc_count == 0)
return true;
/* Allocate space for 3 arelent structures for each Rel structure. */
asect->relocation = ((arelent *)
bfd_alloc (abfd,
asect->reloc_count * 3 * sizeof (arelent)));
if (asect->relocation == NULL)
return false;
/* The slurp_one_reloc_table routine increments reloc_count. */
asect->reloc_count = 0;
if (! mips_elf64_slurp_one_reloc_table (abfd, asect, symbols, &d->rel_hdr))
return false;
if (d->rel_hdr2 != NULL)
{
if (! mips_elf64_slurp_one_reloc_table (abfd, asect, symbols,
d->rel_hdr2))
return false;
}
return true;
}
/* Write out the relocations. */
static void
mips_elf64_write_relocs (abfd, sec, data)
bfd *abfd;
asection *sec;
PTR data;
{
boolean *failedp = (boolean *) data;
unsigned int count;
Elf_Internal_Shdr *rela_hdr;
Elf64_Mips_External_Rela *ext_rela;
unsigned int idx;
asymbol *last_sym = 0;
int last_sym_idx = 0;
/* If we have already failed, don't do anything. */
if (*failedp)
return;
if ((sec->flags & SEC_RELOC) == 0)
return;
/* The linker backend writes the relocs out itself, and sets the
reloc_count field to zero to inhibit writing them here. Also,
sometimes the SEC_RELOC flag gets set even when there aren't any
relocs. */
if (sec->reloc_count == 0)
return;
/* We can combine up to three relocs that refer to the same address
if the latter relocs have no associated symbol. */
count = 0;
for (idx = 0; idx < sec->reloc_count; idx++)
{
bfd_vma addr;
unsigned int i;
++count;
addr = sec->orelocation[idx]->address;
for (i = 0; i < 2; i++)
{
arelent *r;
if (idx + 1 >= sec->reloc_count)
break;
r = sec->orelocation[idx + 1];
if (r->address != addr
|| ! bfd_is_abs_section ((*r->sym_ptr_ptr)->section)
|| (*r->sym_ptr_ptr)->value != 0)
break;
/* We can merge the reloc at IDX + 1 with the reloc at IDX. */
++idx;
}
}
rela_hdr = &elf_section_data (sec)->rel_hdr;
rela_hdr->sh_size = rela_hdr->sh_entsize * count;
rela_hdr->contents = (PTR) bfd_alloc (abfd, rela_hdr->sh_size);
if (rela_hdr->contents == NULL)
{
*failedp = true;
return;
}
ext_rela = (Elf64_Mips_External_Rela *) rela_hdr->contents;
for (idx = 0; idx < sec->reloc_count; idx++, ext_rela++)
{
arelent *ptr;
Elf64_Mips_Internal_Rela int_rela;
asymbol *sym;
int n;
unsigned int i;
ptr = sec->orelocation[idx];
/* The address of an ELF reloc is section relative for an object
file, and absolute for an executable file or shared library.
The address of a BFD reloc is always section relative. */
if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0)
int_rela.r_offset = ptr->address;
else
int_rela.r_offset = ptr->address + sec->vma;
sym = *ptr->sym_ptr_ptr;
if (sym == last_sym)
n = last_sym_idx;
else
{
last_sym = sym;
n = _bfd_elf_symbol_from_bfd_symbol (abfd, &sym);
if (n < 0)
{
*failedp = true;
return;
}
last_sym_idx = n;
}
int_rela.r_sym = n;
int_rela.r_addend = ptr->addend;
int_rela.r_ssym = RSS_UNDEF;
if ((*ptr->sym_ptr_ptr)->the_bfd->xvec != abfd->xvec
&& ! _bfd_elf_validate_reloc (abfd, ptr))
{
*failedp = true;
return;
}
int_rela.r_type = ptr->howto->type;
int_rela.r_type2 = (int) R_MIPS_NONE;
int_rela.r_type3 = (int) R_MIPS_NONE;
for (i = 0; i < 2; i++)
{
arelent *r;
if (idx + 1 >= sec->reloc_count)
break;
r = sec->orelocation[idx + 1];
if (r->address != ptr->address
|| ! bfd_is_abs_section ((*r->sym_ptr_ptr)->section)
|| (*r->sym_ptr_ptr)->value != 0)
break;
/* We can merge the reloc at IDX + 1 with the reloc at IDX. */
if (i == 0)
int_rela.r_type2 = r->howto->type;
else
int_rela.r_type3 = r->howto->type;
++idx;
}
mips_elf64_swap_reloca_out (abfd, &int_rela, ext_rela);
}
BFD_ASSERT (ext_rela - (Elf64_Mips_External_Rela *) rela_hdr->contents
== count);
}
/* The .MIPS.options section holds register information in an
Elf64_Reginfo structure. These routines swap them in and out.
They are globally visible because they are used outside of BFD. */
void
bfd_mips_elf64_swap_reginfo_in (abfd, ex, in)
bfd *abfd;
const Elf64_External_RegInfo *ex;
Elf64_Internal_RegInfo *in;
{
in->ri_gprmask = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_gprmask);
in->ri_pad = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_pad);
in->ri_cprmask[0] = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_cprmask[0]);
in->ri_cprmask[1] = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_cprmask[1]);
in->ri_cprmask[2] = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_cprmask[2]);
in->ri_cprmask[3] = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_cprmask[3]);
in->ri_gp_value = bfd_h_get_64 (abfd, (bfd_byte *) ex->ri_gp_value);
}
void
bfd_mips_elf64_swap_reginfo_out (abfd, in, ex)
bfd *abfd;
const Elf64_Internal_RegInfo *in;
Elf64_External_RegInfo *ex;
{
bfd_h_put_32 (abfd, (bfd_vma) in->ri_gprmask,
(bfd_byte *) ex->ri_gprmask);
bfd_h_put_32 (abfd, (bfd_vma) in->ri_pad,
(bfd_byte *) ex->ri_pad);
bfd_h_put_32 (abfd, (bfd_vma) in->ri_cprmask[0],
(bfd_byte *) ex->ri_cprmask[0]);
bfd_h_put_32 (abfd, (bfd_vma) in->ri_cprmask[1],
(bfd_byte *) ex->ri_cprmask[1]);
bfd_h_put_32 (abfd, (bfd_vma) in->ri_cprmask[2],
(bfd_byte *) ex->ri_cprmask[2]);
bfd_h_put_32 (abfd, (bfd_vma) in->ri_cprmask[3],
(bfd_byte *) ex->ri_cprmask[3]);
bfd_h_put_64 (abfd, (bfd_vma) in->ri_gp_value,
(bfd_byte *) ex->ri_gp_value);
}
/* Handle a 64-bit MIPS ELF specific section. */
static boolean
mips_elf64_section_from_shdr (abfd, hdr, name)
bfd *abfd;
Elf_Internal_Shdr *hdr;
char *name;
{
if (! _bfd_mips_elf_section_from_shdr (abfd, hdr, name))
return false;
/* For a SHT_MIPS_OPTIONS section, look for a ODK_REGINFO entry, and
set the gp value based on what we find. We may see both
SHT_MIPS_REGINFO and SHT_MIPS_OPTIONS/ODK_REGINFO; in that case,
they should agree. */
if (hdr->sh_type == SHT_MIPS_OPTIONS)
{
bfd_byte *contents, *l, *lend;
contents = (bfd_byte *) bfd_malloc (hdr->sh_size);
if (contents == NULL)
return false;
if (! bfd_get_section_contents (abfd, hdr->bfd_section, contents,
(file_ptr) 0, hdr->sh_size))
{
free (contents);
return false;
}
l = contents;
lend = contents + hdr->sh_size;
while (l + sizeof (Elf_External_Options) <= lend)
{
Elf_Internal_Options intopt;
bfd_mips_elf_swap_options_in (abfd, (Elf_External_Options *) l,
&intopt);
if (intopt.kind == ODK_REGINFO)
{
Elf64_Internal_RegInfo intreg;
bfd_mips_elf64_swap_reginfo_in
(abfd,
((Elf64_External_RegInfo *)
(l + sizeof (Elf_External_Options))),
&intreg);
elf_gp (abfd) = intreg.ri_gp_value;
}
l += intopt.size;
}
free (contents);
}
return true;
}
/* Work over a section just before writing it out. We update the GP
value in the SHT_MIPS_OPTIONS section based on the value we are
using. */
static boolean
mips_elf64_section_processing (abfd, hdr)
bfd *abfd;
Elf_Internal_Shdr *hdr;
{
if (hdr->sh_type == SHT_MIPS_OPTIONS
&& hdr->bfd_section != NULL
&& elf_section_data (hdr->bfd_section) != NULL
&& elf_section_data (hdr->bfd_section)->tdata != NULL)
{
bfd_byte *contents, *l, *lend;
/* We stored the section contents in the elf_section_data tdata
field in the set_section_contents routine. We save the
section contents so that we don't have to read them again.
At this point we know that elf_gp is set, so we can look
through the section contents to see if there is an
ODK_REGINFO structure. */
contents = (bfd_byte *) elf_section_data (hdr->bfd_section)->tdata;
l = contents;
lend = contents + hdr->sh_size;
while (l + sizeof (Elf_External_Options) <= lend)
{
Elf_Internal_Options intopt;
bfd_mips_elf_swap_options_in (abfd, (Elf_External_Options *) l,
&intopt);
if (intopt.kind == ODK_REGINFO)
{
bfd_byte buf[8];
if (bfd_seek (abfd,
(hdr->sh_offset
+ (l - contents)
+ sizeof (Elf_External_Options)
+ (sizeof (Elf64_External_RegInfo) - 8)),
SEEK_SET) == -1)
return false;
bfd_h_put_64 (abfd, elf_gp (abfd), buf);
if (bfd_write (buf, 1, 8, abfd) != 8)
return false;
}
l += intopt.size;
}
}
return _bfd_mips_elf_section_processing (abfd, hdr);
}
/* ECOFF swapping routines. These are used when dealing with the
.mdebug section, which is in the ECOFF debugging format. */
static const struct ecoff_debug_swap mips_elf64_ecoff_debug_swap =
{
/* Symbol table magic number. */
magicSym2,
/* Alignment of debugging information. E.g., 4. */
8,
/* Sizes of external symbolic information. */
sizeof (struct hdr_ext),
sizeof (struct dnr_ext),
sizeof (struct pdr_ext),
sizeof (struct sym_ext),
sizeof (struct opt_ext),
sizeof (struct fdr_ext),
sizeof (struct rfd_ext),
sizeof (struct ext_ext),
/* Functions to swap in external symbolic data. */
ecoff_swap_hdr_in,
ecoff_swap_dnr_in,
ecoff_swap_pdr_in,
ecoff_swap_sym_in,
ecoff_swap_opt_in,
ecoff_swap_fdr_in,
ecoff_swap_rfd_in,
ecoff_swap_ext_in,
_bfd_ecoff_swap_tir_in,
_bfd_ecoff_swap_rndx_in,
/* Functions to swap out external symbolic data. */
ecoff_swap_hdr_out,
ecoff_swap_dnr_out,
ecoff_swap_pdr_out,
ecoff_swap_sym_out,
ecoff_swap_opt_out,
ecoff_swap_fdr_out,
ecoff_swap_rfd_out,
ecoff_swap_ext_out,
_bfd_ecoff_swap_tir_out,
_bfd_ecoff_swap_rndx_out,
/* Function to read in symbolic data. */
_bfd_mips_elf_read_ecoff_info
};
/* Relocations in the 64 bit MIPS ELF ABI are more complex than in
standard ELF. This structure is used to redirect the relocation
handling routines. */
const struct elf_size_info mips_elf64_size_info =
{
sizeof (Elf64_External_Ehdr),
sizeof (Elf64_External_Phdr),
sizeof (Elf64_External_Shdr),
sizeof (Elf64_Mips_External_Rel),
sizeof (Elf64_Mips_External_Rela),
sizeof (Elf64_External_Sym),
sizeof (Elf64_External_Dyn),
sizeof (Elf_External_Note),
64, /* arch_size */
8, /* file_align */
ELFCLASS64,
EV_CURRENT,
bfd_elf64_write_out_phdrs,
bfd_elf64_write_shdrs_and_ehdr,
mips_elf64_write_relocs,
bfd_elf64_swap_symbol_out,
mips_elf64_slurp_reloc_table,
bfd_elf64_slurp_symbol_table,
bfd_elf64_swap_dyn_in
};
#define TARGET_LITTLE_SYM bfd_elf64_littlemips_vec
#define TARGET_LITTLE_NAME "elf64-littlemips"
#define TARGET_BIG_SYM bfd_elf64_bigmips_vec
#define TARGET_BIG_NAME "elf64-bigmips"
#define ELF_ARCH bfd_arch_mips
#define ELF_MACHINE_CODE EM_MIPS
#define ELF_MAXPAGESIZE 0x1000
#define elf_backend_size_info mips_elf64_size_info
#define elf_backend_object_p _bfd_mips_elf_object_p
#define elf_backend_section_from_shdr mips_elf64_section_from_shdr
#define elf_backend_fake_sections _bfd_mips_elf_fake_sections
#define elf_backend_section_from_bfd_section \
_bfd_mips_elf_section_from_bfd_section
#define elf_backend_section_processing mips_elf64_section_processing
#define elf_backend_symbol_processing _bfd_mips_elf_symbol_processing
#define elf_backend_final_write_processing \
_bfd_mips_elf_final_write_processing
#define elf_backend_ecoff_debug_swap &mips_elf64_ecoff_debug_swap
#define bfd_elf64_find_nearest_line _bfd_mips_elf_find_nearest_line
#define bfd_elf64_get_reloc_upper_bound mips_elf64_get_reloc_upper_bound
#define bfd_elf64_bfd_reloc_type_lookup mips_elf64_reloc_type_lookup
#define bfd_elf64_set_section_contents _bfd_mips_elf_set_section_contents
#define bfd_elf64_bfd_copy_private_bfd_data \
_bfd_mips_elf_copy_private_bfd_data
#define bfd_elf64_bfd_merge_private_bfd_data \
_bfd_mips_elf_merge_private_bfd_data
#define bfd_elf64_bfd_set_private_flags _bfd_mips_elf_set_private_flags
#include "elf64-target.h"