MSP430: Enable relaxation of relocs in JMP instructions
authorJozef Lawrynowicz <jozef.l@mittosystems.com>
Mon, 10 Feb 2020 20:33:21 +0000 (20:33 +0000)
committerJozef Lawrynowicz <jozef.l@mittosystems.com>
Mon, 10 Feb 2020 20:35:52 +0000 (20:35 +0000)
This patch fixes relocation overflows caused by an inability to relax
unconditional JMP instructions to BR instructions.

bfd/ChangeLog:

2020-02-10  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/ChangeLog
bfd/elf32-msp430.c

index 4dc98e95cf7ae0a51dfd3d3858454ca3e39b27d5..7f6be6295ba40e73dce1084bda54e50a8114979e 100644 (file)
@@ -1,3 +1,12 @@
+2020-02-10  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.
+
 2020-02-10  Jozef Lawrynowicz  <jozef.l@mittosystems.com>
 
        * elf32-msp430.c (msp430_final_link_relocate): Add printf statements for
index 93524dcd6aff22f3c394598accd3967dfc0b4c5c..67ad9584cebf329f3377303f2a42ebb54dfac5cc 100644 (file)
@@ -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);
        }