diff options
author | Maciej W. Rozycki <macro@linux-mips.org> | 2005-02-15 19:57:53 +0000 |
---|---|---|
committer | Maciej W. Rozycki <macro@linux-mips.org> | 2005-02-15 19:57:53 +0000 |
commit | 628df2f3eaa45b3296b2a1d8d1ff0dc79fa8793a (patch) | |
tree | 7d5d1c499eb403524af7bed68dffe1e472a0d808 /bfd | |
parent | 67c1c12e937f4592aa6432a2230485bc909cc1c3 (diff) | |
download | gdb-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/ChangeLog | 71 | ||||
-rw-r--r-- | bfd/bfd-in2.h | 12 | ||||
-rw-r--r-- | bfd/elf32-mips.c | 141 | ||||
-rw-r--r-- | bfd/elf64-mips.c | 218 | ||||
-rw-r--r-- | bfd/elfn32-mips.c | 218 | ||||
-rw-r--r-- | bfd/elfxx-mips.c | 343 | ||||
-rw-r--r-- | bfd/elfxx-mips.h | 4 | ||||
-rw-r--r-- | bfd/libbfd.h | 3 | ||||
-rw-r--r-- | bfd/reloc.c | 16 |
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. |