[binutils-gdb] ALPHA_R_OP_STORE

Alan Modra amodra@sourceware.org
Mon May 26 23:52:51 GMT 2025


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=998d4443ea1e7da6ffa86abc95502152fda80f35

commit 998d4443ea1e7da6ffa86abc95502152fda80f35
Author: Alan Modra <amodra@gmail.com>
Date:   Mon May 26 11:46:05 2025 +0930

    ALPHA_R_OP_STORE
    
    In commit db4ab410dec3 I rewrote OP_STORE handling to support writing
    near the end of a section.  The rewrite had some bugs, fixed in commit
    3e02c4891dcb.  However I wasn't entirely happy with the code writing
    the bitfield:
    - it doesn't support 64-bit fields with a bit offset,
    - the code is duplicated and inelegant,
    - the stack ought to be popped whenever seeing one of these relocs,
      even if the reloc can't be applied.
    This patch fixes all of the above.
    
    In addition, it is clear from the OP_STORE description in the ABI that
    a 64-bit field is encoded as 0 in r_size, so I've decoded that in
    alpha_ecoff_swap_reloc_in.  The aborts there are not appropriate as
    they can be triggered by user input (fuzzed object files).  Also,
    stack underflow wasn't checked in alpha_relocate_section.
    
            * coff-alpha.c (alpha_ecoff_swap_reloc_in): Replace aborts
            with asserts.  Decode ALPHA_R_OP_STORE r_size of zero.
            (write_bit_field): New function.
            (alpha_ecoff_get_relocated_section_contents): Use it.
            (alpha_relocate_section): Here too.  Catch stack underflow.

Diff:
---
 bfd/coff-alpha.c | 117 +++++++++++++++++++++++++++++--------------------------
 1 file changed, 62 insertions(+), 55 deletions(-)

diff --git a/bfd/coff-alpha.c b/bfd/coff-alpha.c
index 60d85bbf707..fcc8aef6673 100644
--- a/bfd/coff-alpha.c
+++ b/bfd/coff-alpha.c
@@ -517,8 +517,7 @@ alpha_ecoff_swap_reloc_in (bfd *abfd,
 	 value is not actually a symbol index, but is instead a
 	 special code.  We put the code in the r_size field, and
 	 clobber the symndx.  */
-      if (intern->r_size != 0)
-	abort ();
+      BFD_ASSERT (intern->r_size == 0);
       intern->r_size = intern->r_symndx;
       intern->r_symndx = RELOC_SECTION_NONE;
     }
@@ -526,12 +525,16 @@ alpha_ecoff_swap_reloc_in (bfd *abfd,
     {
       /* The IGNORE reloc generally follows a GPDISP reloc, and is
 	 against the .lita section.  The section is irrelevant.  */
-      if (! intern->r_extern &&
-	  intern->r_symndx == RELOC_SECTION_ABS)
-	abort ();
+      BFD_ASSERT (intern->r_extern || intern->r_symndx != RELOC_SECTION_ABS);
       if (! intern->r_extern && intern->r_symndx == RELOC_SECTION_LITA)
 	intern->r_symndx = RELOC_SECTION_ABS;
     }
+  else if (intern->r_type == ALPHA_R_OP_STORE)
+    {
+      /* Size of 64 bits is encoded as 0 in this 6-bit field.  */
+      if (intern->r_size == 0)
+	intern->r_size = 64;
+    }
 }
 
 /* Swap a reloc out.  */
@@ -713,6 +716,50 @@ alpha_adjust_reloc_out (bfd *abfd ATTRIBUTE_UNUSED,
     }
 }
 
