[2/3,BFD,MSP430] Enable relaxation of relocs in JMP instructions

Message ID 20200121203408.529e2d3c@jozef-kubuntu
State New
Headers show
Series
  • Fix relocation overflows for -mcpu=msp430
Related show

Commit Message

Jozef Lawrynowicz Jan. 21, 2020, 8:34 p.m.
This patch fixes relocation overflows caused by not being able to relax an
unconditional JMP instruction to a BR.

Patch

From 2f63031747c775a3a8be95e133d16430b085ac82 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Tue, 21 Jan 2020 12:02:23 +0000
Subject: [PATCH 2/3] MSP430: Enable relaxation of relocs in JMP instructions

bfd/ChangeLog:

2020-01-21  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

	* elf32-msp430.c (msp430_elf_relax_add_two_words): Rename to
	msp430_elf_relax_add_words. Support insertion of either one or two
	words.
	(msp430_elf_relax_section): Catch opcode of 0x3c00 when relocation
	needs to be grown. Handle insertion of branch instruction to replace
	jump.
---
 bfd/elf32-msp430.c | 96 +++++++++++++++++++++++++++++++---------------
 1 file changed, 66 insertions(+), 30 deletions(-)

diff --git a/bfd/elf32-msp430.c b/bfd/elf32-msp430.c
index 93524dcd6a..67ad9584ce 100644
--- a/bfd/elf32-msp430.c
+++ b/bfd/elf32-msp430.c
@@ -1768,11 +1768,11 @@  msp430_elf_relax_delete_bytes (bfd * abfd, asection * sec, bfd_vma addr,
   return TRUE;
 }
 
-/* Insert two words into a section whilst relaxing.  */
+/* Insert one or two words into a section whilst relaxing.  */
 
 static bfd_byte *
