/* MIPS-specific support for 32-bit ELF
- Copyright 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
+ Copyright 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
Most of the information added by Ian Lance Taylor, Cygnus Support,
<ian@cygnus.com>.
#define ECOFF_32
#include "ecoffswap.h"
-static bfd_reloc_status_type mips_elf_hi16_reloc PARAMS ((bfd *abfd,
- arelent *reloc,
- asymbol *symbol,
- PTR data,
- asection *section,
- bfd *output_bfd,
- char **error));
-static bfd_reloc_status_type mips_elf_got16_reloc PARAMS ((bfd *abfd,
- arelent *reloc,
- asymbol *symbol,
- PTR data,
- asection *section,
- bfd *output_bfd,
- char **error));
-static bfd_reloc_status_type mips_elf_lo16_reloc PARAMS ((bfd *abfd,
- arelent *reloc,
- asymbol *symbol,
- PTR data,
- asection *section,
- bfd *output_bfd,
- char **error));
-static bfd_reloc_status_type mips_elf_gprel16_reloc PARAMS ((bfd *abfd,
- arelent *reloc,
- asymbol *symbol,
- PTR data,
- asection *section,
- bfd *output_bfd,
- char **error));
-static bfd_reloc_status_type mips_elf_gprel32_reloc PARAMS ((bfd *abfd,
- arelent *reloc,
- asymbol *symbol,
- PTR data,
- asection *section,
- bfd *output_bfd,
- char **error));
+static bfd_reloc_status_type mips32_64bit_reloc
+ PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static reloc_howto_type *bfd_elf32_bfd_reloc_type_lookup
PARAMS ((bfd *, bfd_reloc_code_real_type));
static void mips_info_to_howto_rel
static void bfd_mips_elf32_swap_gptab_out
PARAMS ((bfd *, const Elf32_gptab *, Elf32_External_gptab *));
static boolean mips_elf_sym_is_global PARAMS ((bfd *, asymbol *));
-static boolean mips_elf_object_p PARAMS ((bfd *));
+static boolean mips_elf32_object_p PARAMS ((bfd *));
static boolean mips_elf_create_procedure_table
PARAMS ((PTR, bfd *, struct bfd_link_info *, asection *,
struct ecoff_debug_info *));
static int mips_elf_additional_program_headers PARAMS ((bfd *));
static boolean mips_elf_modify_segment_map PARAMS ((bfd *));
-static void mips_elf_final_write_processing
- PARAMS ((bfd *, boolean));
-static boolean mips_elf_set_private_flags PARAMS ((bfd *, flagword));
-static boolean mips_elf_copy_private_bfd_data PARAMS ((bfd *, bfd *));
-static boolean mips_elf_merge_private_bfd_data PARAMS ((bfd *, bfd *));
+static INLINE int elf_mips_isa PARAMS ((flagword));
static boolean mips_elf32_section_from_shdr
PARAMS ((bfd *, Elf32_Internal_Shdr *, char *));
-static boolean mips_elf_fake_sections
- PARAMS ((bfd *, Elf32_Internal_Shdr *, asection *));
-static boolean mips_elf_section_from_bfd_section
- PARAMS ((bfd *, Elf32_Internal_Shdr *, asection *, int *));
-static boolean mips_elf_section_processing
+static boolean mips_elf32_section_processing
PARAMS ((bfd *, Elf32_Internal_Shdr *));
-static void mips_elf_symbol_processing PARAMS ((bfd *, asymbol *));
-static boolean mips_elf_read_ecoff_info
- PARAMS ((bfd *, asection *, struct ecoff_debug_info *));
static boolean mips_elf_is_local_label
PARAMS ((bfd *, asymbol *));
-static boolean mips_elf_find_nearest_line
- PARAMS ((bfd *, asection *, asymbol **, bfd_vma, const char **,
- const char **, unsigned int *));
static struct bfd_hash_entry *mips_elf_link_hash_newfunc
PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
static struct bfd_link_hash_table *mips_elf_link_hash_table_create
Elf_Internal_Rela *, bfd_byte *, bfd_vma));
static void mips_elf_relocate_global_got
PARAMS ((bfd *, Elf_Internal_Rela *, bfd_byte *, bfd_vma));
+static bfd_reloc_status_type mips16_jump_reloc
+ PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static boolean mips_elf_adjust_dynindx
PARAMS ((struct elf_link_hash_entry *, PTR));
static boolean mips_elf_relocate_section
PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
+static boolean mips_elf_link_output_symbol_hook
+ PARAMS ((bfd *, struct bfd_link_info *, const char *, Elf_Internal_Sym *,
+ asection *));
static boolean mips_elf_create_dynamic_sections
PARAMS ((bfd *, struct bfd_link_info *));
static boolean mips_elf_create_compact_rel_section
R_MIPS_INSERT_B, R_MIPS_DELETE,
R_MIPS_HIGHER, R_MIPS_HIGHEST,
R_MIPS_CALL_HI16, R_MIPS_CALL_LO16,
- R_MIPS_max
+ R_MIPS_max,
+ /* This reloc is used for the mips16. */
+ R_MIPS16_26 = 100
};
static reloc_howto_type elf_mips_howto_table[] =
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
- mips_elf_hi16_reloc, /* special_function */
+ _bfd_mips_elf_hi16_reloc, /* special_function */
"R_MIPS_HI16", /* name */
true, /* partial_inplace */
0xffff, /* src_mask */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
- mips_elf_lo16_reloc, /* special_function */
+ _bfd_mips_elf_lo16_reloc, /* special_function */
"R_MIPS_LO16", /* name */
true, /* partial_inplace */
0xffff, /* src_mask */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
- mips_elf_gprel16_reloc, /* special_function */
+ _bfd_mips_elf_gprel16_reloc, /* special_function */
"R_MIPS_GPREL16", /* name */
true, /* partial_inplace */
0xffff, /* src_mask */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
- mips_elf_gprel16_reloc, /* special_function */
+ _bfd_mips_elf_gprel16_reloc, /* special_function */
"R_MIPS_LITERAL", /* name */
true, /* partial_inplace */
0xffff, /* src_mask */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
- mips_elf_got16_reloc, /* special_function */
+ _bfd_mips_elf_got16_reloc, /* special_function */
"R_MIPS_GOT16", /* name */
false, /* partial_inplace */
0, /* src_mask */
false, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
- mips_elf_gprel32_reloc, /* special_function */
+ _bfd_mips_elf_gprel32_reloc, /* special_function */
"R_MIPS_GPREL32", /* name */
true, /* partial_inplace */
0xffffffff, /* src_mask */
0x000007c4, /* dst_mask */
false), /* pcrel_offset */
- /* A 64 bit relocation. Presumably not used in 32 bit ELF. */
- { R_MIPS_64 },
+ /* A 64 bit relocation. This is used in 32 bit ELF when addresses
+ are 64 bits long; the upper 32 bits are simply a sign extension.
+ The fields of the howto should be the same as for R_MIPS_32,
+ other than the type, name, and special_function. */
+ HOWTO (R_MIPS_64, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ false, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ mips32_64bit_reloc, /* special_function */
+ "R_MIPS_64", /* name */
+ true, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ false), /* pcrel_offset */
/* Displacement in the global offset table. */
/* FIXME: Not handled correctly. */
false) /* pcrel_offset */
};
+/* The reloc used for the mips16 jump instruction. */
+static reloc_howto_type elf_mips16_jump_howto =
+ HOWTO (R_MIPS16_26, /* type */
+ 2, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 26, /* bitsize */
+ false, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ /* This needs complex overflow
+ detection, because the upper four
+ bits must match the PC. */
+ mips16_jump_reloc, /* special_function */
+ "R_MIPS16_26", /* name */
+ true, /* partial_inplace */
+ 0x3ffffff, /* src_mask */
+ 0x3ffffff, /* dst_mask */
+ false); /* pcrel_offset */
+
/* Do a R_MIPS_HI16 relocation. This has to be done in combination
with a R_MIPS_LO16 reloc, because there is a carry from the LO16 to
the HI16. Here we just save the information we need; we do the
static struct mips_hi16 *mips_hi16_list;
-static bfd_reloc_status_type
-mips_elf_hi16_reloc (abfd,
+bfd_reloc_status_type
+_bfd_mips_elf_hi16_reloc (abfd,
reloc_entry,
symbol,
data,
inplace relocation; this function exists in order to do the
R_MIPS_HI16 relocation described above. */
-static bfd_reloc_status_type
-mips_elf_lo16_reloc (abfd,
+bfd_reloc_status_type
+_bfd_mips_elf_lo16_reloc (abfd,
reloc_entry,
symbol,
data,
This implementation suffices for the assembler, but the linker does
not yet know how to create global offset tables. */
-static bfd_reloc_status_type
-mips_elf_got16_reloc (abfd,
+bfd_reloc_status_type
+_bfd_mips_elf_got16_reloc (abfd,
reloc_entry,
symbol,
data,
just like HI16. */
if (output_bfd != (bfd *) NULL
&& (symbol->flags & BSF_SECTION_SYM) != 0)
- return mips_elf_hi16_reloc (abfd, reloc_entry, symbol, data,
- input_section, output_bfd, error_message);
+ return _bfd_mips_elf_hi16_reloc (abfd, reloc_entry, symbol, data,
+ input_section, output_bfd, error_message);
abort ();
}
arelent *, asection *,
boolean, PTR, bfd_vma));
-static bfd_reloc_status_type
-mips_elf_gprel16_reloc (abfd,
- reloc_entry,
- symbol,
- data,
- input_section,
- output_bfd,
- error_message)
+bfd_reloc_status_type
+_bfd_mips_elf_gprel16_reloc (abfd, reloc_entry, symbol, data, input_section,
+ output_bfd, error_message)
bfd *abfd;
arelent *reloc_entry;
asymbol *symbol;
insn = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
/* Set val to the offset into the section or symbol. */
- val = ((insn & 0xffff) + reloc_entry->addend) & 0xffff;
- if (val & 0x8000)
- val -= 0x10000;
+ if (reloc_entry->howto->src_mask == 0)
+ {
+ /* This case occurs with the 64-bit MIPS ELF ABI. */
+ val = reloc_entry->addend;
+ }
+ else
+ {
+ val = ((insn & 0xffff) + reloc_entry->addend) & 0xffff;
+ if (val & 0x8000)
+ val -= 0x10000;
+ }
/* Adjust val for the final section location and GP value. If we
are producing relocateable output, we don't want to do this for
arelent *, asection *,
boolean, PTR, bfd_vma));
-static bfd_reloc_status_type
-mips_elf_gprel32_reloc (abfd,
+bfd_reloc_status_type
+_bfd_mips_elf_gprel32_reloc (abfd,
reloc_entry,
symbol,
data,
if (reloc_entry->address > input_section->_cooked_size)
return bfd_reloc_outofrange;
- val = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
+ if (reloc_entry->howto->src_mask == 0)
+ {
+ /* This case arises with the 64-bit MIPS ELF ABI. */
+ val = 0;
+ }
+ else
+ val = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
/* Set val to the offset into the section or symbol. */
val += reloc_entry->addend;
return bfd_reloc_ok;
}
+/* Handle a 64 bit reloc in a 32 bit MIPS ELF file. These are
+ generated when addreses are 64 bits. The upper 32 bits are a simle
+ sign extension. */
+
+static bfd_reloc_status_type
+mips32_64bit_reloc (abfd, reloc_entry, symbol, data, input_section,
+ output_bfd, error_message)
+ bfd *abfd;
+ arelent *reloc_entry;
+ asymbol *symbol;
+ PTR data;
+ asection *input_section;
+ bfd *output_bfd;
+ char **error_message;
+{
+ bfd_reloc_status_type r;
+ arelent reloc32;
+ unsigned long val;
+ bfd_size_type addr;
+
+ r = bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data,
+ input_section, output_bfd, error_message);
+ if (r != bfd_reloc_continue)
+ return r;
+
+ /* Do a normal 32 bit relocation on the lower 32 bits. */
+ reloc32 = *reloc_entry;
+ if (bfd_big_endian (abfd))
+ reloc32.address += 4;
+ reloc32.howto = &elf_mips_howto_table[R_MIPS_32];
+ r = bfd_perform_relocation (abfd, &reloc32, data, input_section,
+ output_bfd, error_message);
+
+ /* Sign extend into the upper 32 bits. */
+ val = bfd_get_32 (abfd, (bfd_byte *) data + reloc32.address);
+ if ((val & 0x80000000) != 0)
+ val = 0xffffffff;
+ else
+ val = 0;
+ addr = reloc_entry->address;
+ if (bfd_little_endian (abfd))
+ addr += 4;
+ bfd_put_32 (abfd, val, (bfd_byte *) data + addr);
+
+ return r;
+}
+
+/* Handle a mips16 jump. */
+
+static bfd_reloc_status_type
+mips16_jump_reloc (abfd, reloc_entry, symbol, data, input_section,
+ output_bfd, error_message)
+ bfd *abfd;
+ arelent *reloc_entry;
+ asymbol *symbol;
+ PTR data;
+ asection *input_section;
+ bfd *output_bfd;
+ char **error_message;
+{
+ if (output_bfd != (bfd *) NULL
+ && (symbol->flags & BSF_SECTION_SYM) == 0
+ && reloc_entry->addend == 0)
+ {
+ reloc_entry->address += input_section->output_offset;
+ return bfd_reloc_ok;
+ }
+
+ /* FIXME. */
+ abort ();
+}
+
/* A mapping from BFD reloc types to MIPS ELF reloc types. */
struct elf_reloc_map {
{ BFD_RELOC_16, R_MIPS_16 },
{ BFD_RELOC_32, R_MIPS_32 },
{ BFD_RELOC_CTOR, R_MIPS_32 },
- { BFD_RELOC_32_PCREL, R_MIPS_REL32 },
+ { BFD_RELOC_64, R_MIPS_64 },
{ BFD_RELOC_MIPS_JMP, R_MIPS_26 },
{ BFD_RELOC_HI16_S, R_MIPS_HI16 },
{ BFD_RELOC_LO16, R_MIPS_LO16 },
if (mips_reloc_map[i].bfd_reloc_val == code)
return &elf_mips_howto_table[(int) mips_reloc_map[i].elf_reloc_val];
}
+
+ /* Special handling for the MIPS16 jump, since it is a made up reloc
+ type with a large value. */
+ if (code == BFD_RELOC_MIPS16_JMP)
+ return &elf_mips16_jump_howto;
+
return NULL;
}
unsigned int r_type;
r_type = ELF32_R_TYPE (dst->r_info);
- BFD_ASSERT (r_type < (unsigned int) R_MIPS_max);
- cache_ptr->howto = &elf_mips_howto_table[r_type];
+ if (r_type == R_MIPS16_26)
+ cache_ptr->howto = &elf_mips16_jump_howto;
+ else
+ {
+ BFD_ASSERT (r_type < (unsigned int) R_MIPS_max);
+ cache_ptr->howto = &elf_mips_howto_table[r_type];
+ }
/* The addend for a GPREL16 or LITERAL relocation comes from the GP
value for the object file. We get the addend now, rather than
(bfd_byte *) ex->ri_gp_value);
}
+/* In the 64 bit ABI, the .MIPS.options section holds register
+ information in an Elf64_Reginfo structure. These routines swap
+ them in and out. They are globally visible because they are used
+ outside of BFD. These routines are here so that gas can call them
+ without worrying about whether the 64 bit ABI has been included. */
+
+void
+bfd_mips_elf64_swap_reginfo_in (abfd, ex, in)
+ bfd *abfd;
+ const Elf64_External_RegInfo *ex;
+ Elf64_Internal_RegInfo *in;
+{
+ in->ri_gprmask = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_gprmask);
+ in->ri_pad = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_pad);
+ in->ri_cprmask[0] = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_cprmask[0]);
+ in->ri_cprmask[1] = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_cprmask[1]);
+ in->ri_cprmask[2] = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_cprmask[2]);
+ in->ri_cprmask[3] = bfd_h_get_32 (abfd, (bfd_byte *) ex->ri_cprmask[3]);
+ in->ri_gp_value = bfd_h_get_64 (abfd, (bfd_byte *) ex->ri_gp_value);
+}
+
+void
+bfd_mips_elf64_swap_reginfo_out (abfd, in, ex)
+ bfd *abfd;
+ const Elf64_Internal_RegInfo *in;
+ Elf64_External_RegInfo *ex;
+{
+ bfd_h_put_32 (abfd, (bfd_vma) in->ri_gprmask,
+ (bfd_byte *) ex->ri_gprmask);
+ bfd_h_put_32 (abfd, (bfd_vma) in->ri_pad,
+ (bfd_byte *) ex->ri_pad);
+ bfd_h_put_32 (abfd, (bfd_vma) in->ri_cprmask[0],
+ (bfd_byte *) ex->ri_cprmask[0]);
+ bfd_h_put_32 (abfd, (bfd_vma) in->ri_cprmask[1],
+ (bfd_byte *) ex->ri_cprmask[1]);
+ bfd_h_put_32 (abfd, (bfd_vma) in->ri_cprmask[2],
+ (bfd_byte *) ex->ri_cprmask[2]);
+ bfd_h_put_32 (abfd, (bfd_vma) in->ri_cprmask[3],
+ (bfd_byte *) ex->ri_cprmask[3]);
+ bfd_h_put_64 (abfd, (bfd_vma) in->ri_gp_value,
+ (bfd_byte *) ex->ri_gp_value);
+}
+
/* Swap an entry in a .gptab section. Note that these routines rely
on the equivalence of the two elements of the union. */
bfd_h_put_32 (abfd, (bfd_vma) in->konst, ex->konst);
bfd_h_put_32 (abfd, (bfd_vma) in->vaddr, ex->vaddr);
}
+
+/* Swap in an options header. */
+
+void
+bfd_mips_elf_swap_options_in (abfd, ex, in)
+ bfd *abfd;
+ const Elf_External_Options *ex;
+ Elf_Internal_Options *in;
+{
+ in->kind = bfd_h_get_8 (abfd, ex->kind);
+ in->size = bfd_h_get_8 (abfd, ex->size);
+ in->section = bfd_h_get_16 (abfd, ex->section);
+ in->info = bfd_h_get_32 (abfd, ex->info);
+}
+
+/* Swap out an options header. */
+
+void
+bfd_mips_elf_swap_options_out (abfd, in, ex)
+ bfd *abfd;
+ const Elf_Internal_Options *in;
+ Elf_External_Options *ex;
+{
+ bfd_h_put_8 (abfd, in->kind, ex->kind);
+ bfd_h_put_8 (abfd, in->size, ex->size);
+ bfd_h_put_16 (abfd, in->section, ex->section);
+ bfd_h_put_32 (abfd, in->info, ex->info);
+}
\f
/* Determine whether a symbol is global for the purposes of splitting
the symbol table into global symbols and local symbols. At least
return (sym->flags & BSF_SECTION_SYM) == 0 ? true : false;
}
\f
-/* Set the right machine number for a MIPS ELF file. */
+/* Set the right machine number for a MIPS ELF file. This is used for
+ both the 32-bit and the 64-bit ABI. */
-static boolean
-mips_elf_object_p (abfd)
+boolean
+_bfd_mips_elf_object_p (abfd)
bfd *abfd;
{
switch (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH)
{
default:
case E_MIPS_ARCH_1:
- /* Just use the default, which was set in elfcode.h. */
+ (void) bfd_default_set_arch_mach (abfd, bfd_arch_mips, 3000);
break;
case E_MIPS_ARCH_2:
break;
}
+ return true;
+}
+
+/* Set the right machine number for a 32-bit MIPS ELF file. */
+
+static boolean
+mips_elf32_object_p (abfd)
+ bfd *abfd;
+{
/* Irix 5 is broken. Object file symbol tables are not always
sorted correctly such that local symbols precede global symbols,
and the sh_info field in the symbol table is not always right. */
elf_bad_symtab (abfd) = true;
- return true;
+ return _bfd_mips_elf_object_p (abfd);
}
/* The final processing done just before writing out a MIPS ELF object
file. This gets the MIPS architecture right based on the machine
- number. */
+ number. This is used by both the 32-bit and the 64-bit ABI. */
/*ARGSUSED*/
-static void
-mips_elf_final_write_processing (abfd, linker)
+void
+_bfd_mips_elf_final_write_processing (abfd, linker)
bfd *abfd;
boolean linker;
{
unsigned long val;
unsigned int i;
Elf_Internal_Shdr **hdrpp;
+ const char *name;
+ asection *sec;
switch (bfd_get_mach (abfd))
{
i < elf_elfheader (abfd)->e_shnum;
i++, hdrpp++)
{
- if ((*hdrpp)->sh_type == SHT_MIPS_GPTAB)
+ switch ((*hdrpp)->sh_type)
{
- const char *name;
- asection *sec;
+ case SHT_MIPS_LIBLIST:
+ sec = bfd_get_section_by_name (abfd, ".dynstr");
+ if (sec != NULL)
+ (*hdrpp)->sh_link = elf_section_data (sec)->this_idx;
+ break;
+ case SHT_MIPS_GPTAB:
BFD_ASSERT ((*hdrpp)->bfd_section != NULL);
name = bfd_get_section_name (abfd, (*hdrpp)->bfd_section);
BFD_ASSERT (name != NULL
sec = bfd_get_section_by_name (abfd, name + sizeof ".gptab" - 1);
BFD_ASSERT (sec != NULL);
(*hdrpp)->sh_info = elf_section_data (sec)->this_idx;
+ break;
+
+ case SHT_MIPS_CONTENT:
+ BFD_ASSERT ((*hdrpp)->bfd_section != NULL);
+ name = bfd_get_section_name (abfd, (*hdrpp)->bfd_section);
+ BFD_ASSERT (name != NULL
+ && strncmp (name, ".MIPS.content",
+ sizeof ".MIPS.content" - 1) == 0);
+ sec = bfd_get_section_by_name (abfd,
+ name + sizeof ".MIPS.content" - 1);
+ BFD_ASSERT (sec != NULL);
+ (*hdrpp)->sh_info = elf_section_data (sec)->this_idx;
+ break;
+
+ case SHT_MIPS_SYMBOL_LIB:
+ sec = bfd_get_section_by_name (abfd, ".dynsym");
+ if (sec != NULL)
+ (*hdrpp)->sh_link = elf_section_data (sec)->this_idx;
+ sec = bfd_get_section_by_name (abfd, ".liblist");
+ if (sec != NULL)
+ (*hdrpp)->sh_info = elf_section_data (sec)->this_idx;
+ break;
+
+ case SHT_MIPS_EVENTS:
+ BFD_ASSERT ((*hdrpp)->bfd_section != NULL);
+ name = bfd_get_section_name (abfd, (*hdrpp)->bfd_section);
+ BFD_ASSERT (name != NULL);
+ if (strncmp (name, ".MIPS.events", sizeof ".MIPS.events" - 1) == 0)
+ sec = bfd_get_section_by_name (abfd,
+ name + sizeof ".MIPS.events" - 1);
+ else
+ {
+ BFD_ASSERT (strncmp (name, ".MIPS.post_rel",
+ sizeof ".MIPS.post_rel" - 1) == 0);
+ sec = bfd_get_section_by_name (abfd,
+ (name
+ + sizeof ".MIPS.post_rel" - 1));
+ }
+ BFD_ASSERT (sec != NULL);
+ (*hdrpp)->sh_link = elf_section_data (sec)->this_idx;
+ break;
}
}
}
\f
/* Function to keep MIPS specific file flags like as EF_MIPS_PIC. */
-static boolean
-mips_elf_set_private_flags (abfd, flags)
+boolean
+_bfd_mips_elf_set_private_flags (abfd, flags)
bfd *abfd;
flagword flags;
{
/* Copy backend specific data from one object module to another */
-static boolean
-mips_elf_copy_private_bfd_data (ibfd, obfd)
+boolean
+_bfd_mips_elf_copy_private_bfd_data (ibfd, obfd)
bfd *ibfd;
bfd *obfd;
{
- /* This function is selected based on the input vector. We only
- want to copy information over if the output BFD also uses Elf
- format. */
- if (bfd_get_flavour (obfd) != bfd_target_elf_flavour)
+ if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
+ || bfd_get_flavour (obfd) != bfd_target_elf_flavour)
return true;
BFD_ASSERT (!elf_flags_init (obfd)
return true;
}
+/* Return the ISA for a MIPS e_flags value. */
+
+static INLINE int
+elf_mips_isa (flags)
+ flagword flags;
+{
+ switch (flags & EF_MIPS_ARCH)
+ {
+ case E_MIPS_ARCH_1:
+ return 1;
+ case E_MIPS_ARCH_2:
+ return 2;
+ case E_MIPS_ARCH_3:
+ return 3;
+ case E_MIPS_ARCH_4:
+ return 4;
+ }
+ return 4;
+}
+
/* Merge backend specific data from an object file to the output
object file when linking. */
-static boolean
-mips_elf_merge_private_bfd_data (ibfd, obfd)
+boolean
+_bfd_mips_elf_merge_private_bfd_data (ibfd, obfd)
bfd *ibfd;
bfd *obfd;
{
flagword old_flags;
flagword new_flags;
+ boolean ok;
/* Check if we have the same endianess */
if (ibfd->xvec->byteorder != obfd->xvec->byteorder
return false;
}
- /* This function is selected based on the input vector. We only
- want to copy information over if the output BFD also uses Elf
- format. */
- if (bfd_get_flavour (obfd) != bfd_target_elf_flavour)
+ if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
+ || bfd_get_flavour (obfd) != bfd_target_elf_flavour)
return true;
new_flags = elf_elfheader (ibfd)->e_flags;
elf_elfheader (obfd)->e_flags |= new_flags & EF_MIPS_NOREORDER;
old_flags = elf_elfheader (obfd)->e_flags;
- if (!elf_flags_init (obfd)) /* First call, no flags set */
+ if (! elf_flags_init (obfd))
{
elf_flags_init (obfd) = true;
elf_elfheader (obfd)->e_flags = new_flags;
- }
- else if (((new_flags ^ old_flags) & ~EF_MIPS_NOREORDER)
- == 0) /* Compatible flags are ok */
- ;
- else /* Incompatible flags */
- {
- /* Warn about -fPIC mismatch */
- if ((new_flags & EF_MIPS_PIC) != (old_flags & EF_MIPS_PIC))
+
+ if (bfd_get_arch (obfd) == bfd_get_arch (ibfd)
+ && bfd_get_arch_info (obfd)->the_default)
{
- new_flags &= ~EF_MIPS_PIC;
- (*_bfd_error_handler)
- ("%s: needs all files compiled with -fPIC",
- bfd_get_filename (ibfd));
+ if (! bfd_set_arch_mach (obfd, bfd_get_arch (ibfd),
+ bfd_get_mach (ibfd)))
+ return false;
}
- if ((new_flags & EF_MIPS_CPIC) != (old_flags & EF_MIPS_CPIC))
+ return true;
+ }
+
+ /* Check flag compatibility. */
+
+ new_flags &= ~EF_MIPS_NOREORDER;
+ old_flags &= ~EF_MIPS_NOREORDER;
+
+ if (new_flags == old_flags)
+ return true;
+
+ ok = true;
+
+ if ((new_flags & EF_MIPS_PIC) != (old_flags & EF_MIPS_PIC))
+ {
+ new_flags &= ~EF_MIPS_PIC;
+ old_flags &= ~EF_MIPS_PIC;
+ (*_bfd_error_handler)
+ ("%s: linking PIC files with non-PIC files",
+ bfd_get_filename (ibfd));
+ ok = false;
+ }
+
+ if ((new_flags & EF_MIPS_CPIC) != (old_flags & EF_MIPS_CPIC))
+ {
+ new_flags &= ~EF_MIPS_CPIC;
+ old_flags &= ~EF_MIPS_CPIC;
+ (*_bfd_error_handler)
+ ("%s: linking abicalls files with non-abicalls files",
+ bfd_get_filename (ibfd));
+ ok = false;
+ }
+
+ /* Don't warn about mixing -mips1 and -mips2 code, or mixing -mips3
+ and -mips4 code. They will normally use the same data sizes and
+ calling conventions. */
+ if ((new_flags & EF_MIPS_ARCH) != (old_flags & EF_MIPS_ARCH))
+ {
+ int new_isa, old_isa;
+
+ new_isa = elf_mips_isa (new_flags);
+ old_isa = elf_mips_isa (old_flags);
+ if ((new_isa == 1 || new_isa == 2)
+ ? (old_isa != 1 && old_isa != 2)
+ : (old_isa == 1 || old_isa == 2))
{
- new_flags &= ~EF_MIPS_CPIC;
(*_bfd_error_handler)
- ("%s: needs all files compiled with -mabicalls",
- bfd_get_filename (ibfd));
+ ("%s: ISA mismatch (-mips%d) with previous modules (-mips%d)",
+ bfd_get_filename (ibfd), new_isa, old_isa);
+ ok = false;
}
- /* Warn about any other mismatches */
- if (new_flags != old_flags)
- (*_bfd_error_handler)
- ("%s: uses different e_flags (0x%lx) fields than previous modules (0x%lx)",
- bfd_get_filename (ibfd), (unsigned long) new_flags,
- (unsigned long) old_flags);
+ new_flags &= ~ EF_MIPS_ARCH;
+ old_flags &= ~ EF_MIPS_ARCH;
+ }
+ /* Warn about any other mismatches */
+ if (new_flags != old_flags)
+ {
+ (*_bfd_error_handler)
+ ("%s: uses different e_flags (0x%lx) fields than previous modules (0x%lx)",
+ bfd_get_filename (ibfd), (unsigned long) new_flags,
+ (unsigned long) old_flags);
+ ok = false;
+ }
+
+ if (! ok)
+ {
bfd_set_error (bfd_error_bad_value);
return false;
}
|| hdr->sh_size != sizeof (Elf32_External_RegInfo))
return false;
break;
+ case SHT_MIPS_IFACE:
+ if (strcmp (name, ".MIPS.interfaces") != 0)
+ return false;
+ break;
+ case SHT_MIPS_CONTENT:
+ if (strncmp (name, ".MIPS.content", sizeof ".MIPS.content" - 1) != 0)
+ return false;
+ break;
case SHT_MIPS_OPTIONS:
- if (strcmp (name, ".options") != 0)
+ if (strcmp (name, ".options") != 0
+ && strcmp (name, ".MIPS.options") != 0)
return false;
break;
case SHT_MIPS_DWARF:
if (strncmp (name, ".debug_", sizeof ".debug_" - 1) != 0)
return false;
break;
+ case SHT_MIPS_SYMBOL_LIB:
+ if (strcmp (name, ".MIPS.symlib") != 0)
+ return false;
+ break;
case SHT_MIPS_EVENTS:
- if (strncmp (name, ".MIPS.events.", sizeof ".MIPS.events." - 1) != 0)
+ if (strncmp (name, ".MIPS.events", sizeof ".MIPS.events" - 1) != 0
+ && strncmp (name, ".MIPS.post_rel",
+ sizeof ".MIPS.post_rel" - 1) != 0)
return false;
break;
default:
elf_gp (abfd) = s.ri_gp_value;
}
+ /* For a SHT_MIPS_OPTIONS section, look for a ODK_REGINFO entry, and
+ set the gp value based on what we find. We may see both
+ SHT_MIPS_REGINFO and SHT_MIPS_OPTIONS/ODK_REGINFO; in that case,
+ they should agree. */
+ if (hdr->sh_type == SHT_MIPS_OPTIONS)
+ {
+ bfd_byte *contents, *l, *lend;
+
+ contents = (bfd_byte *) bfd_malloc (hdr->sh_size);
+ if (contents == NULL)
+ return false;
+ if (! bfd_get_section_contents (abfd, hdr->bfd_section, contents,
+ (file_ptr) 0, hdr->sh_size))
+ {
+ free (contents);
+ return false;
+ }
+ l = contents;
+ lend = contents + hdr->sh_size;
+ while (l + sizeof (Elf_External_Options) <= lend)
+ {
+ Elf_Internal_Options intopt;
+
+ bfd_mips_elf_swap_options_in (abfd, (Elf_External_Options *) l,
+ &intopt);
+ if (intopt.kind == ODK_REGINFO)
+ {
+ Elf32_RegInfo intreg;
+
+ bfd_mips_elf32_swap_reginfo_in
+ (abfd,
+ ((Elf32_External_RegInfo *)
+ (l + sizeof (Elf_External_Options))),
+ &intreg);
+ elf_gp (abfd) = intreg.ri_gp_value;
+ }
+ l += intopt.size;
+ }
+ free (contents);
+ }
+
return true;
}
/* Set the correct type for a MIPS ELF section. We do this by the
- section name, which is a hack, but ought to work. */
+ section name, which is a hack, but ought to work. This routine is
+ used by both the 32-bit and the 64-bit ABI. */
-static boolean
-mips_elf_fake_sections (abfd, hdr, sec)
+boolean
+_bfd_mips_elf_fake_sections (abfd, hdr, sec)
bfd *abfd;
Elf32_Internal_Shdr *hdr;
asection *sec;
{
hdr->sh_type = SHT_MIPS_LIBLIST;
hdr->sh_info = sec->_raw_size / sizeof (Elf32_Lib);
- /* FIXME: Set the sh_link field. */
+ /* The sh_link field is set in final_write_processing. */
}
else if (strcmp (name, ".msym") == 0)
{
{
hdr->sh_type = SHT_MIPS_GPTAB;
hdr->sh_entsize = sizeof (Elf32_External_gptab);
- /* The sh_info field is set in mips_elf_final_write_processing. */
+ /* The sh_info field is set in final_write_processing. */
}
else if (strcmp (name, ".ucode") == 0)
hdr->sh_type = SHT_MIPS_UCODE;
|| strcmp (name, ".lit4") == 0
|| strcmp (name, ".lit8") == 0)
hdr->sh_flags |= SHF_MIPS_GPREL;
- else if (strcmp (name, ".options") == 0)
+ else if (strcmp (name, ".MIPS.interfaces") == 0)
+ {
+ hdr->sh_type = SHT_MIPS_IFACE;
+ hdr->sh_flags |= SHF_MIPS_NOSTRIP;
+ }
+ else if (strcmp (name, ".MIPS.content") == 0)
+ {
+ hdr->sh_type = SHT_MIPS_CONTENT;
+ /* The sh_info field is set in final_write_processing. */
+ }
+ else if (strcmp (name, ".options") == 0
+ || strcmp (name, ".MIPS.options") == 0)
{
hdr->sh_type = SHT_MIPS_OPTIONS;
hdr->sh_entsize = 1;
+ hdr->sh_flags |= SHF_MIPS_NOSTRIP;
}
else if (strncmp (name, ".debug_", sizeof ".debug_" - 1) == 0)
hdr->sh_type = SHT_MIPS_DWARF;
- else if (strncmp (name, ".MIPS.events.", sizeof ".MIPS.events." - 1) == 0)
- hdr->sh_type = SHT_MIPS_EVENTS;
+ else if (strcmp (name, ".MIPS.symlib") == 0)
+ {
+ hdr->sh_type = SHT_MIPS_SYMBOL_LIB;
+ /* The sh_link and sh_info fields are set in
+ final_write_processing. */
+ }
+ else if (strncmp (name, ".MIPS.events", sizeof ".MIPS.events" - 1) == 0
+ || strncmp (name, ".MIPS.post_rel",
+ sizeof ".MIPS.post_rel" - 1) == 0)
+ {
+ hdr->sh_type = SHT_MIPS_EVENTS;
+ hdr->sh_flags |= SHF_MIPS_NOSTRIP;
+ /* The sh_link field is set in final_write_processing. */
+ }
return true;
}
/* Given a BFD section, try to locate the corresponding ELF section
- index. */
+ index. This is used by both the 32-bit and the 64-bit ABI.
+ Actually, it's not clear to me that the 64-bit ABI supports these,
+ but for non-PIC objects we will certainly want support for at least
+ the .scommon section. */
-static boolean
-mips_elf_section_from_bfd_section (abfd, hdr, sec, retval)
+boolean
+_bfd_mips_elf_section_from_bfd_section (abfd, hdr, sec, retval)
bfd *abfd;
Elf32_Internal_Shdr *hdr;
asection *sec;
return false;
}
-/* Work over a section just before writing it out. We update the GP
- value in the .reginfo section based on the value we are using.
- FIXME: We recognize sections that need the SHF_MIPS_GPREL flag by
- name; there has to be a better way. */
+/* When are writing out the .options or .MIPS.options section,
+ remember the bytes we are writing out, so that we can install the
+ GP value in the section_processing routine. */
-static boolean
-mips_elf_section_processing (abfd, hdr)
+boolean
+_bfd_mips_elf_set_section_contents (abfd, section, location, offset, count)
bfd *abfd;
- Elf32_Internal_Shdr *hdr;
+ sec_ptr section;
+ PTR location;
+ file_ptr offset;
+ bfd_size_type count;
{
- if (hdr->sh_type == SHT_MIPS_REGINFO)
+ if (strcmp (section->name, ".options") == 0
+ || strcmp (section->name, ".MIPS.options") == 0)
{
- bfd_byte buf[4];
+ bfd_byte *c;
- BFD_ASSERT (hdr->sh_size == sizeof (Elf32_External_RegInfo));
- BFD_ASSERT (hdr->contents == NULL);
+ if (elf_section_data (section) == NULL)
+ {
+ section->used_by_bfd =
+ (PTR) bfd_zalloc (abfd, sizeof (struct bfd_elf_section_data));
+ if (elf_section_data (section) == NULL)
+ return false;
+ }
+ c = (bfd_byte *) elf_section_data (section)->tdata;
+ if (c == NULL)
+ {
+ bfd_size_type size;
- if (bfd_seek (abfd,
- hdr->sh_offset + sizeof (Elf32_External_RegInfo) - 4,
- SEEK_SET) == -1)
- return false;
- bfd_h_put_32 (abfd, (bfd_vma) elf_gp (abfd), buf);
- if (bfd_write (buf, (bfd_size_type) 1, (bfd_size_type) 4, abfd) != 4)
- return false;
+ if (section->_cooked_size != 0)
+ size = section->_cooked_size;
+ else
+ size = section->_raw_size;
+ c = (PTR) bfd_zalloc (abfd, size);
+ if (c == NULL)
+ return false;
+ elf_section_data (section)->tdata = (PTR) c;
+ }
+
+ memcpy (c + offset, location, count);
}
+ return _bfd_elf_set_section_contents (abfd, section, location, offset,
+ count);
+}
+
+/* Work over a section just before writing it out. This routine is
+ used by both the 32-bit and the 64-bit ABI. FIXME: We recognize
+ sections that need the SHF_MIPS_GPREL flag by name; there has to be
+ a better way. */
+
+boolean
+_bfd_mips_elf_section_processing (abfd, hdr)
+ bfd *abfd;
+ Elf_Internal_Shdr *hdr;
+{
if (hdr->bfd_section != NULL)
{
const char *name = bfd_get_section_name (abfd, hdr->bfd_section);
return true;
}
+
+/* Work over a section just before writing it out. We update the GP
+ value in the SHT_MIPS_REGINFO and SHT_MIPS_OPTIONS sections based
+ on the value we are using. */
+
+static boolean
+mips_elf32_section_processing (abfd, hdr)
+ bfd *abfd;
+ Elf32_Internal_Shdr *hdr;
+{
+ if (hdr->sh_type == SHT_MIPS_REGINFO)
+ {
+ bfd_byte buf[4];
+
+ BFD_ASSERT (hdr->sh_size == sizeof (Elf32_External_RegInfo));
+ BFD_ASSERT (hdr->contents == NULL);
+
+ if (bfd_seek (abfd,
+ hdr->sh_offset + sizeof (Elf32_External_RegInfo) - 4,
+ SEEK_SET) == -1)
+ return false;
+ bfd_h_put_32 (abfd, (bfd_vma) elf_gp (abfd), buf);
+ if (bfd_write (buf, (bfd_size_type) 1, (bfd_size_type) 4, abfd) != 4)
+ return false;
+ }
+
+ if (hdr->sh_type == SHT_MIPS_OPTIONS
+ && hdr->bfd_section != NULL
+ && elf_section_data (hdr->bfd_section) != NULL
+ && elf_section_data (hdr->bfd_section)->tdata != NULL)
+ {
+ bfd_byte *contents, *l, *lend;
+
+ /* We stored the section contents in the elf_section_data tdata
+ field in the set_section_contents routine. We save the
+ section contents so that we don't have to read them again.
+ At this point we know that elf_gp is set, so we can look
+ through the section contents to see if there is an
+ ODK_REGINFO structure. */
+
+ contents = (bfd_byte *) elf_section_data (hdr->bfd_section)->tdata;
+ l = contents;
+ lend = contents + hdr->sh_size;
+ while (l + sizeof (Elf_External_Options) <= lend)
+ {
+ Elf_Internal_Options intopt;
+
+ bfd_mips_elf_swap_options_in (abfd, (Elf_External_Options *) l,
+ &intopt);
+ if (intopt.kind == ODK_REGINFO)
+ {
+ bfd_byte buf[4];
+
+ if (bfd_seek (abfd,
+ (hdr->sh_offset
+ + (l - contents)
+ + sizeof (Elf_External_Options)
+ + (sizeof (Elf32_External_RegInfo) - 4)),
+ SEEK_SET) == -1)
+ return false;
+ bfd_h_put_32 (abfd, elf_gp (abfd), buf);
+ if (bfd_write (buf, 1, 4, abfd) != 4)
+ return false;
+ }
+ l += intopt.size;
+ }
+ }
+
+ return _bfd_mips_elf_section_processing (abfd, hdr);
+}
\f
/* MIPS ELF uses two common sections. One is the usual one, and the
other is for small objects. All the small objects are kept
static asymbol *mips_elf_scom_symbol_ptr;
/* MIPS ELF also uses an acommon section, which represents an
- allocated common symbol which may be overridden by a
+ allocated common symbol which may be overridden by a
definition in a shared library. */
static asection mips_elf_acom_section;
static asymbol mips_elf_acom_symbol;
static asymbol mips_elf_data_symbol;
static asymbol *mips_elf_data_symbol_ptr;
-/* Handle the special MIPS section numbers that a symbol may use. */
+/* Handle the special MIPS section numbers that a symbol may use.
+ This is used for both the 32-bit and the 64-bit ABI. */
-static void
-mips_elf_symbol_processing (abfd, asym)
+void
+_bfd_mips_elf_symbol_processing (abfd, asym)
bfd *abfd;
asymbol *asym;
{
*pm = m;
}
}
-
+
/* If there are .dynamic and .mdebug sections, we make a room for
the RTPROC header. FIXME: Rewrite without section names. */
if (bfd_get_section_by_name (abfd, ".interp") == NULL
/* Read ECOFF debugging information from a .mdebug section into a
ecoff_debug_info structure. */
-static boolean
-mips_elf_read_ecoff_info (abfd, section, debug)
+boolean
+_bfd_mips_elf_read_ecoff_info (abfd, section, debug)
bfd *abfd;
asection *section;
struct ecoff_debug_info *debug;
struct ecoff_find_line i;
};
-static boolean
-mips_elf_find_nearest_line (abfd, section, symbols, offset, filename_ptr,
- functionname_ptr, line_ptr)
+boolean
+_bfd_mips_elf_find_nearest_line (abfd, section, symbols, offset, filename_ptr,
+ functionname_ptr, line_ptr)
bfd *abfd;
asection *section;
asymbol **symbols;
return false;
}
- if (! mips_elf_read_ecoff_info (abfd, msec, &fi->d))
+ if (! _bfd_mips_elf_read_ecoff_info (abfd, msec, &fi->d))
{
msec->flags = origflags;
return false;
bfd_size_type procedure_count;
/* The size of the .compact_rel section (if SGI_COMPAT). */
bfd_size_type compact_rel_size;
+ /* This flag indicates that the value of DT_MIPS_RLD_MAP dynamic
+ entry is set to the address of __rld_obj_head as in Irix 5. */
+ boolean use_rld_obj_head;
+ /* This is the value of the __rld_map or __rld_obj_head symbol. */
+ bfd_vma rld_value;
};
/* Look up an entry in a MIPS ELF linker hash table. */
ret->dynsym_sec_strindex[i] = (bfd_size_type) -1;
ret->procedure_count = 0;
ret->compact_rel_size = 0;
+ ret->use_rld_obj_head = false;
+ ret->rld_value = 0;
return &ret->root.root;
}
break;
}
+ if (SGI_COMPAT (abfd)
+ && ! info->shared
+ && info->hash->creator == abfd->xvec
+ && strcmp (*namep, "__rld_obj_head") == 0)
+ {
+ struct elf_link_hash_entry *h;
+
+ /* Mark __rld_obj_head as dynamic. */
+ h = NULL;
+ if (! (_bfd_generic_link_add_one_symbol
+ (info, abfd, *namep, BSF_GLOBAL, *secp,
+ (bfd_vma) *valp, (const char *) NULL, false,
+ get_elf_backend_data (abfd)->collect,
+ (struct bfd_link_hash_entry **) &h)))
+ return false;
+ h->elf_link_hash_flags &=~ ELF_LINK_NON_ELF;
+ h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
+ h->type = STT_OBJECT;
+
+ if (! bfd_elf32_link_record_dynamic_symbol (info, h))
+ return false;
+
+ mips_elf_hash_table (info)->use_rld_obj_head = true;
+ }
+
+ /* If this is a mips16 text symbol, add 1 to the value to make it
+ odd. This will cause something like .word SYM to come up with
+ the right value when it is loaded into the PC. */
+ if (sym->st_other == STO_MIPS16)
+ ++*valp;
+
return true;
}
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)
#if 0 /* FIXME? */
h->esym.ifd = 0;
#endif
- }
+ }
if (! bfd_ecoff_debug_one_external (einfo->abfd, einfo->debug, einfo->swap,
h->root.root.root.string,
reginfo.ri_cprmask[3] |= sub.ri_cprmask[3];
/* ri_gp_value is set by the function
- mips_elf_section_processing when the section is
+ mips_elf32_section_processing when the section is
finally written out. */
/* Hack: reset the SEC_HAS_CONTENTS flag so that
}
else
esym.asym.value = last;
-
+
if (! bfd_ecoff_debug_one_external (abfd, &debug, swap,
name[i], &esym))
return false;
/* The ECOFF linking code expects that we have already
read in the debugging information and set up an
ecoff_debug_info structure, so we do that now. */
- if (! mips_elf_read_ecoff_info (input_bfd, input_section,
- &input_debug))
+ if (! _bfd_mips_elf_read_ecoff_info (input_bfd, input_section,
+ &input_debug))
return false;
if (! (bfd_ecoff_debug_accumulate
if (rtproc_sec == NULL)
{
flagword flags = (SEC_HAS_CONTENTS | SEC_IN_MEMORY
- | SEC_READONLY);
+ | SEC_LINKER_CREATED | SEC_READONLY);
rtproc_sec = bfd_make_section (abfd, ".rtproc");
if (rtproc_sec == NULL
bfd_reloc_status_type r;
r_type = ELF32_R_TYPE (rel->r_info);
- if (r_type < 0 || r_type >= (int) R_MIPS_max)
+ if ((r_type < 0 || r_type >= (int) R_MIPS_max) && r_type != R_MIPS16_26)
{
bfd_set_error (bfd_error_bad_value);
return false;
}
- howto = elf_mips_howto_table + r_type;
+ if (r_type != R_MIPS16_26)
+ howto = elf_mips_howto_table + r_type;
+ else
+ howto = &elf_mips16_jump_howto;
if (dynobj != NULL
&& (r_type == R_MIPS_CALL16
/* If this is HI16 or GOT16 with an associated LO16,
adjust the addend accordingly. Otherwise, just
relocate. */
- if (r_type != R_MIPS_HI16 && r_type != R_MIPS_GOT16)
+ if (r_type == R_MIPS_64 && bfd_big_endian (input_bfd))
+ r = _bfd_relocate_contents (howto, input_bfd,
+ addend,
+ contents + rel->r_offset + 4);
+ else if (r_type != R_MIPS_HI16 && r_type != R_MIPS_GOT16)
r = _bfd_relocate_contents (howto, input_bfd,
addend,
contents + rel->r_offset);
value. */
if (ELF_ST_TYPE (sym->st_info) != STT_SECTION)
relocation += sym->st_value;
+
+ /* mips16 text labels should be treated as odd. */
+ if (sym->st_other == STO_MIPS16)
+ ++relocation;
}
else
{
bfd_byte *cr;
if ((info->shared
- || (h != NULL
+ || (elf_hash_table (info)->dynamic_sections_created
+ && h != NULL
&& ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)
== 0)))
&& (input_section->flags & SEC_ALLOC) != 0)
contents, rel->r_offset,
relocation, addend);
}
+ else if (r_type == R_MIPS_64)
+ {
+ bfd_size_type addr;
+ unsigned long val;
+
+ /* Do a 32 bit relocation, and sign extend to 64 bits. */
+ addr = rel->r_offset;
+ if (bfd_big_endian (input_bfd))
+ addr += 4;
+ r = _bfd_final_link_relocate (howto, input_bfd, input_section,
+ contents, addr, relocation,
+ addend);
+ val = bfd_get_32 (input_bfd, contents + addr);
+ if ((val & 0x80000000) != 0)
+ val = 0xffffffff;
+ else
+ val = 0;
+ addr = rel->r_offset;
+ if (bfd_little_endian (input_bfd))
+ addr += 4;
+ bfd_put_32 (input_bfd, val, contents + addr);
+ }
+ else if (r_type == R_MIPS_26
+ && ((h != NULL && h->other == STO_MIPS16)
+ || (sym != NULL && sym->st_other == STO_MIPS16)))
+ {
+ unsigned long insn;
+
+ /* This is a jump to a mips16 routine from a mips32
+ routine. We need to change jal into jalx. */
+ insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+ if (((insn >> 26) & 0x3f) != 0x3
+ && ((insn >> 26) & 0x3f) != 0x1d)
+ {
+ (*_bfd_error_handler)
+ ("%s: %s+0x%lx: jump to mips16 routine which is not jal",
+ bfd_get_filename (input_bfd),
+ input_section->name,
+ (unsigned long) rel->r_offset);
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+ insn = (insn & 0x3ffffff) | (0x1d << 26);
+ bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+ r = _bfd_final_link_relocate (howto, input_bfd, input_section,
+ contents, rel->r_offset,
+ relocation, addend);
+ }
+ else if (r_type == R_MIPS16_26)
+ {
+ /* It's easiest to do the normal relocation, and then
+ dig out the instruction and swap the first word the
+ way the mips16 expects it. */
+ r = _bfd_final_link_relocate (howto, input_bfd, input_section,
+ contents, rel->r_offset,
+ relocation, addend);
+ if (r == bfd_reloc_ok)
+ {
+ unsigned long insn;
+
+ insn = bfd_get_16 (input_bfd, contents + rel->r_offset);
+ insn = ((insn & 0xfc00)
+ | ((insn & 0x1f) << 5)
+ | ((insn & 0x3e0) >> 5));
+ /* If this is a jump to a 32 bit routine, then make
+ it jalx. */
+ if ((h != NULL && h->other != STO_MIPS16)
+ || (sym != NULL && sym->st_other != STO_MIPS16))
+ insn |= 0x400;
+ bfd_put_16 (input_bfd, insn, contents + rel->r_offset);
+ }
+ }
else
r = _bfd_final_link_relocate (howto, input_bfd, input_section,
contents, rel->r_offset,
return true;
}
+
+/* This hook function is called before the linker writes out a global
+ symbol. This is where we undo the increment of the value for a
+ mips16 symbol. */
+
+/*ARGSIGNORED*/
+static boolean
+mips_elf_link_output_symbol_hook (abfd, info, name, sym, input_sec)
+ bfd *abfd;
+ struct bfd_link_info *info;
+ const char *name;
+ Elf_Internal_Sym *sym;
+ asection *input_sec;
+{
+ if (sym->st_other == STO_MIPS16
+ && (sym->st_value & 1) != 0)
+ --sym->st_value;
+ return true;
+}
\f
/* Functions for the dynamic linker. */
const char * const *namep;
flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
- | SEC_READONLY);
+ | SEC_LINKER_CREATED | SEC_READONLY);
/* Mips ABI requests the .dynamic section to be read only. */
s = bfd_get_section_by_name (abfd, ".dynamic");
return false;
}
+ if (SGI_COMPAT (abfd)
+ && !info->shared
+ && bfd_get_section_by_name (abfd, ".rld_map") == NULL)
+ {
+ s = bfd_make_section (abfd, ".rld_map");
+ if (s == NULL
+ || ! bfd_set_section_flags (abfd, s, flags & ~SEC_READONLY)
+ || ! bfd_set_section_alignment (abfd, s, 2))
+ return false;
+ }
+
if (SGI_COMPAT (abfd))
{
for (namep = mips_elf_dynsym_rtproc_names; *namep != NULL; namep++)
if (! bfd_elf32_link_record_dynamic_symbol (info, h))
return false;
+
+ if (! mips_elf_hash_table (info)->use_rld_obj_head)
+ {
+ /* __rld_map is a four byte word located in the .data section
+ and is filled in by the rtld to contain a pointer to
+ the _r_debug structure. Its symbol value will be set in
+ mips_elf_finish_dynamic_symbol. */
+ s = bfd_get_section_by_name (abfd, ".rld_map");
+ BFD_ASSERT (s != NULL);
+
+ h = NULL;
+ if (! (_bfd_generic_link_add_one_symbol
+ (info, abfd, "__rld_map", BSF_GLOBAL, s,
+ (bfd_vma) 0, (const char *) NULL, false,
+ get_elf_backend_data (abfd)->collect,
+ (struct bfd_link_hash_entry **) &h)))
+ return false;
+ h->elf_link_hash_flags &=~ ELF_LINK_NON_ELF;
+ h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
+ h->type = STT_OBJECT;
+
+ if (! bfd_elf32_link_record_dynamic_symbol (info, h))
+ return false;
+ }
}
return true;
if (bfd_get_section_by_name (abfd, ".compact_rel") == NULL)
{
- flags = SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_READONLY;
+ flags = (SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED
+ | SEC_READONLY);
s = bfd_make_section (abfd, ".compact_rel");
if (s == NULL
return true;
}
-
+
/* Create the .got section to hold the global offset table. */
static boolean
if (bfd_get_section_by_name (abfd, ".got") != NULL)
return true;
- flags = SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY;
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+ | SEC_LINKER_CREATED);
s = bfd_make_section (abfd, ".got");
if (s == NULL
sym_hashes = elf_sym_hashes (abfd);
extsymoff = (elf_bad_symtab (abfd)) ? 0 : symtab_hdr->sh_info;
- sgot = NULL;
+ if (dynobj == NULL)
+ {
+ sgot = NULL;
+ g = NULL;
+ }
+ else
+ {
+ sgot = bfd_get_section_by_name (dynobj, ".got");
+ if (sgot == NULL)
+ g = NULL;
+ else
+ {
+ BFD_ASSERT (elf_section_data (sgot) != NULL);
+ g = (struct mips_got_info *) elf_section_data (sgot)->tdata;
+ BFD_ASSERT (g != NULL);
+ }
+ }
+
sreloc = NULL;
rel_end = relocs + sec->reloc_count;
h = sym_hashes[r_symndx - extsymoff];
/* Some relocs require a global offset table. */
- if (dynobj == NULL)
+ if (dynobj == NULL || sgot == NULL)
{
switch (ELF32_R_TYPE (rel->r_info))
{
case R_MIPS_CALL_LO16:
case R_MIPS_GOT_HI16:
case R_MIPS_GOT_LO16:
- elf_hash_table (info)->dynobj = dynobj = abfd;
+ if (dynobj == NULL)
+ elf_hash_table (info)->dynobj = dynobj = abfd;
if (! mips_elf_create_got_section (dynobj, info))
return false;
+ sgot = bfd_get_section_by_name (dynobj, ".got");
+ BFD_ASSERT (sgot != NULL);
+ BFD_ASSERT (elf_section_data (sgot) != NULL);
+ g = (struct mips_got_info *) elf_section_data (sgot)->tdata;
+ BFD_ASSERT (g != NULL);
+ break;
+
+ case R_MIPS_32:
+ case R_MIPS_REL32:
+ if (dynobj == NULL
+ && (info->shared || h != NULL)
+ && (sec->flags & SEC_ALLOC) != 0)
+ elf_hash_table (info)->dynobj = dynobj = abfd;
break;
default:
case R_MIPS_CALL_HI16:
case R_MIPS_CALL_LO16:
/* This symbol requires a global offset table entry. */
- if (sgot == NULL)
- {
- sgot = bfd_get_section_by_name (dynobj, ".got");
- BFD_ASSERT (sgot != NULL);
- BFD_ASSERT (elf_section_data (sgot) != NULL);
- g = (struct mips_got_info *) elf_section_data (sgot)->tdata;
- BFD_ASSERT (g != NULL);
- }
BFD_ASSERT (h != NULL);
case R_MIPS_GOT_HI16:
case R_MIPS_GOT_LO16:
/* This symbol requires a global offset table entry. */
- if (sgot == NULL)
- {
- sgot = bfd_get_section_by_name (dynobj, ".got");
- BFD_ASSERT (sgot != NULL);
- BFD_ASSERT (elf_section_data (sgot) != NULL);
- g = (struct mips_got_info *) elf_section_data (sgot)->tdata;
- BFD_ASSERT (g != NULL);
- }
if (h != NULL)
{
if ((info->shared || h != NULL)
&& (sec->flags & SEC_ALLOC) != 0)
{
- /* When creating a shared object, we must copy these
- reloc types into the output file as R_MIPS_REL32
- relocs. We create the .rel.dyn reloc section in
- dynobj and make room for this reloc. */
if (sreloc == NULL)
{
const char *name = ".rel.dyn";
| SEC_LOAD
| SEC_HAS_CONTENTS
| SEC_IN_MEMORY
+ | SEC_LINKER_CREATED
| SEC_READONLY))
- || ! bfd_set_section_alignment (dynobj, sreloc, 4))
+ || ! bfd_set_section_alignment (dynobj, sreloc,
+ 4))
return false;
-
+ }
+ }
+ if (info->shared)
+ {
+ /* When creating a shared object, we must copy these
+ reloc types into the output file as R_MIPS_REL32
+ relocs. We make room for this reloc in the
+ .rel.dyn reloc section */
+ if (sreloc->_raw_size == 0)
+ {
/* Add a null element. */
sreloc->_raw_size += sizeof (Elf32_External_Rel);
++sreloc->reloc_count;
}
+ sreloc->_raw_size += sizeof (Elf32_External_Rel);
}
-
- if (info->shared)
- sreloc->_raw_size += sizeof (Elf32_External_Rel);
else
{
struct mips_elf_link_hash_entry *hmips;
s = bfd_get_section_by_name (dynobj, ".rel.dyn");
BFD_ASSERT (s != NULL);
+ if (s->_raw_size == 0)
+ {
+ /* Make room for a null element. */
+ s->_raw_size += sizeof (Elf32_External_Rel);
+ ++s->reloc_count;
+ }
s->_raw_size += hmips->mips_32_relocs * sizeof (Elf32_External_Rel);
}
of the dynobj section names depend upon the input files. */
name = bfd_get_section_name (dynobj, s);
- if ((s->flags & SEC_IN_MEMORY) == 0)
+ if ((s->flags & SEC_LINKER_CREATED) == 0)
continue;
strip = false;
strip = true;
else
{
+ const char *outname;
asection *target;
/* If this relocation section applies to a read only
assert a DT_TEXTREL entry rather than testing whether
there exists a relocation to a read only section or
not. */
- target = bfd_get_section_by_name (output_bfd, name + 4);
+ 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)
- || strcmp (name, ".rel.dyn") == 0)
+ || strcmp (outname, ".rel.dyn") == 0)
reltext = true;
/* We use the reloc_count field as a counter if we need
of .text section. So put a dummy. XXX */
s->_raw_size += MIPS_FUNCTION_STUB_SIZE;
}
+ else if (! info->shared
+ && ! mips_elf_hash_table (info)->use_rld_obj_head
+ && strncmp (name, ".rld_map", 8) == 0)
+ {
+ /* We add a room for __rld_map. It will be filled in by the
+ rtld to contain a pointer to the _r_debug structure. */
+ s->_raw_size += 4;
+ }
else if (SGI_COMPAT (output_bfd)
&& strncmp (name, ".compact_rel", 12) == 0)
s->_raw_size += mips_elf_hash_table (info)->compact_rel_size;
dynamic linker and used by the debugger. */
if (! info->shared)
{
- if (! bfd_elf32_add_dynamic_entry (info, DT_DEBUG, 0))
- return false;
+ if (SGI_COMPAT (output_bfd))
+ {
+ /* SGI object has the equivalence of DT_DEBUG in the
+ DT_MIPS_RLD_MAP entry. */
+ if (! bfd_elf32_add_dynamic_entry (info, DT_MIPS_RLD_MAP, 0))
+ return false;
+ }
+ else
+ if (! bfd_elf32_add_dynamic_entry (info, DT_DEBUG, 0))
+ return false;
}
if (reltext)
struct bfd_strtab_hash *dynstr;
struct mips_got_info *g;
+ c = 0;
if (elf_hash_table (info)->dynamic_sections_created)
{
if (SGI_COMPAT (output_bfd))
}
}
- s = bfd_get_section_by_name (dynobj, ".got");
- BFD_ASSERT (s != NULL);
- BFD_ASSERT (elf_section_data (s) != NULL);
- g = (struct mips_got_info *) elf_section_data (s)->tdata;
- BFD_ASSERT (g != NULL);
-
- /* If there are no global got symbols, fake the last symbol so for
- safety. */
- if (g->global_gotsym)
- g->global_gotsym += c;
- else
- g->global_gotsym = elf_hash_table (info)->dynsymcount - 1;
+ if (sgot != NULL)
+ {
+ BFD_ASSERT (elf_section_data (sgot) != NULL);
+ g = (struct mips_got_info *) elf_section_data (sgot)->tdata;
+ BFD_ASSERT (g != NULL);
+
+ /* If there are no global got symbols, fake the last symbol so
+ for safety. */
+ if (g->global_gotsym)
+ g->global_gotsym += c;
+ else
+ g->global_gotsym = elf_hash_table (info)->dynsymcount - 1;
+ }
}
return true;
}
}
+ if (SGI_COMPAT (output_bfd)
+ && ! info->shared)
+ {
+ if (! mips_elf_hash_table (info)->use_rld_obj_head
+ && strcmp (name, "__rld_map") == 0)
+ {
+ asection *s = bfd_get_section_by_name (dynobj, ".rld_map");
+ BFD_ASSERT (s != NULL);
+ sym->st_value = s->output_section->vma + s->output_offset;
+ bfd_put_32 (output_bfd, (bfd_vma) 0, s->contents);
+ if (mips_elf_hash_table (info)->rld_value == 0)
+ mips_elf_hash_table (info)->rld_value = sym->st_value;
+ }
+ else if (mips_elf_hash_table (info)->use_rld_obj_head
+ && strcmp (name, "__rld_obj_head") == 0)
+ {
+ asection *s = bfd_get_section_by_name (dynobj, ".rld_map");
+ BFD_ASSERT (s != NULL);
+ mips_elf_hash_table (info)->rld_value = sym->st_value;
+ }
+ }
+
+ /* If this is a mips16 symbol, force the value to be even. */
+ if (sym->st_other == STO_MIPS16
+ && (sym->st_value & 1) != 0)
+ --sym->st_value;
+
return true;
}
sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
sgot = bfd_get_section_by_name (dynobj, ".got");
- BFD_ASSERT (sgot != NULL);
-
- BFD_ASSERT (elf_section_data (sgot) != NULL);
- g = (struct mips_got_info *) elf_section_data (sgot)->tdata;
- BFD_ASSERT (g != NULL);
+ if (sgot == NULL)
+ g = NULL;
+ else
+ {
+ BFD_ASSERT (elf_section_data (sgot) != NULL);
+ g = (struct mips_got_info *) elf_section_data (sgot)->tdata;
+ BFD_ASSERT (g != NULL);
+ }
if (elf_hash_table (info)->dynamic_sections_created)
{
Elf32_External_Dyn *dyncon, *dynconend;
BFD_ASSERT (sdyn != NULL);
+ BFD_ASSERT (g != NULL);
dyncon = (Elf32_External_Dyn *) sdyn->contents;
dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->_raw_size);
time ((time_t *) &dyn.d_un.d_val);
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
break;
-
+
case DT_MIPS_ICHECKSUM:
/* XXX FIXME: */
break;
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
break;
+ case DT_MIPS_RLD_MAP:
+ dyn.d_un.d_ptr = mips_elf_hash_table (info)->rld_value;
+ bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
+ break;
+
}
}
}
/* The first entry of the global offset table will be filled at
runtime. The second entry will be used by some runtime loaders.
This isn't the case of Irix rld. */
- if (sgot->_raw_size > 0)
+ if (sgot != NULL && sgot->_raw_size > 0)
{
bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents);
bfd_put_32 (output_bfd, (bfd_vma) 0x80000000, sgot->contents + 4);
}
- elf_section_data (sgot->output_section)->this_hdr.sh_entsize = 4;
+ if (sgot != NULL)
+ elf_section_data (sgot->output_section)->this_hdr.sh_entsize = 4;
{
asection *sdynsym;
+ sizeof (Elf32_External_compact_rel));
cpt.reserved0 = 0;
cpt.reserved1 = 0;
- bfd_elf32_swap_compact_rel_out (output_bfd, &cpt,
+ bfd_elf32_swap_compact_rel_out (output_bfd, &cpt,
((Elf32_External_compact_rel *)
s->contents));
/* Clean up a first relocation in .rel.dyn. */
s = bfd_get_section_by_name (dynobj, ".rel.dyn");
- if (s != NULL)
+ if (s != NULL && s->_raw_size > 0)
memset (s->contents, 0, sizeof (Elf32_External_Rel));
}
fall over on its own. */
}
else if ((*parent)->howto->special_function
- == mips_elf_gprel16_reloc)
+ == _bfd_mips_elf_gprel16_reloc)
{
/* bypass special_function call */
r = gprel16_with_gp (input_bfd, sym, *parent, input_section,
\f
/* ECOFF swapping routines. These are used when dealing with the
.mdebug section, which is in the ECOFF debugging format. */
-static const struct ecoff_debug_swap mips_elf_ecoff_debug_swap =
+static const struct ecoff_debug_swap mips_elf32_ecoff_debug_swap =
{
/* Symbol table magic number. */
magicSym,
_bfd_ecoff_swap_tir_out,
_bfd_ecoff_swap_rndx_out,
/* Function to read in symbolic data. */
- mips_elf_read_ecoff_info
+ _bfd_mips_elf_read_ecoff_info
};
\f
#define TARGET_LITTLE_SYM bfd_elf32_littlemips_vec
#define TARGET_BIG_NAME "elf32-bigmips"
#define ELF_ARCH bfd_arch_mips
#define ELF_MACHINE_CODE EM_MIPS
-#define ELF_MAXPAGESIZE 0x10000
+
+/* The SVR4 MIPS ABI says that this should be 0x10000, but Irix 5 uses
+ a value of 0x1000, and we are compatible. */
+#define ELF_MAXPAGESIZE 0x1000
+
#define elf_backend_collect true
#define elf_backend_type_change_ok true
#define elf_info_to_howto 0
#define elf_info_to_howto_rel mips_info_to_howto_rel
#define elf_backend_sym_is_global mips_elf_sym_is_global
-#define elf_backend_object_p mips_elf_object_p
+#define elf_backend_object_p mips_elf32_object_p
#define elf_backend_section_from_shdr mips_elf32_section_from_shdr
-#define elf_backend_fake_sections mips_elf_fake_sections
+#define elf_backend_fake_sections _bfd_mips_elf_fake_sections
#define elf_backend_section_from_bfd_section \
- mips_elf_section_from_bfd_section
-#define elf_backend_section_processing mips_elf_section_processing
-#define elf_backend_symbol_processing mips_elf_symbol_processing
+ _bfd_mips_elf_section_from_bfd_section
+#define elf_backend_section_processing mips_elf32_section_processing
+#define elf_backend_symbol_processing _bfd_mips_elf_symbol_processing
#define elf_backend_additional_program_headers \
mips_elf_additional_program_headers
#define elf_backend_modify_segment_map mips_elf_modify_segment_map
#define elf_backend_final_write_processing \
- mips_elf_final_write_processing
-#define elf_backend_ecoff_debug_swap &mips_elf_ecoff_debug_swap
+ _bfd_mips_elf_final_write_processing
+#define elf_backend_ecoff_debug_swap &mips_elf32_ecoff_debug_swap
#define bfd_elf32_bfd_is_local_label mips_elf_is_local_label
-#define bfd_elf32_find_nearest_line mips_elf_find_nearest_line
-
+#define bfd_elf32_find_nearest_line _bfd_mips_elf_find_nearest_line
+#define bfd_elf32_set_section_contents _bfd_mips_elf_set_section_contents
#define bfd_elf32_bfd_link_hash_table_create \
mips_elf_link_hash_table_create
#define bfd_elf32_bfd_final_link mips_elf_final_link
#define bfd_elf32_bfd_copy_private_bfd_data \
- mips_elf_copy_private_bfd_data
+ _bfd_mips_elf_copy_private_bfd_data
#define bfd_elf32_bfd_merge_private_bfd_data \
- mips_elf_merge_private_bfd_data
-#define bfd_elf32_bfd_set_private_flags mips_elf_set_private_flags
+ _bfd_mips_elf_merge_private_bfd_data
+#define bfd_elf32_bfd_set_private_flags _bfd_mips_elf_set_private_flags
#define elf_backend_add_symbol_hook mips_elf_add_symbol_hook
#define elf_backend_create_dynamic_sections \
mips_elf_create_dynamic_sections
#define elf_backend_size_dynamic_sections \
mips_elf_size_dynamic_sections
#define elf_backend_relocate_section mips_elf_relocate_section
+#define elf_backend_link_output_symbol_hook \
+ mips_elf_link_output_symbol_hook
#define elf_backend_finish_dynamic_symbol \
mips_elf_finish_dynamic_symbol
#define elf_backend_finish_dynamic_sections \