aboutsummaryrefslogtreecommitdiff
path: root/bfd/elfxx-mips.c
diff options
context:
space:
mode:
Diffstat (limited to 'bfd/elfxx-mips.c')
-rw-r--r--bfd/elfxx-mips.c96
1 files changed, 79 insertions, 17 deletions
diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index 530e8d6..3c38f51 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -2294,6 +2294,17 @@ lo16_reloc_p (int r_type)
}
static inline bool
+tls_hi16_reloc_p (int r_type)
+{
+ return (r_type == R_MIPS_TLS_DTPREL_HI16
+ || r_type == R_MIPS_TLS_TPREL_HI16
+ || r_type == R_MIPS16_TLS_DTPREL_HI16
+ || r_type == R_MIPS16_TLS_TPREL_HI16
+ || r_type == R_MICROMIPS_TLS_DTPREL_HI16
+ || r_type == R_MICROMIPS_TLS_TPREL_HI16);
+}
+
+static inline bool
mips16_call_reloc_p (int r_type)
{
return r_type == R_MIPS16_26 || r_type == R_MIPS16_CALL16;
@@ -2600,20 +2611,22 @@ _bfd_mips_elf_lo16_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
extended. For example, an addend of 0x38000 would have 0x0004 in
the high part and 0x8000 (=0xff..f8000) in the low part.
To extract the actual addend, calculate
- ((hi & 0xffff) << 16) + ((lo & 0xffff) ^ 0x8000) - 0x8000.
- We will be applying (symbol + addend) & 0xffff to the low insn,
- and we want to apply (symbol + addend + 0x8000) >> 16 to the
- high insn (the +0x8000 adjusting for when the applied low part is
- negative). */
- vallo = ((bfd_get_32 (abfd, location) & 0xffff) ^ 0x8000) - 0x8000;
+ ((hi & 0xffff) << 16) + ((lo & 0xffff) ^ 0x8000) - 0x8000. */
+ vallo = _bfd_mips_elf_sign_extend (bfd_get_32 (abfd, location) & 0xffff, 16);
_bfd_mips_elf_reloc_shuffle (abfd, reloc_entry->howto->type, false,
location);
+ /* Add in the separate addend, if any. Since we are REL here this
+ will have been set and the in-place addend cleared if we have
+ been called from GAS via `bfd_install_relocation'. */
+ vallo += reloc_entry->addend;
tdata = mips_elf_tdata (abfd);
while (tdata->mips_hi16_list != NULL)
{
bfd_reloc_status_type ret;
struct mips_hi16 *hi;
+ bfd_vma addhi;
+ bfd_vma addlo;
hi = tdata->mips_hi16_list;
@@ -2629,7 +2642,19 @@ _bfd_mips_elf_lo16_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
else if (hi->rel.howto->type == R_MICROMIPS_GOT16)
hi->rel.howto = MIPS_ELF_RTYPE_TO_HOWTO (abfd, R_MICROMIPS_HI16, false);
- hi->rel.addend += vallo;
+ /* We will be applying (symbol + addend) & 0xffff to the low insn,
+ and we want to apply (symbol + addend + 0x8000) >> 16 to the
+ high insn (the +0x8000 adjusting for when the applied low part is
+ negative). */
+ addhi = (hi->rel.addend + 0x8000) & ~(bfd_vma) 0xffff;
+ addlo = vallo;
+
+ /* For a PC-relative relocation the PCLO16 part of the addend
+ is relative to its PC and not ours, so we need to adjust it. */
+ if (hi->rel.howto->type == R_MIPS_PCHI16)
+ addlo -= reloc_entry->address - hi->rel.address;
+
+ hi->rel.addend = addhi + _bfd_mips_elf_sign_extend (addlo & 0xffff, 16);
ret = _bfd_mips_elf_generic_reloc (abfd, &hi->rel, symbol, hi->data,
hi->input_section, output_bfd,
@@ -8298,14 +8323,44 @@ mips_elf_add_lo16_rel_addend (bfd *abfd,
bfd_vma l;
r_type = ELF_R_TYPE (abfd, rel->r_info);
- if (mips16_reloc_p (r_type))
- lo16_type = R_MIPS16_LO16;
- else if (micromips_reloc_p (r_type))
- lo16_type = R_MICROMIPS_LO16;
- else if (r_type == R_MIPS_PCHI16)
- lo16_type = R_MIPS_PCLO16;
- else
- lo16_type = R_MIPS_LO16;
+ switch (r_type)
+ {
+ case R_MIPS_HI16:
+ case R_MIPS_GOT16:
+ lo16_type = R_MIPS_LO16;
+ break;
+ case R_MIPS_PCHI16:
+ lo16_type = R_MIPS_PCLO16;
+ break;
+ case R_MIPS_TLS_DTPREL_HI16:
+ lo16_type = R_MIPS_TLS_DTPREL_LO16;
+ break;
+ case R_MIPS_TLS_TPREL_HI16:
+ lo16_type = R_MIPS_TLS_TPREL_LO16;
+ break;
+ case R_MIPS16_HI16:
+ case R_MIPS16_GOT16:
+ lo16_type = R_MIPS16_LO16;
+ break;
+ case R_MIPS16_TLS_DTPREL_HI16:
+ lo16_type = R_MIPS16_TLS_DTPREL_LO16;
+ break;
+ case R_MIPS16_TLS_TPREL_HI16:
+ lo16_type = R_MIPS16_TLS_TPREL_LO16;
+ break;
+ case R_MICROMIPS_HI16:
+ case R_MICROMIPS_GOT16:
+ lo16_type = R_MICROMIPS_LO16;
+ break;
+ case R_MICROMIPS_TLS_DTPREL_HI16:
+ lo16_type = R_MICROMIPS_TLS_DTPREL_LO16;
+ break;
+ case R_MICROMIPS_TLS_TPREL_HI16:
+ lo16_type = R_MICROMIPS_TLS_TPREL_LO16;
+ break;
+ default:
+ abort ();
+ }
/* The combined value is the sum of the HI16 addend, left-shifted by
sixteen bits, and the LO16 addend, sign extended. (Usually, the
@@ -8334,6 +8389,10 @@ mips_elf_add_lo16_rel_addend (bfd *abfd,
contents);
l <<= lo16_howto->rightshift;
+ /* For a PC-relative relocation the PCLO16 part of the addend
+ is relative to its PC and not ours, so we need to adjust it. */
+ if (r_type == R_MIPS_PCHI16)
+ l = (l - (lo16_relocation->r_offset - rel->r_offset)) & 0xffff;
l = _bfd_mips_elf_sign_extend (l, 16);
*addend <<= 16;
@@ -10529,7 +10588,8 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
if (hi16_reloc_p (r_type)
|| (got16_reloc_p (r_type)
&& mips_elf_local_relocation_p (input_bfd, rel,
- local_sections)))
+ local_sections))
+ || tls_hi16_reloc_p (r_type))
{
if (!mips_elf_add_lo16_rel_addend (input_bfd, input_section,
rel, relend,
@@ -10567,7 +10627,9 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
if (!rela_relocation_p && rel->r_addend)
{
addend += rel->r_addend;
- if (hi16_reloc_p (r_type) || got16_reloc_p (r_type))
+ if (hi16_reloc_p (r_type)
+ || got16_reloc_p (r_type)
+ || tls_hi16_reloc_p (r_type))
addend = mips_elf_high (addend);
else if (r_type == R_MIPS_HIGHER)
addend = mips_elf_higher (addend);