#include "bfd.h"
#include "sysdep.h"
#include "libbfd.h"
#include "bfdlink.h"
#include "genlink.h"
#include "elf-bfd.h"
#include "elfxx-mips.h"
#include "elf/mips.h"
#include "coff/sym.h"
#include "coff/symconst.h"
#include "coff/internal.h"
#include "coff/ecoff.h"
#include "coff/mips.h"
#define ECOFF_SIGNED_32
#include "ecoffswap.h"
static bfd_reloc_status_type mips_elf_hi16_reloc
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static bfd_reloc_status_type mips_elf_lo16_reloc
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static bfd_reloc_status_type mips_elf_got16_reloc
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static bfd_reloc_status_type mips_elf_gprel32_reloc
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
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 reloc_howto_type *mips_elf32_rtype_to_howto
PARAMS ((unsigned int, bfd_boolean));
static void mips_info_to_howto_rel
PARAMS ((bfd *, arelent *, Elf_Internal_Rela *));
static void mips_info_to_howto_rela
PARAMS ((bfd *, arelent *, Elf_Internal_Rela *));
static bfd_boolean mips_elf_sym_is_global
PARAMS ((bfd *, asymbol *));
static bfd_boolean mips_elf32_object_p
PARAMS ((bfd *));
static bfd_boolean mips_elf_is_local_label_name
PARAMS ((bfd *, const char *));
static bfd_reloc_status_type mips16_jump_reloc
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static bfd_reloc_status_type mips16_gprel_reloc
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static bfd_reloc_status_type mips_elf_final_gp
PARAMS ((bfd *, asymbol *, bfd_boolean, char **, bfd_vma *));
static bfd_boolean mips_elf_assign_gp
PARAMS ((bfd *, bfd_vma *));
static bfd_boolean elf32_mips_grok_prstatus
PARAMS ((bfd *, Elf_Internal_Note *));
static bfd_boolean elf32_mips_grok_psinfo
PARAMS ((bfd *, Elf_Internal_Note *));
static irix_compat_t elf32_mips_irix_compat
PARAMS ((bfd *));
extern const bfd_target bfd_elf32_bigmips_vec;
extern const bfd_target bfd_elf32_littlemips_vec;
#define ABI_N32_P(abfd) \
((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI2) != 0)
#define SGI_COMPAT(abfd) \
(elf32_mips_irix_compat (abfd) != ict_none)
#define MIPS_RESERVED_GOTNO (2)
#define MINUS_ONE (((bfd_vma)0) - 1)
static reloc_howto_type elf_mips_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,
mips_elf_hi16_reloc,
"R_MIPS_HI16",
TRUE,
0x0000ffff,
0x0000ffff,
FALSE),
HOWTO (R_MIPS_LO16,
0,
2,
16,
FALSE,
0,
complain_overflow_dont,
mips_elf_lo16_reloc,
"R_MIPS_LO16",
TRUE,
0x0000ffff,
0x0000ffff,
FALSE),
HOWTO (R_MIPS_GPREL16,
0,
2,
16,
FALSE,
0,
complain_overflow_signed,
_bfd_mips_elf32_gprel16_reloc,
"R_MIPS_GPREL16",
TRUE,
0x0000ffff,
0x0000ffff,
FALSE),
HOWTO (R_MIPS_LITERAL,
0,
2,
16,
FALSE,
0,
complain_overflow_signed,
_bfd_mips_elf32_gprel16_reloc,
"R_MIPS_LITERAL",
TRUE,
0x0000ffff,
0x0000ffff,
FALSE),
HOWTO (R_MIPS_GOT16,
0,
2,
16,
FALSE,
0,
complain_overflow_signed,
mips_elf_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_elf_gprel32_reloc,
"R_MIPS_GPREL32",
TRUE,
0xffffffff,
0xffffffff,
FALSE),
EMPTY_HOWTO (13),
EMPTY_HOWTO (14),
EMPTY_HOWTO (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,
bfd_elf_generic_reloc,
"R_MIPS_SHIFT6",
TRUE,
0x000007c4,
0x000007c4,
FALSE),
HOWTO (R_MIPS_64,
0,
4,
64,
FALSE,
0,
complain_overflow_dont,
mips32_64bit_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),
EMPTY_HOWTO (R_MIPS_INSERT_A),
EMPTY_HOWTO (R_MIPS_INSERT_B),
EMPTY_HOWTO (R_MIPS_DELETE),
HOWTO (R_MIPS_HIGHER,
0,
2,
16,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_HIGHER",
TRUE,
0x0000ffff,
0x0000ffff,
FALSE),
HOWTO (R_MIPS_HIGHEST,
0,
2,
16,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_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),
EMPTY_HOWTO (R_MIPS_REL16),
EMPTY_HOWTO (R_MIPS_ADD_IMMEDIATE),
EMPTY_HOWTO (R_MIPS_PJUMP),
EMPTY_HOWTO (R_MIPS_RELGOT),
HOWTO (R_MIPS_JALR,
0,
2,
32,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_MIPS_JALR",
FALSE,
0x00000000,
0x00000000,
FALSE),
};
static reloc_howto_type elf_mips_ctor64_howto =
HOWTO (R_MIPS_64,
0,
4,
32,
FALSE,
0,
complain_overflow_signed,
mips32_64bit_reloc,
"R_MIPS_64",
TRUE,
0xffffffff,
0xffffffff,
FALSE);
static reloc_howto_type elf_mips16_jump_howto =
HOWTO (R_MIPS16_26,
2,
2,
26,
FALSE,
0,
complain_overflow_dont,
mips16_jump_reloc,
"R_MIPS16_26",
TRUE,
0x3ffffff,
0x3ffffff,
FALSE);
static reloc_howto_type elf_mips16_gprel_howto =
HOWTO (R_MIPS16_GPREL,
0,
2,
16,
FALSE,
0,
complain_overflow_signed,
mips16_gprel_reloc,
"R_MIPS16_GPREL",
TRUE,
0x07ff001f,
0x07ff001f,
FALSE);
static reloc_howto_type elf_mips_gnu_rel_hi16 =
HOWTO (R_MIPS_GNU_REL_HI16,
0,
2,
16,
TRUE,
0,
complain_overflow_dont,
mips_elf_hi16_reloc,
"R_MIPS_GNU_REL_HI16",
TRUE,
0xffff,
0xffff,
TRUE);
static reloc_howto_type elf_mips_gnu_rel_lo16 =
HOWTO (R_MIPS_GNU_REL_LO16,
0,
2,
16,
TRUE,
0,
complain_overflow_dont,
mips_elf_lo16_reloc,
"R_MIPS_GNU_REL_LO16",
TRUE,
0xffff,
0xffff,
TRUE);
static reloc_howto_type elf_mips_gnu_rel16_s2 =
HOWTO (R_MIPS_GNU_REL16_S2,
2,
2,
16,
TRUE,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_MIPS_GNU_REL16_S2",
TRUE,
0xffff,
0xffff,
TRUE);
static reloc_howto_type elf_mips_gnu_pcrel64 =
HOWTO (R_MIPS_PC64,
0,
4,
64,
TRUE,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_MIPS_PC64",
TRUE,
MINUS_ONE,
MINUS_ONE,
TRUE);
static reloc_howto_type elf_mips_gnu_pcrel32 =
HOWTO (R_MIPS_PC32,
0,
2,
32,
TRUE,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_MIPS_PC32",
TRUE,
0xffffffff,
0xffffffff,
TRUE);
static reloc_howto_type elf_mips_gnu_vtinherit_howto =
HOWTO (R_MIPS_GNU_VTINHERIT,
0,
2,
0,
FALSE,
0,
complain_overflow_dont,
NULL,
"R_MIPS_GNU_VTINHERIT",
FALSE,
0,
0,
FALSE);
static reloc_howto_type elf_mips_gnu_vtentry_howto =
HOWTO (R_MIPS_GNU_VTENTRY,
0,
2,
0,
FALSE,
0,
complain_overflow_dont,
_bfd_elf_rel_vtable_reloc_fn,
"R_MIPS_GNU_VTENTRY",
FALSE,
0,
0,
FALSE);
struct mips_hi16
{
struct mips_hi16 *next;
bfd_byte *addr;
bfd_vma addend;
};
static struct mips_hi16 *mips_hi16_list;
static bfd_reloc_status_type
mips_elf_hi16_reloc (abfd, reloc_entry, symbol, data, input_section,
output_bfd, error_message)
bfd *abfd ATTRIBUTE_UNUSED;
arelent *reloc_entry;
asymbol *symbol;
PTR data;
asection *input_section;
bfd *output_bfd;
char **error_message;
{
bfd_reloc_status_type ret;
bfd_vma relocation;
struct mips_hi16 *n;
if (output_bfd != (bfd *) NULL
&& (symbol->flags & BSF_SECTION_SYM) == 0
&& reloc_entry->addend == 0)
{
reloc_entry->address += input_section->output_offset;
return bfd_reloc_ok;
}
ret = bfd_reloc_ok;
if (strcmp (bfd_asymbol_name (symbol), "_gp_disp") == 0)
{
bfd_boolean relocateable;
bfd_vma gp;
if (ret == bfd_reloc_undefined)
abort ();
if (output_bfd != NULL)
relocateable = TRUE;
else
{
relocateable = FALSE;
output_bfd = symbol->section->output_section->owner;
}
ret = mips_elf_final_gp (output_bfd, symbol, relocateable,
error_message, &gp);
if (ret != bfd_reloc_ok)
return ret;
relocation = gp - reloc_entry->address;
}
else
{
if (bfd_is_und_section (symbol->section)
&& output_bfd == (bfd *) NULL)
ret = bfd_reloc_undefined;
if (bfd_is_com_section (symbol->section))
relocation = 0;
else
relocation = symbol->value;
}
relocation += symbol->section->output_section->vma;
relocation += symbol->section->output_offset;
relocation += reloc_entry->addend;
if (reloc_entry->address > input_section->_cooked_size)
return bfd_reloc_outofrange;
n = (struct mips_hi16 *) bfd_malloc ((bfd_size_type) sizeof *n);
if (n == NULL)
return bfd_reloc_outofrange;
n->addr = (bfd_byte *) data + reloc_entry->address;
n->addend = relocation;
n->next = mips_hi16_list;
mips_hi16_list = n;
if (output_bfd != (bfd *) NULL)
reloc_entry->address += input_section->output_offset;
return ret;
}
static bfd_reloc_status_type
mips_elf_lo16_reloc (abfd, reloc_entry, symbol, data, input_section,
output_bfd, error_message)
bfd *abfd;
arelent *reloc_entry;
asymbol *symbol;
PTR data;
asection *input_section;
bfd *output_bfd;
char **error_message;
{
arelent gp_disp_relent;
if (mips_hi16_list != NULL)
{
struct mips_hi16 *l;
l = mips_hi16_list;
while (l != NULL)
{
unsigned long insn;
unsigned long val;
unsigned long vallo;
struct mips_hi16 *next;
insn = bfd_get_32 (abfd, l->addr);
vallo = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
vallo = ((vallo & 0xffff) ^ 0x8000) - 0x8000;
val = ((insn & 0xffff) << 16) + vallo;
val += l->addend;
if (reloc_entry->howto->pc_relative)
val -= reloc_entry->address;
val += 0x8000;
val = (val >> 16) & 0xffff;
insn &= ~ (bfd_vma) 0xffff;
insn |= val;
bfd_put_32 (abfd, (bfd_vma) insn, l->addr);
if (strcmp (bfd_asymbol_name (symbol), "_gp_disp") == 0)
{
gp_disp_relent = *reloc_entry;
reloc_entry = &gp_disp_relent;
reloc_entry->addend = l->addend;
}
next = l->next;
free (l);
l = next;
}
mips_hi16_list = NULL;
}
else if (strcmp (bfd_asymbol_name (symbol), "_gp_disp") == 0)
{
bfd_reloc_status_type ret;
bfd_vma gp, relocation;
ret = mips_elf_final_gp (output_bfd, symbol, TRUE, error_message, &gp);
if (ret != bfd_reloc_ok)
return ret;
relocation = gp - reloc_entry->address;
relocation += symbol->section->output_section->vma;
relocation += symbol->section->output_offset;
relocation += reloc_entry->addend;
if (reloc_entry->address > input_section->_cooked_size)
return bfd_reloc_outofrange;
gp_disp_relent = *reloc_entry;
reloc_entry = &gp_disp_relent;
reloc_entry->addend = relocation - 4;
}
return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data,
input_section, output_bfd, error_message);
}
static bfd_reloc_status_type
mips_elf_got16_reloc (abfd, reloc_entry, symbol, data, input_section,
output_bfd, error_message)
bfd *abfd;
arelent *reloc_entry;
asymbol *symbol;
PTR data;
asection *input_section;
bfd *output_bfd;
char **error_message;
{
if (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_elf_hi16_reloc (abfd, reloc_entry, symbol, data,
input_section, output_bfd, error_message);
abort ();
}
static bfd_boolean
mips_elf_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_elf_final_gp (output_bfd, symbol, relocateable, error_message, pgp)
bfd *output_bfd;
asymbol *symbol;
bfd_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_elf_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
_bfd_mips_elf32_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;
{
bfd_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_elf_final_gp (output_bfd, symbol, relocateable, error_message,
&gp);
if (ret != bfd_reloc_ok)
return ret;
return _bfd_mips_elf_gprel16_with_gp (abfd, symbol, reloc_entry,
input_section, relocateable,
data, gp);
}
static bfd_reloc_status_type gprel32_with_gp
PARAMS ((bfd *, asymbol *, arelent *, asection *, bfd_boolean, PTR,
bfd_vma));
static bfd_reloc_status_type
mips_elf_gprel32_reloc (abfd, reloc_entry, symbol, data, input_section,
output_bfd, error_message)
bfd *abfd;
arelent *reloc_entry;
asymbol *symbol;
PTR data;
asection *input_section;
bfd *output_bfd;
char **error_message;
{
bfd_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)
{
*error_message = (char *)
_("32bits gp relative relocation occurs for an external symbol");
return bfd_reloc_outofrange;
}
if (output_bfd != (bfd *) NULL)
{
relocateable = TRUE;
gp = _bfd_get_gp_value (output_bfd);
}
else
{
relocateable = FALSE;
output_bfd = symbol->section->output_section->owner;
ret = mips_elf_final_gp (output_bfd, symbol, relocateable,
error_message, &gp);
if (ret != bfd_reloc_ok)
return ret;
}
return gprel32_with_gp (abfd, symbol, reloc_entry, input_section,
relocateable, data, gp);
}
static bfd_reloc_status_type
gprel32_with_gp (abfd, symbol, reloc_entry, input_section, relocateable, data,
gp)
bfd *abfd;
asymbol *symbol;
arelent *reloc_entry;
asection *input_section;
bfd_boolean relocateable;
PTR data;
bfd_vma gp;
{
bfd_vma relocation;
unsigned long val;
if (bfd_is_com_section (symbol->section))
relocation = 0;
else
relocation = symbol->value;
relocation += symbol->section->output_section->vma;
relocation += symbol->section->output_offset;
if (reloc_entry->address > input_section->_cooked_size)
return bfd_reloc_outofrange;
if (reloc_entry->howto->src_mask == 0)
{
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, (bfd_vma) val, (bfd_byte *) data + reloc_entry->address);
if (relocateable)
reloc_entry->address += input_section->output_offset;
return bfd_reloc_ok;
}
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;
reloc32 = *reloc_entry;
if (bfd_big_endian (abfd))
reloc32.address += 4;
reloc32.howto = &elf_mips_howto_table_rel[R_MIPS_32];
r = bfd_perform_relocation (abfd, &reloc32, data, input_section,
output_bfd, error_message);
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, (bfd_vma) val, (bfd_byte *) data + addr);
return r;
}
static bfd_reloc_status_type
mips16_jump_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->addend == 0)
{
reloc_entry->address += input_section->output_offset;
return bfd_reloc_ok;
}
{
static bfd_boolean warned;
if (! warned)
(*_bfd_error_handler)
(_("Linking mips16 objects into %s format is not supported"),
bfd_get_target (input_section->output_section->owner));
warned = TRUE;
}
return bfd_reloc_undefined;
}
static bfd_reloc_status_type
mips16_gprel_reloc (abfd, reloc_entry, symbol, data, input_section,
output_bfd, error_message)
bfd *abfd;
arelent *reloc_entry;
asymbol *symbol;
PTR data;
asection *input_section;
bfd *output_bfd;
char **error_message;
{
bfd_boolean relocateable;
bfd_reloc_status_type ret;
bfd_vma gp;
unsigned short extend, insn;
unsigned long final;
if (output_bfd != NULL
&& (symbol->flags & BSF_SECTION_SYM) == 0
&& reloc_entry->addend == 0)
{
reloc_entry->address += input_section->output_offset;
return bfd_reloc_ok;
}
if (output_bfd != NULL)
relocateable = TRUE;
else
{
relocateable = FALSE;
output_bfd = symbol->section->output_section->owner;
}
ret = mips_elf_final_gp (output_bfd, symbol, relocateable, error_message,
&gp);
if (ret != bfd_reloc_ok)
return ret;
if (reloc_entry->address > input_section->_cooked_size)
return bfd_reloc_outofrange;
extend = bfd_get_16 (abfd, (bfd_byte *) data + reloc_entry->address);
insn = bfd_get_16 (abfd, (bfd_byte *) data + reloc_entry->address + 2);
bfd_put_32 (abfd,
(bfd_vma) (((extend & 0x1f) << 11)
| (extend & 0x7e0)
| (insn & 0x1f)),
(bfd_byte *) data + reloc_entry->address);
ret = _bfd_mips_elf_gprel16_with_gp (abfd, symbol, reloc_entry,
input_section, relocateable, data, gp);
final = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
bfd_put_16 (abfd,
(bfd_vma) ((extend & 0xf800)
| ((final >> 11) & 0x1f)
| (final & 0x7e0)),
(bfd_byte *) data + reloc_entry->address);
bfd_put_16 (abfd,
(bfd_vma) ((insn & 0xffe0)
| (final & 0x1f)),
(bfd_byte *) data + reloc_entry->address + 2);
return ret;
}
struct elf_reloc_map {
bfd_reloc_code_real_type bfd_val;
enum elf_mips_reloc_type elf_val;
};
static const struct elf_reloc_map mips_reloc_map[] =
{
{ BFD_RELOC_NONE, R_MIPS_NONE },
{ BFD_RELOC_16, R_MIPS_16 },
{ BFD_RELOC_32, R_MIPS_32 },
{ BFD_RELOC_64, R_MIPS_64 },
{ BFD_RELOC_MIPS_JMP, R_MIPS_26 },
{ BFD_RELOC_HI16_S, R_MIPS_HI16 },
{ BFD_RELOC_LO16, R_MIPS_LO16 },
{ BFD_RELOC_GPREL16, R_MIPS_GPREL16 },
{ BFD_RELOC_MIPS_LITERAL, R_MIPS_LITERAL },
{ BFD_RELOC_MIPS_GOT16, R_MIPS_GOT16 },
{ BFD_RELOC_16_PCREL, R_MIPS_PC16 },
{ BFD_RELOC_MIPS_CALL16, R_MIPS_CALL16 },
{ BFD_RELOC_GPREL32, R_MIPS_GPREL32 },
{ BFD_RELOC_MIPS_GOT_HI16, R_MIPS_GOT_HI16 },
{ BFD_RELOC_MIPS_GOT_LO16, R_MIPS_GOT_LO16 },
{ BFD_RELOC_MIPS_CALL_HI16, R_MIPS_CALL_HI16 },
{ BFD_RELOC_MIPS_CALL_LO16, R_MIPS_CALL_LO16 },
{ BFD_RELOC_MIPS_SUB, R_MIPS_SUB },
{ BFD_RELOC_MIPS_GOT_PAGE, R_MIPS_GOT_PAGE },
{ BFD_RELOC_MIPS_GOT_OFST, R_MIPS_GOT_OFST },
{ BFD_RELOC_MIPS_GOT_DISP, R_MIPS_GOT_DISP }
};
static reloc_howto_type *
bfd_elf32_bfd_reloc_type_lookup (abfd, code)
bfd *abfd;
bfd_reloc_code_real_type code;
{
unsigned int i;
reloc_howto_type *howto_table = elf_mips_howto_table_rel;
for (i = 0; i < sizeof (mips_reloc_map) / sizeof (struct elf_reloc_map);
i++)
{
if (mips_reloc_map[i].bfd_val == code)
return &howto_table[(int) mips_reloc_map[i].elf_val];
}
switch (code)
{
default:
bfd_set_error (bfd_error_bad_value);
return NULL;
case BFD_RELOC_CTOR:
if (bfd_arch_bits_per_address (abfd) == 32)
return &howto_table[(int) R_MIPS_32];
else
return &elf_mips_ctor64_howto;
case BFD_RELOC_MIPS16_JMP:
return &elf_mips16_jump_howto;
case BFD_RELOC_MIPS16_GPREL:
return &elf_mips16_gprel_howto;
case BFD_RELOC_VTABLE_INHERIT:
return &elf_mips_gnu_vtinherit_howto;
case BFD_RELOC_VTABLE_ENTRY:
return &elf_mips_gnu_vtentry_howto;
case BFD_RELOC_PCREL_HI16_S:
return &elf_mips_gnu_rel_hi16;
case BFD_RELOC_PCREL_LO16:
return &elf_mips_gnu_rel_lo16;
case BFD_RELOC_16_PCREL_S2:
return &elf_mips_gnu_rel16_s2;
case BFD_RELOC_64_PCREL:
return &elf_mips_gnu_pcrel64;
case BFD_RELOC_32_PCREL:
return &elf_mips_gnu_pcrel32;
}
}
static reloc_howto_type *
mips_elf32_rtype_to_howto (r_type, rela_p)
unsigned int r_type;
bfd_boolean rela_p ATTRIBUTE_UNUSED;
{
switch (r_type)
{
case R_MIPS16_26:
return &elf_mips16_jump_howto;
case R_MIPS16_GPREL:
return &elf_mips16_gprel_howto;
case R_MIPS_GNU_VTINHERIT:
return &elf_mips_gnu_vtinherit_howto;
case R_MIPS_GNU_VTENTRY:
return &elf_mips_gnu_vtentry_howto;
case R_MIPS_GNU_REL_HI16:
return &elf_mips_gnu_rel_hi16;
case R_MIPS_GNU_REL_LO16:
return &elf_mips_gnu_rel_lo16;
case R_MIPS_GNU_REL16_S2:
return &elf_mips_gnu_rel16_s2;
case R_MIPS_PC64:
return &elf_mips_gnu_pcrel64;
case R_MIPS_PC32:
return &elf_mips_gnu_pcrel32;
default:
BFD_ASSERT (r_type < (unsigned int) R_MIPS_max);
return &elf_mips_howto_table_rel[r_type];
}
}
static void
mips_info_to_howto_rel (abfd, cache_ptr, dst)
bfd *abfd;
arelent *cache_ptr;
Elf_Internal_Rela *dst;
{
unsigned int r_type;
r_type = ELF32_R_TYPE (dst->r_info);
cache_ptr->howto = mips_elf32_rtype_to_howto (r_type, FALSE);
if (((*cache_ptr->sym_ptr_ptr)->flags & BSF_SECTION_SYM) != 0
&& (r_type == (unsigned int) R_MIPS_GPREL16
|| r_type == (unsigned int) R_MIPS_LITERAL))
cache_ptr->addend = elf_gp (abfd);
}
static void
mips_info_to_howto_rela (abfd, cache_ptr, dst)
bfd *abfd;
arelent *cache_ptr;
Elf_Internal_Rela *dst;
{
mips_info_to_howto_rel (abfd, cache_ptr, dst);
}
static bfd_boolean
mips_elf_sym_is_global (abfd, sym)
bfd *abfd ATTRIBUTE_UNUSED;
asymbol *sym;
{
if (SGI_COMPAT (abfd))
return (sym->flags & BSF_SECTION_SYM) == 0;
else
return ((sym->flags & (BSF_GLOBAL | BSF_WEAK)) != 0
|| bfd_is_und_section (bfd_get_section (sym))
|| bfd_is_com_section (bfd_get_section (sym)));
}
static bfd_boolean
mips_elf32_object_p (abfd)
bfd *abfd;
{
unsigned long mach;
if (SGI_COMPAT (abfd))
elf_bad_symtab (abfd) = TRUE;
if (ABI_N32_P (abfd))
return FALSE;
mach = _bfd_elf_mips_mach (elf_elfheader (abfd)->e_flags);
bfd_default_set_arch_mach (abfd, bfd_arch_mips, mach);
return TRUE;
}
static bfd_boolean
mips_elf_is_local_label_name (abfd, name)
bfd *abfd;
const char *name;
{
if (name[0] == '$')
return TRUE;
return _bfd_elf_is_local_label_name (abfd, name);
}
static bfd_boolean
elf32_mips_grok_prstatus (abfd, note)
bfd *abfd;
Elf_Internal_Note *note;
{
int offset;
unsigned int raw_size;
switch (note->descsz)
{
default:
return FALSE;
case 256:
elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12);
elf_tdata (abfd)->core_pid = bfd_get_32 (abfd, note->descdata + 24);
offset = 72;
raw_size = 180;
break;
}
return _bfd_elfcore_make_pseudosection (abfd, ".reg",
raw_size, note->descpos + offset);
}
static bfd_boolean
elf32_mips_grok_psinfo (abfd, note)
bfd *abfd;
Elf_Internal_Note *note;
{
switch (note->descsz)
{
default:
return FALSE;
case 128:
elf_tdata (abfd)->core_program
= _bfd_elfcore_strndup (abfd, note->descdata + 32, 16);
elf_tdata (abfd)->core_command
= _bfd_elfcore_strndup (abfd, note->descdata + 48, 80);
}
{
char *command = elf_tdata (abfd)->core_command;
int n = strlen (command);
if (0 < n && command[n - 1] == ' ')
command[n - 1] = '\0';
}
return TRUE;
}
static irix_compat_t
elf32_mips_irix_compat (abfd)
bfd *abfd;
{
if ((abfd->xvec == &bfd_elf32_bigmips_vec)
|| (abfd->xvec == &bfd_elf32_littlemips_vec))
return ict_irix5;
else
return ict_none;
}
bfd_boolean
bfd_mips_elf32_create_embedded_relocs (abfd, info, datasec, relsec, errmsg)
bfd *abfd;
struct bfd_link_info *info;
asection *datasec;
asection *relsec;
char **errmsg;
{
Elf_Internal_Shdr *symtab_hdr;
Elf_Internal_Sym *isymbuf = NULL;
Elf_Internal_Rela *internal_relocs = NULL;
Elf_Internal_Rela *irel, *irelend;
bfd_byte *p;
BFD_ASSERT (! info->relocateable);
*errmsg = NULL;
if (datasec->reloc_count == 0)
return TRUE;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
if (symtab_hdr->sh_info != 0)
{
isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
if (isymbuf == NULL)
isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr,
symtab_hdr->sh_info, 0,
NULL, NULL, NULL);
if (isymbuf == NULL)
goto error_return;
}
internal_relocs = (_bfd_elf32_link_read_relocs
(abfd, datasec, (PTR) NULL, (Elf_Internal_Rela *) NULL,
info->keep_memory));
if (internal_relocs == NULL)
goto error_return;
relsec->contents = (bfd_byte *) bfd_alloc (abfd, datasec->reloc_count * 12);
if (relsec->contents == NULL)
goto error_return;
p = relsec->contents;
irelend = internal_relocs + datasec->reloc_count;
for (irel = internal_relocs; irel < irelend; irel++, p += 12)
{
asection *targetsec;
if ((ELF32_R_TYPE (irel->r_info) != (int) R_MIPS_32) &&
(ELF32_R_TYPE (irel->r_info) != (int) R_MIPS_64))
{
*errmsg = _("unsupported reloc type");
bfd_set_error (bfd_error_bad_value);
goto error_return;
}
if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
{
Elf_Internal_Sym *isym;
isym = isymbuf + ELF32_R_SYM (irel->r_info);
targetsec = bfd_section_from_elf_index (abfd, isym->st_shndx);
}
else
{
unsigned long indx;
struct elf_link_hash_entry *h;
indx = ELF32_R_SYM (irel->r_info);
h = elf_sym_hashes (abfd)[indx];
targetsec = NULL;
BFD_ASSERT(h != NULL);
if (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak)
targetsec = h->root.u.def.section;
}
bfd_put_32 (abfd, ((irel->r_offset + datasec->output_offset) +
((ELF32_R_TYPE (irel->r_info) == (int) R_MIPS_64) ? 1 : 0)),
p);
memset (p + 4, 0, 8);
if (targetsec != NULL)
strncpy (p + 4, targetsec->output_section->name, 8);
}
if (internal_relocs != NULL
&& elf_section_data (datasec)->relocs != internal_relocs)
free (internal_relocs);
if (isymbuf != NULL
&& symtab_hdr->contents != (unsigned char *) isymbuf)
free (isymbuf);
return TRUE;
error_return:
if (internal_relocs != NULL
&& elf_section_data (datasec)->relocs != internal_relocs)
free (internal_relocs);
if (isymbuf != NULL
&& symtab_hdr->contents != (unsigned char *) isymbuf)
free (isymbuf);
return FALSE;
}
static const struct ecoff_debug_swap mips_elf32_ecoff_debug_swap = {
magicSym,
4,
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
};
#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_info_to_howto_rela
#define elf_info_to_howto_rel mips_info_to_howto_rel
#define elf_backend_sym_is_global mips_elf_sym_is_global
#define elf_backend_object_p mips_elf32_object_p
#define elf_backend_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 \
_bfd_mips_elf_create_dynamic_sections
#define elf_backend_check_relocs _bfd_mips_elf_check_relocs
#define elf_backend_adjust_dynamic_symbol \
_bfd_mips_elf_adjust_dynamic_symbol
#define elf_backend_always_size_sections \
_bfd_mips_elf_always_size_sections
#define elf_backend_size_dynamic_sections \
_bfd_mips_elf_size_dynamic_sections
#define elf_backend_relocate_section _bfd_mips_elf_relocate_section
#define elf_backend_finish_dynamic_symbol \
_bfd_mips_elf_finish_dynamic_symbol
#define elf_backend_finish_dynamic_sections \
_bfd_mips_elf_finish_dynamic_sections
#define elf_backend_final_write_processing \
_bfd_mips_elf_final_write_processing
#define elf_backend_additional_program_headers \
_bfd_mips_elf_additional_program_headers
#define elf_backend_modify_segment_map _bfd_mips_elf_modify_segment_map
#define elf_backend_gc_mark_hook _bfd_mips_elf_gc_mark_hook
#define elf_backend_gc_sweep_hook _bfd_mips_elf_gc_sweep_hook
#define elf_backend_copy_indirect_symbol \
_bfd_mips_elf_copy_indirect_symbol
#define elf_backend_hide_symbol _bfd_mips_elf_hide_symbol
#define elf_backend_grok_prstatus elf32_mips_grok_prstatus
#define elf_backend_grok_psinfo elf32_mips_grok_psinfo
#define elf_backend_ecoff_debug_swap &mips_elf32_ecoff_debug_swap
#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 0
#define elf_backend_default_use_rela_p 0
#define elf_backend_sign_extend_vma TRUE
#define elf_backend_discard_info _bfd_mips_elf_discard_info
#define elf_backend_ignore_discarded_relocs \
_bfd_mips_elf_ignore_discarded_relocs
#define elf_backend_mips_irix_compat elf32_mips_irix_compat
#define elf_backend_mips_rtype_to_howto mips_elf32_rtype_to_howto
#define bfd_elf32_bfd_is_local_label_name \
mips_elf_is_local_label_name
#define bfd_elf32_find_nearest_line _bfd_mips_elf_find_nearest_line
#define bfd_elf32_new_section_hook _bfd_mips_elf_new_section_hook
#define bfd_elf32_set_section_contents _bfd_mips_elf_set_section_contents
#define bfd_elf32_bfd_get_relocated_section_contents \
_bfd_elf_mips_get_relocated_section_contents
#define bfd_elf32_bfd_link_hash_table_create \
_bfd_mips_elf_link_hash_table_create
#define bfd_elf32_bfd_final_link _bfd_mips_elf_final_link
#define bfd_elf32_bfd_merge_private_bfd_data \
_bfd_mips_elf_merge_private_bfd_data
#define bfd_elf32_bfd_set_private_flags _bfd_mips_elf_set_private_flags
#define bfd_elf32_bfd_print_private_bfd_data \
_bfd_mips_elf_print_private_bfd_data
#define TARGET_LITTLE_SYM bfd_elf32_littlemips_vec
#define TARGET_LITTLE_NAME "elf32-littlemips"
#define TARGET_BIG_SYM bfd_elf32_bigmips_vec
#define TARGET_BIG_NAME "elf32-bigmips"
#include "elf32-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_elf32_tradlittlemips_vec
#define TARGET_LITTLE_NAME "elf32-tradlittlemips"
#define TARGET_BIG_SYM bfd_elf32_tradbigmips_vec
#define TARGET_BIG_NAME "elf32-tradbigmips"
#include "elf32-target.h"