diff options
author | Jozef Lawrynowicz <jozef.l@mittosystems.com> | 2020-02-10 20:33:21 +0000 |
---|---|---|
committer | Jozef Lawrynowicz <jozef.l@mittosystems.com> | 2020-02-10 20:35:52 +0000 |
commit | 8d6cb116f47b5edb2a7c2fa540fcb9dbcd3effc2 (patch) | |
tree | d2a49f50c1a879aad09dee29a966b3970a67ae78 /bfd/elf32-msp430.c | |
parent | d60f54486a99e6b702cccdb16b3429f3524021fd (diff) | |
download | binutils-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.c | 96 |
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); } |