summaryrefslogtreecommitdiff
path: root/bfd
diff options
context:
space:
mode:
authorMaciej W. Rozycki <macro@linux-mips.org>2005-02-15 19:57:53 +0000
committerMaciej W. Rozycki <macro@linux-mips.org>2005-02-15 19:57:53 +0000
commit628df2f3eaa45b3296b2a1d8d1ff0dc79fa8793a (patch)
tree7d5d1c499eb403524af7bed68dffe1e472a0d808 /bfd
parent67c1c12e937f4592aa6432a2230485bc909cc1c3 (diff)
downloadgdb-628df2f3eaa45b3296b2a1d8d1ff0dc79fa8793a.tar.gz
bfd/:
2005-02-15 Nigel Stephens <nigel@mips.com> Maciej W. Rozycki <macro@mips.com> * elf32-mips.c (elf_mips16_howto_table_rel): New array for MIPS16 reloc howtos. Add R_MIPS16_HI16 and R_MIPS16_LO16 relocs and R_MIPS16_GOT16 and R_MIPS16_CALL16 placeholders. (elf_mips16_jump_howto): Move into elf_mips16_howto_table_rel. (elf_mips16_gprel_howto): Likewise. Redefine src_mask and dst_mask. (mips16_gprel_reloc): Remove bit shuffling; call _bfd_mips16_elf_reloc_unshuffle(), _bfd_mips_elf_gprel16_with_gp() and _bfd_mips16_elf_reloc_shuffle() instead. (mips16_reloc_map): New reloc map for MIPS16 relocs. (bfd_elf32_bfd_reloc_type_lookup): Use mips16_reloc_map for MIPS16 relocs. (mips_elf32_rtype_to_howto): Fetch MIPS16 howtos from elf_mips16_howto_table_rel. * elf64-mips.c (mips16_elf64_howto_table_rel): New array for MIPS16 REL reloc howtos. Add R_MIPS16_HI16 and R_MIPS16_LO16 relocs and R_MIPS16_GOT16 and R_MIPS16_CALL16 placeholders. (elf_mips16_jump_howto): Move into mips16_elf64_howto_table_rel. (elf_mips16_gprel_howto): Likewise. Redefine src_mask and dst_mask. (mips16_elf64_howto_table_rela): New array for MIPS16 RELA reloc howtos. Add R_MIPS16_26, R_MIPS16_GPREL, R_MIPS16_HI16 and R_MIPS16_LO16 relocs and R_MIPS16_GOT16 and R_MIPS16_CALL16 placeholders. (mips16_gprel_reloc): Remove bit shuffling; call _bfd_mips16_elf_reloc_unshuffle(), _bfd_mips_elf_gprel16_with_gp() and _bfd_mips16_elf_reloc_shuffle() instead. (mips16_reloc_map): New reloc map for MIPS16 relocs. (bfd_elf64_bfd_reloc_type_lookup): Use mips16_reloc_map for MIPS16 relocs. (mips_elf64_rtype_to_howto): Fetch MIPS16 howtos from mips16_elf64_howto_table_rela or mips16_elf64_howto_table_rel. * elfn32-mips.c (elf_mips16_howto_table_rel): New array for MIPS16 REL reloc howtos. Add R_MIPS16_HI16 and R_MIPS16_LO16 relocs and R_MIPS16_GOT16 and R_MIPS16_CALL16 placeholders. (elf_mips16_jump_howto): Move into elf_mips16_howto_table_rel. (elf_mips16_gprel_howto): Likewise. Redefine src_mask and dst_mask. (mips16_gprel_reloc): Remove bit shuffling; call _bfd_mips16_elf_reloc_unshuffle(), _bfd_mips_elf_gprel16_with_gp() and _bfd_mips16_elf_reloc_shuffle() instead. (mips16_reloc_map): New reloc map for MIPS16 relocs. (bfd_elf32_bfd_reloc_type_lookup): Use mips16_reloc_map for MIPS16 relocs. (mips_elf_n32_rtype_to_howto): Fetch MIPS16 howtos from elf_mips16_howto_table_rela or elf_mips16_howto_table_rel. * elfxx-mips.c (_bfd_mips16_elf_reloc_unshuffle): New function to handle bit shuffling for MIPS16 relocs. (_bfd_mips16_elf_reloc_shuffle): Likewise. (_bfd_mips_elf_lo16_reloc): Use _bfd_mips16_elf_reloc_unshuffle() and _bfd_mips16_elf_reloc_shuffle(). (_bfd_mips_elf_generic_reloc): Likewise. (mips_elf_calculate_relocation): Likewise. Handle R_MIPS16_HI16 and R_MIPS16_LO16. (mips_elf_obtain_contents): Remove bit shuffling. (mips_elf_perform_relocation): Likewise; call _bfd_mips16_elf_reloc_unshuffle() and _bfd_mips16_elf_reloc_shuffle() instead. (_bfd_mips_elf_relocate_section): Likewise. Handle R_MIPS16_HI16 and R_MIPS16_LO16. * elfxx-mips.h (_bfd_mips16_elf_reloc_unshuffle): Declare. (_bfd_mips16_elf_reloc_shuffle): Likewise. * reloc.c (BFD_RELOC_MIPS16_HI16): New reloc. (BFD_RELOC_MIPS16_HI16_S): Likewise. (BFD_RELOC_MIPS16_LO16): Likewise. * bfd-in2.h: Regenerate. * libbfd.h: Regenerate. gas/: 2005-02-15 Nigel Stephens <nigel@mips.com> Maciej W. Rozycki <macro@mips.com> * config/tc-mips.c (reloc_needs_lo_p): Handle BFD_RELOC_MIPS16_HI16_S. (fixup_has_matching_lo_p): Handle BFD_RELOC_MIPS16_LO16. (append_insn): Add BFD_RELOC_MIPS16_GPREL, BFD_RELOC_MIPS16_HI16_S and BFD_RELOC_MIPS16_LO16 to relocs to suppress overflow complaints on. (mips16_ip): Resolve BFD_RELOC_MIPS16_HI16_S, BFD_RELOC_MIPS16_HI16 and BFD_RELOC_MIPS16_LO16 for constants. Call my_getSmallExpression() to parse percent operators. (percent_op_match, mips_percent_op): Separate definitions. (mips16_percent_op): Define percent operators for the MIPS16 mode. (parse_relocation): Handle the MIPS16 mode using mips16_percent_op. (md_apply_fix3): Handle BFD_RELOC_MIPS16_HI16, BFD_RELOC_MIPS16_HI16_S and BFD_RELOC_MIPS16_LO16. gas/testsuite/: 2005-02-15 Nigel Stephens <nigel@mips.com> Maciej W. Rozycki <macro@mips.com> * gas/mips/mips16-hilo.d: New test for the R_MIPS16_HI16 and R_MIPS16_LO16 relocs. * gas/mips/mips16-hilo-n32.d: Likewise, for the n32 ABI. * gas/mips/mips16-hilo.s: Source for the new tests. * gas/mips/mips.exp: Run the new tests. include/: 2005-02-15 Nigel Stephens <nigel@mips.com> Maciej W. Rozycki <macro@mips.com> * elf/mips.h (R_MIPS16_GOT16): New reloc code. (R_MIPS16_CALL16): Likewise. (R_MIPS16_HI16): Likewise. (R_MIPS16_LO16): Likewise. (R_MIPS16_min): New fake reloc code. (R_MIPS16_max): Likewise. ld/testsuite/: 2005-02-15 Nigel Stephens <nigel@mips.com> Maciej W. Rozycki <macro@mips.com> * ld-mips-elf/mips16-hilo.d: New test for the R_MIPS16_HI16 and R_MIPS16_LO16 relocs. * ld-mips-elf/mips16-hilo-n32.d: Likewise, for the n32 ABI. * ld-mips-elf/mips16-hilo.s: Auxiliary source for the new tests. * ld-mips-elf/mips-elf.exp: Run the new tests.
Diffstat (limited to 'bfd')
-rw-r--r--bfd/ChangeLog71
-rw-r--r--bfd/bfd-in2.h12
-rw-r--r--bfd/elf32-mips.c141
-rw-r--r--bfd/elf64-mips.c218
-rw-r--r--bfd/elfn32-mips.c218
-rw-r--r--bfd/elfxx-mips.c343
-rw-r--r--bfd/elfxx-mips.h4
-rw-r--r--bfd/libbfd.h3
-rw-r--r--bfd/reloc.c16
9 files changed, 695 insertions, 331 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index d0e74d2901b..120fb5b850a 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,3 +1,74 @@
+2005-02-15 Nigel Stephens <nigel@mips.com>
+ Maciej W. Rozycki <macro@mips.com>
+
+ * elf32-mips.c (elf_mips16_howto_table_rel): New array for MIPS16
+ reloc howtos. Add R_MIPS16_HI16 and R_MIPS16_LO16 relocs and
+ R_MIPS16_GOT16 and R_MIPS16_CALL16 placeholders.
+ (elf_mips16_jump_howto): Move into elf_mips16_howto_table_rel.
+ (elf_mips16_gprel_howto): Likewise. Redefine src_mask and
+ dst_mask.
+ (mips16_gprel_reloc): Remove bit shuffling; call
+ _bfd_mips16_elf_reloc_unshuffle(), _bfd_mips_elf_gprel16_with_gp()
+ and _bfd_mips16_elf_reloc_shuffle() instead.
+ (mips16_reloc_map): New reloc map for MIPS16 relocs.
+ (bfd_elf32_bfd_reloc_type_lookup): Use mips16_reloc_map for MIPS16
+ relocs.
+ (mips_elf32_rtype_to_howto): Fetch MIPS16 howtos from
+ elf_mips16_howto_table_rel.
+ * elf64-mips.c (mips16_elf64_howto_table_rel): New array for
+ MIPS16 REL reloc howtos. Add R_MIPS16_HI16 and R_MIPS16_LO16
+ relocs and R_MIPS16_GOT16 and R_MIPS16_CALL16 placeholders.
+ (elf_mips16_jump_howto): Move into mips16_elf64_howto_table_rel.
+ (elf_mips16_gprel_howto): Likewise. Redefine src_mask and
+ dst_mask.
+ (mips16_elf64_howto_table_rela): New array for MIPS16 RELA
+ reloc howtos. Add R_MIPS16_26, R_MIPS16_GPREL, R_MIPS16_HI16 and
+ R_MIPS16_LO16 relocs and R_MIPS16_GOT16 and R_MIPS16_CALL16
+ placeholders.
+ (mips16_gprel_reloc): Remove bit shuffling; call
+ _bfd_mips16_elf_reloc_unshuffle(), _bfd_mips_elf_gprel16_with_gp()
+ and _bfd_mips16_elf_reloc_shuffle() instead.
+ (mips16_reloc_map): New reloc map for MIPS16 relocs.
+ (bfd_elf64_bfd_reloc_type_lookup): Use mips16_reloc_map for MIPS16
+ relocs.
+ (mips_elf64_rtype_to_howto): Fetch MIPS16 howtos from
+ mips16_elf64_howto_table_rela or mips16_elf64_howto_table_rel.
+ * elfn32-mips.c (elf_mips16_howto_table_rel): New array for MIPS16
+ REL reloc howtos. Add R_MIPS16_HI16 and R_MIPS16_LO16 relocs and
+ R_MIPS16_GOT16 and R_MIPS16_CALL16 placeholders.
+ (elf_mips16_jump_howto): Move into elf_mips16_howto_table_rel.
+ (elf_mips16_gprel_howto): Likewise. Redefine src_mask and
+ dst_mask.
+ (mips16_gprel_reloc): Remove bit shuffling; call
+ _bfd_mips16_elf_reloc_unshuffle(), _bfd_mips_elf_gprel16_with_gp()
+ and _bfd_mips16_elf_reloc_shuffle() instead.
+ (mips16_reloc_map): New reloc map for MIPS16 relocs.
+ (bfd_elf32_bfd_reloc_type_lookup): Use mips16_reloc_map for MIPS16
+ relocs.
+ (mips_elf_n32_rtype_to_howto): Fetch MIPS16 howtos from
+ elf_mips16_howto_table_rela or elf_mips16_howto_table_rel.
+ * elfxx-mips.c (_bfd_mips16_elf_reloc_unshuffle): New function to
+ handle bit shuffling for MIPS16 relocs.
+ (_bfd_mips16_elf_reloc_shuffle): Likewise.
+ (_bfd_mips_elf_lo16_reloc): Use _bfd_mips16_elf_reloc_unshuffle()
+ and _bfd_mips16_elf_reloc_shuffle().
+ (_bfd_mips_elf_generic_reloc): Likewise.
+ (mips_elf_calculate_relocation): Likewise. Handle R_MIPS16_HI16
+ and R_MIPS16_LO16.
+ (mips_elf_obtain_contents): Remove bit shuffling.
+ (mips_elf_perform_relocation): Likewise; call
+ _bfd_mips16_elf_reloc_unshuffle() and _bfd_mips16_elf_reloc_shuffle()
+ instead.
+ (_bfd_mips_elf_relocate_section): Likewise. Handle R_MIPS16_HI16
+ and R_MIPS16_LO16.
+ * elfxx-mips.h (_bfd_mips16_elf_reloc_unshuffle): Declare.
+ (_bfd_mips16_elf_reloc_shuffle): Likewise.
+ * reloc.c (BFD_RELOC_MIPS16_HI16): New reloc.
+ (BFD_RELOC_MIPS16_HI16_S): Likewise.
+ (BFD_RELOC_MIPS16_LO16): Likewise.
+ * bfd-in2.h: Regenerate.
+ * libbfd.h: Regenerate.
+
2005-02-15 Jan Beulich <jbeulich@novell.com>
* elfxx-ia64.c (ia64_howto_table): Correct strings for
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index a03b69afc53..c05e7d7f251 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -2354,6 +2354,18 @@ to compensate for the borrow when the low bits are added. */
/* Low 16 bits. */
BFD_RELOC_LO16,
+/* MIPS16 high 16 bits of 32-bit value. */
+ BFD_RELOC_MIPS16_HI16,
+
+/* MIPS16 high 16 bits of 32-bit value but the low 16 bits will be sign
+extended and added to form the final result. If the low 16
+bits form a negative number, we need to add one to the high value
+to compensate for the borrow when the low bits are added. */
+ BFD_RELOC_MIPS16_HI16_S,
+
+/* MIPS16 low 16 bits. */
+ BFD_RELOC_MIPS16_LO16,
+
/* Relocation against a MIPS literal section. */
BFD_RELOC_MIPS_LITERAL,
diff --git a/bfd/elf32-mips.c b/bfd/elf32-mips.c
index 5c259f101f3..7fc125c9e0e 100644
--- a/bfd/elf32-mips.c
+++ b/bfd/elf32-mips.c
@@ -564,8 +564,9 @@ static reloc_howto_type elf_mips_ctor64_howto =
0xffffffff, /* dst_mask */
FALSE); /* pcrel_offset */
-/* The reloc used for the mips16 jump instruction. */
-static reloc_howto_type elf_mips16_jump_howto =
+static reloc_howto_type elf_mips16_howto_table_rel[] =
+{
+ /* The reloc used for the mips16 jump instruction. */
HOWTO (R_MIPS16_26, /* type */
2, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
@@ -581,10 +582,9 @@ static reloc_howto_type elf_mips16_jump_howto =
TRUE, /* partial_inplace */
0x3ffffff, /* src_mask */
0x3ffffff, /* dst_mask */
- FALSE); /* pcrel_offset */
+ FALSE), /* pcrel_offset */
-/* The reloc used for the mips16 gprel instruction. */
-static reloc_howto_type elf_mips16_gprel_howto =
+ /* The reloc used for the mips16 gprel instruction. */
HOWTO (R_MIPS16_GPREL, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
@@ -595,9 +595,46 @@ static reloc_howto_type elf_mips16_gprel_howto =
mips16_gprel_reloc, /* special_function */
"R_MIPS16_GPREL", /* name */
TRUE, /* partial_inplace */
- 0x07ff001f, /* src_mask */
- 0x07ff001f, /* dst_mask */
- FALSE); /* pcrel_offset */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A placeholder for MIPS16 reference to global offset table. */
+ EMPTY_HOWTO (R_MIPS16_GOT16),
+
+ /* A placeholder for MIPS16 16 bit call through global offset table. */
+ EMPTY_HOWTO (R_MIPS16_CALL16),
+
+ /* MIPS16 high 16 bits of symbol value. */
+ HOWTO (R_MIPS16_HI16, /* type */
+ 16, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_hi16_reloc, /* special_function */
+ "R_MIPS16_HI16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* MIPS16 low 16 bits of symbol value. */
+ HOWTO (R_MIPS16_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_lo16_reloc, /* special_function */
+ "R_MIPS16_LO16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+};
/* 16 bit offset for pc-relative branches. */
static reloc_howto_type elf_mips_gnu_rel16_s2 =
@@ -957,11 +994,8 @@ mips16_gprel_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
{
bfd_boolean relocatable;
bfd_reloc_status_type ret;
+ bfd_byte *location;
bfd_vma gp;
- unsigned short extend = 0;
- unsigned short insn = 0;
- bfd_signed_vma val;
- bfd_vma relocation;
/* If we're relocating, and this is an external symbol, we don't want
to change anything. */
@@ -986,55 +1020,16 @@ mips16_gprel_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
if (ret != bfd_reloc_ok)
return ret;
- if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
- return bfd_reloc_outofrange;
-
- if (bfd_is_com_section (symbol->section))
- relocation = 0;
- else
- relocation = symbol->value;
-
- relocation += symbol->section->output_section->vma;
- relocation += symbol->section->output_offset;
-
- /* Set val to the offset into the section or symbol. */
- val = reloc_entry->addend;
+ location = (bfd_byte *) data + reloc_entry->address;
+ _bfd_mips16_elf_reloc_unshuffle (abfd, reloc_entry->howto->type, FALSE,
+ location);
+ ret = _bfd_mips_elf_gprel16_with_gp (abfd, symbol, reloc_entry,
+ input_section, relocatable,
+ data, gp);
+ _bfd_mips16_elf_reloc_shuffle (abfd, reloc_entry->howto->type, !relocatable,
+ location);
- if (reloc_entry->howto->partial_inplace)
- {
- /* Pick up the mips16 extend instruction and the real instruction. */
- extend = bfd_get_16 (abfd, (bfd_byte *) data + reloc_entry->address);
- insn = bfd_get_16 (abfd, (bfd_byte *) data + reloc_entry->address + 2);
- val += ((extend & 0x1f) << 11) | (extend & 0x7e0) | (insn & 0x1f);
- }
-
- _bfd_mips_elf_sign_extend(val, 16);
-
- /* Adjust val for the final section location and GP value. If we
- are producing relocatable output, we don't want to do this for
- an external symbol. */
- if (! relocatable
- || (symbol->flags & BSF_SECTION_SYM) != 0)
- val += relocation - gp;
-
- if (reloc_entry->howto->partial_inplace)
- {
- bfd_put_16 (abfd,
- (extend & 0xf800) | ((val >> 11) & 0x1f) | (val & 0x7e0),
- (bfd_byte *) data + reloc_entry->address);
- bfd_put_16 (abfd,
- (insn & 0xffe0) | (val & 0x1f),
- (bfd_byte *) data + reloc_entry->address + 2);
- }
- else
- reloc_entry->addend = val;
-
- if (relocatable)
- reloc_entry->address += input_section->output_offset;
- else if (((val & ~0xffff) != ~0xffff) && ((val & ~0xffff) != 0))
- return bfd_reloc_overflow;
-
- return bfd_reloc_ok;
+ return ret;
}
/* A mapping from BFD reloc types to MIPS ELF reloc types. */
@@ -1070,6 +1065,14 @@ static const struct elf_reloc_map mips_reloc_map[] =
{ BFD_RELOC_MIPS_GOT_DISP, R_MIPS_GOT_DISP }
};
+static const struct elf_reloc_map mips16_reloc_map[] =
+{
+ { BFD_RELOC_MIPS16_JMP, R_MIPS16_26 - R_MIPS16_min },
+ { BFD_RELOC_MIPS16_GPREL, R_MIPS16_GPREL - R_MIPS16_min },
+ { BFD_RELOC_MIPS16_HI16_S, R_MIPS16_HI16 - R_MIPS16_min },
+ { BFD_RELOC_MIPS16_LO16, R_MIPS16_LO16 - R_MIPS16_min },
+};
+
/* Given a BFD reloc type, return a howto structure. */
static reloc_howto_type *
@@ -1077,6 +1080,7 @@ bfd_elf32_bfd_reloc_type_lookup (bfd *abfd, bfd_reloc_code_real_type code)
{
unsigned int i;
reloc_howto_type *howto_table = elf_mips_howto_table_rel;
+ reloc_howto_type *howto16_table = elf_mips16_howto_table_rel;
for (i = 0; i < sizeof (mips_reloc_map) / sizeof (struct elf_reloc_map);
i++)
@@ -1085,6 +1089,13 @@ bfd_elf32_bfd_reloc_type_lookup (bfd *abfd, bfd_reloc_code_real_type code)
return &howto_table[(int) mips_reloc_map[i].elf_val];
}
+ for (i = 0; i < sizeof (mips16_reloc_map) / sizeof (struct elf_reloc_map);
+ i++)
+ {
+ if (mips16_reloc_map[i].bfd_val == code)
+ return &howto16_table[(int) mips16_reloc_map[i].elf_val];
+ }
+
switch (code)
{
default:
@@ -1101,10 +1112,6 @@ bfd_elf32_bfd_reloc_type_lookup (bfd *abfd, bfd_reloc_code_real_type code)
else
return &howto_table[(int) R_MIPS_32];
- 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:
@@ -1124,10 +1131,6 @@ mips_elf32_rtype_to_howto (unsigned int r_type,
{
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:
@@ -1137,6 +1140,8 @@ mips_elf32_rtype_to_howto (unsigned int r_type,
case R_MIPS_PC32:
return &elf_mips_gnu_pcrel32;
default:
+ if (r_type >= R_MIPS16_min && r_type < R_MIPS16_max)
+ return &elf_mips16_howto_table_rel[r_type - R_MIPS16_min];
BFD_ASSERT (r_type < (unsigned int) R_MIPS_max);
return &elf_mips_howto_table_rel[r_type];
}
diff --git a/bfd/elf64-mips.c b/bfd/elf64-mips.c
index b36743562fa..ce74166ecd4 100644
--- a/bfd/elf64-mips.c
+++ b/bfd/elf64-mips.c
@@ -1153,8 +1153,9 @@ static reloc_howto_type mips_elf64_howto_table_rela[] =
FALSE), /* pcrel_offset */
};
-/* The reloc used for the mips16 jump instruction. */
-static reloc_howto_type elf_mips16_jump_howto =
+static reloc_howto_type mips16_elf64_howto_table_rel[] =
+{
+ /* The reloc used for the mips16 jump instruction. */
HOWTO (R_MIPS16_26, /* type */
2, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
@@ -1170,10 +1171,9 @@ static reloc_howto_type elf_mips16_jump_howto =
TRUE, /* partial_inplace */
0x3ffffff, /* src_mask */
0x3ffffff, /* dst_mask */
- FALSE); /* pcrel_offset */
+ FALSE), /* pcrel_offset */
-/* The reloc used for the mips16 gprel instruction. */
-static reloc_howto_type elf_mips16_gprel_howto =
+ /* The reloc used for the mips16 gprel instruction. */
HOWTO (R_MIPS16_GPREL, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
@@ -1184,9 +1184,118 @@ static reloc_howto_type elf_mips16_gprel_howto =
mips16_gprel_reloc, /* special_function */
"R_MIPS16_GPREL", /* name */
TRUE, /* partial_inplace */
- 0x07ff001f, /* src_mask */
- 0x07ff001f, /* dst_mask */
- FALSE); /* pcrel_offset */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A placeholder for MIPS16 reference to global offset table. */
+ EMPTY_HOWTO (R_MIPS16_GOT16),
+
+ /* A placeholder for MIPS16 16 bit call through global offset table. */
+ EMPTY_HOWTO (R_MIPS16_CALL16),
+
+ /* MIPS16 high 16 bits of symbol value. */
+ HOWTO (R_MIPS16_HI16, /* type */
+ 16, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_hi16_reloc, /* special_function */
+ "R_MIPS16_HI16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* MIPS16 low 16 bits of symbol value. */
+ HOWTO (R_MIPS16_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_lo16_reloc, /* special_function */
+ "R_MIPS16_LO16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+};
+
+static reloc_howto_type mips16_elf64_howto_table_rela[] =
+{
+ /* The reloc used for the mips16 jump instruction. */
+ 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 */
+ FALSE, /* partial_inplace */
+ 0x3ffffff, /* src_mask */
+ 0x3ffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* The reloc used for the mips16 gprel instruction. */
+ HOWTO (R_MIPS16_GPREL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips16_gprel_reloc, /* special_function */
+ "R_MIPS16_GPREL", /* name */
+ FALSE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A placeholder for MIPS16 reference to global offset table. */
+ EMPTY_HOWTO (R_MIPS16_GOT16),
+
+ /* A placeholder for MIPS16 16 bit call through global offset table. */
+ EMPTY_HOWTO (R_MIPS16_CALL16),
+
+ /* MIPS16 high 16 bits of symbol value. */
+ HOWTO (R_MIPS16_HI16, /* type */
+ 16, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_hi16_reloc, /* special_function */
+ "R_MIPS16_HI16", /* name */
+ FALSE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* MIPS16 low 16 bits of symbol value. */
+ HOWTO (R_MIPS16_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_lo16_reloc, /* special_function */
+ "R_MIPS16_LO16", /* name */
+ FALSE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+};
/* GNU extension to record C++ vtable hierarchy */
static reloc_howto_type elf_mips_gnu_vtinherit_howto =
@@ -1703,11 +1812,8 @@ mips16_gprel_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
{
bfd_boolean relocatable;
bfd_reloc_status_type ret;
+ bfd_byte *location;
bfd_vma gp;
- unsigned short extend = 0;
- unsigned short insn = 0;
- bfd_signed_vma val;
- bfd_vma relocation;
/* If we're relocating, and this is an external symbol, we don't want
to change anything. */
@@ -1732,55 +1838,16 @@ mips16_gprel_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
if (ret != bfd_reloc_ok)
return ret;
- if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
- return bfd_reloc_outofrange;
+ location = (bfd_byte *) data + reloc_entry->address;
+ _bfd_mips16_elf_reloc_unshuffle (abfd, reloc_entry->howto->type, FALSE,
+ location);
+ ret = _bfd_mips_elf_gprel16_with_gp (abfd, symbol, reloc_entry,
+ input_section, relocatable,
+ data, gp);
+ _bfd_mips16_elf_reloc_shuffle (abfd, reloc_entry->howto->type, !relocatable,
+ location);
- if (bfd_is_com_section (symbol->section))
- relocation = 0;
- else
- relocation = symbol->value;
-
- relocation += symbol->section->output_section->vma;
- relocation += symbol->section->output_offset;
-
- /* Set val to the offset into the section or symbol. */
- val = reloc_entry->addend;
-
- if (reloc_entry->howto->partial_inplace)
- {
- /* Pick up the mips16 extend instruction and the real instruction. */
- extend = bfd_get_16 (abfd, (bfd_byte *) data + reloc_entry->address);
- insn = bfd_get_16 (abfd, (bfd_byte *) data + reloc_entry->address + 2);
- val += ((extend & 0x1f) << 11) | (extend & 0x7e0) | (insn & 0x1f);
- }
-
- _bfd_mips_elf_sign_extend(val, 16);
-
- /* Adjust val for the final section location and GP value. If we
- are producing relocatable output, we don't want to do this for
- an external symbol. */
- if (! relocatable
- || (symbol->flags & BSF_SECTION_SYM) != 0)
- val += relocation - gp;
-
- if (reloc_entry->howto->partial_inplace)
- {
- bfd_put_16 (abfd,
- (extend & 0xf800) | ((val >> 11) & 0x1f) | (val & 0x7e0),
- (bfd_byte *) data + reloc_entry->address);
- bfd_put_16 (abfd,
- (insn & 0xffe0) | (val & 0x1f),
- (bfd_byte *) data + reloc_entry->address + 2);
- }
- else
- reloc_entry->addend = val;
-
- if (relocatable)
- reloc_entry->address += input_section->output_offset;
- else if (((val & ~0xffff) != ~0xffff) && ((val & ~0xffff) != 0))
- return bfd_reloc_overflow;
-
- return bfd_reloc_ok;
+ return ret;
}
/* A mapping from BFD reloc types to MIPS ELF reloc types. */
@@ -1829,6 +1896,14 @@ static const struct elf_reloc_map mips_reloc_map[] =
{ BFD_RELOC_MIPS_JALR, R_MIPS_JALR }
};
+static const struct elf_reloc_map mips16_reloc_map[] =
+{
+ { BFD_RELOC_MIPS16_JMP, R_MIPS16_26 - R_MIPS16_min },
+ { BFD_RELOC_MIPS16_GPREL, R_MIPS16_GPREL - R_MIPS16_min },
+ { BFD_RELOC_MIPS16_HI16_S, R_MIPS16_HI16 - R_MIPS16_min },
+ { BFD_RELOC_MIPS16_LO16, R_MIPS16_LO16 - R_MIPS16_min },
+};
+
/* Given a BFD reloc type, return a howto structure. */
static reloc_howto_type *
@@ -1839,6 +1914,7 @@ bfd_elf64_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
/* FIXME: We default to RELA here instead of choosing the right
relocation variant. */
reloc_howto_type *howto_table = mips_elf64_howto_table_rela;
+ reloc_howto_type *howto16_table = mips16_elf64_howto_table_rela;
for (i = 0; i < sizeof (mips_reloc_map) / sizeof (struct elf_reloc_map);
i++)
@@ -1847,12 +1923,15 @@ bfd_elf64_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
return &howto_table[(int) mips_reloc_map[i].elf_val];
}
+ for (i = 0; i < sizeof (mips16_reloc_map) / sizeof (struct elf_reloc_map);
+ i++)
+ {
+ if (mips16_reloc_map[i].bfd_val == code)
+ return &howto16_table[(int) mips16_reloc_map[i].elf_val];
+ }
+
switch (code)
{
- 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:
@@ -1872,10 +1951,6 @@ mips_elf64_rtype_to_howto (unsigned int r_type, bfd_boolean rela_p)
{
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:
@@ -1886,6 +1961,13 @@ mips_elf64_rtype_to_howto (unsigned int r_type, bfd_boolean rela_p)
else
return &elf_mips_gnu_rel16_s2;
default:
+ if (r_type >= R_MIPS16_min && r_type < R_MIPS16_max)
+ {
+ if (rela_p)
+ return &mips16_elf64_howto_table_rela[r_type - R_MIPS16_min];
+ else
+ return &mips16_elf64_howto_table_rel[r_type - R_MIPS16_min];
+ }
BFD_ASSERT (r_type < (unsigned int) R_MIPS_max);
if (rela_p)
return &mips_elf64_howto_table_rela[r_type];
diff --git a/bfd/elfn32-mips.c b/bfd/elfn32-mips.c
index 228601caa58..92cc02c4c58 100644
--- a/bfd/elfn32-mips.c
+++ b/bfd/elfn32-mips.c
@@ -1119,8 +1119,9 @@ static reloc_howto_type elf_mips_howto_table_rela[] =
FALSE), /* pcrel_offset */
};
-/* The reloc used for the mips16 jump instruction. */
-static reloc_howto_type elf_mips16_jump_howto =
+static reloc_howto_type elf_mips16_howto_table_rel[] =
+{
+ /* The reloc used for the mips16 jump instruction. */
HOWTO (R_MIPS16_26, /* type */
2, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
@@ -1136,10 +1137,9 @@ static reloc_howto_type elf_mips16_jump_howto =
TRUE, /* partial_inplace */
0x3ffffff, /* src_mask */
0x3ffffff, /* dst_mask */
- FALSE); /* pcrel_offset */
+ FALSE), /* pcrel_offset */
-/* The reloc used for the mips16 gprel instruction. */
-static reloc_howto_type elf_mips16_gprel_howto =
+ /* The reloc used for the mips16 gprel instruction. */
HOWTO (R_MIPS16_GPREL, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
@@ -1150,9 +1150,118 @@ static reloc_howto_type elf_mips16_gprel_howto =
mips16_gprel_reloc, /* special_function */
"R_MIPS16_GPREL", /* name */
TRUE, /* partial_inplace */
- 0x07ff001f, /* src_mask */
- 0x07ff001f, /* dst_mask */
- FALSE); /* pcrel_offset */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A placeholder for MIPS16 reference to global offset table. */
+ EMPTY_HOWTO (R_MIPS16_GOT16),
+
+ /* A placeholder for MIPS16 16 bit call through global offset table. */
+ EMPTY_HOWTO (R_MIPS16_CALL16),
+
+ /* MIPS16 high 16 bits of symbol value. */
+ HOWTO (R_MIPS16_HI16, /* type */
+ 16, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_hi16_reloc, /* special_function */
+ "R_MIPS16_HI16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* MIPS16 low 16 bits of symbol value. */
+ HOWTO (R_MIPS16_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_lo16_reloc, /* special_function */
+ "R_MIPS16_LO16", /* name */
+ TRUE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+};
+
+static reloc_howto_type elf_mips16_howto_table_rela[] =
+{
+ /* The reloc used for the mips16 jump instruction. */
+ 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 */
+ FALSE, /* partial_inplace */
+ 0x3ffffff, /* src_mask */
+ 0x3ffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* The reloc used for the mips16 gprel instruction. */
+ HOWTO (R_MIPS16_GPREL, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ mips16_gprel_reloc, /* special_function */
+ "R_MIPS16_GPREL", /* name */
+ FALSE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* A placeholder for MIPS16 reference to global offset table. */
+ EMPTY_HOWTO (R_MIPS16_GOT16),
+
+ /* A placeholder for MIPS16 16 bit call through global offset table. */
+ EMPTY_HOWTO (R_MIPS16_CALL16),
+
+ /* MIPS16 high 16 bits of symbol value. */
+ HOWTO (R_MIPS16_HI16, /* type */
+ 16, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_hi16_reloc, /* special_function */
+ "R_MIPS16_HI16", /* name */
+ FALSE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* MIPS16 low 16 bits of symbol value. */
+ HOWTO (R_MIPS16_LO16, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ _bfd_mips_elf_lo16_reloc, /* special_function */
+ "R_MIPS16_LO16", /* name */
+ FALSE, /* partial_inplace */
+ 0x0000ffff, /* src_mask */
+ 0x0000ffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+};
/* GNU extension to record C++ vtable hierarchy */
static reloc_howto_type elf_mips_gnu_vtinherit_howto =
@@ -1511,11 +1620,8 @@ mips16_gprel_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
{
bfd_boolean relocatable;
bfd_reloc_status_type ret;
+ bfd_byte *location;
bfd_vma gp;
- unsigned short extend = 0;
- unsigned short insn = 0;
- bfd_signed_vma val;
- bfd_vma relocation;
/* If we're relocating, and this is an external symbol, we don't want
to change anything. */
@@ -1540,55 +1646,16 @@ mips16_gprel_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
if (ret != bfd_reloc_ok)
return ret;
- if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
- return bfd_reloc_outofrange;
-
- if (bfd_is_com_section (symbol->section))
- relocation = 0;
- else
- relocation = symbol->value;
+ location = (bfd_byte *) data + reloc_entry->address;
+ _bfd_mips16_elf_reloc_unshuffle (abfd, reloc_entry->howto->type, FALSE,
+ location);
+ ret = _bfd_mips_elf_gprel16_with_gp (abfd, symbol, reloc_entry,
+ input_section, relocatable,
+ data, gp);
+ _bfd_mips16_elf_reloc_shuffle (abfd, reloc_entry->howto->type, !relocatable,
+ location);
- relocation += symbol->section->output_section->vma;
- relocation += symbol->section->output_offset;
-
- /* Set val to the offset into the section or symbol. */
- val = reloc_entry->addend;
-
- if (reloc_entry->howto->partial_inplace)
- {
- /* Pick up the mips16 extend instruction and the real instruction. */
- extend = bfd_get_16 (abfd, (bfd_byte *) data + reloc_entry->address);
- insn = bfd_get_16 (abfd, (bfd_byte *) data + reloc_entry->address + 2);
- val += ((extend & 0x1f) << 11) | (extend & 0x7e0) | (insn & 0x1f);
- }
-
- _bfd_mips_elf_sign_extend(val, 16);
-
- /* Adjust val for the final section location and GP value. If we
- are producing relocatable output, we don't want to do this for
- an external symbol. */
- if (! relocatable
- || (symbol->flags & BSF_SECTION_SYM) != 0)
- val += relocation - gp;
-
- if (reloc_entry->howto->partial_inplace)
- {
- bfd_put_16 (abfd,
- (extend & 0xf800) | ((val >> 11) & 0x1f) | (val & 0x7e0),
- (bfd_byte *) data + reloc_entry->address);
- bfd_put_16 (abfd,
- (insn & 0xffe0) | (val & 0x1f),
- (bfd_byte *) data + reloc_entry->address + 2);
- }
- else
- reloc_entry->addend = val;
-
- if (relocatable)
- reloc_entry->address += input_section->output_offset;
- else if (((val & ~0xffff) != ~0xffff) && ((val & ~0xffff) != 0))
- return bfd_reloc_overflow;
-
- return bfd_reloc_ok;
+ return ret;
}
/* A mapping from BFD reloc types to MIPS ELF reloc types. */
@@ -1637,6 +1704,14 @@ static const struct elf_reloc_map mips_reloc_map[] =
{ BFD_RELOC_MIPS_JALR, R_MIPS_JALR }
};
+static const struct elf_reloc_map mips16_reloc_map[] =
+{
+ { BFD_RELOC_MIPS16_JMP, R_MIPS16_26 - R_MIPS16_min },
+ { BFD_RELOC_MIPS16_GPREL, R_MIPS16_GPREL - R_MIPS16_min },
+ { BFD_RELOC_MIPS16_HI16_S, R_MIPS16_HI16 - R_MIPS16_min },
+ { BFD_RELOC_MIPS16_LO16, R_MIPS16_LO16 - R_MIPS16_min },
+};
+
/* Given a BFD reloc type, return a howto structure. */
static reloc_howto_type *
@@ -1647,6 +1722,7 @@ bfd_elf32_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
/* FIXME: We default to RELA here instead of choosing the right
relocation variant. */
reloc_howto_type *howto_table = elf_mips_howto_table_rela;
+ reloc_howto_type *howto16_table = elf_mips16_howto_table_rela;
for (i = 0; i < sizeof (mips_reloc_map) / sizeof (struct elf_reloc_map);
i++)
@@ -1655,12 +1731,15 @@ bfd_elf32_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
return &howto_table[(int) mips_reloc_map[i].elf_val];
}
+ for (i = 0; i < sizeof (mips16_reloc_map) / sizeof (struct elf_reloc_map);
+ i++)
+ {
+ if (mips16_reloc_map[i].bfd_val == code)
+ return &howto16_table[(int) mips16_reloc_map[i].elf_val];
+ }
+
switch (code)
{
- 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:
@@ -1680,10 +1759,6 @@ mips_elf_n32_rtype_to_howto (unsigned int r_type, bfd_boolean rela_p)
{
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:
@@ -1694,6 +1769,13 @@ mips_elf_n32_rtype_to_howto (unsigned int r_type, bfd_boolean rela_p)
else
return &elf_mips_gnu_rel16_s2;
default:
+ if (r_type >= R_MIPS16_min && r_type < R_MIPS16_max)
+ {
+ if (rela_p)
+ return &elf_mips16_howto_table_rela[r_type - R_MIPS16_min];
+ else
+ return &elf_mips16_howto_table_rel[r_type - R_MIPS16_min];
+ }
BFD_ASSERT (r_type < (unsigned int) R_MIPS_max);
if (rela_p)
return &elf_mips_howto_table_rela[r_type];
diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index be0e7dcb6a1..54fc808c3a3 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -1061,6 +1061,152 @@ mips_elf_check_mips16_stubs (struct mips_elf_link_hash_entry *h,
return TRUE;
}
+/* R_MIPS16_26 is used for the mips16 jal and jalx instructions.
+ Most mips16 instructions are 16 bits, but these instructions
+ are 32 bits.
+
+ The format of these instructions is:
+
+ +--------------+--------------------------------+
+ | JALX | X| Imm 20:16 | Imm 25:21 |
+ +--------------+--------------------------------+
+ | Immediate 15:0 |
+ +-----------------------------------------------+
+
+ JALX is the 5-bit value 00011. X is 0 for jal, 1 for jalx.
+ Note that the immediate value in the first word is swapped.
+
+ When producing a relocatable object file, R_MIPS16_26 is
+ handled mostly like R_MIPS_26. In particular, the addend is
+ stored as a straight 26-bit value in a 32-bit instruction.
+ (gas makes life simpler for itself by never adjusting a
+ R_MIPS16_26 reloc to be against a section, so the addend is
+ always zero). However, the 32 bit instruction is stored as 2
+ 16-bit values, rather than a single 32-bit value. In a
+ big-endian file, the result is the same; in a little-endian
+ file, the two 16-bit halves of the 32 bit value are swapped.
+ This is so that a disassembler can recognize the jal
+ instruction.
+
+ When doing a final link, R_MIPS16_26 is treated as a 32 bit
+ instruction stored as two 16-bit values. The addend A is the
+ contents of the targ26 field. The calculation is the same as
+ R_MIPS_26. When storing the calculated value, reorder the
+ immediate value as shown above, and don't forget to store the
+ value as two 16-bit values.
+
+ To put it in MIPS ABI terms, the relocation field is T-targ26-16,
+ defined as
+
+ big-endian:
+ +--------+----------------------+
+ | | |
+ | | targ26-16 |
+ |31 26|25 0|
+ +--------+----------------------+
+
+ little-endian:
+ +----------+------+-------------+
+ | | | |
+ | sub1 | | sub2 |
+ |0 9|10 15|16 31|
+ +----------+--------------------+
+ where targ26-16 is sub1 followed by sub2 (i.e., the addend field A is
+ ((sub1 << 16) | sub2)).
+
+ When producing a relocatable object file, the calculation is
+ (((A < 2) | ((P + 4) & 0xf0000000) + S) >> 2)
+ When producing a fully linked file, the calculation is
+ let R = (((A < 2) | ((P + 4) & 0xf0000000) + S) >> 2)
+ ((R & 0x1f0000) << 5) | ((R & 0x3e00000) >> 5) | (R & 0xffff)
+
+ R_MIPS16_GPREL is used for GP-relative addressing in mips16
+ mode. A typical instruction will have a format like this:
+
+ +--------------+--------------------------------+
+ | EXTEND | Imm 10:5 | Imm 15:11 |
+ +--------------+--------------------------------+
+ | Major | rx | ry | Imm 4:0 |
+ +--------------+--------------------------------+
+
+ EXTEND is the five bit value 11110. Major is the instruction
+ opcode.
+
+ This is handled exactly like R_MIPS_GPREL16, except that the
+ addend is retrieved and stored as shown in this diagram; that
+ is, the Imm fields above replace the V-rel16 field.
+
+ All we need to do here is shuffle the bits appropriately. As
+ above, the two 16-bit halves must be swapped on a
+ little-endian system.
+
+ R_MIPS16_HI16 and R_MIPS16_LO16 are used in mips16 mode to
+ access data when neither GP-relative nor PC-relative addressing
+ can be used. They are handled like R_MIPS_HI16 and R_MIPS_LO16,
+ except that the addend is retrieved and stored as shown above
+ for R_MIPS16_GPREL.
+ */
+void
+_bfd_mips16_elf_reloc_unshuffle (bfd *abfd, int r_type,
+ bfd_boolean jal_shuffle, bfd_byte *data)
+{
+ bfd_vma extend, insn, val;
+
+ if (r_type != R_MIPS16_26 && r_type != R_MIPS16_GPREL
+ && r_type != R_MIPS16_HI16 && r_type != R_MIPS16_LO16)
+ return;
+
+ /* Pick up the mips16 extend instruction and the real instruction. */
+ extend = bfd_get_16 (abfd, data);
+ insn = bfd_get_16 (abfd, data + 2);
+ if (r_type == R_MIPS16_26)
+ {
+ if (jal_shuffle)
+ val = ((extend & 0xfc00) << 16) | ((extend & 0x3e0) << 11)
+ | ((extend & 0x1f) << 21) | insn;
+ else
+ val = extend << 16 | insn;
+ }
+ else
+ val = ((extend & 0xf800) << 16) | ((insn & 0xffe0) << 11)
+ | ((extend & 0x1f) << 11) | (extend & 0x7e0) | (insn & 0x1f);
+ bfd_put_32 (abfd, val, data);
+}
+
+void
+_bfd_mips16_elf_reloc_shuffle (bfd *abfd, int r_type,
+ bfd_boolean jal_shuffle, bfd_byte *data)
+{
+ bfd_vma extend, insn, val;
+
+ if (r_type != R_MIPS16_26 && r_type != R_MIPS16_GPREL
+ && r_type != R_MIPS16_HI16 && r_type != R_MIPS16_LO16)
+ return;
+
+ val = bfd_get_32 (abfd, data);
+ if (r_type == R_MIPS16_26)
+ {
+ if (jal_shuffle)
+ {
+ insn = val & 0xffff;
+ extend = ((val >> 16) & 0xfc00) | ((val >> 11) & 0x3e0)
+ | ((val >> 21) & 0x1f);
+ }
+ else
+ {
+ insn = val & 0xffff;
+ extend = val >> 16;
+ }
+ }
+ else
+ {
+ insn = ((val >> 11) & 0xffe0) | (val & 0x1f);
+ extend = ((val >> 16) & 0xf800) | ((val >> 11) & 0x1f) | (val & 0x7e0);
+ }
+ bfd_put_16 (abfd, insn, data + 2);
+ bfd_put_16 (abfd, extend, data);
+}
+
bfd_reloc_status_type
_bfd_mips_elf_gprel16_with_gp (bfd *abfd, asymbol *symbol,
arelent *reloc_entry, asection *input_section,
@@ -1194,11 +1340,17 @@ _bfd_mips_elf_lo16_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
bfd *output_bfd, char **error_message)
{
bfd_vma vallo;
+ bfd_byte *location = (bfd_byte *) data + reloc_entry->address;
if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
return bfd_reloc_outofrange;
- vallo = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
+ _bfd_mips16_elf_reloc_unshuffle (abfd, reloc_entry->howto->type, FALSE,
+ location);
+ vallo = bfd_get_32 (abfd, location);
+ _bfd_mips16_elf_reloc_shuffle (abfd, reloc_entry->howto->type, FALSE,
+ location);
+
while (mips_hi16_list != NULL)
{
bfd_reloc_status_type ret;
@@ -1284,13 +1436,19 @@ _bfd_mips_elf_generic_reloc (bfd *abfd ATTRIBUTE_UNUSED, arelent *reloc_entry,
reloc_entry->addend += val;
else
{
+ bfd_byte *location = (bfd_byte *) data + reloc_entry->address;
+
/* Add in the separate addend, if any. */
val += reloc_entry->addend;
/* Add VAL to the relocation field. */
+ _bfd_mips16_elf_reloc_unshuffle (abfd, reloc_entry->howto->type, FALSE,
+ location);
status = _bfd_relocate_contents (reloc_entry->howto, abfd, val,
- (bfd_byte *) data
- + reloc_entry->address);
+ location);
+ _bfd_mips16_elf_reloc_shuffle (abfd, reloc_entry->howto->type, FALSE,
+ location);
+
if (status != bfd_reloc_ok)
return status;
}
@@ -3078,7 +3236,8 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
{
/* Relocations against _gp_disp are permitted only with
R_MIPS_HI16 and R_MIPS_LO16 relocations. */
- if (r_type != R_MIPS_HI16 && r_type != R_MIPS_LO16)
+ if (r_type != R_MIPS_HI16 && r_type != R_MIPS_LO16
+ && r_type != R_MIPS16_HI16 && r_type != R_MIPS16_LO16)
return bfd_reloc_notsupported;
gp_disp_p = TRUE;
@@ -3265,10 +3424,12 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
case R_MIPS_HI16:
case R_MIPS_LO16:
- case R_MIPS16_GPREL:
case R_MIPS_GPREL16:
case R_MIPS_GPREL32:
case R_MIPS_LITERAL:
+ case R_MIPS16_HI16:
+ case R_MIPS16_LO16:
+ case R_MIPS16_GPREL:
gp0 = _bfd_get_gp_value (input_bfd);
gp = _bfd_get_gp_value (abfd);
if (elf_hash_table (info)->dynobj)
@@ -3360,6 +3521,7 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
break;
case R_MIPS_HI16:
+ case R_MIPS16_HI16:
if (!gp_disp_p)
{
value = mips_elf_high (addend + symbol);
@@ -3367,17 +3529,35 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
}
else
{
- value = mips_elf_high (addend + gp - p);
+ /* For MIPS16 ABI code we generate this sequence
+ 0: li $v0,%hi(_gp_disp)
+ 4: addiupc $v1,%lo(_gp_disp)
+ 8: sll $v0,16
+ 12: addu $v0,$v1
+ 14: move $gp,$v0
+ So the offsets of hi and lo relocs are the same, but the
+ $pc is four higher than $t9 would be, so reduce
+ both reloc addends by 4. */
+ if (r_type == R_MIPS16_HI16)
+ value = mips_elf_high (addend + gp - p - 4);
+ else
+ value = mips_elf_high (addend + gp - p);
overflowed_p = mips_elf_overflow_p (value, 16);
}
break;
case R_MIPS_LO16:
+ case R_MIPS16_LO16:
if (!gp_disp_p)
value = (symbol + addend) & howto->dst_mask;
else
{
- value = addend + gp - p + 4;
+ /* See the comment for R_MIPS16_HI16 above for the reason
+ for this conditional. */
+ if (r_type == R_MIPS16_LO16)
+ value = addend + gp - p;
+ else
+ value = addend + gp - p + 4;
/* The MIPS ABI requires checking the R_MIPS_LO16 relocation
for overflow. But, on, say, IRIX5, relocations against
_gp_disp are normally generated from the .cpload
@@ -3563,13 +3743,6 @@ mips_elf_obtain_contents (reloc_howto_type *howto,
/* Obtain the bytes. */
x = bfd_get ((8 * bfd_get_reloc_size (howto)), input_bfd, location);
- if ((ELF_R_TYPE (input_bfd, relocation->r_info) == R_MIPS16_26
- || ELF_R_TYPE (input_bfd, relocation->r_info) == R_MIPS16_GPREL)
- && bfd_little_endian (input_bfd))
- /* The two 16-bit words will be reversed on a little-endian system.
- See mips_elf_perform_relocation for more details. */
- x = (((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16));
-
return x;
}
@@ -3597,107 +3770,14 @@ mips_elf_perform_relocation (struct bfd_link_info *info,
/* Figure out where the relocation is occurring. */
location = contents + relocation->r_offset;
+ _bfd_mips16_elf_reloc_unshuffle (input_bfd, r_type, FALSE, location);
+
/* Obtain the current value. */
x = mips_elf_obtain_contents (howto, relocation, input_bfd, contents);
/* Clear the field we are setting. */
x &= ~howto->dst_mask;
- /* If this is the R_MIPS16_26 relocation, we must store the
- value in a funny way. */
- if (r_type == R_MIPS16_26)
- {
- /* R_MIPS16_26 is used for the mips16 jal and jalx instructions.
- Most mips16 instructions are 16 bits, but these instructions
- are 32 bits.
-
- The format of these instructions is:
-
- +--------------+--------------------------------+
- ! JALX ! X! Imm 20:16 ! Imm 25:21 !
- +--------------+--------------------------------+
- ! Immediate 15:0 !
- +-----------------------------------------------+
-
- JALX is the 5-bit value 00011. X is 0 for jal, 1 for jalx.
- Note that the immediate value in the first word is swapped.
-
- When producing a relocatable object file, R_MIPS16_26 is
- handled mostly like R_MIPS_26. In particular, the addend is
- stored as a straight 26-bit value in a 32-bit instruction.
- (gas makes life simpler for itself by never adjusting a
- R_MIPS16_26 reloc to be against a section, so the addend is
- always zero). However, the 32 bit instruction is stored as 2
- 16-bit values, rather than a single 32-bit value. In a
- big-endian file, the result is the same; in a little-endian
- file, the two 16-bit halves of the 32 bit value are swapped.
- This is so that a disassembler can recognize the jal
- instruction.
-
- When doing a final link, R_MIPS16_26 is treated as a 32 bit
- instruction stored as two 16-bit values. The addend A is the
- contents of the targ26 field. The calculation is the same as
- R_MIPS_26. When storing the calculated value, reorder the
- immediate value as shown above, and don't forget to store the
- value as two 16-bit values.
-
- To put it in MIPS ABI terms, the relocation field is T-targ26-16,
- defined as
-
- big-endian:
- +--------+----------------------+
- | | |
- | | targ26-16 |
- |31 26|25 0|
- +--------+----------------------+
-
- little-endian:
- +----------+------+-------------+
- | | | |
- | sub1 | | sub2 |
- |0 9|10 15|16 31|
- +----------+--------------------+
- where targ26-16 is sub1 followed by sub2 (i.e., the addend field A is
- ((sub1 << 16) | sub2)).
-
- When producing a relocatable object file, the calculation is
- (((A < 2) | ((P + 4) & 0xf0000000) + S) >> 2)
- When producing a fully linked file, the calculation is
- let R = (((A < 2) | ((P + 4) & 0xf0000000) + S) >> 2)
- ((R & 0x1f0000) << 5) | ((R & 0x3e00000) >> 5) | (R & 0xffff) */
-
- if (!info->relocatable)
- /* Shuffle the bits according to the formula above. */
- value = (((value & 0x1f0000) << 5)
- | ((value & 0x3e00000) >> 5)
- | (value & 0xffff));
- }
- else if (r_type == R_MIPS16_GPREL)
- {
- /* R_MIPS16_GPREL is used for GP-relative addressing in mips16
- mode. A typical instruction will have a format like this:
-
- +--------------+--------------------------------+
- ! EXTEND ! Imm 10:5 ! Imm 15:11 !
- +--------------+--------------------------------+
- ! Major ! rx ! ry ! Imm 4:0 !
- +--------------+--------------------------------+
-
- EXTEND is the five bit value 11110. Major is the instruction
- opcode.
-
- This is handled exactly like R_MIPS_GPREL16, except that the
- addend is retrieved and stored as shown in this diagram; that
- is, the Imm fields above replace the V-rel16 field.
-
- All we need to do here is shuffle the bits appropriately. As
- above, the two 16-bit halves must be swapped on a
- little-endian system. */
- value = (((value & 0x7e0) << 16)
- | ((value & 0xf800) << 5)
- | (value & 0x1f));
- }
-
/* Set the field. */
x |= (value & howto->dst_mask);
@@ -3763,14 +3843,12 @@ mips_elf_perform_relocation (struct bfd_link_info *info,
x = 0x04110000 | (((bfd_vma) off >> 2) & 0xffff); /* bal addr */
}
- /* Swap the high- and low-order 16 bits on little-endian systems
- when doing a MIPS16 relocation. */
- if ((r_type == R_MIPS16_GPREL || r_type == R_MIPS16_26)
- && bfd_little_endian (input_bfd))
- x = (((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16));
-
/* Put the value into the output. */
bfd_put (8 * bfd_get_reloc_size (howto), input_bfd, x, location);
+
+ _bfd_mips16_elf_reloc_shuffle(input_bfd, r_type, !info->relocatable,
+ location);
+
return TRUE;
}
@@ -6290,18 +6368,25 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
rel_hdr = elf_section_data (input_section)->rel_hdr2;
if (rel_hdr->sh_entsize == MIPS_ELF_REL_SIZE (input_bfd))
{
+ bfd_byte *location = contents + rel->r_offset;
+
/* Note that this is a REL relocation. */
rela_relocation_p = FALSE;
/* Get the addend, which is stored in the input file. */
+ _bfd_mips16_elf_reloc_unshuffle (input_bfd, r_type, FALSE,
+ location);
addend = mips_elf_obtain_contents (howto, rel, input_bfd,
contents);
+ _bfd_mips16_elf_reloc_shuffle(input_bfd, r_type, FALSE,
+ location);
+
addend &= howto->src_mask;
/* For some kinds of relocations, the ADDEND is a
combination of the addend stored in two different
relocations. */
- if (r_type == R_MIPS_HI16
+ if (r_type == R_MIPS_HI16 || r_type == R_MIPS16_HI16
|| (r_type == R_MIPS_GOT16
&& mips_elf_local_relocation_p (input_bfd, rel,
local_sections, FALSE)))
@@ -6309,6 +6394,13 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
bfd_vma l;
const Elf_Internal_Rela *lo16_relocation;
reloc_howto_type *lo16_howto;
+ bfd_byte *lo16_location;
+ int lo16_type;
+
+ if (r_type == R_MIPS16_HI16)
+ lo16_type = R_MIPS16_LO16;
+ else
+ lo16_type = R_MIPS_LO16;
/* The combined value is the sum of the HI16 addend,
left-shifted by sixteen bits, and the LO16
@@ -6327,16 +6419,22 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
as one of these. We permit a similar extension
in general, as that is useful for GCC. */
lo16_relocation = mips_elf_next_relocation (input_bfd,
- R_MIPS_LO16,
+ lo16_type,
rel, relend);
if (lo16_relocation == NULL)
return FALSE;
+ lo16_location = contents + lo16_relocation->r_offset;
+
/* Obtain the addend kept there. */
lo16_howto = MIPS_ELF_RTYPE_TO_HOWTO (input_bfd,
- R_MIPS_LO16, FALSE);
+ lo16_type, FALSE);
+ _bfd_mips16_elf_reloc_unshuffle (input_bfd, lo16_type, FALSE,
+ lo16_location);
l = mips_elf_obtain_contents (lo16_howto, lo16_relocation,
input_bfd, contents);
+ _bfd_mips16_elf_reloc_shuffle (input_bfd, lo16_type, FALSE,
+ lo16_location);
l &= lo16_howto->src_mask;
l <<= lo16_howto->rightshift;
l = _bfd_mips_elf_sign_extend (l, 16);
@@ -6346,15 +6444,6 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
/* Compute the combined addend. */
addend += l;
}
- else if (r_type == R_MIPS16_GPREL)
- {
- /* The addend is scrambled in the object file. See
- mips_elf_perform_relocation for details on the
- format. */
- addend = (((addend & 0x1f0000) >> 5)
- | ((addend & 0x7e00000) >> 16)
- | (addend & 0x1f));
- }
else
addend <<= howto->rightshift;
}
diff --git a/bfd/elfxx-mips.h b/bfd/elfxx-mips.h
index de48c63be8c..1776b4e1e30 100644
--- a/bfd/elfxx-mips.h
+++ b/bfd/elfxx-mips.h
@@ -103,6 +103,10 @@ extern bfd_boolean _bfd_mips_elf_write_section
extern bfd_boolean _bfd_mips_elf_read_ecoff_info
(bfd *, asection *, struct ecoff_debug_info *);
+extern void _bfd_mips16_elf_reloc_unshuffle
+ (bfd *, int, bfd_boolean, bfd_byte *);
+extern void _bfd_mips16_elf_reloc_shuffle
+ (bfd *, int, bfd_boolean, bfd_byte *);
extern bfd_reloc_status_type _bfd_mips_elf_gprel16_with_gp
(bfd *, asymbol *, arelent *, asection *, bfd_boolean, void *, bfd_vma);
extern bfd_reloc_status_type _bfd_mips_elf32_gprel16_reloc
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index a454115f97f..f021a9781bc 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -902,6 +902,9 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
"BFD_RELOC_HI16",
"BFD_RELOC_HI16_S",
"BFD_RELOC_LO16",
+ "BFD_RELOC_MIPS16_HI16",
+ "BFD_RELOC_MIPS16_HI16_S",
+ "BFD_RELOC_MIPS16_LO16",
"BFD_RELOC_MIPS_LITERAL",
"BFD_RELOC_MIPS_GOT16",
"BFD_RELOC_MIPS_CALL16",
diff --git a/bfd/reloc.c b/bfd/reloc.c
index f3049355c52..a32d0b21fa7 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -2063,6 +2063,22 @@ ENUMDOC
Low 16 bits.
ENUM
+ BFD_RELOC_MIPS16_HI16
+ENUMDOC
+ MIPS16 high 16 bits of 32-bit value.
+ENUM
+ BFD_RELOC_MIPS16_HI16_S
+ENUMDOC
+ MIPS16 high 16 bits of 32-bit value but the low 16 bits will be sign
+ extended and added to form the final result. If the low 16
+ bits form a negative number, we need to add one to the high value
+ to compensate for the borrow when the low bits are added.
+ENUM
+ BFD_RELOC_MIPS16_LO16
+ENUMDOC
+ MIPS16 low 16 bits.
+
+ENUM
BFD_RELOC_MIPS_LITERAL
ENUMDOC
Relocation against a MIPS literal section.