_bfd_mips_elf_lo16_reloc vallo comment

Message ID ZFGkCFaFJysW0kh1@squeak.grove.modra.org
State Accepted
Headers
Series _bfd_mips_elf_lo16_reloc vallo comment |

Checks

Context Check Description
snail/binutils-gdb-check success Github commit url

Commit Message

Alan Modra May 3, 2023, midnight UTC
  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.
  

Patch

diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index c9cd2f8099f..49355a42f7d 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -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,