_bfd_mips_elf_lo16_reloc vallo comment
authorAlan Modra <amodra@gmail.com>
Sat, 29 Apr 2023 01:30:30 +0000 (11:00 +0930)
committerAlan Modra <amodra@gmail.com>
Tue, 2 May 2023 23:33:01 +0000 (09:03 +0930)
This explains exactly why the high reloc adjustment is as it is,
replacing the rather nebulous existing comment.  I've also changed the
expression from (lo+0x8000)&0xffff to (lo&0xffff)^0x8000 which better
matches part of the standard 16-bit sign extension (resulting in
exactly the same value), and hoisted the calculation out of the loop.

* elfxx-mips.c (_bfd_mips_elf_lo16_reloc): Expand vallo
comment.  Hoist calculation out of loop.

bfd/elfxx-mips.c

index c9cd2f8099f30e24a8353eaf3d61c4905cd5fcac..49355a42f7d6a8330d0d8da28dd72d2606ac4cb9 100644 (file)
@@ -2624,7 +2624,21 @@ _bfd_mips_elf_lo16_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
 
   _bfd_mips_elf_reloc_unshuffle (abfd, reloc_entry->howto->type, false,
                                 location);
-  vallo = bfd_get_32 (abfd, location);
+  /* The high 16 bits of the addend are stored in the high insn, the
+     low 16 bits in the low insn, but there is a catch:  You can't
+     just concatenate the high and low parts.  The high part of the
+     addend is adjusted for the fact that the low part is sign
+     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 (a)
+     ((hi & 0xffff) << 16) + ((lo & 0xffff) ^ 0x8000) - 0x8000.
+     We will be applying (symbol + addend) & 0xffff to the low insn,
+     and we want to apply (b) (symbol + addend + 0x8000) >> 16 to the
+     high insn (the +0x8000 adjusting for when the applied low part is
+     negative).  Substituting (a) into (b) and recognising that
+     (hi & 0xffff) is already in the high insn gives a high part
+     addend adjustment of (lo & 0xffff) ^ 0x8000.  */
+  vallo = (bfd_get_32 (abfd, location) & 0xffff) ^ 0x8000;
   _bfd_mips_elf_reloc_shuffle (abfd, reloc_entry->howto->type, false,
                               location);
 
@@ -2648,9 +2662,7 @@ _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);
 
-      /* VALLO is a signed 16-bit number.  Bias it by 0x8000 so that any
-        carry or borrow will induce a change of +1 or -1 in the high part.  */
-      hi->rel.addend += (vallo + 0x8000) & 0xffff;
+      hi->rel.addend += vallo;
 
       ret = _bfd_mips_elf_generic_reloc (abfd, &hi->rel, symbol, hi->data,
                                         hi->input_section, output_bfd,