aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf32-msp430.c
diff options
context:
space:
mode:
authorJozef Lawrynowicz <jozef.l@mittosystems.com>2020-02-10 20:33:21 +0000
committerJozef Lawrynowicz <jozef.l@mittosystems.com>2020-02-10 20:35:52 +0000
commit8d6cb116f47b5edb2a7c2fa540fcb9dbcd3effc2 (patch)
treed2a49f50c1a879aad09dee29a966b3970a67ae78 /bfd/elf32-msp430.c
parentd60f54486a99e6b702cccdb16b3429f3524021fd (diff)
downloadbinutils-8d6cb116f47b5edb2a7c2fa540fcb9dbcd3effc2.zip
binutils-8d6cb116f47b5edb2a7c2fa540fcb9dbcd3effc2.tar.gz
binutils-8d6cb116f47b5edb2a7c2fa540fcb9dbcd3effc2.tar.bz2
MSP430: Enable relaxation of relocs in JMP instructions
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.
Diffstat (limited to 'bfd/elf32-msp430.c')
-rw-r--r--bfd/elf32-msp430.c96
1 files changed, 66 insertions, 30 deletions
diff --git a/bfd/elf32-msp430.c b/bfd/elf32-msp430.c
index 93524dc..67ad958 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);
}