#include "bfd.h"
#include "sysdep.h"
#include "libbfd.h"
#include "aout/ar.h"
#include "bfdlink.h"
#include "genlink.h"
#include "elf-bfd.h"
#include "elf/mips.h"
#include "coff/sym.h"
#include "coff/symconst.h"
#include "coff/internal.h"
#include "coff/ecoff.h"
#include "coff/alpha.h"
#define ECOFF_SIGNED_64
#include "ecoffswap.h"
struct mips_elf64_link_hash_entry;
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 *));
static void mips_elf64_swap_reloc_out
PARAMS ((bfd *, const Elf64_Mips_Internal_Rel *,
Elf64_Mips_External_Rel *));
static void mips_elf64_swap_reloca_out
PARAMS ((bfd *, const Elf64_Mips_Internal_Rela *,
Elf64_Mips_External_Rela *));
static void mips_elf64_be_swap_reloc_in
PARAMS ((bfd *, const bfd_byte *, Elf_Internal_Rel *));
static void mips_elf64_be_swap_reloc_out
PARAMS ((bfd *, const Elf_Internal_Rel *, bfd_byte *));
static void mips_elf64_be_swap_reloca_in
PARAMS ((bfd *, const bfd_byte *, Elf_Internal_Rela *));
static void mips_elf64_be_swap_reloca_out
PARAMS ((bfd *, const Elf_Internal_Rela *, bfd_byte *));
static bfd_vma mips_elf64_high PARAMS ((bfd_vma));
static bfd_vma mips_elf64_higher PARAMS ((bfd_vma));
static bfd_vma mips_elf64_highest PARAMS ((bfd_vma));
static reloc_howto_type *mips_elf64_reloc_type_lookup
PARAMS ((bfd *, bfd_reloc_code_real_type));
static void mips_elf64_info_to_howto_rel
PARAMS ((bfd *, arelent *, Elf64_Internal_Rel *));
static void mips_elf64_info_to_howto_rela
PARAMS ((bfd *, arelent *, Elf64_Internal_Rela *));
static long mips_elf64_get_reloc_upper_bound PARAMS ((bfd *, asection *));
static boolean mips_elf64_slurp_one_reloc_table
PARAMS ((bfd *, asection *, asymbol **, const Elf_Internal_Shdr *));
static boolean mips_elf64_slurp_reloc_table
PARAMS ((bfd *, asection *, asymbol **, boolean));
static void mips_elf64_write_relocs PARAMS ((bfd *, asection *, PTR));
static void mips_elf64_write_rel
PARAMS((bfd *, asection *, Elf_Internal_Shdr *, int *, PTR));
static void mips_elf64_write_rela
PARAMS((bfd *, asection *, Elf_Internal_Shdr *, int *, PTR));
static struct bfd_hash_entry *mips_elf64_link_hash_newfunc
PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
static bfd_reloc_status_type mips_elf64_hi16_reloc
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static bfd_reloc_status_type mips_elf64_higher_reloc
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static bfd_reloc_status_type mips_elf64_highest_reloc
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static bfd_reloc_status_type mips_elf64_gprel16_reloc
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static bfd_reloc_status_type mips_elf64_gprel16_reloca
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static bfd_reloc_status_type mips_elf64_literal_reloc
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static bfd_reloc_status_type mips_elf64_gprel32_reloc
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static bfd_reloc_status_type mips_elf64_shift6_reloc
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static bfd_reloc_status_type mips_elf64_got16_reloc
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static boolean mips_elf64_assign_gp PARAMS ((bfd *, bfd_vma *));
static bfd_reloc_status_type mips_elf64_final_gp
PARAMS ((bfd *, asymbol *, boolean, char **, bfd_vma *));
static bfd_reloc_status_type gprel16_with_gp
PARAMS ((bfd *, asymbol *, arelent *, asection *, boolean, PTR, bfd_vma));
static int mips_elf64_additional_program_headers PARAMS ((bfd *));
static struct bfd_link_hash_table *mips_elf64_link_hash_table_create
PARAMS((bfd *));
static bfd_vma mips_elf64_got_offset_from_index
PARAMS ((bfd *, bfd *, bfd_vma));
static struct mips_elf64_got_info *_mips_elf64_got_info
PARAMS ((bfd *, asection **));
static bfd_vma mips_elf64_sign_extend PARAMS ((bfd_vma, int));
static boolean mips_elf64_overflow_p PARAMS ((bfd_vma, int));
static bfd_vma mips_elf64_global_got_index
PARAMS ((bfd *, struct elf_link_hash_entry *));
static boolean mips_elf64_sort_hash_table_f
PARAMS ((struct mips_elf64_link_hash_entry *, PTR));
static boolean mips_elf64_sort_hash_table
PARAMS ((struct bfd_link_info *, unsigned long));
static void mips_elf64_swap_msym_out
PARAMS ((bfd *, const Elf32_Internal_Msym *, Elf32_External_Msym *));
static bfd_vma mips_elf64_create_local_got_entry
PARAMS ((bfd *abfd, struct mips_elf64_got_info *, asection *,
bfd_vma value));
static bfd_vma mips_elf64_local_got_index
PARAMS ((bfd *, struct bfd_link_info *, bfd_vma));
static bfd_vma mips_elf64_got_page
PARAMS ((bfd *, struct bfd_link_info *, bfd_vma, bfd_vma *));
static bfd_vma mips_elf64_got16_entry
PARAMS ((bfd *, struct bfd_link_info *, bfd_vma, boolean));
static boolean mips_elf64_local_relocation_p
PARAMS ((bfd *, const Elf_Internal_Rela *, asection **, boolean));
static const Elf_Internal_Rela *mips_elf64_next_relocation
PARAMS ((unsigned int, const Elf_Internal_Rela *,
const Elf_Internal_Rela *));
static boolean mips_elf64_create_dynamic_relocation
PARAMS ((bfd *, struct bfd_link_info *, const Elf_Internal_Rela *,
struct mips_elf64_link_hash_entry *, asection *, bfd_vma,
bfd_vma *, asection *));
static bfd_reloc_status_type mips_elf64_calculate_relocation
PARAMS ((bfd *, bfd *, asection *, struct bfd_link_info *,
const Elf_Internal_Rela *, bfd_vma, reloc_howto_type *,
Elf_Internal_Sym *, asection **, bfd_vma *, const char **,
boolean *));
static bfd_vma mips_elf64_obtain_contents
PARAMS ((reloc_howto_type *, const Elf_Internal_Rela *, bfd *, bfd_byte *));
static boolean mips_elf64_perform_relocation
PARAMS ((struct bfd_link_info *, reloc_howto_type *,
const Elf_Internal_Rela *, bfd_vma,
bfd *, asection *, bfd_byte *, boolean));
static boolean mips_elf64_relocate_section
PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
boolean mips_elf64_create_dynamic_sections
PARAMS ((bfd *, struct bfd_link_info *));
boolean mips_elf64_adjust_dynamic_symbol
PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *h));
boolean mips_elf64_always_size_sections
PARAMS ((bfd *, struct bfd_link_info *));
static boolean mips_elf64_check_mips16_stubs
PARAMS ((struct mips_elf64_link_hash_entry *, PTR));
boolean mips_elf64_size_dynamic_sections
PARAMS ((bfd *, struct bfd_link_info *));
boolean mips_elf64_finish_dynamic_symbol
PARAMS ((bfd *, struct bfd_link_info *, struct elf_link_hash_entry *,
Elf_Internal_Sym *));
boolean mips_elf64_finish_dynamic_sections
PARAMS ((bfd *, struct bfd_link_info *info));
asection *mips_elf64_gc_mark_hook
PARAMS ((bfd *, struct bfd_link_info *, Elf_Internal_Rela *,
struct elf_link_hash_entry *, Elf_Internal_Sym *));
boolean mips_elf64_gc_sweep_hook
PARAMS ((bfd *, struct bfd_link_info *, asection *,
const Elf_Internal_Rela *));
static boolean mips_elf64_create_got_section
PARAMS ((bfd *, struct bfd_link_info *));
static boolean mips_elf64_record_global_got_symbol
PARAMS ((struct elf_link_hash_entry *, struct bfd_link_info *,
struct mips_elf64_got_info *));
static asection *mips_elf64_create_msym_section PARAMS((bfd *));
static void mips_elf64_allocate_dynamic_relocations
PARAMS ((bfd *, unsigned int));
static boolean mips_elf64_stub_section_p PARAMS ((bfd *, asection *));
boolean mips_elf64_check_relocs
PARAMS ((bfd *, struct bfd_link_info *, asection *,
const Elf_Internal_Rela *));
static boolean mips_elf64_output_extsym
PARAMS ((struct mips_elf64_link_hash_entry *, PTR));
static void mips_elf64_swap_gptab_in
PARAMS ((bfd *, const Elf32_External_gptab *, Elf32_gptab *));
static void mips_elf64_swap_gptab_out
PARAMS ((bfd *, const Elf32_gptab *, Elf32_External_gptab *));
static int gptab_compare PARAMS ((const PTR, const PTR));
boolean mips_elf64_final_link PARAMS ((bfd *, struct bfd_link_info *));
extern const bfd_target bfd_elf64_bigmips_vec;
extern const bfd_target bfd_elf64_littlemips_vec;
static bfd_vma prev_reloc_addend = 0;
static bfd_size_type prev_reloc_address = 0;
#define SGI_COMPAT(abfd) \
((abfd->xvec == &bfd_elf64_bigmips_vec) \
|| (abfd->xvec == &bfd_elf64_littlemips_vec) ? true : false)
#define MINUS_ONE (((bfd_vma)0) - 1)
#define MIPS_RESERVED_GOTNO (2)
#define ELF_MIPS_GP_OFFSET(abfd) 0x7ff0
#define STUB_LW 0xdf998010
#define STUB_MOVE 0x03e07825
#define STUB_JALR 0x0320f809
#define STUB_LI16 0x34180000
#define MIPS_FUNCTION_STUB_SIZE (16)
#define UNUSED_RELOC(num) { num, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
static reloc_howto_type mips_elf64_howto_table_rel[] =
{
HOWTO (R_MIPS_NONE,
0,
0,
0,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_NONE",
false,
0,
0,
false),
HOWTO (R_MIPS_16,
0,
2,
16,
false,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_MIPS_16",
true,
0x0000ffff,
0x0000ffff,
false),
HOWTO (R_MIPS_32,
0,
2,
32,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_32",
true,
0xffffffff,
0xffffffff,
false),
HOWTO (R_MIPS_REL32,
0,
2,
32,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_REL32",
true,
0xffffffff,
0xffffffff,
false),
HOWTO (R_MIPS_26,
2,
2,
26,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_26",
true,
0x03ffffff,
0x03ffffff,
false),
HOWTO (R_MIPS_HI16,
0,
2,
16,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_HI16",
true,
0x0000ffff,
0x0000ffff,
false),
HOWTO (R_MIPS_LO16,
0,
2,
16,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_LO16",
true,
0x0000ffff,
0x0000ffff,
false),
HOWTO (R_MIPS_GPREL16,
0,
2,
16,
false,
0,
complain_overflow_signed,
mips_elf64_gprel16_reloc,
"R_MIPS_GPREL16",
true,
0x0000ffff,
0x0000ffff,
false),
HOWTO (R_MIPS_LITERAL,
0,
2,
16,
false,
0,
complain_overflow_signed,
mips_elf64_literal_reloc,
"R_MIPS_LITERAL",
true,
0x0000ffff,
0x0000ffff,
false),
HOWTO (R_MIPS_GOT16,
0,
2,
16,
false,
0,
complain_overflow_signed,
mips_elf64_got16_reloc,
"R_MIPS_GOT16",
true,
0x0000ffff,
0x0000ffff,
false),
HOWTO (R_MIPS_PC16,
0,
2,
16,
true,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_MIPS_PC16",
true,
0x0000ffff,
0x0000ffff,
true),
HOWTO (R_MIPS_CALL16,
0,
2,
16,
false,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_MIPS_CALL16",
true,
0x0000ffff,
0x0000ffff,
false),
HOWTO (R_MIPS_GPREL32,
0,
2,
32,
false,
0,
complain_overflow_dont,
mips_elf64_gprel32_reloc,
"R_MIPS_GPREL32",
true,
0xffffffff,
0xffffffff,
false),
UNUSED_RELOC (13),
UNUSED_RELOC (14),
UNUSED_RELOC (15),
HOWTO (R_MIPS_SHIFT5,
0,
2,
5,
false,
6,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_MIPS_SHIFT5",
true,
0x000007c0,
0x000007c0,
false),
HOWTO (R_MIPS_SHIFT6,
0,
2,
6,
false,
6,
complain_overflow_bitfield,
mips_elf64_shift6_reloc,
"R_MIPS_SHIFT6",
true,
0x000007c4,
0x000007c4,
false),
HOWTO (R_MIPS_64,
0,
4,
64,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_64",
true,
MINUS_ONE,
MINUS_ONE,
false),
HOWTO (R_MIPS_GOT_DISP,
0,
2,
16,
false,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_MIPS_GOT_DISP",
true,
0x0000ffff,
0x0000ffff,
false),
HOWTO (R_MIPS_GOT_PAGE,
0,
2,
16,
false,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_MIPS_GOT_PAGE",
true,
0x0000ffff,
0x0000ffff,
false),
HOWTO (R_MIPS_GOT_OFST,
0,
2,
16,
false,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_MIPS_GOT_OFST",
true,
0x0000ffff,
0x0000ffff,
false),
HOWTO (R_MIPS_GOT_HI16,
0,
2,
16,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_GOT_HI16",
true,
0x0000ffff,
0x0000ffff,
false),
HOWTO (R_MIPS_GOT_LO16,
0,
2,
16,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_GOT_LO16",
true,
0x0000ffff,
0x0000ffff,
false),
HOWTO (R_MIPS_SUB,
0,
4,
64,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_SUB",
true,
MINUS_ONE,
MINUS_ONE,
false),
HOWTO (R_MIPS_INSERT_A,
0,
2,
32,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_INSERT_A",
true,
0xffffffff,
0xffffffff,
false),
HOWTO (R_MIPS_INSERT_B,
0,
2,
32,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_INSERT_B",
true,
0xffffffff,
0xffffffff,
false),
HOWTO (R_MIPS_DELETE,
0,
2,
32,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_DELETE",
true,
0xffffffff,
0xffffffff,
false),
HOWTO (R_MIPS_HIGHER,
0,
2,
16,
false,
0,
complain_overflow_dont,
mips_elf64_higher_reloc,
"R_MIPS_HIGHER",
true,
0x0000ffff,
0x0000ffff,
false),
HOWTO (R_MIPS_HIGHEST,
0,
2,
16,
false,
0,
complain_overflow_dont,
mips_elf64_highest_reloc,
"R_MIPS_HIGHEST",
true,
0x0000ffff,
0x0000ffff,
false),
HOWTO (R_MIPS_CALL_HI16,
0,
2,
16,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_CALL_HI16",
true,
0x0000ffff,
0x0000ffff,
false),
HOWTO (R_MIPS_CALL_LO16,
0,
2,
16,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_CALL_LO16",
true,
0x0000ffff,
0x0000ffff,
false),
HOWTO (R_MIPS_SCN_DISP,
0,
2,
32,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_SCN_DISP",
true,
0xffffffff,
0xffffffff,
false),
HOWTO (R_MIPS_REL16,
0,
1,
16,
false,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_MIPS_REL16",
true,
0xffff,
0xffff,
false),
EMPTY_HOWTO (R_MIPS_ADD_IMMEDIATE),
EMPTY_HOWTO (R_MIPS_PJUMP),
HOWTO (R_MIPS_RELGOT,
0,
2,
32,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_RELGOT",
true,
0xffffffff,
0xffffffff,
false),
HOWTO (R_MIPS_JALR,
0,
2,
32,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_JALR",
false,
0,
0x00000000,
false),
};
static reloc_howto_type mips_elf64_howto_table_rela[] =
{
HOWTO (R_MIPS_NONE,
0,
0,
0,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_NONE",
false,
0,
0,
false),
HOWTO (R_MIPS_16,
0,
2,
16,
false,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_MIPS_16",
false,
0,
0x0000ffff,
false),
HOWTO (R_MIPS_32,
0,
2,
32,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_32",
false,
0,
0xffffffff,
false),
HOWTO (R_MIPS_REL32,
0,
2,
32,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_REL32",
false,
0,
0xffffffff,
false),
HOWTO (R_MIPS_26,
2,
2,
26,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_26",
false,
0,
0x03ffffff,
false),
HOWTO (R_MIPS_HI16,
0,
2,
16,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_HI16",
false,
0,
0x0000ffff,
false),
HOWTO (R_MIPS_LO16,
0,
2,
16,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_LO16",
false,
0,
0x0000ffff,
false),
HOWTO (R_MIPS_GPREL16,
0,
2,
16,
false,
0,
complain_overflow_signed,
mips_elf64_gprel16_reloca,
"R_MIPS_GPREL16",
false,
0,
0x0000ffff,
false),
HOWTO (R_MIPS_LITERAL,
0,
2,
16,
false,
0,
complain_overflow_signed,
mips_elf64_literal_reloc,
"R_MIPS_LITERAL",
false,
0,
0x0000ffff,
false),
HOWTO (R_MIPS_GOT16,
0,
2,
16,
false,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_MIPS_GOT16",
false,
0,
0x0000ffff,
false),
HOWTO (R_MIPS_PC16,
0,
2,
16,
true,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_MIPS_PC16",
false,
0,
0x0000ffff,
true),
HOWTO (R_MIPS_CALL16,
0,
2,
16,
false,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_MIPS_CALL16",
false,
0,
0x0000ffff,
false),
HOWTO (R_MIPS_GPREL32,
0,
2,
32,
false,
0,
complain_overflow_dont,
mips_elf64_gprel32_reloc,
"R_MIPS_GPREL32",
false,
0,
0xffffffff,
false),
UNUSED_RELOC (13),
UNUSED_RELOC (14),
UNUSED_RELOC (15),
HOWTO (R_MIPS_SHIFT5,
0,
2,
5,
false,
6,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_MIPS_SHIFT5",
false,
0,
0x000007c0,
false),
HOWTO (R_MIPS_SHIFT6,
0,
2,
6,
false,
6,
complain_overflow_bitfield,
mips_elf64_shift6_reloc,
"R_MIPS_SHIFT6",
false,
0,
0x000007c4,
false),
HOWTO (R_MIPS_64,
0,
4,
64,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_64",
false,
0,
MINUS_ONE,
false),
HOWTO (R_MIPS_GOT_DISP,
0,
2,
16,
false,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_MIPS_GOT_DISP",
false,
0,
0x0000ffff,
false),
HOWTO (R_MIPS_GOT_PAGE,
0,
2,
16,
false,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_MIPS_GOT_PAGE",
false,
0,
0x0000ffff,
false),
HOWTO (R_MIPS_GOT_OFST,
0,
2,
16,
false,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_MIPS_GOT_OFST",
false,
0,
0x0000ffff,
false),
HOWTO (R_MIPS_GOT_HI16,
0,
2,
16,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_GOT_HI16",
false,
0,
0x0000ffff,
false),
HOWTO (R_MIPS_GOT_LO16,
0,
2,
16,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_GOT_LO16",
false,
0,
0x0000ffff,
false),
HOWTO (R_MIPS_SUB,
0,
4,
64,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_SUB",
false,
0,
MINUS_ONE,
false),
HOWTO (R_MIPS_INSERT_A,
0,
2,
32,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_INSERT_A",
false,
0,
0xffffffff,
false),
HOWTO (R_MIPS_INSERT_B,
0,
2,
32,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_INSERT_B",
false,
0,
0xffffffff,
false),
HOWTO (R_MIPS_DELETE,
0,
2,
32,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_DELETE",
false,
0,
0xffffffff,
false),
HOWTO (R_MIPS_HIGHER,
0,
2,
16,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_HIGHER",
false,
0,
0x0000ffff,
false),
HOWTO (R_MIPS_HIGHEST,
0,
2,
16,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_HIGHEST",
false,
0,
0x0000ffff,
false),
HOWTO (R_MIPS_CALL_HI16,
0,
2,
16,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_CALL_HI16",
false,
0,
0x0000ffff,
false),
HOWTO (R_MIPS_CALL_LO16,
0,
2,
16,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_CALL_LO16",
false,
0,
0x0000ffff,
false),
HOWTO (R_MIPS_SCN_DISP,
0,
2,
32,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_SCN_DISP",
false,
0,
0xffffffff,
false),
HOWTO (R_MIPS_REL16,
0,
1,
16,
false,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_MIPS_REL16",
false,
0,
0xffff,
false),
EMPTY_HOWTO (R_MIPS_ADD_IMMEDIATE),
EMPTY_HOWTO (R_MIPS_PJUMP),
HOWTO (R_MIPS_RELGOT,
0,
2,
32,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_RELGOT",
false,
0,
0xffffffff,
false),
HOWTO (R_MIPS_JALR,
0,
2,
32,
false,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_JALR",
false,
0,
0x00000000,
false),
};
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 = H_GET_64 (abfd, src->r_offset);
dst->r_sym = H_GET_32 (abfd, src->r_sym);
dst->r_ssym = H_GET_8 (abfd, src->r_ssym);
dst->r_type3 = H_GET_8 (abfd, src->r_type3);
dst->r_type2 = H_GET_8 (abfd, src->r_type2);
dst->r_type = H_GET_8 (abfd, src->r_type);
}
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 = H_GET_64 (abfd, src->r_offset);
dst->r_sym = H_GET_32 (abfd, src->r_sym);
dst->r_ssym = H_GET_8 (abfd, src->r_ssym);
dst->r_type3 = H_GET_8 (abfd, src->r_type3);
dst->r_type2 = H_GET_8 (abfd, src->r_type2);
dst->r_type = H_GET_8 (abfd, src->r_type);
dst->r_addend = H_GET_S64 (abfd, src->r_addend);
}
static void
mips_elf64_swap_reloc_out (abfd, src, dst)
bfd *abfd;
const Elf64_Mips_Internal_Rel *src;
Elf64_Mips_External_Rel *dst;
{
H_PUT_64 (abfd, src->r_offset, dst->r_offset);
H_PUT_32 (abfd, src->r_sym, dst->r_sym);
H_PUT_8 (abfd, src->r_ssym, dst->r_ssym);
H_PUT_8 (abfd, src->r_type3, dst->r_type3);
H_PUT_8 (abfd, src->r_type2, dst->r_type2);
H_PUT_8 (abfd, src->r_type, dst->r_type);
}
static void
mips_elf64_swap_reloca_out (abfd, src, dst)
bfd *abfd;
const Elf64_Mips_Internal_Rela *src;
Elf64_Mips_External_Rela *dst;
{
H_PUT_64 (abfd, src->r_offset, dst->r_offset);
H_PUT_32 (abfd, src->r_sym, dst->r_sym);
H_PUT_8 (abfd, src->r_ssym, dst->r_ssym);
H_PUT_8 (abfd, src->r_type3, dst->r_type3);
H_PUT_8 (abfd, src->r_type2, dst->r_type2);
H_PUT_8 (abfd, src->r_type, dst->r_type);
H_PUT_S64 (abfd, src->r_addend, dst->r_addend);
}
static void
mips_elf64_be_swap_reloc_in (abfd, src, dst)
bfd *abfd;
const bfd_byte *src;
Elf_Internal_Rel *dst;
{
Elf64_Mips_Internal_Rel mirel;
mips_elf64_swap_reloc_in (abfd,
(const Elf64_Mips_External_Rel *) src,
&mirel);
dst[0].r_offset = mirel.r_offset;
dst[0].r_info = ELF64_R_INFO (mirel.r_sym, mirel.r_type);
dst[1].r_offset = mirel.r_offset;
dst[1].r_info = ELF64_R_INFO (mirel.r_ssym, mirel.r_type2);
dst[2].r_offset = mirel.r_offset;
dst[2].r_info = ELF64_R_INFO (STN_UNDEF, mirel.r_type3);
}
static void
mips_elf64_be_swap_reloca_in (abfd, src, dst)
bfd *abfd;
const bfd_byte *src;
Elf_Internal_Rela *dst;
{
Elf64_Mips_Internal_Rela mirela;
mips_elf64_swap_reloca_in (abfd,
(const Elf64_Mips_External_Rela *) src,
&mirela);
dst[0].r_offset = mirela.r_offset;
dst[0].r_info = ELF64_R_INFO (mirela.r_sym, mirela.r_type);
dst[0].r_addend = mirela.r_addend;
dst[1].r_offset = mirela.r_offset;
dst[1].r_info = ELF64_R_INFO (mirela.r_ssym, mirela.r_type2);
dst[1].r_addend = 0;
dst[2].r_offset = mirela.r_offset;
dst[2].r_info = ELF64_R_INFO (STN_UNDEF, mirela.r_type3);
dst[2].r_addend = 0;
}
static void
mips_elf64_be_swap_reloc_out (abfd, src, dst)
bfd *abfd;
const Elf_Internal_Rel *src;
bfd_byte *dst;
{
Elf64_Mips_Internal_Rel mirel;
mirel.r_offset = src[0].r_offset;
BFD_ASSERT(src[0].r_offset == src[1].r_offset);
BFD_ASSERT(src[0].r_offset == src[2].r_offset);
mirel.r_type = ELF64_MIPS_R_TYPE (src[0].r_info);
mirel.r_sym = ELF64_R_SYM (src[0].r_info);
mirel.r_type2 = ELF64_MIPS_R_TYPE2 (src[1].r_info);
mirel.r_ssym = ELF64_MIPS_R_SSYM (src[1].r_info);
mirel.r_type3 = ELF64_MIPS_R_TYPE3 (src[2].r_info);
mips_elf64_swap_reloc_out (abfd, &mirel,
(Elf64_Mips_External_Rel *) dst);
}
static void
mips_elf64_be_swap_reloca_out (abfd, src, dst)
bfd *abfd;
const Elf_Internal_Rela *src;
bfd_byte *dst;
{
Elf64_Mips_Internal_Rela mirela;
mirela.r_offset = src[0].r_offset;
BFD_ASSERT(src[0].r_offset == src[1].r_offset);
BFD_ASSERT(src[0].r_offset == src[2].r_offset);
mirela.r_type = ELF64_MIPS_R_TYPE (src[0].r_info);
mirela.r_sym = ELF64_R_SYM (src[0].r_info);
mirela.r_addend = src[0].r_addend;
BFD_ASSERT(src[1].r_addend == 0);
BFD_ASSERT(src[2].r_addend == 0);
mirela.r_type2 = ELF64_MIPS_R_TYPE2 (src[1].r_info);
mirela.r_ssym = ELF64_MIPS_R_SSYM (src[1].r_info);
mirela.r_type3 = ELF64_MIPS_R_TYPE3 (src[2].r_info);
mips_elf64_swap_reloca_out (abfd, &mirela,
(Elf64_Mips_External_Rela *) dst);
}
static bfd_vma
mips_elf64_high (value)
bfd_vma value;
{
return ((value + (bfd_vma) 0x8000) >> 16) & 0xffff;
}
static bfd_vma
mips_elf64_higher (value)
bfd_vma value;
{
return ((value + (bfd_vma) 0x80008000) >> 32) & 0xffff;
}
static bfd_vma
mips_elf64_highest (value)
bfd_vma value;
{
return ((value + (bfd_vma) 0x800080008000) >> 48) & 0xffff;
}
bfd_reloc_status_type
mips_elf64_hi16_reloc (abfd,
reloc_entry,
symbol,
data,
input_section,
output_bfd,
error_message)
bfd *abfd ATTRIBUTE_UNUSED;
arelent *reloc_entry;
asymbol *symbol;
PTR data ATTRIBUTE_UNUSED;
asection *input_section;
bfd *output_bfd;
char **error_message ATTRIBUTE_UNUSED;
{
if (output_bfd != (bfd *) NULL
&& (symbol->flags & BSF_SECTION_SYM) == 0
&& (! reloc_entry->howto->partial_inplace
|| reloc_entry->addend == 0))
{
reloc_entry->address += input_section->output_offset;
return bfd_reloc_ok;
}
if (((reloc_entry->addend & 0xffff) + 0x8000) & ~0xffff)
reloc_entry->addend += 0x8000;
return bfd_reloc_continue;
}
bfd_reloc_status_type
mips_elf64_higher_reloc (abfd,
reloc_entry,
symbol,
data,
input_section,
output_bfd,
error_message)
bfd *abfd ATTRIBUTE_UNUSED;
arelent *reloc_entry;
asymbol *symbol;
PTR data ATTRIBUTE_UNUSED;
asection *input_section;
bfd *output_bfd;
char **error_message ATTRIBUTE_UNUSED;
{
if (output_bfd != (bfd *) NULL
&& (symbol->flags & BSF_SECTION_SYM) == 0
&& (! reloc_entry->howto->partial_inplace
|| reloc_entry->addend == 0))
{
reloc_entry->address += input_section->output_offset;
return bfd_reloc_ok;
}
if (((reloc_entry->addend & 0xffffffff) + 0x80008000)
& ~0xffffffff)
reloc_entry->addend += 0x80008000;
return bfd_reloc_continue;
}
bfd_reloc_status_type
mips_elf64_highest_reloc (abfd,
reloc_entry,
symbol,
data,
input_section,
output_bfd,
error_message)
bfd *abfd ATTRIBUTE_UNUSED;
arelent *reloc_entry;
asymbol *symbol;
PTR data ATTRIBUTE_UNUSED;
asection *input_section;
bfd *output_bfd;
char **error_message ATTRIBUTE_UNUSED;
{
if (output_bfd != (bfd *) NULL
&& (symbol->flags & BSF_SECTION_SYM) == 0
&& (! reloc_entry->howto->partial_inplace
|| reloc_entry->addend == 0))
{
reloc_entry->address += input_section->output_offset;
return bfd_reloc_ok;
}
if (((reloc_entry->addend & 0xffffffffffff) + 0x800080008000)
& ~0xffffffffffff)
reloc_entry->addend += 0x800080008000;
return bfd_reloc_continue;
}
bfd_reloc_status_type
mips_elf64_got16_reloc (abfd,
reloc_entry,
symbol,
data,
input_section,
output_bfd,
error_message)
bfd *abfd;
arelent *reloc_entry;
asymbol *symbol;
PTR data;
asection *input_section;
bfd *output_bfd;
char **error_message;
{
if (output_bfd != (bfd *) NULL
&& (symbol->flags & BSF_SECTION_SYM) == 0
&& reloc_entry->addend == 0)
{
reloc_entry->address += input_section->output_offset;
return bfd_reloc_ok;
}
if (output_bfd != (bfd *) NULL
&& (symbol->flags & BSF_SECTION_SYM) != 0)
return mips_elf64_hi16_reloc (abfd, reloc_entry, symbol, data,
input_section, output_bfd, error_message);
abort ();
}
static boolean
mips_elf64_assign_gp (output_bfd, pgp)
bfd *output_bfd;
bfd_vma *pgp;
{
unsigned int count;
asymbol **sym;
unsigned int i;
*pgp = _bfd_get_gp_value (output_bfd);
if (*pgp)
return true;
count = bfd_get_symcount (output_bfd);
sym = bfd_get_outsymbols (output_bfd);
if (sym == (asymbol **) NULL)
i = count;
else
{
for (i = 0; i < count; i++, sym++)
{
register CONST char *name;
name = bfd_asymbol_name (*sym);
if (*name == '_' && strcmp (name, "_gp") == 0)
{
*pgp = bfd_asymbol_value (*sym);
_bfd_set_gp_value (output_bfd, *pgp);
break;
}
}
}
if (i >= count)
{
*pgp = 4;
_bfd_set_gp_value (output_bfd, *pgp);
return false;
}
return true;
}
static bfd_reloc_status_type
mips_elf64_final_gp (output_bfd, symbol, relocateable, error_message, pgp)
bfd *output_bfd;
asymbol *symbol;
boolean relocateable;
char **error_message;
bfd_vma *pgp;
{
if (bfd_is_und_section (symbol->section)
&& ! relocateable)
{
*pgp = 0;
return bfd_reloc_undefined;
}
*pgp = _bfd_get_gp_value (output_bfd);
if (*pgp == 0
&& (! relocateable
|| (symbol->flags & BSF_SECTION_SYM) != 0))
{
if (relocateable)
{
*pgp = symbol->section->output_section->vma + 0x4000;
_bfd_set_gp_value (output_bfd, *pgp);
}
else if (!mips_elf64_assign_gp (output_bfd, pgp))
{
*error_message =
(char *) _("GP relative relocation when _gp not defined");
return bfd_reloc_dangerous;
}
}
return bfd_reloc_ok;
}
bfd_reloc_status_type
mips_elf64_gprel16_reloc (abfd, reloc_entry, symbol, data, input_section,
output_bfd, error_message)
bfd *abfd;
arelent *reloc_entry;
asymbol *symbol;
PTR data;
asection *input_section;
bfd *output_bfd;
char **error_message;
{
boolean relocateable;
bfd_reloc_status_type ret;
bfd_vma gp;
if (output_bfd != (bfd *) NULL
&& (symbol->flags & BSF_SECTION_SYM) == 0
&& reloc_entry->addend == 0)
{
reloc_entry->address += input_section->output_offset;
return bfd_reloc_ok;
}
if (output_bfd != (bfd *) NULL)
relocateable = true;
else
{
relocateable = false;
output_bfd = symbol->section->output_section->owner;
}
ret = mips_elf64_final_gp (output_bfd, symbol, relocateable, error_message,
&gp);
if (ret != bfd_reloc_ok)
return ret;
return gprel16_with_gp (abfd, symbol, reloc_entry, input_section,
relocateable, data, gp);
}
static bfd_reloc_status_type
gprel16_with_gp (abfd, symbol, reloc_entry, input_section, relocateable, data,
gp)
bfd *abfd;
asymbol *symbol;
arelent *reloc_entry;
asection *input_section;
boolean relocateable;
PTR data;
bfd_vma gp;
{
bfd_vma relocation;
unsigned long insn;
unsigned long val;
if (bfd_is_com_section (symbol->section))
relocation = 0;
else
relocation = symbol->value;
relocation += symbol->section->output_section->vma;
relocation += symbol->section->output_offset;
if (reloc_entry->address > input_section->_cooked_size)
return bfd_reloc_outofrange;
insn = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
if (reloc_entry->howto->src_mask == 0)
{
val = reloc_entry->addend;
}
else
{
val = ((insn & 0xffff) + reloc_entry->addend) & 0xffff;
if (val & 0x8000)
val -= 0x10000;
}
if (! relocateable
|| (symbol->flags & BSF_SECTION_SYM) != 0)
val += relocation - gp;
insn = (insn & ~0xffff) | (val & 0xffff);
bfd_put_32 (abfd, insn, (bfd_byte *) data + reloc_entry->address);
if (relocateable)
reloc_entry->address += input_section->output_offset;
else if ((long) val >= 0x8000 || (long) val < -0x8000)
return bfd_reloc_overflow;
return bfd_reloc_ok;
}
bfd_reloc_status_type
mips_elf64_gprel16_reloca (abfd, reloc_entry, symbol, data, input_section,
output_bfd, error_message)
bfd *abfd;
arelent *reloc_entry;
asymbol *symbol;
PTR data ATTRIBUTE_UNUSED;
asection *input_section;
bfd *output_bfd;
char **error_message;
{
boolean relocateable;
bfd_vma gp;
BFD_ASSERT (reloc_entry->howto->src_mask == 0);
if (output_bfd != (bfd *) NULL
&& (symbol->flags & BSF_SECTION_SYM) == 0
&& reloc_entry->addend == 0)
{
reloc_entry->address += input_section->output_offset;
return bfd_reloc_ok;
}
if (output_bfd != (bfd *) NULL)
relocateable = true;
else
{
relocateable = false;
output_bfd = symbol->section->output_section->owner;
}
if (prev_reloc_address != reloc_entry->address)
prev_reloc_address = reloc_entry->address;
else
{
mips_elf64_final_gp (output_bfd, symbol, relocateable, error_message,
&gp);
prev_reloc_addend = reloc_entry->addend + reloc_entry->address - gp;
if (symbol->flags & BSF_LOCAL)
prev_reloc_addend += _bfd_get_gp_value (abfd);
}
return bfd_reloc_ok;
}
bfd_reloc_status_type
mips_elf64_literal_reloc (abfd, reloc_entry, symbol, data, input_section,
output_bfd, error_message)
bfd *abfd;
arelent *reloc_entry;
asymbol *symbol;
PTR data;
asection *input_section;
bfd *output_bfd;
char **error_message;
{
if (output_bfd != (bfd *) NULL
&& (symbol->flags & BSF_SECTION_SYM) == 0
&& (! reloc_entry->howto->partial_inplace
|| reloc_entry->addend == 0))
{
reloc_entry->address += input_section->output_offset;
return bfd_reloc_ok;
}
return mips_elf64_gprel16_reloc (abfd, reloc_entry, symbol, data,
input_section, output_bfd, error_message);
}
bfd_reloc_status_type
mips_elf64_gprel32_reloc (abfd,
reloc_entry,
symbol,
data,
input_section,
output_bfd,
error_message)
bfd *abfd;
arelent *reloc_entry;
asymbol *symbol;
PTR data;
asection *input_section;
bfd *output_bfd;
char **error_message;
{
boolean relocateable;
bfd_reloc_status_type ret;
bfd_vma gp;
bfd_vma relocation;
unsigned long val;
if (output_bfd != (bfd *) NULL
&& (symbol->flags & BSF_SECTION_SYM) == 0
&& reloc_entry->addend == 0)
{
*error_message = (char *)
_("32bits gp relative relocation occurs for an external symbol");
return bfd_reloc_outofrange;
}
if (output_bfd != (bfd *) NULL)
{
relocateable = true;
gp = _bfd_get_gp_value (output_bfd);
}
else
{
relocateable = false;
output_bfd = symbol->section->output_section->owner;
ret = mips_elf64_final_gp (output_bfd, symbol, relocateable,
error_message, &gp);
if (ret != bfd_reloc_ok)
return ret;
}
if (bfd_is_com_section (symbol->section))
relocation = 0;
else
relocation = symbol->value;
relocation += symbol->section->output_section->vma;
relocation += symbol->section->output_offset;
if (reloc_entry->address > input_section->_cooked_size)
return bfd_reloc_outofrange;
if (reloc_entry->howto->src_mask == 0)
{
val = 0;
}
else
val = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
val += reloc_entry->addend;
if (! relocateable
|| (symbol->flags & BSF_SECTION_SYM) != 0)
val += relocation - gp;
bfd_put_32 (abfd, val, (bfd_byte *) data + reloc_entry->address);
if (relocateable)
reloc_entry->address += input_section->output_offset;
return bfd_reloc_ok;
}
bfd_reloc_status_type
mips_elf64_shift6_reloc (abfd, reloc_entry, symbol, data, input_section,
output_bfd, error_message)
bfd *abfd ATTRIBUTE_UNUSED;
arelent *reloc_entry;
asymbol *symbol;
PTR data ATTRIBUTE_UNUSED;
asection *input_section;
bfd *output_bfd;
char **error_message ATTRIBUTE_UNUSED;
{
if (output_bfd != (bfd *) NULL
&& (symbol->flags & BSF_SECTION_SYM) == 0
&& (! reloc_entry->howto->partial_inplace
|| reloc_entry->addend == 0))
{
reloc_entry->address += input_section->output_offset;
return bfd_reloc_ok;
}
reloc_entry->addend = (reloc_entry->addend & 0x00007c0)
| (reloc_entry->addend & 0x00000800) >> 9;
return bfd_reloc_continue;
}
static int
mips_elf64_additional_program_headers (abfd)
bfd *abfd;
{
int ret = 0;
if (bfd_get_section_by_name (abfd, ".MIPS.options"))
++ret;
return ret;
}
static reloc_howto_type *
mips_elf64_reloc_type_lookup (abfd, code)
bfd *abfd ATTRIBUTE_UNUSED;
bfd_reloc_code_real_type code;
{
reloc_howto_type *howto_table = mips_elf64_howto_table_rela;
switch (code)
{
case BFD_RELOC_NONE:
return &howto_table[R_MIPS_NONE];
case BFD_RELOC_16:
return &howto_table[R_MIPS_16];
case BFD_RELOC_32:
return &howto_table[R_MIPS_32];
case BFD_RELOC_64:
case BFD_RELOC_CTOR:
if (bfd_arch_bits_per_address (abfd) == 32)
return &howto_table[R_MIPS_32];
else
return &howto_table[R_MIPS_64];
case BFD_RELOC_16_PCREL:
return &howto_table[R_MIPS_PC16];
case BFD_RELOC_HI16_S:
return &howto_table[R_MIPS_HI16];
case BFD_RELOC_LO16:
return &howto_table[R_MIPS_LO16];
case BFD_RELOC_GPREL16:
return &howto_table[R_MIPS_GPREL16];
case BFD_RELOC_GPREL32:
return &howto_table[R_MIPS_GPREL32];
case BFD_RELOC_MIPS_JMP:
return &howto_table[R_MIPS_26];
case BFD_RELOC_MIPS_LITERAL:
return &howto_table[R_MIPS_LITERAL];
case BFD_RELOC_MIPS_GOT16:
return &howto_table[R_MIPS_GOT16];
case BFD_RELOC_MIPS_CALL16:
return &howto_table[R_MIPS_CALL16];
case BFD_RELOC_MIPS_SHIFT5:
return &howto_table[R_MIPS_SHIFT5];
case BFD_RELOC_MIPS_SHIFT6:
return &howto_table[R_MIPS_SHIFT6];
case BFD_RELOC_MIPS_GOT_DISP:
return &howto_table[R_MIPS_GOT_DISP];
case BFD_RELOC_MIPS_GOT_PAGE:
return &howto_table[R_MIPS_GOT_PAGE];
case BFD_RELOC_MIPS_GOT_OFST:
return &howto_table[R_MIPS_GOT_OFST];
case BFD_RELOC_MIPS_GOT_HI16:
return &howto_table[R_MIPS_GOT_HI16];
case BFD_RELOC_MIPS_GOT_LO16:
return &howto_table[R_MIPS_GOT_LO16];
case BFD_RELOC_MIPS_SUB:
return &howto_table[R_MIPS_SUB];
case BFD_RELOC_MIPS_INSERT_A:
return &howto_table[R_MIPS_INSERT_A];
case BFD_RELOC_MIPS_INSERT_B:
return &howto_table[R_MIPS_INSERT_B];
case BFD_RELOC_MIPS_DELETE:
return &howto_table[R_MIPS_DELETE];
case BFD_RELOC_MIPS_HIGHEST:
return &howto_table[R_MIPS_HIGHEST];
case BFD_RELOC_MIPS_HIGHER:
return &howto_table[R_MIPS_HIGHER];
case BFD_RELOC_MIPS_CALL_HI16:
return &howto_table[R_MIPS_CALL_HI16];
case BFD_RELOC_MIPS_CALL_LO16:
return &howto_table[R_MIPS_CALL_LO16];
case BFD_RELOC_MIPS_SCN_DISP:
return &howto_table[R_MIPS_SCN_DISP];
case BFD_RELOC_MIPS_REL16:
return &howto_table[R_MIPS_REL16];
case BFD_RELOC_MIPS_RELGOT:
return &howto_table[R_MIPS_RELGOT];
case BFD_RELOC_MIPS_JALR:
return &howto_table[R_MIPS_JALR];
default:
bfd_set_error (bfd_error_bad_value);
return NULL;
}
}
static void
mips_elf64_info_to_howto_rel (abfd, cache_ptr, dst)
bfd *abfd ATTRIBUTE_UNUSED;
arelent *cache_ptr ATTRIBUTE_UNUSED;
Elf64_Internal_Rel *dst ATTRIBUTE_UNUSED;
{
BFD_ASSERT (0);
}
static void
mips_elf64_info_to_howto_rela (abfd, cache_ptr, dst)
bfd *abfd ATTRIBUTE_UNUSED;
arelent *cache_ptr ATTRIBUTE_UNUSED;
Elf64_Internal_Rela *dst ATTRIBUTE_UNUSED;
{
BFD_ASSERT (0);
}
static long
mips_elf64_get_reloc_upper_bound (abfd, sec)
bfd *abfd ATTRIBUTE_UNUSED;
asection *sec;
{
return (sec->reloc_count * 3 + 1) * sizeof (arelent *);
}
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;
bfd_vma count;
bfd_vma i;
int entsize;
reloc_howto_type *howto_table;
allocated = (PTR) bfd_malloc (rel_hdr->sh_size);
if (allocated == NULL)
return false;
if (bfd_seek (abfd, rel_hdr->sh_offset, SEEK_SET) != 0
|| (bfd_bread (allocated, rel_hdr->sh_size, abfd) != rel_hdr->sh_size))
goto error_return;
native_relocs = (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;
}
used_sym = false;
used_ssym = false;
for (ir = 0; ir < 3; ir++)
{
enum elf_mips_reloc_type type;
switch (ir)
{
default:
abort ();
case 0:
type = (enum elf_mips_reloc_type) rela.r_type;
break;
case 1:
type = (enum elf_mips_reloc_type) rela.r_type2;
break;
case 2:
type = (enum elf_mips_reloc_type) rela.r_type3;
break;
}
if (type == R_MIPS_NONE)
{
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;
}
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:
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;
}
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;
}
static boolean
mips_elf64_slurp_reloc_table (abfd, asect, symbols, dynamic)
bfd *abfd;
asection *asect;
asymbol **symbols;
boolean dynamic;
{
bfd_size_type amt;
struct bfd_elf_section_data * const d = elf_section_data (asect);
if (dynamic)
{
bfd_set_error (bfd_error_invalid_operation);
return false;
}
if (asect->relocation != NULL
|| (asect->flags & SEC_RELOC) == 0
|| asect->reloc_count == 0)
return true;
amt = asect->reloc_count;
amt *= 3 * sizeof (arelent);
asect->relocation = (arelent *) bfd_alloc (abfd, amt);
if (asect->relocation == NULL)
return false;
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;
}
static void
mips_elf64_write_relocs (abfd, sec, data)
bfd *abfd;
asection *sec;
PTR data;
{
boolean *failedp = (boolean *) data;
int count;
Elf_Internal_Shdr *rel_hdr;
unsigned int idx;
if (*failedp)
return;
if ((sec->flags & SEC_RELOC) == 0)
return;
if (sec->reloc_count == 0)
return;
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;
++idx;
}
}
rel_hdr = &elf_section_data (sec)->rel_hdr;
if (rel_hdr->sh_entsize == sizeof(Elf64_Mips_External_Rel))
mips_elf64_write_rel (abfd, sec, rel_hdr, &count, data);
else if (rel_hdr->sh_entsize == sizeof(Elf64_Mips_External_Rela))
mips_elf64_write_rela (abfd, sec, rel_hdr, &count, data);
else
BFD_ASSERT (0);
}
static void
mips_elf64_write_rel (abfd, sec, rel_hdr, count, data)
bfd *abfd;
asection *sec;
Elf_Internal_Shdr *rel_hdr;
int *count;
PTR data;
{
boolean *failedp = (boolean *) data;
Elf64_Mips_External_Rel *ext_rel;
unsigned int idx;
asymbol *last_sym = 0;
int last_sym_idx = 0;
rel_hdr->sh_size = (bfd_vma)(rel_hdr->sh_entsize * *count);
rel_hdr->contents = (PTR) bfd_alloc (abfd, rel_hdr->sh_size);
if (rel_hdr->contents == NULL)
{
*failedp = true;
return;
}
ext_rel = (Elf64_Mips_External_Rel *) rel_hdr->contents;
for (idx = 0; idx < sec->reloc_count; idx++, ext_rel++)
{
arelent *ptr;
Elf64_Mips_Internal_Rel int_rel;
asymbol *sym;
int n;
unsigned int i;
ptr = sec->orelocation[idx];
if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0)
int_rel.r_offset = ptr->address;
else
int_rel.r_offset = ptr->address + sec->vma;
sym = *ptr->sym_ptr_ptr;
if (sym == last_sym)
n = last_sym_idx;
else
{
last_sym = sym;
n = _bfd_elf_symbol_from_bfd_symbol (abfd, &sym);
if (n < 0)
{
*failedp = true;
return;
}
last_sym_idx = n;
}
int_rel.r_sym = n;
int_rel.r_ssym = RSS_UNDEF;
if ((*ptr->sym_ptr_ptr)->the_bfd->xvec != abfd->xvec
&& ! _bfd_elf_validate_reloc (abfd, ptr))
{
*failedp = true;
return;
}
int_rel.r_type = ptr->howto->type;
int_rel.r_type2 = (int) R_MIPS_NONE;
int_rel.r_type3 = (int) R_MIPS_NONE;
for (i = 0; i < 2; i++)
{
arelent *r;
if (idx + 1 >= sec->reloc_count)
break;
r = sec->orelocation[idx + 1];
if (r->address != ptr->address
|| ! bfd_is_abs_section ((*r->sym_ptr_ptr)->section)
|| (*r->sym_ptr_ptr)->value != 0)
break;
if (i == 0)
int_rel.r_type2 = r->howto->type;
else
int_rel.r_type3 = r->howto->type;
++idx;
}
mips_elf64_swap_reloc_out (abfd, &int_rel, ext_rel);
}
BFD_ASSERT (ext_rel - (Elf64_Mips_External_Rel *) rel_hdr->contents
== *count);
}
static void
mips_elf64_write_rela (abfd, sec, rela_hdr, count, data)
bfd *abfd;
asection *sec;
Elf_Internal_Shdr *rela_hdr;
int *count;
PTR data;
{
boolean *failedp = (boolean *) data;
Elf64_Mips_External_Rela *ext_rela;
unsigned int idx;
asymbol *last_sym = 0;
int last_sym_idx = 0;
rela_hdr->sh_size = (bfd_vma)(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];
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;
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);
}
struct mips_elf64_got_info
{
struct elf_link_hash_entry *global_gotsym;
unsigned int global_gotno;
unsigned int local_gotno;
unsigned int assigned_gotno;
};
struct mips_elf64_link_hash_entry
{
struct elf_link_hash_entry root;
EXTR esym;
unsigned int possibly_dynamic_relocs;
boolean readonly_reloc;
unsigned int min_dyn_reloc_index;
boolean no_fn_stub;
asection *fn_stub;
boolean need_fn_stub;
asection *call_stub;
asection *call_fp_stub;
};
#define FN_STUB ".mips16.fn."
#define CALL_STUB ".mips16.call."
#define CALL_FP_STUB ".mips16.call.fp."
struct mips_elf64_link_hash_table
{
struct elf_link_hash_table root;
boolean mips16_stubs_seen;
};
#define mips_elf64_link_hash_lookup(table, string, create, copy, follow) \
((struct mips_elf64_link_hash_entry *) \
elf_link_hash_lookup (&(table)->root, (string), (create), \
(copy), (follow)))
#define mips_elf64_link_hash_traverse(table, func, info) \
(elf_link_hash_traverse \
(&(table)->root, \
(boolean (*) PARAMS ((struct elf_link_hash_entry *, PTR))) (func), \
(info)))
#define mips_elf64_hash_table(p) \
((struct mips_elf64_link_hash_table *) ((p)->hash))
static struct bfd_hash_entry *
mips_elf64_link_hash_newfunc (entry, table, string)
struct bfd_hash_entry *entry;
struct bfd_hash_table *table;
const char *string;
{
struct mips_elf64_link_hash_entry *ret =
(struct mips_elf64_link_hash_entry *) entry;
if (ret == (struct mips_elf64_link_hash_entry *) NULL)
ret = ((struct mips_elf64_link_hash_entry *)
bfd_hash_allocate (table,
sizeof (struct mips_elf64_link_hash_entry)));
if (ret == (struct mips_elf64_link_hash_entry *) NULL)
return (struct bfd_hash_entry *) ret;
ret = ((struct mips_elf64_link_hash_entry *)
_bfd_elf_link_hash_newfunc ((struct bfd_hash_entry *) ret,
table, string));
if (ret != (struct mips_elf64_link_hash_entry *) NULL)
{
memset (&ret->esym, 0, sizeof (EXTR));
ret->esym.ifd = -2;
ret->possibly_dynamic_relocs = 0;
ret->readonly_reloc = false;
ret->min_dyn_reloc_index = 0;
ret->no_fn_stub = false;
ret->fn_stub = NULL;
ret->need_fn_stub = false;
ret->call_stub = NULL;
ret->call_fp_stub = NULL;
}
return (struct bfd_hash_entry *) ret;
}
struct bfd_link_hash_table *
mips_elf64_link_hash_table_create (abfd)
bfd *abfd;
{
struct mips_elf64_link_hash_table *ret;
ret = ((struct mips_elf64_link_hash_table *)
bfd_alloc (abfd, sizeof (struct mips_elf64_link_hash_table)));
if (ret == (struct mips_elf64_link_hash_table *) NULL)
return NULL;
if (! _bfd_elf_link_hash_table_init (&ret->root, abfd,
mips_elf64_link_hash_newfunc))
{
bfd_release (abfd, ret);
return NULL;
}
ret->mips16_stubs_seen = false;
return &ret->root.root;
}
static bfd_vma
mips_elf64_got_offset_from_index (dynobj, output_bfd, index)
bfd *dynobj;
bfd *output_bfd;
bfd_vma index;
{
asection *sgot;
bfd_vma gp;
sgot = bfd_get_section_by_name (dynobj, ".got");
gp = _bfd_get_gp_value (output_bfd);
return (sgot->output_section->vma + sgot->output_offset + index -
gp);
}
static struct mips_elf64_got_info *
_mips_elf64_got_info (abfd, sgotp)
bfd *abfd;
asection **sgotp;
{
asection *sgot;
struct mips_elf64_got_info *g;
sgot = bfd_get_section_by_name (abfd, ".got");
BFD_ASSERT (sgot != NULL);
BFD_ASSERT (elf_section_data (sgot) != NULL);
g = (struct mips_elf64_got_info *) elf_section_data (sgot)->tdata;
BFD_ASSERT (g != NULL);
if (sgotp)
*sgotp = sgot;
return g;
}
static bfd_vma
mips_elf64_sign_extend (value, bits)
bfd_vma value;
int bits;
{
if (value & ((bfd_vma)1 << (bits - 1)))
value |= ((bfd_vma) - 1) << bits;
return value;
}
static boolean
mips_elf64_overflow_p (value, bits)
bfd_vma value;
int bits;
{
bfd_signed_vma svalue = (bfd_signed_vma) value;
if (svalue > (1 << (bits - 1)) - 1)
return true;
else if (svalue < -(1 << (bits - 1)))
return true;
return false;
}
static bfd_vma
mips_elf64_global_got_index (abfd, h)
bfd *abfd;
struct elf_link_hash_entry *h;
{
bfd_vma index;
asection *sgot;
struct mips_elf64_got_info *g;
g = _mips_elf64_got_info (abfd, &sgot);
BFD_ASSERT (h->dynindx >= g->global_gotsym->dynindx);
index = ((h->dynindx - g->global_gotsym->dynindx + g->local_gotno)
* (get_elf_backend_data (abfd)->s->arch_size / 8));
BFD_ASSERT (index < sgot->_raw_size);
return index;
}
struct mips_elf64_hash_sort_data
{
struct elf_link_hash_entry *low;
long min_got_dynindx;
long max_non_got_dynindx;
};
static boolean
mips_elf64_sort_hash_table_f (h, data)
struct mips_elf64_link_hash_entry *h;
PTR data;
{
struct mips_elf64_hash_sort_data *hsd
= (struct mips_elf64_hash_sort_data *) data;
if (h->root.dynindx == -1)
return true;
if (h->root.got.offset != 1)
h->root.dynindx = hsd->max_non_got_dynindx++;
else
{
h->root.dynindx = --hsd->min_got_dynindx;
hsd->low = (struct elf_link_hash_entry *) h;
}
return true;
}
static boolean
mips_elf64_sort_hash_table (info, max_local)
struct bfd_link_info *info;
unsigned long max_local;
{
struct mips_elf64_hash_sort_data hsd;
struct mips_elf64_got_info *g;
bfd *dynobj;
dynobj = elf_hash_table (info)->dynobj;
hsd.low = NULL;
hsd.min_got_dynindx = elf_hash_table (info)->dynsymcount;
hsd.max_non_got_dynindx = max_local;
mips_elf64_link_hash_traverse (((struct mips_elf64_link_hash_table *)
elf_hash_table (info)),
mips_elf64_sort_hash_table_f,
&hsd);
BFD_ASSERT (hsd.max_non_got_dynindx <= hsd.min_got_dynindx);
g = _mips_elf64_got_info (dynobj, NULL);
g->global_gotsym = hsd.low;
return true;
}
#if 0
static void
mips_elf64_swap_msym_in (abfd, ex, in)
bfd *abfd;
const Elf32_External_Msym *ex;
Elf32_Internal_Msym *in;
{
in->ms_hash_value = H_GET_32 (abfd, ex->ms_hash_value);
in->ms_info = H_GET_32 (abfd, ex->ms_info);
}
#endif
static void
mips_elf64_swap_msym_out (abfd, in, ex)
bfd *abfd;
const Elf32_Internal_Msym *in;
Elf32_External_Msym *ex;
{
H_PUT_32 (abfd, in->ms_hash_value, ex->ms_hash_value);
H_PUT_32 (abfd, in->ms_info, ex->ms_info);
}
static bfd_vma
mips_elf64_create_local_got_entry (abfd, g, sgot, value)
bfd *abfd;
struct mips_elf64_got_info *g;
asection *sgot;
bfd_vma value;
{
CONST bfd_vma got_size = get_elf_backend_data (abfd)->s->arch_size / 8;
if (g->assigned_gotno >= g->local_gotno)
{
(*_bfd_error_handler)
(_("not enough GOT space for local GOT entries"));
bfd_set_error (bfd_error_bad_value);
return (bfd_vma) -1;
}
bfd_put_64 (abfd, value, (sgot->contents + got_size * g->assigned_gotno));
return got_size * g->assigned_gotno++;
}
static bfd_vma
mips_elf64_local_got_index (abfd, info, value)
bfd *abfd;
struct bfd_link_info *info;
bfd_vma value;
{
CONST bfd_vma got_size = get_elf_backend_data (abfd)->s->arch_size / 8;
asection *sgot;
struct mips_elf64_got_info *g;
bfd_byte *entry;
g = _mips_elf64_got_info (elf_hash_table (info)->dynobj, &sgot);
for (entry = (sgot->contents + got_size * MIPS_RESERVED_GOTNO);
entry != sgot->contents + got_size * g->assigned_gotno;
entry += got_size)
{
bfd_vma address = bfd_get_64 (abfd, entry);
if (address == value)
return entry - sgot->contents;
}
return mips_elf64_create_local_got_entry (abfd, g, sgot, value);
}
static bfd_vma
mips_elf64_got_page (abfd, info, value, offsetp)
bfd *abfd;
struct bfd_link_info *info;
bfd_vma value;
bfd_vma *offsetp;
{
CONST bfd_vma got_size = get_elf_backend_data (abfd)->s->arch_size / 8;
asection *sgot;
struct mips_elf64_got_info *g;
bfd_byte *entry;
bfd_byte *last_entry;
bfd_vma index = 0;
bfd_vma address;
g = _mips_elf64_got_info (elf_hash_table (info)->dynobj, &sgot);
last_entry = sgot->contents + got_size * g->assigned_gotno;
for (entry = (sgot->contents + got_size * MIPS_RESERVED_GOTNO);
entry != last_entry;
entry += got_size)
{
address = bfd_get_64 (abfd, entry);
if (!mips_elf64_overflow_p (value - address, 16))
{
index = entry - sgot->contents;
break;
}
}
if (entry == last_entry)
index = mips_elf64_create_local_got_entry (abfd, g, sgot, value);
if (offsetp)
{
address = bfd_get_64 (abfd, entry);
*offsetp = value - address;
}
return index;
}
static bfd_vma
mips_elf64_got16_entry (abfd, info, value, external)
bfd *abfd;
struct bfd_link_info *info;
bfd_vma value;
boolean external;
{
CONST bfd_vma got_size = get_elf_backend_data (abfd)->s->arch_size / 8;
asection *sgot;
struct mips_elf64_got_info *g;
bfd_byte *entry;
bfd_byte *last_entry;
bfd_vma index = 0;
bfd_vma address;
if (! external)
{
value = mips_elf64_high (value) << 16;
}
g = _mips_elf64_got_info (elf_hash_table (info)->dynobj, &sgot);
last_entry = sgot->contents + got_size * g->assigned_gotno;
for (entry = (sgot->contents + got_size * MIPS_RESERVED_GOTNO);
entry != last_entry;
entry += got_size)
{
address = bfd_get_64 (abfd, entry);
if (address == value)
{
index = entry - sgot->contents;
break;
}
}
if (entry == last_entry)
index = mips_elf64_create_local_got_entry (abfd, g, sgot, value);
return index;
}
static boolean
mips_elf64_local_relocation_p (input_bfd, relocation, local_sections,
check_forced)
bfd *input_bfd;
const Elf_Internal_Rela *relocation;
asection **local_sections;
boolean check_forced;
{
unsigned long r_symndx;
Elf_Internal_Shdr *symtab_hdr;
struct mips_elf64_link_hash_entry* h;
size_t extsymoff;
r_symndx = ELF64_R_SYM (relocation->r_info);
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
extsymoff = (elf_bad_symtab (input_bfd)) ? 0 : symtab_hdr->sh_info;
if (r_symndx < extsymoff)
return true;
if (elf_bad_symtab (input_bfd) && local_sections[r_symndx] != NULL)
return true;
if (check_forced)
{
h = (struct mips_elf64_link_hash_entry *)
elf_sym_hashes (input_bfd) [r_symndx - extsymoff];
while (h->root.root.type == bfd_link_hash_indirect
|| h->root.root.type == bfd_link_hash_warning)
h = (struct mips_elf64_link_hash_entry *) h->root.root.u.i.link;
if ((h->root.elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0)
return true;
}
return false;
}
static const Elf_Internal_Rela *
mips_elf64_next_relocation (r_type, relocation, relend)
unsigned int r_type;
const Elf_Internal_Rela *relocation;
const Elf_Internal_Rela *relend;
{
while (relocation < relend)
{
if (ELF64_MIPS_R_TYPE (relocation->r_info) == r_type)
return relocation;
++relocation;
}
bfd_set_error (bfd_error_bad_value);
return NULL;
}
static boolean
mips_elf64_create_dynamic_relocation (output_bfd, info, rel, h, sec,
symbol, addendp, input_section)
bfd *output_bfd;
struct bfd_link_info *info;
const Elf_Internal_Rela *rel;
struct mips_elf64_link_hash_entry *h;
asection *sec;
bfd_vma symbol;
bfd_vma *addendp;
asection *input_section;
{
Elf_Internal_Rel outrel[3];
boolean skip;
asection *sreloc;
bfd *dynobj;
int r_type;
r_type = ELF64_MIPS_R_TYPE (rel->r_info);
dynobj = elf_hash_table (info)->dynobj;
sreloc = bfd_get_section_by_name (dynobj, ".rel.dyn");
BFD_ASSERT (sreloc != NULL);
BFD_ASSERT (sreloc->contents != NULL);
BFD_ASSERT ((sreloc->reloc_count
* get_elf_backend_data (output_bfd)->s->sizeof_rel)
< sreloc->_raw_size);
skip = false;
outrel[0].r_offset = _bfd_elf_section_offset (output_bfd, info,
input_section,
rel[0].r_offset);
if (elf_section_data (input_section)->sec_info_type != ELF_INFO_TYPE_STABS)
{
outrel[1].r_offset = rel[1].r_offset;
outrel[2].r_offset = rel[2].r_offset;
}
else
{
outrel[1].r_offset = outrel[0].r_offset;
outrel[2].r_offset = outrel[0].r_offset;
if (outrel[0].r_offset == (bfd_vma) -1)
skip = true;
}
if (skip)
memset (outrel, 0, sizeof (Elf_Internal_Rel) * 3);
else
{
long indx;
bfd_vma section_offset;
if (h != NULL
&& (! info->symbolic || (h->root.elf_link_hash_flags
& ELF_LINK_HASH_DEF_REGULAR) == 0))
{
indx = h->root.dynindx;
if (indx == -1)
indx = 0;
}
else
{
if (sec != NULL && bfd_is_abs_section (sec))
indx = 0;
else if (sec == NULL || sec->owner == NULL)
{
bfd_set_error (bfd_error_bad_value);
return false;
}
else
{
indx = elf_section_data (sec->output_section)->dynindx;
if (indx == 0)
abort ();
}
section_offset = symbol - sec->output_section->vma;
*addendp += section_offset;
symbol = sec->output_section->vma;
}
if (!indx && r_type != R_MIPS_REL32)
*addendp += symbol;
outrel[0].r_info = ELF64_R_INFO (indx, R_MIPS_REL32);
outrel[0].r_offset += (input_section->output_section->vma
+ input_section->output_offset);
outrel[1].r_offset += (input_section->output_section->vma
+ input_section->output_offset);
outrel[2].r_offset += (input_section->output_section->vma
+ input_section->output_offset);
}
mips_elf64_be_swap_reloc_out (output_bfd, outrel,
(sreloc->contents
+ sreloc->reloc_count
* sizeof (Elf64_Mips_External_Rel)));
if (h != NULL
&& (h->min_dyn_reloc_index == 0
|| sreloc->reloc_count < h->min_dyn_reloc_index))
h->min_dyn_reloc_index = sreloc->reloc_count;
++sreloc->reloc_count;
elf_section_data (input_section->output_section)->this_hdr.sh_flags
|= SHF_WRITE;
return true;
}
static bfd_reloc_status_type
mips_elf64_calculate_relocation (abfd, input_bfd, input_section, info,
relocation, addend, howto, local_syms,
local_sections, valuep, namep, require_jalxp)
bfd *abfd;
bfd *input_bfd;
asection *input_section;
struct bfd_link_info *info;
const Elf_Internal_Rela *relocation;
bfd_vma addend;
reloc_howto_type *howto;
Elf_Internal_Sym *local_syms;
asection **local_sections;
bfd_vma *valuep;
const char **namep;
boolean *require_jalxp;
{
bfd_vma value;
bfd_vma symbol = 0;
bfd_vma gp = (bfd_vma) - 1;
bfd_vma p;
bfd_vma gp0 = (bfd_vma) - 1;
bfd_vma g = (bfd_vma) - 1;
asection *sec = NULL;
struct mips_elf64_link_hash_entry* h = NULL;
boolean local_p;
Elf_Internal_Shdr *symtab_hdr;
size_t extsymoff;
unsigned long r_symndx;
int r_type;
boolean overflowed_p;
boolean target_is_16_bit_code_p = false;
r_symndx = ELF64_R_SYM (relocation->r_info);
r_type = ELF64_MIPS_R_TYPE (relocation->r_info);
p = (input_section->output_section->vma
+ input_section->output_offset
+ relocation->r_offset);
overflowed_p = false;
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
local_p = mips_elf64_local_relocation_p (input_bfd, relocation,
local_sections, false);
if (! elf_bad_symtab (input_bfd))
extsymoff = symtab_hdr->sh_info;
else
{
extsymoff = 0;
}
if (local_p)
{
Elf_Internal_Sym *sym;
sym = local_syms + r_symndx;
sec = local_sections[r_symndx];
symbol = sec->output_section->vma + sec->output_offset;
if (ELF_ST_TYPE (sym->st_info) != STT_SECTION)
symbol += sym->st_value;
if (sym->st_other == STO_MIPS16)
++symbol;
*namep = bfd_elf_string_from_elf_section (input_bfd,
symtab_hdr->sh_link,
sym->st_name);
if (*namep == '\0')
*namep = bfd_section_name (input_bfd, sec);
target_is_16_bit_code_p = (sym->st_other == STO_MIPS16);
}
else
{
h = ((struct mips_elf64_link_hash_entry *)
elf_sym_hashes (input_bfd) [r_symndx - extsymoff]);
while (h->root.root.type == bfd_link_hash_indirect
|| h->root.root.type == bfd_link_hash_warning)
h = (struct mips_elf64_link_hash_entry *) h->root.root.u.i.link;
*namep = h->root.root.root.string;
if ((h->root.root.type == bfd_link_hash_defined
|| h->root.root.type == bfd_link_hash_defweak)
&& h->root.root.u.def.section)
{
sec = h->root.root.u.def.section;
if (sec->output_section)
symbol = (h->root.root.u.def.value
+ sec->output_section->vma
+ sec->output_offset);
else
symbol = h->root.root.u.def.value;
}
else if (h->root.root.type == bfd_link_hash_undefweak)
symbol = 0;
else if (info->shared
&& (!info->symbolic || info->allow_shlib_undefined)
&& !info->no_undefined
&& ELF_ST_VISIBILITY (h->root.other) == STV_DEFAULT)
symbol = 0;
else if (strcmp (h->root.root.root.string, "_DYNAMIC_LINK") == 0 ||
strcmp (h->root.root.root.string, "_DYNAMIC_LINKING") == 0)
{
BFD_ASSERT (! info->shared);
BFD_ASSERT (bfd_get_section_by_name (abfd, ".dynamic") == NULL);
symbol = 0;
}
else
{
if (! ((*info->callbacks->undefined_symbol)
(info, h->root.root.root.string, input_bfd,
input_section, relocation->r_offset,
(!info->shared || info->no_undefined
|| ELF_ST_VISIBILITY (h->root.other)))))
return bfd_reloc_undefined;
symbol = 0;
}
target_is_16_bit_code_p = (h->root.other == STO_MIPS16);
}
if (r_type != R_MIPS16_26 && !info->relocateable
&& ((h != NULL && h->fn_stub != NULL)
|| (local_p && elf_tdata (input_bfd)->local_stubs != NULL
&& elf_tdata (input_bfd)->local_stubs[r_symndx] != NULL))
&& !mips_elf64_stub_section_p (input_bfd, input_section))
{
if (local_p)
sec = elf_tdata (input_bfd)->local_stubs[r_symndx];
else
{
BFD_ASSERT (h->need_fn_stub);
sec = h->fn_stub;
}
symbol = sec->output_section->vma + sec->output_offset;
}
else if (r_type == R_MIPS16_26 && !info->relocateable
&& h != NULL
&& (h->call_stub != NULL || h->call_fp_stub != NULL)
&& !target_is_16_bit_code_p)
{
if (h->call_stub != NULL && h->call_fp_stub != NULL)
{
asection *o;
sec = NULL;
for (o = input_bfd->sections; o != NULL; o = o->next)
{
if (strncmp (bfd_get_section_name (input_bfd, o),
CALL_FP_STUB, sizeof CALL_FP_STUB - 1) == 0)
{
sec = h->call_fp_stub;
break;
}
}
if (sec == NULL)
sec = h->call_stub;
}
else if (h->call_stub != NULL)
sec = h->call_stub;
else
sec = h->call_fp_stub;
BFD_ASSERT (sec->_raw_size > 0);
symbol = sec->output_section->vma + sec->output_offset;
}
*require_jalxp = (!info->relocateable
&& ((r_type == R_MIPS16_26) != target_is_16_bit_code_p));
local_p = mips_elf64_local_relocation_p (input_bfd, relocation,
local_sections, true);
switch (r_type)
{
case R_MIPS_CALL16:
case R_MIPS_GOT16:
case R_MIPS_GOT_DISP:
case R_MIPS_GOT_HI16:
case R_MIPS_CALL_HI16:
case R_MIPS_GOT_LO16:
case R_MIPS_CALL_LO16:
if (!local_p)
{
BFD_ASSERT (addend == 0);
g = mips_elf64_global_got_index (elf_hash_table (info)->dynobj,
(struct elf_link_hash_entry*) h);
if (! elf_hash_table(info)->dynamic_sections_created
|| (info->shared
&& (info->symbolic || h->root.dynindx == -1)
&& (h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)))
{
bfd *tmpbfd = elf_hash_table (info)->dynobj;
asection *sgot = bfd_get_section_by_name (tmpbfd, ".got");
bfd_put_64 (tmpbfd, symbol + addend, sgot->contents + g);
}
}
else if (r_type == R_MIPS_GOT16 || r_type == R_MIPS_CALL16)
break;
else
{
g = mips_elf64_local_got_index (abfd, info, symbol + addend);
if (g == (bfd_vma) -1)
return false;
}
g = mips_elf64_got_offset_from_index (elf_hash_table (info)->dynobj,
abfd, g);
break;
case R_MIPS_HI16:
case R_MIPS_LO16:
case R_MIPS_GPREL16:
case R_MIPS_GPREL32:
case R_MIPS_LITERAL:
gp0 = _bfd_get_gp_value (input_bfd);
gp = _bfd_get_gp_value (abfd);
break;
default:
break;
}
switch (r_type)
{
case R_MIPS_NONE:
return bfd_reloc_continue;
case R_MIPS_16:
value = symbol + mips_elf64_sign_extend (addend, 16);
overflowed_p = mips_elf64_overflow_p (value, 16);
break;
case R_MIPS_32:
case R_MIPS_REL32:
case R_MIPS_64:
if ((info->shared
|| (elf_hash_table (info)->dynamic_sections_created
&& h != NULL
&& ((h->root.elf_link_hash_flags
& ELF_LINK_HASH_DEF_DYNAMIC) != 0)
&& ((h->root.elf_link_hash_flags
& ELF_LINK_HASH_DEF_REGULAR) == 0)))
&& r_symndx != 0
&& (input_section->flags & SEC_ALLOC) != 0)
{
value = addend;
if (!mips_elf64_create_dynamic_relocation (abfd, info, relocation,
h, sec, symbol, &value,
input_section))
return false;
}
else
{
if (r_type != R_MIPS_REL32)
value = symbol + addend;
else
value = addend;
}
value &= howto->dst_mask;
break;
case R_MIPS_PC32:
case R_MIPS_PC64:
case R_MIPS_GNU_REL_LO16:
value = symbol + addend - p;
value &= howto->dst_mask;
break;
case R_MIPS_GNU_REL16_S2:
value = symbol + mips_elf64_sign_extend (addend << 2, 18) - p;
overflowed_p = mips_elf64_overflow_p (value, 18);
value = (value >> 2) & howto->dst_mask;
break;
case R_MIPS_GNU_REL_HI16:
value = mips_elf64_high (addend + symbol - p);
value &= howto->dst_mask;
break;
case R_MIPS16_26:
case R_MIPS_26:
if (local_p)
value = (((addend << 2) | ((p + 4) & 0xf0000000)) + symbol) >> 2;
else
value = (mips_elf64_sign_extend (addend << 2, 28) + symbol) >> 2;
value &= howto->dst_mask;
break;
case R_MIPS_HI16:
value = mips_elf64_high (addend + symbol);
value &= howto->dst_mask;
break;
case R_MIPS_LO16:
value = (addend + symbol) & 0xffff;
value &= howto->dst_mask;
break;
case R_MIPS_LITERAL:
case R_MIPS_GPREL16:
if (local_p)
value = mips_elf64_sign_extend (addend, 16) + symbol + gp0 - gp;
else
value = mips_elf64_sign_extend (addend, 16) + symbol - gp;
overflowed_p = mips_elf64_overflow_p (value, 16);
break;
case R_MIPS_PC16:
value = mips_elf64_sign_extend (addend, 16) + symbol - p;
overflowed_p = mips_elf64_overflow_p (value, 16);
value = (bfd_vma) ((bfd_signed_vma) value / 4);
break;
case R_MIPS_GOT16:
case R_MIPS_CALL16:
if (local_p)
{
boolean forced;
forced = ! mips_elf64_local_relocation_p (input_bfd, relocation,
local_sections, false);
value = mips_elf64_got16_entry (abfd, info, symbol + addend, forced);
if (value == (bfd_vma) -1)
return false;
value
= mips_elf64_got_offset_from_index (elf_hash_table (info)->dynobj,
abfd,
value);
overflowed_p = mips_elf64_overflow_p (value, 16);
break;
}
case R_MIPS_GOT_DISP:
value = g;
overflowed_p = mips_elf64_overflow_p (value, 16);
break;
case R_MIPS_GPREL32:
value = (addend + symbol + gp0 - gp) & howto->dst_mask;
break;
case R_MIPS_GOT_HI16:
case R_MIPS_CALL_HI16:
value = g;
value = mips_elf64_high (value);
value &= howto->dst_mask;
break;
case R_MIPS_GOT_LO16:
case R_MIPS_CALL_LO16:
value = g & howto->dst_mask;
break;
case R_MIPS_GOT_PAGE:
value = mips_elf64_got_page (abfd, info, symbol + addend, NULL);
if (value == (bfd_vma) -1)
return false;
value = mips_elf64_got_offset_from_index (elf_hash_table (info)->dynobj,
abfd,
value);
overflowed_p = mips_elf64_overflow_p (value, 16);
break;
case R_MIPS_GOT_OFST:
mips_elf64_got_page (abfd, info, symbol + addend, &value);
overflowed_p = mips_elf64_overflow_p (value, 16);
break;
case R_MIPS_SUB:
value = symbol - addend;
value &= howto->dst_mask;
break;
case R_MIPS_HIGHER:
value = mips_elf64_higher (addend + symbol);
value &= howto->dst_mask;
break;
case R_MIPS_HIGHEST:
value = mips_elf64_highest (addend + symbol);
value &= howto->dst_mask;
break;
case R_MIPS_SCN_DISP:
value = symbol + addend - sec->output_offset;
value &= howto->dst_mask;
break;
case R_MIPS_PJUMP:
case R_MIPS_JALR:
return bfd_reloc_continue;
case R_MIPS_GNU_VTINHERIT:
case R_MIPS_GNU_VTENTRY:
return bfd_reloc_continue;
default:
return bfd_reloc_notsupported;
}
*valuep = value;
return overflowed_p ? bfd_reloc_overflow : bfd_reloc_ok;
}
static bfd_vma
mips_elf64_obtain_contents (howto, relocation, input_bfd, contents)
reloc_howto_type *howto;
const Elf_Internal_Rela *relocation;
bfd *input_bfd;
bfd_byte *contents;
{
bfd_byte *location = contents + relocation->r_offset;
return bfd_get (8 * bfd_get_reloc_size (howto), input_bfd, location);
}
static boolean
mips_elf64_perform_relocation (info, howto, relocation, value,
input_bfd, input_section,
contents, require_jalx)
struct bfd_link_info *info;
reloc_howto_type *howto;
const Elf_Internal_Rela *relocation;
bfd_vma value;
bfd *input_bfd;
asection *input_section;
bfd_byte *contents;
boolean require_jalx;
{
bfd_vma x;
bfd_byte *location;
int r_type = ELF32_R_TYPE (relocation->r_info);
location = contents + relocation->r_offset;
x = mips_elf64_obtain_contents (howto, relocation, input_bfd, contents);
x &= ~howto->dst_mask;
if (r_type == R_MIPS16_26)
{
if (!info->relocateable)
value = (((value & 0x1f0000) << 5)
| ((value & 0x3e00000) >> 5)
| (value & 0xffff));
}
else if (r_type == R_MIPS16_GPREL)
{
value = (((value & 0x7e0) << 16)
| ((value & 0xf800) << 5)
| (value & 0x1f));
}
x |= (value & howto->dst_mask);
if (require_jalx)
{
boolean ok;
bfd_vma opcode = x >> 26;
bfd_vma jalx_opcode;
if (r_type == R_MIPS16_26)
{
ok = ((opcode == 0x6) || (opcode == 0x7));
jalx_opcode = 0x7;
}
else
{
ok = ((opcode == 0x3) || (opcode == 0x1d));
jalx_opcode = 0x1d;
}
if (!ok)
{
(*_bfd_error_handler)
(_("%s: %s+0x%lx: jump to stub routine which is not jal"),
bfd_archive_filename (input_bfd),
input_section->name,
(unsigned long) relocation->r_offset);
bfd_set_error (bfd_error_bad_value);
return false;
}
x = (x & ~(0x3f << 26)) | (jalx_opcode << 26);
}
if ((r_type == R_MIPS16_GPREL || r_type == R_MIPS16_26)
&& bfd_little_endian (input_bfd))
x = (((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16));
bfd_put (8 * bfd_get_reloc_size (howto), input_bfd, x, location);
return true;
}
static boolean
mips_elf64_stub_section_p (abfd, section)
bfd *abfd ATTRIBUTE_UNUSED;
asection *section;
{
const char *name = bfd_get_section_name (abfd, section);
return (strncmp (name, FN_STUB, sizeof FN_STUB - 1) == 0
|| strncmp (name, CALL_STUB, sizeof CALL_STUB - 1) == 0
|| strncmp (name, CALL_FP_STUB, sizeof CALL_FP_STUB - 1) == 0);
}
static boolean
mips_elf64_relocate_section (output_bfd, info, input_bfd, input_section,
contents, relocs, local_syms, local_sections)
bfd *output_bfd;
struct bfd_link_info *info;
bfd *input_bfd;
asection *input_section;
bfd_byte *contents;
Elf_Internal_Rela *relocs;
Elf_Internal_Sym *local_syms;
asection **local_sections;
{
Elf_Internal_Rela *rel;
const Elf_Internal_Rela *relend;
bfd_vma addend = 0;
boolean use_saved_addend_p = false;
struct elf_backend_data *bed;
bed = get_elf_backend_data (output_bfd);
relend = relocs + input_section->reloc_count * bed->s->int_rels_per_ext_rel;
for (rel = relocs; rel < relend; ++rel)
{
const char *name;
bfd_vma value;
reloc_howto_type *howto;
boolean require_jalx;
boolean rela_relocation_p = true;
int r_type = ELF64_MIPS_R_TYPE (rel->r_info);
const char *msg = (const char *) NULL;
howto = &mips_elf64_howto_table_rela[r_type];
if (!use_saved_addend_p)
{
Elf_Internal_Shdr *rel_hdr;
rel_hdr = &elf_section_data (input_section)->rel_hdr;
if ((size_t) (rel - relocs)
>= (NUM_SHDR_ENTRIES (rel_hdr) * bed->s->int_rels_per_ext_rel))
rel_hdr = elf_section_data (input_section)->rel_hdr2;
if (rel_hdr->sh_entsize
== (get_elf_backend_data (input_bfd)->s->sizeof_rel))
{
rela_relocation_p = false;
howto = &mips_elf64_howto_table_rel[r_type];
addend = mips_elf64_obtain_contents (howto,
rel,
input_bfd,
contents);
addend &= howto->src_mask;
if (r_type == R_MIPS_HI16
|| r_type == R_MIPS_GNU_REL_HI16
|| (r_type == R_MIPS_GOT16
&& mips_elf64_local_relocation_p (input_bfd, rel,
local_sections, false)))
{
bfd_vma l;
const Elf_Internal_Rela *lo16_relocation;
reloc_howto_type *lo16_howto;
int lo;
if (r_type == R_MIPS_GNU_REL_HI16)
lo = R_MIPS_GNU_REL_LO16;
else
lo = R_MIPS_LO16;
lo16_relocation
= mips_elf64_next_relocation (lo, rel, relend);
if (lo16_relocation == NULL)
return false;
if (rela_relocation_p == false)
lo16_howto = &mips_elf64_howto_table_rel[lo];
else
lo16_howto = &mips_elf64_howto_table_rela[lo];
l = mips_elf64_obtain_contents (lo16_howto,
lo16_relocation,
input_bfd, contents);
l &= lo16_howto->src_mask;
l = mips_elf64_sign_extend (l, 16);
addend <<= 16;
addend += l;
}
}
else
addend = rel->r_addend;
}
if (info->relocateable)
{
Elf_Internal_Sym *sym;
unsigned long r_symndx;
if (!mips_elf64_local_relocation_p (input_bfd, rel, local_sections,
false))
continue;
if (r_type == R_MIPS_GPREL16
|| r_type == R_MIPS_GPREL32
|| r_type == R_MIPS_LITERAL)
addend -= (_bfd_get_gp_value (output_bfd)
- _bfd_get_gp_value (input_bfd));
else if (r_type == R_MIPS_26 || r_type == R_MIPS_GNU_REL16_S2)
addend <<= 2;
r_symndx = ELF64_R_SYM (rel->r_info);
sym = local_syms + r_symndx;
if (ELF_ST_TYPE (sym->st_info) == STT_SECTION)
addend += local_sections[r_symndx]->output_offset;
#if 0
if (r_type == R_MIPS_HI16 || r_type == R_MIPS_GOT16
|| r_type == R_MIPS_GNU_REL_HI16)
addend = mips_elf64_high (addend);
else if (r_type == R_MIPS_HIGHER)
addend = mips_elf64_higher (addend);
else if (r_type == R_MIPS_HIGHEST)
addend = mips_elf64_highest (addend);
#endif
if (r_type == R_MIPS_26 || r_type == R_MIPS_GNU_REL16_S2)
addend >>= 2;
if (rela_relocation_p)
rel->r_addend = addend;
else
{
addend &= howto->src_mask;
if (!mips_elf64_perform_relocation (info, howto, rel, addend,
input_bfd, input_section,
contents, false))
return false;
}
continue;
}
if (rel + 1 < relend
&& rel->r_offset == rel[1].r_offset
&& ELF64_MIPS_R_TYPE (rel[1].r_info) != R_MIPS_NONE)
use_saved_addend_p = true;
else
use_saved_addend_p = false;
switch (mips_elf64_calculate_relocation (output_bfd, input_bfd,
input_section, info, rel,
addend, howto, local_syms,
local_sections, &value, &name,
&require_jalx))
{
case bfd_reloc_continue:
continue;
case bfd_reloc_undefined:
continue;
case bfd_reloc_notsupported:
msg = _("internal error: unsupported relocation error");
info->callbacks->warning
(info, msg, name, input_bfd, input_section, rel->r_offset);
return false;
case bfd_reloc_overflow:
if (use_saved_addend_p)
;
else
{
BFD_ASSERT (name != NULL);
if (! ((*info->callbacks->reloc_overflow)
(info, name, howto->name, (bfd_vma) 0,
input_bfd, input_section, rel->r_offset)))
return false;
}
break;
case bfd_reloc_ok:
break;
default:
abort ();
break;
}
if (use_saved_addend_p)
{
addend = value;
continue;
}
if (!mips_elf64_perform_relocation (info, howto, rel, value, input_bfd,
input_section, contents,
require_jalx))
return false;
}
return true;
}
boolean
mips_elf64_create_dynamic_sections (abfd, info)
bfd *abfd;
struct bfd_link_info *info;
{
flagword flags;
register asection *s;
flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
| SEC_LINKER_CREATED | SEC_READONLY);
s = bfd_get_section_by_name (abfd, ".dynamic");
if (s != NULL)
{
if (! bfd_set_section_flags (abfd, s, flags))
return false;
}
if (! mips_elf64_create_got_section (abfd, info))
return false;
if (!mips_elf64_create_msym_section (abfd))
return false;
if (bfd_get_section_by_name (abfd, ".MIPS.stubs") == NULL)
{
s = bfd_make_section (abfd, ".MIPS.stubs");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags | SEC_CODE)
|| ! bfd_set_section_alignment (abfd, s, 3))
return false;
}
return true;
}
boolean
mips_elf64_adjust_dynamic_symbol (info, h)
struct bfd_link_info *info;
struct elf_link_hash_entry *h;
{
bfd *dynobj;
struct mips_elf64_link_hash_entry *hmips;
asection *s;
dynobj = elf_hash_table (info)->dynobj;
BFD_ASSERT (dynobj != NULL
&& ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT)
|| h->weakdef != NULL
|| ((h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_DYNAMIC) != 0
&& (h->elf_link_hash_flags
& ELF_LINK_HASH_REF_REGULAR) != 0
&& (h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_REGULAR) == 0)));
hmips = (struct mips_elf64_link_hash_entry *) h;
if (! info->relocateable
&& hmips->possibly_dynamic_relocs != 0
&& (h->root.type == bfd_link_hash_defweak
|| (h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_REGULAR) == 0))
{
mips_elf64_allocate_dynamic_relocations (dynobj,
hmips->possibly_dynamic_relocs);
if (hmips->readonly_reloc)
info->flags |= DF_TEXTREL;
}
if (! hmips->no_fn_stub
&& (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0)
{
if (! elf_hash_table (info)->dynamic_sections_created)
return true;
if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
{
s = bfd_get_section_by_name (dynobj, ".MIPS.stubs");
BFD_ASSERT (s != NULL);
h->root.u.def.section = s;
h->root.u.def.value = s->_raw_size;
h->plt.offset = s->_raw_size;
s->_raw_size += MIPS_FUNCTION_STUB_SIZE;
return true;
}
}
else if ((h->type == STT_FUNC)
&& (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) == 0)
{
h->root.u.def.value = 0;
return true;
}
if (h->weakdef != NULL)
{
BFD_ASSERT (h->weakdef->root.type == bfd_link_hash_defined
|| h->weakdef->root.type == bfd_link_hash_defweak);
h->root.u.def.section = h->weakdef->root.u.def.section;
h->root.u.def.value = h->weakdef->root.u.def.value;
return true;
}
return true;
}
boolean
mips_elf64_always_size_sections (output_bfd, info)
bfd *output_bfd ATTRIBUTE_UNUSED;
struct bfd_link_info *info ATTRIBUTE_UNUSED;
{
if (info->relocateable
|| ! mips_elf64_hash_table (info)->mips16_stubs_seen)
return true;
mips_elf64_link_hash_traverse (mips_elf64_hash_table (info),
mips_elf64_check_mips16_stubs,
(PTR) NULL);
return true;
}
static boolean
mips_elf64_check_mips16_stubs (h, data)
struct mips_elf64_link_hash_entry *h;
PTR data ATTRIBUTE_UNUSED;
{
if (h->fn_stub != NULL
&& ! h->need_fn_stub)
{
h->fn_stub->_raw_size = 0;
h->fn_stub->_cooked_size = 0;
h->fn_stub->flags &= ~SEC_RELOC;
h->fn_stub->reloc_count = 0;
h->fn_stub->flags |= SEC_EXCLUDE;
}
if (h->call_stub != NULL
&& h->root.other == STO_MIPS16)
{
h->call_stub->_raw_size = 0;
h->call_stub->_cooked_size = 0;
h->call_stub->flags &= ~SEC_RELOC;
h->call_stub->reloc_count = 0;
h->call_stub->flags |= SEC_EXCLUDE;
}
if (h->call_fp_stub != NULL
&& h->root.other == STO_MIPS16)
{
h->call_fp_stub->_raw_size = 0;
h->call_fp_stub->_cooked_size = 0;
h->call_fp_stub->flags &= ~SEC_RELOC;
h->call_fp_stub->reloc_count = 0;
h->call_fp_stub->flags |= SEC_EXCLUDE;
}
return true;
}
boolean
mips_elf64_size_dynamic_sections (output_bfd, info)
bfd *output_bfd;
struct bfd_link_info *info;
{
bfd *dynobj;
asection *s;
boolean reltext;
struct mips_elf64_got_info *g = NULL;
dynobj = elf_hash_table (info)->dynobj;
BFD_ASSERT (dynobj != NULL);
if (elf_hash_table (info)->dynamic_sections_created)
{
if (! info->shared)
{
s = bfd_get_section_by_name (dynobj, ".interp");
BFD_ASSERT (s != NULL);
s->_raw_size = strlen ("/usr/lib64/libc.so.1") + 1;
s->contents = (bfd_byte *) "/usr/lib64/libc.so.1";
}
}
reltext = false;
for (s = dynobj->sections; s != NULL; s = s->next)
{
const char *name;
boolean strip;
name = bfd_get_section_name (dynobj, s);
if ((s->flags & SEC_LINKER_CREATED) == 0)
continue;
strip = false;
if (strncmp (name, ".rel", 4) == 0)
{
if (s->_raw_size == 0)
{
if (s->output_section != NULL
&& strcmp (name,
bfd_get_section_name (s->output_section->owner,
s->output_section)) == 0)
strip = true;
}
else
{
const char *outname;
asection *target;
outname = bfd_get_section_name (output_bfd,
s->output_section);
target = bfd_get_section_by_name (output_bfd, outname + 4);
if ((target != NULL
&& (target->flags & SEC_READONLY) != 0
&& (target->flags & SEC_ALLOC) != 0)
|| strcmp (outname, "rel.dyn") == 0)
reltext = true;
if (strcmp (name, "rel.dyn") != 0)
s->reloc_count = 0;
}
}
else if (strncmp (name, ".got", 4) == 0)
{
int i;
bfd_size_type loadable_size = 0;
bfd_size_type local_gotno;
bfd *sub;
BFD_ASSERT (elf_section_data (s) != NULL);
g = (struct mips_elf64_got_info *) elf_section_data (s)->tdata;
BFD_ASSERT (g != NULL);
for (sub = info->input_bfds; sub; sub = sub->link_next)
{
asection *subsection;
for (subsection = sub->sections;
subsection;
subsection = subsection->next)
{
if ((subsection->flags & SEC_ALLOC) == 0)
continue;
loadable_size += (subsection->_raw_size + 0xf) & ~0xf;
}
}
loadable_size += MIPS_FUNCTION_STUB_SIZE;
local_gotno = (loadable_size >> 16) + 5;
local_gotno *= 2;
g->local_gotno += local_gotno;
s->_raw_size += local_gotno * 8;
if (!mips_elf64_sort_hash_table (info, 1))
return false;
if (g->global_gotsym != NULL)
i = elf_hash_table (info)->dynsymcount - g->global_gotsym->dynindx;
else
i = 0;
g->global_gotno = i;
s->_raw_size += i * 8;
}
else if (strcmp (name, ".MIPS.stubs") == 0)
{
s->_raw_size += MIPS_FUNCTION_STUB_SIZE;
}
else if (strcmp (name, ".msym")
== 0)
s->_raw_size = (sizeof (Elf32_External_Msym)
* (elf_hash_table (info)->dynsymcount
+ bfd_count_sections (output_bfd)));
else if (strncmp (name, ".init", 5) != 0)
{
continue;
}
if (strip)
{
_bfd_strip_section_from_output (info, s);
continue;
}
s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->_raw_size);
if (s->contents == NULL && s->_raw_size != 0)
{
bfd_set_error (bfd_error_no_memory);
return false;
}
}
if (elf_hash_table (info)->dynamic_sections_created)
{
if (! info->shared)
{
if (!bfd_elf64_add_dynamic_entry (info, DT_MIPS_RLD_MAP, 0))
return false;
if (!SGI_COMPAT (output_bfd))
{
if (!bfd_elf64_add_dynamic_entry (info, DT_DEBUG, 0))
return false;
}
}
else
{
if (!SGI_COMPAT (output_bfd))
{
if (!bfd_elf64_add_dynamic_entry (info, DT_DEBUG, 0))
return false;
}
}
if (reltext && SGI_COMPAT (output_bfd))
info->flags |= DF_TEXTREL;
if ((info->flags & DF_TEXTREL) != 0)
{
if (! bfd_elf64_add_dynamic_entry (info, DT_TEXTREL, 0))
return false;
}
if (! bfd_elf64_add_dynamic_entry (info, DT_PLTGOT, 0))
return false;
if (bfd_get_section_by_name (dynobj, "rel.dyn"))
{
if (! bfd_elf64_add_dynamic_entry (info, DT_REL, 0))
return false;
if (! bfd_elf64_add_dynamic_entry (info, DT_RELSZ, 0))
return false;
if (! bfd_elf64_add_dynamic_entry (info, DT_RELENT, 0))
return false;
}
if (SGI_COMPAT (output_bfd))
{
if (!bfd_elf64_add_dynamic_entry (info, DT_MIPS_CONFLICTNO, 0))
return false;
}
if (SGI_COMPAT (output_bfd))
{
if (!bfd_elf64_add_dynamic_entry (info, DT_MIPS_LIBLISTNO, 0))
return false;
}
if (bfd_get_section_by_name (dynobj, ".conflict") != NULL)
{
if (! bfd_elf64_add_dynamic_entry (info, DT_MIPS_CONFLICT, 0))
return false;
s = bfd_get_section_by_name (dynobj, ".liblist");
BFD_ASSERT (s != NULL);
if (! bfd_elf64_add_dynamic_entry (info, DT_MIPS_LIBLIST, 0))
return false;
}
if (! bfd_elf64_add_dynamic_entry (info, DT_MIPS_RLD_VERSION, 0))
return false;
if (! bfd_elf64_add_dynamic_entry (info, DT_MIPS_FLAGS, 0))
return false;
#if 0
if (! bfd_elf64_add_dynamic_entry (info, DT_MIPS_TIME_STAMP, 0))
return false;
#endif
#if 0
if (! bfd_elf64_add_dynamic_entry (info, DT_MIPS_ICHECKSUM, 0))
return false;
#endif
#if 0
if (! bfd_elf64_add_dynamic_entry (info, DT_MIPS_IVERSION, 0))
return false;
#endif
if (! bfd_elf64_add_dynamic_entry (info, DT_MIPS_BASE_ADDRESS, 0))
return false;
if (! bfd_elf64_add_dynamic_entry (info, DT_MIPS_LOCAL_GOTNO, 0))
return false;
if (! bfd_elf64_add_dynamic_entry (info, DT_MIPS_SYMTABNO, 0))
return false;
if (! bfd_elf64_add_dynamic_entry (info, DT_MIPS_UNREFEXTNO, 0))
return false;
if (! bfd_elf64_add_dynamic_entry (info, DT_MIPS_GOTSYM, 0))
return false;
if ((bfd_get_section_by_name(dynobj, ".MIPS.options"))
&& !bfd_elf64_add_dynamic_entry (info, DT_MIPS_OPTIONS, 0))
return false;
if (bfd_get_section_by_name (dynobj, ".msym")
&& !bfd_elf64_add_dynamic_entry (info, DT_MIPS_MSYM, 0))
return false;
}
return true;
}
boolean
mips_elf64_finish_dynamic_symbol (output_bfd, info, h, sym)
bfd *output_bfd;
struct bfd_link_info *info;
struct elf_link_hash_entry *h;
Elf_Internal_Sym *sym;
{
bfd *dynobj;
bfd_vma gval;
asection *sgot;
asection *smsym;
struct mips_elf64_got_info *g;
const char *name;
struct mips_elf64_link_hash_entry *mh;
dynobj = elf_hash_table (info)->dynobj;
gval = sym->st_value;
mh = (struct mips_elf64_link_hash_entry *) h;
if (h->plt.offset != (bfd_vma) -1)
{
asection *s;
bfd_byte stub[MIPS_FUNCTION_STUB_SIZE];
BFD_ASSERT (h->dynindx != -1);
s = bfd_get_section_by_name (dynobj, ".MIPS.stubs");
BFD_ASSERT (s != NULL);
if (h->dynindx & 0xffff0000)
return false;
bfd_put_32 (output_bfd, STUB_LW, stub);
bfd_put_32 (output_bfd, STUB_MOVE, stub + 4);
bfd_put_32 (output_bfd, STUB_JALR, stub + 8);
bfd_put_32 (output_bfd, STUB_LI16 + h->dynindx, stub + 12);
BFD_ASSERT (h->plt.offset <= s->_raw_size);
memcpy (s->contents + h->plt.offset, stub, MIPS_FUNCTION_STUB_SIZE);
sym->st_shndx = SHN_UNDEF;
gval = s->output_section->vma + s->output_offset + h->plt.offset;
sym->st_value = gval;
}
BFD_ASSERT (h->dynindx != -1
|| (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0);
sgot = bfd_get_section_by_name (dynobj, ".got");
BFD_ASSERT (sgot != NULL);
BFD_ASSERT (elf_section_data (sgot) != NULL);
g = (struct mips_elf64_got_info *) elf_section_data (sgot)->tdata;
BFD_ASSERT (g != NULL);
if (g->global_gotsym != NULL
&& h->dynindx >= g->global_gotsym->dynindx)
{
bfd_vma offset;
bfd_vma value;
if (sym->st_value)
value = sym->st_value;
else
{
if (info->shared && h->root.type == bfd_link_hash_undefined)
value = 0;
else
value = h->root.u.def.value;
}
offset = mips_elf64_global_got_index (dynobj, h);
bfd_put_64 (output_bfd, value, sgot->contents + offset);
}
smsym = bfd_get_section_by_name (dynobj, ".msym");
if (smsym)
{
Elf32_Internal_Msym msym;
msym.ms_hash_value = bfd_elf_hash (h->root.root.string);
msym.ms_info = ELF32_MS_INFO (mh->min_dyn_reloc_index, 1);
mips_elf64_swap_msym_out
(dynobj, &msym,
((Elf32_External_Msym *) smsym->contents) + h->dynindx);
}
name = h->root.root.string;
if (strcmp (name, "_DYNAMIC") == 0
|| strcmp (name, "_GLOBAL_OFFSET_TABLE_") == 0)
sym->st_shndx = SHN_ABS;
else if (strcmp (name, "_DYNAMIC_LINK") == 0
|| strcmp (name, "_DYNAMIC_LINKING") == 0)
{
sym->st_shndx = SHN_ABS;
sym->st_info = ELF_ST_INFO (STB_GLOBAL, STT_SECTION);
sym->st_value = 1;
}
else if (sym->st_shndx != SHN_UNDEF && sym->st_shndx != SHN_ABS)
{
if (h->type == STT_FUNC)
sym->st_shndx = SHN_MIPS_TEXT;
else if (h->type == STT_OBJECT)
sym->st_shndx = SHN_MIPS_DATA;
}
{
static const char* const text_section_symbols[] = {
"_ftext",
"_etext",
"__dso_displacement",
"__elf_header",
"__program_header_table",
NULL
};
static const char* const data_section_symbols[] = {
"_fdata",
"_edata",
"_end",
"_fbss",
NULL
};
const char* const *p;
int i;
for (i = 0; i < 2; ++i)
for (p = (i == 0) ? text_section_symbols : data_section_symbols;
*p;
++p)
if (strcmp (*p, name) == 0)
{
sym->st_info = ELF_ST_INFO (STB_GLOBAL, STT_SECTION);
if (i == 0)
sym->st_shndx = SHN_MIPS_TEXT;
else
sym->st_shndx = SHN_MIPS_DATA;
break;
}
}
return true;
}
boolean
mips_elf64_finish_dynamic_sections (output_bfd, info)
bfd *output_bfd;
struct bfd_link_info *info;
{
bfd *dynobj;
asection *sdyn;
asection *sgot;
struct mips_elf64_got_info *g;
dynobj = elf_hash_table (info)->dynobj;
sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
sgot = bfd_get_section_by_name (dynobj, ".got");
if (sgot == NULL)
g = NULL;
else
{
BFD_ASSERT (elf_section_data (sgot) != NULL);
g = (struct mips_elf64_got_info *) elf_section_data (sgot)->tdata;
BFD_ASSERT (g != NULL);
}
if (elf_hash_table (info)->dynamic_sections_created)
{
bfd_byte *b;
BFD_ASSERT (sdyn != NULL);
BFD_ASSERT (g != NULL);
for (b = sdyn->contents;
b < sdyn->contents + sdyn->_raw_size;
b += get_elf_backend_data (dynobj)->s->sizeof_dyn)
{
Elf_Internal_Dyn dyn;
const char *name;
size_t elemsize;
asection *s;
boolean swap_out_p;
(*get_elf_backend_data (dynobj)->s->swap_dyn_in) (dynobj, b, &dyn);
swap_out_p = true;
switch (dyn.d_tag)
{
case DT_RELENT:
s = bfd_get_section_by_name(dynobj, "rel.dyn");
BFD_ASSERT (s != NULL);
dyn.d_un.d_val = get_elf_backend_data (dynobj)->s->sizeof_rel;
break;
case DT_STRSZ:
dyn.d_un.d_val =
_bfd_elf_strtab_size (elf_hash_table (info)->dynstr);
break;
case DT_PLTGOT:
name = ".got";
goto get_vma;
case DT_MIPS_CONFLICT:
name = ".conflict";
goto get_vma;
case DT_MIPS_LIBLIST:
name = ".liblist";
get_vma:
s = bfd_get_section_by_name (output_bfd, name);
BFD_ASSERT (s != NULL);
dyn.d_un.d_ptr = s->vma;
break;
case DT_MIPS_RLD_VERSION:
dyn.d_un.d_val = 1;
break;
case DT_MIPS_FLAGS:
dyn.d_un.d_val = RHF_NOTPOT;
break;
case DT_MIPS_CONFLICTNO:
name = ".conflict";
elemsize = sizeof (Elf32_Conflict);
goto set_elemno;
case DT_MIPS_LIBLISTNO:
name = ".liblist";
elemsize = sizeof (Elf32_Lib);
set_elemno:
s = bfd_get_section_by_name (output_bfd, name);
if (s != NULL)
{
if (s->_cooked_size != 0)
dyn.d_un.d_val = s->_cooked_size / elemsize;
else
dyn.d_un.d_val = s->_raw_size / elemsize;
}
else
dyn.d_un.d_val = 0;
break;
case DT_MIPS_TIME_STAMP:
time ((time_t *) &dyn.d_un.d_val);
break;
case DT_MIPS_ICHECKSUM:
swap_out_p = false;
break;
case DT_MIPS_IVERSION:
swap_out_p = false;
break;
case DT_MIPS_BASE_ADDRESS:
s = output_bfd->sections;
BFD_ASSERT (s != NULL);
dyn.d_un.d_ptr = s->vma & ~(0xffff);
break;
case DT_MIPS_LOCAL_GOTNO:
dyn.d_un.d_val = g->local_gotno;
break;
case DT_MIPS_UNREFEXTNO:
dyn.d_un.d_val = bfd_count_sections (output_bfd) + 1;
break;
case DT_MIPS_GOTSYM:
if (g->global_gotsym)
{
dyn.d_un.d_val = g->global_gotsym->dynindx;
break;
}
case DT_MIPS_SYMTABNO:
name = ".dynsym";
elemsize = get_elf_backend_data (output_bfd)->s->sizeof_sym;
s = bfd_get_section_by_name (output_bfd, name);
BFD_ASSERT (s != NULL);
if (s->_cooked_size != 0)
dyn.d_un.d_val = s->_cooked_size / elemsize;
else
dyn.d_un.d_val = s->_raw_size / elemsize;
break;
case DT_MIPS_HIPAGENO:
dyn.d_un.d_val = g->local_gotno - MIPS_RESERVED_GOTNO;
break;
case DT_MIPS_OPTIONS:
s = bfd_get_section_by_name(output_bfd, ".MIPS.options");
dyn.d_un.d_ptr = s->vma;
break;
case DT_MIPS_MSYM:
s = bfd_get_section_by_name(output_bfd, ".msym");
dyn.d_un.d_ptr = s->vma;
break;
default:
swap_out_p = false;
break;
}
if (swap_out_p)
(*get_elf_backend_data (dynobj)->s->swap_dyn_out)
(dynobj, &dyn, b);
}
}
if (sgot != NULL && sgot->_raw_size > 0)
{
bfd_put_64 (output_bfd, (bfd_vma) 0, sgot->contents);
bfd_put_64 (output_bfd, (bfd_vma) 0x80000000, sgot->contents + 8);
}
if (sgot != NULL)
elf_section_data (sgot->output_section)->this_hdr.sh_entsize = 8;
{
asection *smsym;
asection *s;
smsym = bfd_get_section_by_name (dynobj, ".msym");
if (smsym != NULL)
{
Elf32_Internal_Msym msym;
msym.ms_hash_value = 0;
msym.ms_info = ELF32_MS_INFO (0, 1);
for (s = output_bfd->sections; s != NULL; s = s->next)
{
long dynindx = elf_section_data (s)->dynindx;
mips_elf64_swap_msym_out
(output_bfd, &msym,
(((Elf32_External_Msym *) smsym->contents)
+ dynindx));
}
}
s = bfd_get_section_by_name (dynobj, "rel.dyn");
if (s != NULL && s->_raw_size > 0)
memset (s->contents, 0, get_elf_backend_data (dynobj)->s->sizeof_rel);
}
return true;
}
asection *
mips_elf64_gc_mark_hook (abfd, info, rel, h, sym)
bfd *abfd;
struct bfd_link_info *info ATTRIBUTE_UNUSED;
Elf_Internal_Rela *rel;
struct elf_link_hash_entry *h;
Elf_Internal_Sym *sym;
{
if (h != NULL)
{
switch (ELF64_R_TYPE (rel->r_info))
{
case R_MIPS_GNU_VTINHERIT:
case R_MIPS_GNU_VTENTRY:
break;
default:
switch (h->root.type)
{
case bfd_link_hash_defined:
case bfd_link_hash_defweak:
return h->root.u.def.section;
case bfd_link_hash_common:
return h->root.u.c.p->section;
default:
break;
}
}
}
else
{
return bfd_section_from_elf_index (abfd, sym->st_shndx);
}
return NULL;
}
boolean
mips_elf64_gc_sweep_hook (abfd, info, sec, relocs)
bfd *abfd ATTRIBUTE_UNUSED;
struct bfd_link_info *info ATTRIBUTE_UNUSED;
asection *sec ATTRIBUTE_UNUSED;
const Elf_Internal_Rela *relocs ATTRIBUTE_UNUSED;
{
#if 0
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
bfd_signed_vma *local_got_refcounts;
const Elf_Internal_Rela *rel, *relend;
unsigned long r_symndx;
struct elf_link_hash_entry *h;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (abfd);
local_got_refcounts = elf_local_got_refcounts (abfd);
relend = relocs + sec->reloc_count;
for (rel = relocs; rel < relend; rel++)
switch (ELF64_R_TYPE (rel->r_info))
{
case R_MIPS_GOT16:
case R_MIPS_CALL16:
case R_MIPS_CALL_HI16:
case R_MIPS_CALL_LO16:
case R_MIPS_GOT_HI16:
case R_MIPS_GOT_LO16:
break;
default:
break;
}
#endif
return true;
}
static boolean
mips_elf64_create_got_section (abfd, info)
bfd *abfd;
struct bfd_link_info *info;
{
flagword flags;
register asection *s;
struct elf_link_hash_entry *h;
struct mips_elf64_got_info *g;
if (bfd_get_section_by_name (abfd, ".got"))
return true;
flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
| SEC_LINKER_CREATED);
s = bfd_make_section (abfd, ".got");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags)
|| ! bfd_set_section_alignment (abfd, s, 4))
return false;
h = NULL;
if (! (_bfd_generic_link_add_one_symbol
(info, abfd, "_GLOBAL_OFFSET_TABLE_", BSF_GLOBAL, s,
(bfd_vma) 0, (const char *) NULL, false,
get_elf_backend_data (abfd)->collect,
(struct bfd_link_hash_entry **) &h)))
return false;
h->elf_link_hash_flags &=~ ELF_LINK_NON_ELF;
h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
h->type = STT_OBJECT;
if (info->shared
&& ! bfd_elf64_link_record_dynamic_symbol (info, h))
return false;
s->_raw_size = MIPS_RESERVED_GOTNO * (get_elf_backend_data (abfd)->s->arch_size / 8);
g = (struct mips_elf64_got_info *) bfd_alloc (abfd,
sizeof (struct mips_elf64_got_info));
if (g == NULL)
return false;
g->global_gotsym = NULL;
g->local_gotno = MIPS_RESERVED_GOTNO;
g->assigned_gotno = MIPS_RESERVED_GOTNO;
if (elf_section_data (s) == NULL)
{
s->used_by_bfd =
(PTR) bfd_zalloc (abfd, sizeof (struct bfd_elf_section_data));
if (elf_section_data (s) == NULL)
return false;
}
elf_section_data (s)->tdata = (PTR) g;
elf_section_data (s)->this_hdr.sh_flags
|= SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL;
return true;
}
static boolean
mips_elf64_record_global_got_symbol (h, info, g)
struct elf_link_hash_entry *h;
struct bfd_link_info *info;
struct mips_elf64_got_info *g ATTRIBUTE_UNUSED;
{
if (h->dynindx == -1
&& !bfd_elf64_link_record_dynamic_symbol (info, h))
return false;
if (h->got.offset != (bfd_vma) - 1)
return true;
h->got.offset = 1;
return true;
}
static asection *
mips_elf64_create_msym_section (abfd)
bfd *abfd;
{
asection *s;
s = bfd_get_section_by_name (abfd, ".msym");
if (!s)
{
s = bfd_make_section (abfd, ".msym");
if (!s
|| !bfd_set_section_flags (abfd, s,
SEC_ALLOC
| SEC_LOAD
| SEC_HAS_CONTENTS
| SEC_LINKER_CREATED
| SEC_READONLY)
|| !bfd_set_section_alignment (abfd, s, 3))
return NULL;
}
return s;
}
static void
mips_elf64_allocate_dynamic_relocations (abfd, n)
bfd *abfd;
unsigned int n;
{
asection *s;
s = bfd_get_section_by_name (abfd, ".rel.dyn");
BFD_ASSERT (s != NULL);
if (s->_raw_size == 0)
{
s->_raw_size += get_elf_backend_data (abfd)->s->sizeof_rel;
++s->reloc_count;
}
s->_raw_size += n * get_elf_backend_data (abfd)->s->sizeof_rel;
}
boolean
mips_elf64_check_relocs (abfd, info, sec, relocs)
bfd *abfd;
struct bfd_link_info *info;
asection *sec;
const Elf_Internal_Rela *relocs;
{
const char *name;
bfd *dynobj;
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
struct mips_elf64_got_info *g;
size_t extsymoff;
const Elf_Internal_Rela *rel;
const Elf_Internal_Rela *rel_end;
asection *sgot;
asection *sreloc;
struct elf_backend_data *bed;
if (info->relocateable)
return true;
dynobj = elf_hash_table (info)->dynobj;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (abfd);
extsymoff = (elf_bad_symtab (abfd)) ? 0 : symtab_hdr->sh_info;
name = bfd_get_section_name (abfd, sec);
if (strncmp (name, FN_STUB, sizeof FN_STUB - 1) == 0)
{
unsigned long r_symndx;
r_symndx = ELF64_R_SYM (relocs->r_info);
if (r_symndx < extsymoff
|| sym_hashes[r_symndx - extsymoff] == NULL)
{
asection *o;
for (o = abfd->sections; o != NULL; o = o->next)
{
Elf_Internal_Rela *sec_relocs;
const Elf_Internal_Rela *r, *rend;
if ((o->flags & SEC_RELOC) == 0
|| o->reloc_count == 0
|| strncmp (bfd_get_section_name (abfd, o), FN_STUB,
sizeof FN_STUB - 1) == 0
|| strncmp (bfd_get_section_name (abfd, o), CALL_STUB,
sizeof CALL_STUB - 1) == 0
|| strncmp (bfd_get_section_name (abfd, o), CALL_FP_STUB,
sizeof CALL_FP_STUB - 1) == 0)
continue;
sec_relocs = (_bfd_elf64_link_read_relocs
(abfd, o, (PTR) NULL,
(Elf_Internal_Rela *) NULL,
info->keep_memory));
if (sec_relocs == NULL)
return false;
rend = sec_relocs + o->reloc_count;
for (r = sec_relocs; r < rend; r++)
if (ELF64_R_SYM (r->r_info) == r_symndx
&& ELF64_R_TYPE (r->r_info) != R_MIPS16_26)
break;
if (! info->keep_memory)
free (sec_relocs);
if (r < rend)
break;
}
if (o == NULL)
{
sec->flags |= SEC_EXCLUDE;
return true;
}
if (elf_tdata (abfd)->local_stubs == NULL)
{
unsigned long symcount;
asection **n;
bfd_size_type amt;
if (elf_bad_symtab (abfd))
symcount = NUM_SHDR_ENTRIES (symtab_hdr);
else
symcount = symtab_hdr->sh_info;
amt = symcount * sizeof (asection *);
n = (asection **) bfd_zalloc (abfd, amt);
if (n == NULL)
return false;
elf_tdata (abfd)->local_stubs = n;
}
elf_tdata (abfd)->local_stubs[r_symndx] = sec;
}
else
{
struct mips_elf64_link_hash_entry *h;
h = ((struct mips_elf64_link_hash_entry *)
sym_hashes[r_symndx - extsymoff]);
h->fn_stub = sec;
mips_elf64_hash_table (info)->mips16_stubs_seen = true;
}
}
else if (strncmp (name, CALL_STUB, sizeof CALL_STUB - 1) == 0
|| strncmp (name, CALL_FP_STUB, sizeof CALL_FP_STUB - 1) == 0)
{
unsigned long r_symndx;
struct mips_elf64_link_hash_entry *h;
asection **loc;
r_symndx = ELF64_R_SYM (relocs->r_info);
if (r_symndx < extsymoff
|| sym_hashes[r_symndx - extsymoff] == NULL)
{
sec->flags |= SEC_EXCLUDE;
return true;
}
h = ((struct mips_elf64_link_hash_entry *)
sym_hashes[r_symndx - extsymoff]);
if (strncmp (name, CALL_FP_STUB, sizeof CALL_FP_STUB - 1) == 0)
loc = &h->call_fp_stub;
else
loc = &h->call_stub;
if (*loc != NULL || h->root.other == STO_MIPS16)
{
sec->flags |= SEC_EXCLUDE;
return true;
}
*loc = sec;
mips_elf64_hash_table (info)->mips16_stubs_seen = true;
}
if (dynobj == NULL)
{
sgot = NULL;
g = NULL;
}
else
{
sgot = bfd_get_section_by_name (dynobj, ".got");
if (sgot == NULL)
g = NULL;
else
{
BFD_ASSERT (elf_section_data (sgot) != NULL);
g = (struct mips_elf64_got_info *) elf_section_data (sgot)->tdata;
BFD_ASSERT (g != NULL);
}
}
sreloc = NULL;
bed = get_elf_backend_data (abfd);
rel_end = relocs + sec->reloc_count * bed->s->int_rels_per_ext_rel;
for (rel = relocs; rel < rel_end; ++rel)
{
unsigned long r_symndx;
int r_type;
struct elf_link_hash_entry *h;
r_symndx = ELF64_R_SYM (rel->r_info);
r_type = ELF64_MIPS_R_TYPE (rel->r_info);
if (r_symndx < extsymoff)
h = NULL;
else if (r_symndx >= extsymoff + NUM_SHDR_ENTRIES (symtab_hdr))
{
(*_bfd_error_handler)
(_("%s: Malformed reloc detected for section %s"),
bfd_archive_filename (abfd), name);
bfd_set_error (bfd_error_bad_value);
return false;
}
else
{
h = sym_hashes[r_symndx - extsymoff];
if (h != NULL)
{
while (h->root.type == bfd_link_hash_indirect)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
}
}
if (dynobj == NULL || sgot == NULL)
{
switch (r_type)
{
case R_MIPS_GOT16:
case R_MIPS_CALL16:
case R_MIPS_CALL_HI16:
case R_MIPS_CALL_LO16:
case R_MIPS_GOT_HI16:
case R_MIPS_GOT_LO16:
case R_MIPS_GOT_PAGE:
case R_MIPS_GOT_OFST:
case R_MIPS_GOT_DISP:
if (dynobj == NULL)
elf_hash_table (info)->dynobj = dynobj = abfd;
if (! mips_elf64_create_got_section (dynobj, info))
return false;
g = _mips_elf64_got_info (dynobj, &sgot);
break;
case R_MIPS_32:
case R_MIPS_REL32:
case R_MIPS_64:
if (dynobj == NULL
&& (info->shared || h != NULL)
&& (sec->flags & SEC_ALLOC) != 0)
elf_hash_table (info)->dynobj = dynobj = abfd;
break;
default:
break;
}
}
if (!h && (r_type == R_MIPS_CALL_LO16
|| r_type == R_MIPS_GOT_LO16
|| r_type == R_MIPS_GOT_DISP))
{
g->local_gotno++;
sgot->_raw_size += get_elf_backend_data (dynobj)->s->arch_size / 8;
}
switch (r_type)
{
case R_MIPS_CALL16:
if (h == NULL)
{
(*_bfd_error_handler)
(_("%s: CALL16 reloc at 0x%lx not against global symbol"),
bfd_archive_filename (abfd), (unsigned long) rel->r_offset);
bfd_set_error (bfd_error_bad_value);
return false;
}
case R_MIPS_CALL_HI16:
case R_MIPS_CALL_LO16:
if (h != NULL)
{
if (!mips_elf64_record_global_got_symbol (h, info, g))
return false;
h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
h->type = STT_FUNC;
}
break;
case R_MIPS_GOT16:
case R_MIPS_GOT_HI16:
case R_MIPS_GOT_LO16:
case R_MIPS_GOT_DISP:
if (h && !mips_elf64_record_global_got_symbol (h, info, g))
return false;
break;
case R_MIPS_32:
case R_MIPS_REL32:
case R_MIPS_64:
if ((info->shared || h != NULL)
&& (sec->flags & SEC_ALLOC) != 0)
{
if (sreloc == NULL)
{
const char *name = ".rel.dyn";
sreloc = bfd_get_section_by_name (dynobj, name);
if (sreloc == NULL)
{
sreloc = bfd_make_section (dynobj, name);
if (sreloc == NULL
|| ! bfd_set_section_flags (dynobj, sreloc,
(SEC_ALLOC
| SEC_LOAD
| SEC_HAS_CONTENTS
| SEC_IN_MEMORY
| SEC_LINKER_CREATED
| SEC_READONLY))
|| ! bfd_set_section_alignment (dynobj, sreloc,
4))
return false;
}
}
#define MIPS_READONLY_SECTION (SEC_ALLOC | SEC_LOAD | SEC_READONLY)
if (info->shared)
{
mips_elf64_allocate_dynamic_relocations (dynobj, 1);
if ((sec->flags & MIPS_READONLY_SECTION)
== MIPS_READONLY_SECTION)
info->flags |= DF_TEXTREL;
}
else
{
struct mips_elf64_link_hash_entry *hmips;
hmips = (struct mips_elf64_link_hash_entry *) h;
++hmips->possibly_dynamic_relocs;
if ((sec->flags & MIPS_READONLY_SECTION)
== MIPS_READONLY_SECTION)
hmips->readonly_reloc = true;
}
if (h != NULL
&& !mips_elf64_record_global_got_symbol (h, info, g))
return false;
}
break;
case R_MIPS_26:
case R_MIPS_GPREL16:
case R_MIPS_LITERAL:
case R_MIPS_GPREL32:
break;
case R_MIPS_GNU_VTINHERIT:
if (!_bfd_elf64_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
return false;
break;
case R_MIPS_GNU_VTENTRY:
if (!_bfd_elf64_gc_record_vtentry (abfd, sec, h, rel->r_offset))
return false;
break;
default:
break;
}
}
return true;
}
struct extsym_info
{
bfd *abfd;
struct bfd_link_info *info;
struct ecoff_debug_info *debug;
const struct ecoff_debug_swap *swap;
boolean failed;
};
static boolean
mips_elf64_output_extsym (h, data)
struct mips_elf64_link_hash_entry *h;
PTR data;
{
struct extsym_info *einfo = (struct extsym_info *) data;
boolean strip;
asection *sec, *output_section;
if (h->root.indx == -2)
strip = false;
else if (((h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0
|| (h->root.elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) != 0)
&& (h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0
&& (h->root.elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) == 0)
strip = true;
else if (einfo->info->strip == strip_all
|| (einfo->info->strip == strip_some
&& bfd_hash_lookup (einfo->info->keep_hash,
h->root.root.root.string,
false, false) == NULL))
strip = true;
else
strip = false;
if (strip)
return true;
if (h->esym.ifd == -2)
{
h->esym.jmptbl = 0;
h->esym.cobol_main = 0;
h->esym.weakext = 0;
h->esym.reserved = 0;
h->esym.ifd = ifdNil;
h->esym.asym.value = 0;
h->esym.asym.st = stGlobal;
if (h->root.root.type == bfd_link_hash_undefined
|| h->root.root.type == bfd_link_hash_undefweak)
{
const char *name;
name = h->root.root.root.string;
h->esym.asym.sc = scUndefined;
}
else if (h->root.root.type != bfd_link_hash_defined
&& h->root.root.type != bfd_link_hash_defweak)
h->esym.asym.sc = scAbs;
else
{
const char *name;
sec = h->root.root.u.def.section;
output_section = sec->output_section;
if (output_section == NULL)
h->esym.asym.sc = scUndefined;
else
{
name = bfd_section_name (output_section->owner, output_section);
if (strcmp (name, ".text") == 0)
h->esym.asym.sc = scText;
else if (strcmp (name, ".data") == 0)
h->esym.asym.sc = scData;
else if (strcmp (name, ".sdata") == 0)
h->esym.asym.sc = scSData;
else if (strcmp (name, ".rodata") == 0
|| strcmp (name, ".rdata") == 0)
h->esym.asym.sc = scRData;
else if (strcmp (name, ".bss") == 0)
h->esym.asym.sc = scBss;
else if (strcmp (name, ".sbss") == 0)
h->esym.asym.sc = scSBss;
else if (strcmp (name, ".init") == 0)
h->esym.asym.sc = scInit;
else if (strcmp (name, ".fini") == 0)
h->esym.asym.sc = scFini;
else
h->esym.asym.sc = scAbs;
}
}
h->esym.asym.reserved = 0;
h->esym.asym.index = indexNil;
}
if (h->root.root.type == bfd_link_hash_common)
h->esym.asym.value = h->root.root.u.c.size;
else if (h->root.root.type == bfd_link_hash_defined
|| h->root.root.type == bfd_link_hash_defweak)
{
if (h->esym.asym.sc == scCommon)
h->esym.asym.sc = scBss;
else if (h->esym.asym.sc == scSCommon)
h->esym.asym.sc = scSBss;
sec = h->root.root.u.def.section;
output_section = sec->output_section;
if (output_section != NULL)
h->esym.asym.value = (h->root.root.u.def.value
+ sec->output_offset
+ output_section->vma);
else
h->esym.asym.value = 0;
}
else if ((h->root.elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0)
{
struct mips_elf64_link_hash_entry *hd = h;
boolean no_fn_stub = h->no_fn_stub;
while (hd->root.root.type == bfd_link_hash_indirect)
{
hd = (struct mips_elf64_link_hash_entry *)h->root.root.u.i.link;
no_fn_stub = no_fn_stub || hd->no_fn_stub;
}
if (!no_fn_stub)
{
h->esym.asym.st = stProc;
sec = hd->root.root.u.def.section;
if (sec == NULL)
h->esym.asym.value = 0;
else
{
output_section = sec->output_section;
if (output_section != NULL)
h->esym.asym.value = (hd->root.plt.offset
+ sec->output_offset
+ output_section->vma);
else
h->esym.asym.value = 0;
}
#if 0
h->esym.ifd = 0;
#endif
}
}
if (! bfd_ecoff_debug_one_external (einfo->abfd, einfo->debug, einfo->swap,
h->root.root.root.string,
&h->esym))
{
einfo->failed = true;
return false;
}
return true;
}
static void
mips_elf64_swap_gptab_in (abfd, ex, in)
bfd *abfd;
const Elf32_External_gptab *ex;
Elf32_gptab *in;
{
in->gt_entry.gt_g_value = H_GET_32 (abfd, ex->gt_entry.gt_g_value);
in->gt_entry.gt_bytes = H_GET_32 (abfd, ex->gt_entry.gt_bytes);
}
static void
mips_elf64_swap_gptab_out (abfd, in, ex)
bfd *abfd;
const Elf32_gptab *in;
Elf32_External_gptab *ex;
{
H_PUT_32 (abfd, (bfd_vma) in->gt_entry.gt_g_value,
ex->gt_entry.gt_g_value);
H_PUT_32 (abfd, (bfd_vma) in->gt_entry.gt_bytes,
ex->gt_entry.gt_bytes);
}
static int
gptab_compare (p1, p2)
const PTR p1;
const PTR p2;
{
const Elf32_gptab *a1 = (const Elf32_gptab *) p1;
const Elf32_gptab *a2 = (const Elf32_gptab *) p2;
return a1->gt_entry.gt_g_value - a2->gt_entry.gt_g_value;
}
boolean
mips_elf64_final_link (abfd, info)
bfd *abfd;
struct bfd_link_info *info;
{
asection **secpp;
asection *o;
struct bfd_link_order *p;
asection *mdebug_sec, *gptab_data_sec, *gptab_bss_sec;
struct ecoff_debug_info debug;
const struct ecoff_debug_swap *swap
= get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap;
HDRR *symhdr = &debug.symbolic_header;
PTR mdebug_handle = NULL;
asection *s;
EXTR esym;
unsigned int i;
static const char * const secname[] =
{ ".text", ".init", ".fini", ".data",
".rodata", ".sdata", ".sbss", ".bss" };
static const int sc[] = { scText, scInit, scFini, scData,
scRData, scSData, scSBss, scBss };
if (!info->shared
&& !info->relocateable
&& elf_elfheader (abfd)->e_flags & EF_MIPS_PIC)
{
elf_elfheader (abfd)->e_flags &= ~EF_MIPS_PIC;
elf_elfheader (abfd)->e_flags |= EF_MIPS_CPIC;
}
if (elf_hash_table (info)->dynamic_sections_created)
{
bfd *dynobj;
asection *got;
struct mips_elf64_got_info *g;
if (!mips_elf64_sort_hash_table (info, (info->shared
? bfd_count_sections (abfd) + 1
: 1)))
return false;
dynobj = elf_hash_table (info)->dynobj;
got = bfd_get_section_by_name (dynobj, ".got");
g = (struct mips_elf64_got_info *) elf_section_data (got)->tdata;
if (g->global_gotsym != NULL)
BFD_ASSERT ((elf_hash_table (info)->dynsymcount
- g->global_gotsym->dynindx)
<= g->global_gotno);
}
for (secpp = &abfd->sections; *secpp != NULL; secpp = &(*secpp)->next)
{
if (strcmp ((*secpp)->name, ".MIPS.options") == 0)
{
for (p = (*secpp)->link_order_head; p != NULL; p = p->next)
if (p->type == bfd_indirect_link_order)
p->u.indirect.section->flags &=~ SEC_HAS_CONTENTS;
(*secpp)->link_order_head = NULL;
bfd_section_list_remove (abfd, secpp);
--abfd->section_count;
break;
}
}
if (elf_gp (abfd) == 0)
{
struct bfd_link_hash_entry *h;
h = bfd_link_hash_lookup (info->hash, "_gp", false, false, true);
if (h != (struct bfd_link_hash_entry *) NULL
&& h->type == bfd_link_hash_defined)
elf_gp (abfd) = (h->u.def.value
+ h->u.def.section->output_section->vma
+ h->u.def.section->output_offset);
else if (info->relocateable)
{
bfd_vma lo = MINUS_ONE;
for (o = abfd->sections; o != NULL; o = o->next)
if (o->vma < lo
&& (elf_section_data (o)->this_hdr.sh_flags & SHF_MIPS_GPREL))
lo = o->vma;
elf_gp (abfd) = (lo + 0x7ff0);
}
else
{
}
}
mdebug_sec = NULL;
gptab_data_sec = NULL;
gptab_bss_sec = NULL;
for (o = abfd->sections; o != (asection *) NULL; o = o->next)
{
if (strcmp (o->name, ".mdebug") == 0)
{
struct extsym_info einfo;
bfd_vma last;
symhdr->magic = swap->sym_magic;
symhdr->vstamp = 0;
symhdr->ilineMax = 0;
symhdr->cbLine = 0;
symhdr->idnMax = 0;
symhdr->ipdMax = 0;
symhdr->isymMax = 0;
symhdr->ioptMax = 0;
symhdr->iauxMax = 0;
symhdr->issMax = 0;
symhdr->issExtMax = 0;
symhdr->ifdMax = 0;
symhdr->crfd = 0;
symhdr->iextMax = 0;
debug.line = NULL;
debug.external_dnr = NULL;
debug.external_pdr = NULL;
debug.external_sym = NULL;
debug.external_opt = NULL;
debug.external_aux = NULL;
debug.ss = NULL;
debug.ssext = debug.ssext_end = NULL;
debug.external_fdr = NULL;
debug.external_rfd = NULL;
debug.external_ext = debug.external_ext_end = NULL;
mdebug_handle = bfd_ecoff_debug_init (abfd, &debug, swap, info);
if (mdebug_handle == (PTR) NULL)
return false;
esym.jmptbl = 0;
esym.cobol_main = 0;
esym.weakext = 0;
esym.reserved = 0;
esym.ifd = ifdNil;
esym.asym.iss = issNil;
esym.asym.st = stLocal;
esym.asym.reserved = 0;
esym.asym.index = indexNil;
last = 0;
for (i = 0; i < sizeof (secname) / sizeof (secname[0]); i++)
{
esym.asym.sc = sc[i];
s = bfd_get_section_by_name (abfd, secname[i]);
if (s != NULL)
{
esym.asym.value = s->vma;
last = s->vma + s->_raw_size;
}
else
esym.asym.value = last;
if (!bfd_ecoff_debug_one_external (abfd, &debug, swap,
secname[i], &esym))
return false;
}
for (p = o->link_order_head;
p != (struct bfd_link_order *) NULL;
p = p->next)
{
asection *input_section;
bfd *input_bfd;
const struct ecoff_debug_swap *input_swap;
struct ecoff_debug_info input_debug;
char *eraw_src;
char *eraw_end;
if (p->type != bfd_indirect_link_order)
{
if (p->type == bfd_fill_link_order)
continue;
abort ();
}
input_section = p->u.indirect.section;
input_bfd = input_section->owner;
if (bfd_get_flavour (input_bfd) != bfd_target_elf_flavour
|| (get_elf_backend_data (input_bfd)
->elf_backend_ecoff_debug_swap) == NULL)
{
continue;
}
input_swap = (get_elf_backend_data (input_bfd)
->elf_backend_ecoff_debug_swap);
BFD_ASSERT (p->size == input_section->_raw_size);
if (! _bfd_mips_elf_read_ecoff_info (input_bfd, input_section,
&input_debug))
return false;
if (! (bfd_ecoff_debug_accumulate
(mdebug_handle, abfd, &debug, swap, input_bfd,
&input_debug, input_swap, info)))
return false;
eraw_src = input_debug.external_ext;
eraw_end = (eraw_src
+ (input_debug.symbolic_header.iextMax
* input_swap->external_ext_size));
for (;
eraw_src < eraw_end;
eraw_src += input_swap->external_ext_size)
{
EXTR ext;
const char *name;
struct mips_elf64_link_hash_entry *h;
(*input_swap->swap_ext_in) (input_bfd, (PTR) eraw_src, &ext);
if (ext.asym.sc == scNil
|| ext.asym.sc == scUndefined
|| ext.asym.sc == scSUndefined)
continue;
name = input_debug.ssext + ext.asym.iss;
h = mips_elf64_link_hash_lookup (mips_elf64_hash_table (info),
name, false, false, true);
if (h == NULL || h->esym.ifd != -2)
continue;
if (ext.ifd != -1)
{
BFD_ASSERT (ext.ifd
< input_debug.symbolic_header.ifdMax);
ext.ifd = input_debug.ifdmap[ext.ifd];
}
h->esym = ext;
}
free (input_debug.line);
free (input_debug.external_dnr);
free (input_debug.external_pdr);
free (input_debug.external_sym);
free (input_debug.external_opt);
free (input_debug.external_aux);
free (input_debug.ss);
free (input_debug.ssext);
free (input_debug.external_fdr);
free (input_debug.external_rfd);
free (input_debug.external_ext);
input_section->flags &=~ SEC_HAS_CONTENTS;
}
einfo.abfd = abfd;
einfo.info = info;
einfo.debug = &debug;
einfo.swap = swap;
einfo.failed = false;
mips_elf64_link_hash_traverse (mips_elf64_hash_table (info),
mips_elf64_output_extsym,
(PTR) &einfo);
if (einfo.failed)
return false;
o->_raw_size = bfd_ecoff_debug_size (abfd, &debug, swap);
o->link_order_head = (struct bfd_link_order *) NULL;
mdebug_sec = o;
}
if (strncmp (o->name, ".gptab.", sizeof ".gptab." - 1) == 0)
{
const char *subname;
unsigned int c;
Elf32_gptab *tab;
Elf32_External_gptab *ext_tab;
unsigned int i;
if (! info->relocateable)
{
asection **secpp;
for (p = o->link_order_head;
p != (struct bfd_link_order *) NULL;
p = p->next)
{
asection *input_section;
if (p->type != bfd_indirect_link_order)
{
if (p->type == bfd_fill_link_order)
continue;
abort ();
}
input_section = p->u.indirect.section;
input_section->flags &=~ SEC_HAS_CONTENTS;
}
o->link_order_head = (struct bfd_link_order *) NULL;
for (secpp = &abfd->sections;
*secpp != o;
secpp = &(*secpp)->next)
;
bfd_section_list_remove (abfd, secpp);
--abfd->section_count;
continue;
}
if (strcmp (o->name, ".gptab.sdata") == 0)
gptab_data_sec = o;
else if (strcmp (o->name, ".gptab.sbss") == 0)
gptab_bss_sec = o;
else
{
(*_bfd_error_handler)
(_("%s: illegal section name `%s'"),
bfd_archive_filename (abfd), o->name);
bfd_set_error (bfd_error_nonrepresentable_section);
return false;
}
subname = o->name + sizeof ".gptab" - 1;
if (bfd_get_section_by_name (abfd, subname) == NULL)
{
if (o == gptab_data_sec)
o->name = ".gptab.data";
else
o->name = ".gptab.bss";
subname = o->name + sizeof ".gptab" - 1;
BFD_ASSERT (bfd_get_section_by_name (abfd, subname) != NULL);
}
c = 1;
tab = (Elf32_gptab *) bfd_malloc (c * sizeof (Elf32_gptab));
if (tab == NULL)
return false;
tab[0].gt_header.gt_current_g_value = elf_gp_size (abfd);
tab[0].gt_header.gt_unused = 0;
for (p = o->link_order_head;
p != (struct bfd_link_order *) NULL;
p = p->next)
{
asection *input_section;
bfd *input_bfd;
bfd_size_type size;
unsigned long last;
bfd_size_type gpentry;
if (p->type != bfd_indirect_link_order)
{
if (p->type == bfd_fill_link_order)
continue;
abort ();
}
input_section = p->u.indirect.section;
input_bfd = input_section->owner;
size = bfd_section_size (input_bfd, input_section);
last = 0;
for (gpentry = sizeof (Elf32_External_gptab);
gpentry < size;
gpentry += sizeof (Elf32_External_gptab))
{
Elf32_External_gptab ext_gptab;
Elf32_gptab int_gptab;
unsigned long val;
unsigned long add;
boolean exact;
unsigned int look;
if (! (bfd_get_section_contents
(input_bfd, input_section, (PTR) &ext_gptab,
gpentry, sizeof (Elf32_External_gptab))))
{
free (tab);
return false;
}
mips_elf64_swap_gptab_in (input_bfd, &ext_gptab,
&int_gptab);
val = int_gptab.gt_entry.gt_g_value;
add = int_gptab.gt_entry.gt_bytes - last;
exact = false;
for (look = 1; look < c; look++)
{
if (tab[look].gt_entry.gt_g_value >= val)
tab[look].gt_entry.gt_bytes += add;
if (tab[look].gt_entry.gt_g_value == val)
exact = true;
}
if (! exact)
{
Elf32_gptab *new_tab;
unsigned int max;
new_tab = ((Elf32_gptab *)
bfd_realloc ((PTR) tab,
(c + 1) * sizeof (Elf32_gptab)));
if (new_tab == NULL)
{
free (tab);
return false;
}
tab = new_tab;
tab[c].gt_entry.gt_g_value = val;
tab[c].gt_entry.gt_bytes = add;
max = 0;
for (look = 1; look < c; look++)
{
if (tab[look].gt_entry.gt_g_value < val
&& (max == 0
|| (tab[look].gt_entry.gt_g_value
> tab[max].gt_entry.gt_g_value)))
max = look;
}
if (max != 0)
tab[c].gt_entry.gt_bytes +=
tab[max].gt_entry.gt_bytes;
++c;
}
last = int_gptab.gt_entry.gt_bytes;
}
input_section->flags &=~ SEC_HAS_CONTENTS;
}
if (c > 2)
qsort (tab + 1, c - 1, sizeof (tab[0]), gptab_compare);
ext_tab = ((Elf32_External_gptab *)
bfd_alloc (abfd, c * sizeof (Elf32_External_gptab)));
if (ext_tab == NULL)
{
free (tab);
return false;
}
for (i = 0; i < c; i++)
mips_elf64_swap_gptab_out (abfd, tab + i, ext_tab + i);
free (tab);
o->_raw_size = c * sizeof (Elf32_External_gptab);
o->contents = (bfd_byte *) ext_tab;
o->link_order_head = (struct bfd_link_order *) NULL;
}
}
if (!bfd_elf64_bfd_final_link (abfd, info))
return false;
if (mdebug_sec != (asection *) NULL)
{
BFD_ASSERT (abfd->output_has_begun);
if (! bfd_ecoff_write_accumulated_debug (mdebug_handle, abfd, &debug,
swap, info,
mdebug_sec->filepos))
return false;
bfd_ecoff_debug_free (mdebug_handle, abfd, &debug, swap, info);
}
if (gptab_data_sec != (asection *) NULL)
{
if (! bfd_set_section_contents (abfd, gptab_data_sec,
gptab_data_sec->contents,
(file_ptr) 0,
gptab_data_sec->_raw_size))
return false;
}
if (gptab_bss_sec != (asection *) NULL)
{
if (! bfd_set_section_contents (abfd, gptab_bss_sec,
gptab_bss_sec->contents,
(file_ptr) 0,
gptab_bss_sec->_raw_size))
return false;
}
return true;
}
static const struct ecoff_debug_swap mips_elf64_ecoff_debug_swap =
{
magicSym2,
8,
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),
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,
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,
_bfd_mips_elf_read_ecoff_info
};
const struct elf_size_info mips_elf64_size_info =
{
sizeof (Elf64_External_Ehdr),
sizeof (Elf64_External_Phdr),
sizeof (Elf64_External_Shdr),
sizeof (Elf64_Mips_External_Rel),
sizeof (Elf64_Mips_External_Rela),
sizeof (Elf64_External_Sym),
sizeof (Elf64_External_Dyn),
sizeof (Elf_External_Note),
4,
3,
64,
8,
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,
bfd_elf64_swap_dyn_out,
mips_elf64_be_swap_reloc_in,
mips_elf64_be_swap_reloc_out,
mips_elf64_be_swap_reloca_in,
mips_elf64_be_swap_reloca_out
};
#define ELF_ARCH bfd_arch_mips
#define ELF_MACHINE_CODE EM_MIPS
#define ELF_MAXPAGESIZE 0x1000
#define elf_backend_collect true
#define elf_backend_type_change_ok true
#define elf_backend_can_gc_sections true
#define elf_info_to_howto mips_elf64_info_to_howto_rela
#define elf_info_to_howto_rel mips_elf64_info_to_howto_rel
#define elf_backend_object_p _bfd_mips_elf_object_p
#define elf_backend_symbol_processing _bfd_mips_elf_symbol_processing
#define elf_backend_section_processing _bfd_mips_elf_section_processing
#define elf_backend_section_from_shdr _bfd_mips_elf_section_from_shdr
#define elf_backend_fake_sections _bfd_mips_elf_fake_sections
#define elf_backend_section_from_bfd_section \
_bfd_mips_elf_section_from_bfd_section
#define elf_backend_add_symbol_hook _bfd_mips_elf_add_symbol_hook
#define elf_backend_link_output_symbol_hook \
_bfd_mips_elf_link_output_symbol_hook
#define elf_backend_create_dynamic_sections \
mips_elf64_create_dynamic_sections
#define elf_backend_check_relocs mips_elf64_check_relocs
#define elf_backend_adjust_dynamic_symbol \
mips_elf64_adjust_dynamic_symbol
#define elf_backend_always_size_sections \
mips_elf64_always_size_sections
#define elf_backend_size_dynamic_sections \
mips_elf64_size_dynamic_sections
#define elf_backend_relocate_section mips_elf64_relocate_section
#define elf_backend_finish_dynamic_symbol \
mips_elf64_finish_dynamic_symbol
#define elf_backend_finish_dynamic_sections \
mips_elf64_finish_dynamic_sections
#define elf_backend_final_write_processing \
_bfd_mips_elf_final_write_processing
#define elf_backend_additional_program_headers \
mips_elf64_additional_program_headers
#define elf_backend_modify_segment_map _bfd_mips_elf_modify_segment_map
#define elf_backend_gc_mark_hook mips_elf64_gc_mark_hook
#define elf_backend_gc_sweep_hook mips_elf64_gc_sweep_hook
#define elf_backend_ecoff_debug_swap &mips_elf64_ecoff_debug_swap
#define elf_backend_size_info mips_elf64_size_info
#define elf_backend_got_header_size (4 * MIPS_RESERVED_GOTNO)
#define elf_backend_plt_header_size 0
#define elf_backend_may_use_rel_p 1
#define elf_backend_may_use_rela_p 1
#define elf_backend_default_use_rela_p 1
#define bfd_elf64_find_nearest_line _bfd_mips_elf_find_nearest_line
#define bfd_elf64_set_section_contents _bfd_mips_elf_set_section_contents
#define bfd_elf64_bfd_link_hash_table_create \
mips_elf64_link_hash_table_create
#define bfd_elf64_bfd_final_link mips_elf64_final_link
#define bfd_elf64_bfd_merge_private_bfd_data \
_bfd_mips_elf_merge_private_bfd_data
#define bfd_elf64_bfd_set_private_flags _bfd_mips_elf_set_private_flags
#define bfd_elf64_bfd_print_private_bfd_data \
_bfd_mips_elf_print_private_bfd_data
#define bfd_elf64_get_reloc_upper_bound mips_elf64_get_reloc_upper_bound
#define bfd_elf64_bfd_reloc_type_lookup mips_elf64_reloc_type_lookup
#define bfd_elf64_archive_functions
extern boolean bfd_elf64_archive_slurp_armap
PARAMS((bfd *));
extern boolean bfd_elf64_archive_write_armap
PARAMS((bfd *, unsigned int, struct orl *, unsigned int, int));
#define bfd_elf64_archive_slurp_extended_name_table \
_bfd_archive_coff_slurp_extended_name_table
#define bfd_elf64_archive_construct_extended_name_table \
_bfd_archive_coff_construct_extended_name_table
#define bfd_elf64_archive_truncate_arname \
_bfd_archive_coff_truncate_arname
#define bfd_elf64_archive_read_ar_hdr _bfd_archive_coff_read_ar_hdr
#define bfd_elf64_archive_openr_next_archived_file \
_bfd_archive_coff_openr_next_archived_file
#define bfd_elf64_archive_get_elt_at_index \
_bfd_archive_coff_get_elt_at_index
#define bfd_elf64_archive_generic_stat_arch_elt \
_bfd_archive_coff_generic_stat_arch_elt
#define bfd_elf64_archive_update_armap_timestamp \
_bfd_archive_coff_update_armap_timestamp
#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"
#include "elf64-target.h"
#define INCLUDED_TARGET_FILE
#undef TARGET_LITTLE_SYM
#undef TARGET_LITTLE_NAME
#undef TARGET_BIG_SYM
#undef TARGET_BIG_NAME
#define TARGET_LITTLE_SYM bfd_elf64_tradlittlemips_vec
#define TARGET_LITTLE_NAME "elf64-tradlittlemips"
#define TARGET_BIG_SYM bfd_elf64_tradbigmips_vec
#define TARGET_BIG_NAME "elf64-tradbigmips"
#include "elf64-target.h"