-msp430_elf_relax_add_two_words (bfd * abfd, asection * sec, bfd_vma addr,
-				int word1, int word2)
+msp430_elf_relax_add_words (bfd * abfd, asection * sec, bfd_vma addr,
+			    int num_words, int word1, int word2)
 {
   Elf_Internal_Shdr *symtab_hdr;
   unsigned int sec_shndx;
@@ -1787,22 +1787,24 @@  msp430_elf_relax_add_two_words (bfd * abfd, asection * sec, bfd_vma addr,
   bfd_vma sec_end;
   asection *p;
   if (debug_relocs)
-    printf ("      adding two words at 0x%lx\n",
+    printf ("      adding %d words at 0x%lx\n", num_words,
 	    sec->output_section->vma + sec->output_offset + addr);
 
   contents = elf_section_data (sec)->this_hdr.contents;
   sec_end = sec->size;
+  int num_bytes = num_words * 2;
 
   /* Make space for the new words.  */
-  contents = bfd_realloc (contents, sec_end + 4);
-  memmove (contents + addr + 4, contents + addr, sec_end - addr);
+  contents = bfd_realloc (contents, sec_end + num_bytes);
+  memmove (contents + addr + num_bytes, contents + addr, sec_end - addr);
 
   /* Insert the new words.  */
   bfd_put_16 (abfd, word1, contents + addr);
-  bfd_put_16 (abfd, word2, contents + addr + 2);
+  if (num_words == 2)
+    bfd_put_16 (abfd, word2, contents + addr + 2);
 
   /* Update the section information.  */
-  sec->size += 4;
+  sec->size += num_bytes;
   elf_section_data (sec)->this_hdr.contents = contents;
 
   /* Adjust all the relocs.  */
@@ -1811,12 +1813,12 @@  msp430_elf_relax_add_two_words (bfd * abfd, asection * sec, bfd_vma addr,
 
   for (; irel < irelend; irel++)
     if ((irel->r_offset >= addr && irel->r_offset < sec_end))
-      irel->r_offset += 4;
+      irel->r_offset += num_bytes;
 
   /* Adjust the local symbols defined in this section.  */
   sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec);
   for (p = abfd->sections; p != NULL; p = p->next)
-    msp430_elf_relax_adjust_locals (abfd, p, addr, -4,
+    msp430_elf_relax_adjust_locals (abfd, p, addr, -num_bytes,
 				    sec_shndx, sec_end);
 
   /* Adjust the global symbols affected by the move.  */
@@ -1830,8 +1832,8 @@  msp430_elf_relax_add_two_words (bfd * abfd, asection * sec, bfd_vma addr,
 	  printf ("      adjusting value of local symbol %s from 0x%lx to "
 		  "0x%lx\n", bfd_elf_string_from_elf_section
 		  (abfd, symtab_hdr->sh_link, isym->st_name),
-		  isym->st_value, isym->st_value + 4);
-	isym->st_value += 4;
+		  isym->st_value, isym->st_value + num_bytes);
+	isym->st_value += num_bytes;
       }
 
   /* Now adjust the global symbols defined in this section.  */
@@ -1848,7 +1850,7 @@  msp430_elf_relax_add_two_words (bfd * abfd, asection * sec, bfd_vma addr,
 	  && sym_hash->root.u.def.section == sec
 	  && sym_hash->root.u.def.value >= addr
 	  && sym_hash->root.u.def.value < sec_end)
-	sym_hash->root.u.def.value += 4;
+	sym_hash->root.u.def.value += num_bytes;
     }
 
   return contents;
@@ -2015,8 +2017,12 @@  msp430_elf_relax_section (bfd * abfd, asection * sec,
       opcode = bfd_get_16 (abfd, contents + irel->r_offset);
 
       /* Compute the new opcode.  We are going to convert:
+	 JMP label
+	   into:
+	 BR[A] label
+	   or
 	 J<cond> label
-	 into:
+	   into:
 	 J<inv-cond> 1f
 	 BR[A] #label
 	 1:			*/
@@ -2036,8 +2042,14 @@  msp430_elf_relax_section (bfd * abfd, asection * sec,
 	     1: br label
 	     2:		       */
 	  continue;
+	case 0x3c00:
+	  if (uses_msp430x_relocs (abfd))
+	    opcode = 0x0080;	/* JMP -> BRA  */
+	  else
+	    opcode = 0x4030;	/* JMP -> BR  */
+	  break;
 	default:
-	  /* Not a conditional branch instruction.  */
+	  /* Unhandled branch instruction.  */
 	  /* fprintf (stderr, "unrecog: %x\n", opcode); */
 	  continue;
 	}
@@ -2057,16 +2069,29 @@  msp430_elf_relax_section (bfd * abfd, asection * sec,
 	    printf ("      R_MSP430X_10_PCREL -> R_MSP430X_ABS20_ADR_SRC "
 		    "(growing with new opcode 0x%x)\n", opcode);
 
-	  /* Insert an absolute branch (aka MOVA) instruction.  */
-	  contents = msp430_elf_relax_add_two_words
-	    (abfd, sec, irel->r_offset + 2, 0x0080, 0x0000);
+	  /* Insert an absolute branch (aka MOVA) instruction.
+	     Note that bits 19:16 of the address are stored in the first word
+	     of the insn, so this is where r_offset will point to.  */
+	  if (opcode == 0x0080)
+	    {
+	      /* If we're inserting a BRA because we are converting from a JMP,
+		 then only add one word for destination address; the BRA opcode
+		 has already been written.  */
+	      contents = msp430_elf_relax_add_words
+		(abfd, sec, irel->r_offset + 2, 1, 0x0000, 0);
+	    }
+	  else
+	    {
+	      contents = msp430_elf_relax_add_words
+		(abfd, sec, irel->r_offset + 2, 2, 0x0080, 0x0000);
+	      /* Update the relocation to point to the inserted branch
+		 instruction.  Note - we are changing a PC-relative reloc
+		 into an absolute reloc, but this is OK because we have
+		 arranged with the assembler to have the reloc's value be
+		 a (local) symbol, not a section+offset value.  */
+	      irel->r_offset += 2;
+	    }
 
-	  /* Update the relocation to point to the inserted branch
-	     instruction.  Note - we are changing a PC-relative reloc
-	     into an absolute reloc, but this is OK because we have
-	     arranged with the assembler to have the reloc's value be
-	     a (local) symbol, not a section+offset value.  */
-	  irel->r_offset += 2;
 	  irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
 				       R_MSP430X_ABS20_ADR_SRC);
 	}
@@ -2075,12 +2100,23 @@  msp430_elf_relax_section (bfd * abfd, asection * sec,
 	  if (debug_relocs)
 	    printf ("      R_MSP430_10_PCREL -> R_MSP430_16 "
 		    "(growing with new opcode 0x%x)\n", opcode);
-	  contents = msp430_elf_relax_add_two_words
-	    (abfd, sec, irel->r_offset + 2, 0x4030, 0x0000);
-
-	  /* See comment above about converting a 10-bit PC-rel
-	     relocation into a 16-bit absolute relocation.  */
-	  irel->r_offset += 4;
+	  if (opcode == 0x4030)
+	    {
+	      /* If we're inserting a BR because we are converting from a JMP,
+		 then only add one word for destination address; the BR opcode
+		 has already been written.  */
+	      contents = msp430_elf_relax_add_words
+		(abfd, sec, irel->r_offset + 2, 1, 0x0000, 0);
+	      irel->r_offset += 2;
+	    }
+	  else
+	    {
+	      contents = msp430_elf_relax_add_words
+		(abfd, sec, irel->r_offset + 2, 2, 0x4030, 0x0000);
+	      /* See comment above about converting a 10-bit PC-rel
+		 relocation into a 16-bit absolute relocation.  */
+	      irel->r_offset += 4;
+	    }
 	  irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
 				       R_MSP430_16);
 	}
-- 
2.17.1