+/* Write VAL to a little-endian bitfield specified by BITOFFSET and
+   BITSIZE at CONTENTS + SECOFFSET.  Verify that these parameter are
+   valid for SEC in ABFD.  */
+
+static bool
+write_bit_field (bfd *abfd, asection *sec,
+		 bfd_byte *contents, bfd_size_type secoffset,
+		 unsigned int bitoffset, unsigned int bitsize, uint64_t val)
+{
+  if (bitsize == 0)
+    return true;
+
+  bfd_size_type secsize = bfd_get_section_limit_octets (abfd, sec);
+  unsigned int startbyte = bitoffset >> 3;
+  unsigned int endbyte = (bitoffset + bitsize - 1) >> 3;
+
+  if (secoffset > secsize || secsize - secoffset <= endbyte)
+    return false;
+
+  unsigned int startbit = bitoffset & 7;
+  unsigned int endbit = (bitoffset + bitsize - 1) & 7;
+  unsigned int mask = -1u << startbit;
+  unsigned char *p = contents + secoffset;
+  if (startbyte != endbyte)
+    {
+      p[startbyte] = (p[startbyte] & ~mask) | ((val << startbit) & mask);
+      val = val >> (8 - startbit);
+
+      for (unsigned int off = startbyte + 1; off < endbyte; ++off)
+	{
+	  p[off] = val;
+	  val >>= 8;
+	}
+      mask = ~(-1u << (1 + endbit));
+    }
+  else
+    {
+      val = val << startbit;
+      mask = mask & ~(-1u << (1 + endbit));
+    }
+  p[endbyte] = (p[endbyte] & ~mask) | (val & mask);
+  return true;
+}
+
 /* The size of the stack for the relocation evaluator.  */
 #define RELOC_STACKSIZE (10)
 
@@ -1005,30 +1052,10 @@ alpha_ecoff_get_relocated_section_contents (bfd *abfd,
 	       into the addend field by alpha_adjust_reloc_in.  */
 	    unsigned int offset = (rel->addend >> 8) & 0xff;
 	    unsigned int size = rel->addend & 0xff;
-	    unsigned int startbyte = offset >> 3;
-	    unsigned int endbyte = (offset + size + 7) >> 3;
-	    unsigned int bytes = endbyte - startbyte;
-
-	    if (bytes <= 8
-		&& rel->address + startbyte + bytes >= rel->address
-		&& (rel->address + startbyte + bytes
-		    <= bfd_get_section_limit_octets (input_bfd, input_section)))
-	      {
-		uint64_t val = 0;
-		for (int off = bytes - 1; off >= 0; --off)
-		  val = (val << 8) | data[rel->address + startbyte + off];
-
-		offset -= startbyte << 3;
-		uint64_t mask = (((uint64_t) 1 << size) - 1) << offset;
-		val = (val & ~mask) | ((stack[--tos] << offset) & mask);
-
-		for (unsigned int off = 0; off < bytes; ++off)
-		  {
-		    data[rel->address + startbyte + off] = val & 0xff;
-		    val >>= 8;
-		  }
-	      }
-	    else
+
+	    if (!write_bit_field (input_bfd, input_section,
+				  data, rel->address,
+				  offset, size, stack[--tos]))
 	      r = bfd_reloc_outofrange;
 	  }
 	  break;
@@ -1778,32 +1805,12 @@ alpha_relocate_section (bfd *output_bfd,
 	     adjust the address of the reloc.  */
 	  if (! bfd_link_relocatable (info))
 	    {
-	      unsigned int startbyte = r_offset >> 3;
-	      unsigned int endbyte = (r_offset + r_size + 7) >> 3;
-	      unsigned int bytes = endbyte - startbyte;
-
-	      if (bytes <= 8
-		  && r_vaddr >= input_section->vma
-		  && r_vaddr - input_section->vma < input_section->size
-		  && (input_section->size - (r_vaddr - input_section->vma)
-		      >= startbyte + bytes))
-		{
-		  bfd_byte *p = contents + (r_vaddr - input_section->vma);
-		  uint64_t val = 0;
-		  for (int off = bytes - 1; off >= 0; --off)
-		    val = (val << 8) | p[startbyte + off];
-
-		  r_offset -= startbyte << 3;
-		  uint64_t mask = (((uint64_t) 1 << r_size) - 1) << r_offset;
-		  val = (val & ~mask) | ((stack[--tos] << r_offset) & mask);
-
-		  for (unsigned int off = 0; off < bytes; ++off)
-		    {
-		      p[startbyte + off] = val & 0xff;
-		      val >>= 8;
-		    }
-		}
-	      else
+	      if (tos == 0)
+		r = bfd_reloc_notsupported;
+	      else if (!write_bit_field (input_bfd, input_section,
+					 contents,
+					 r_vaddr - input_section->vma,
+					 r_offset, r_size, stack[--tos]))
 		r = bfd_reloc_outofrange;
 	    }
 	  break;


More information about the Binutils-cvs mailing list