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.
This commit is contained in:
Jozef Lawrynowicz
2020-02-10 20:33:21 +00:00
parent d60f54486a
commit 8d6cb116f4
2 changed files with 75 additions and 30 deletions

View 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> 2020-02-10 Jozef Lawrynowicz <jozef.l@mittosystems.com>
* elf32-msp430.c (msp430_final_link_relocate): Add printf statements for * elf32-msp430.c (msp430_final_link_relocate): Add printf statements for

View File

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