X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=bfd%2Felf32-msp430.c;h=9670213b2659e32be713a79483753f74a18ba25a;hb=48e5bada0aa8dacfbdee9700638fb20f5c17e55f;hp=eeed0c6fc040f0627e21870e58cd81488f65d814;hpb=bb2942085c83289c56cc3ef8b9ab0ecf6267efa3;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/elf32-msp430.c b/bfd/elf32-msp430.c index eeed0c6fc0..9670213b26 100644 --- a/bfd/elf32-msp430.c +++ b/bfd/elf32-msp430.c @@ -1,5 +1,5 @@ /* MSP430-specific support for 32-bit ELF - Copyright (C) 2002-2019 Free Software Foundation, Inc. + Copyright (C) 2002-2020 Free Software Foundation, Inc. Contributed by Dmitry Diky This file is part of BFD, the Binary File Descriptor library. @@ -26,6 +26,8 @@ #include "elf-bfd.h" #include "elf/msp430.h" +static bfd_boolean debug_relocs = 0; + /* All users of this file have bfd_octets_per_byte (abfd, sec) == 1. */ #define OCTETS_PER_BYTE(ABFD, SEC) 1 @@ -742,6 +744,10 @@ msp430_final_link_relocate (reloc_howto_type * howto, BFD_ASSERT (! is_rel_reloc || rel->r_addend == 0); } + if (debug_relocs) + printf ("writing relocation (%p) at 0x%lx type: %d\n", rel, + (long) (input_section->output_section->vma + input_section->output_offset + + rel->r_offset), howto->type); if (sym_diff_section != NULL) { BFD_ASSERT (sym_diff_section == input_section); @@ -1663,6 +1669,9 @@ msp430_elf_relax_delete_bytes (bfd * abfd, asection * sec, bfd_vma addr, contents = elf_section_data (sec)->this_hdr.contents; toaddr = sec->size; + if (debug_relocs) + printf (" deleting %d bytes between 0x%lx to 0x%lx\n", + count, (long) addr, (long) toaddr); irel = elf_section_data (sec)->relocs; irelend = irel + sec->reloc_count; @@ -1710,10 +1719,15 @@ msp430_elf_relax_delete_bytes (bfd * abfd, asection * sec, bfd_vma addr, && (CONST_STRNEQ (name, ".Letext") || CONST_STRNEQ (name, ".LFE"))))) { + if (debug_relocs) + printf (" adjusting value of local symbol %s from 0x%lx ", + name, (long) isym->st_value); if (isym->st_value < addr + count) isym->st_value = addr; else isym->st_value -= count; + if (debug_relocs) + printf ("to 0x%lx\n", (long) isym->st_value); } /* Adjust the function symbol's size as well. */ else if (ELF_ST_TYPE (isym->st_info) == STT_FUNC @@ -1754,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; @@ -1772,20 +1786,25 @@ msp430_elf_relax_add_two_words (bfd * abfd, asection * sec, bfd_vma addr, unsigned int symcount; bfd_vma sec_end; asection *p; + if (debug_relocs) + printf (" adding %d words at 0x%lx\n", num_words, + (long) (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. */ @@ -1794,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. */ @@ -1808,7 +1827,14 @@ msp430_elf_relax_add_two_words (bfd * abfd, asection * sec, bfd_vma addr, for (isymend = isym + symtab_hdr->sh_info; isym < isymend; isym++) if (isym->st_shndx == sec_shndx && isym->st_value >= addr && isym->st_value < sec_end) - isym->st_value += 4; + { + if (debug_relocs) + 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), + (long) isym->st_value, (long)(isym->st_value + num_bytes)); + isym->st_value += num_bytes; + } /* Now adjust the global symbols defined in this section. */ symcount = (symtab_hdr->sh_size / sizeof (Elf32_External_Sym) @@ -1824,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; @@ -1853,6 +1879,10 @@ msp430_elf_relax_section (bfd * abfd, asection * sec, || sec->reloc_count == 0 || (sec->flags & SEC_CODE) == 0) return TRUE; + if (debug_relocs) + printf ("Relaxing %s (%p), output_offset: 0x%lx sec size: 0x%lx\n", + sec->name, sec, (long) sec->output_offset, (long) sec->size); + symtab_hdr = & elf_tdata (abfd)->symtab_hdr; /* Get a copy of the native relocations. */ @@ -1864,6 +1894,8 @@ msp430_elf_relax_section (bfd * abfd, asection * sec, /* Walk through them looking for relaxing opportunities. */ irelend = internal_relocs + sec->reloc_count; + if (debug_relocs) + printf (" trying code size growing relocs\n"); /* Do code size growing relocs first. */ for (irel = internal_relocs; irel < irelend; irel++) { @@ -1920,6 +1952,15 @@ msp430_elf_relax_section (bfd * abfd, asection * sec, sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx); symval = (isym->st_value + sym_sec->output_section->vma + sym_sec->output_offset); + + if (debug_relocs) + printf (" processing reloc at 0x%lx for local sym: %s " + "st_value: 0x%lx adj value: 0x%lx\n", + (long) (sec->output_offset + sec->output_section->vma + + irel->r_offset), + bfd_elf_string_from_elf_section (abfd, symtab_hdr->sh_link, + isym->st_name), + (long) isym->st_value, (long) symval); } else { @@ -1941,6 +1982,13 @@ msp430_elf_relax_section (bfd * abfd, asection * sec, symval = (h->root.u.def.value + h->root.u.def.section->output_section->vma + h->root.u.def.section->output_offset); + if (debug_relocs) + printf (" processing reloc at 0x%lx for global sym: %s " + "st_value: 0x%lx adj value: 0x%lx\n", + (long) (sec->output_offset + sec->output_section->vma + + irel->r_offset), + h->root.root.string, (long) h->root.u.def.value, + (long) symval); } /* For simplicity of coding, we are going to modify the section @@ -1960,6 +2008,7 @@ msp430_elf_relax_section (bfd * abfd, asection * sec, value -= (sec->output_section->vma + sec->output_offset); value -= irel->r_offset; value -= 2; + /* Scale. */ value >>= 1; @@ -1971,8 +2020,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 label - into: + into: J 1f BR[A] #label 1: */ @@ -1992,8 +2045,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; } @@ -2009,27 +2068,58 @@ msp430_elf_relax_section (bfd * abfd, asection * sec, /* Insert the new branch instruction. */ if (uses_msp430x_relocs (abfd)) { - /* Insert an absolute branch (aka MOVA) instruction. */ - contents = msp430_elf_relax_add_two_words - (abfd, sec, irel->r_offset + 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; + if (debug_relocs) + 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. + 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; + } + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_MSP430X_ABS20_ADR_SRC); } else { - 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 (debug_relocs) + printf (" R_MSP430_10_PCREL -> R_MSP430_16 " + "(growing with new opcode 0x%x)\n", opcode); + 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); } @@ -2039,6 +2129,9 @@ msp430_elf_relax_section (bfd * abfd, asection * sec, *again = TRUE; } + if (debug_relocs) + printf (" trying code size shrinking relocs\n"); + for (irel = internal_relocs; irel < irelend; irel++) { bfd_vma symval; @@ -2083,6 +2176,15 @@ msp430_elf_relax_section (bfd * abfd, asection * sec, sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx); symval = (isym->st_value + sym_sec->output_section->vma + sym_sec->output_offset); + + if (debug_relocs) + printf (" processing reloc at 0x%lx for local sym: %s " + "st_value: 0x%lx adj value: 0x%lx\n", + (long) (sec->output_offset + sec->output_section->vma + + irel->r_offset), + bfd_elf_string_from_elf_section + (abfd, symtab_hdr->sh_link, isym->st_name), + (long) isym->st_value, (long) symval); } else { @@ -2104,6 +2206,13 @@ msp430_elf_relax_section (bfd * abfd, asection * sec, symval = (h->root.u.def.value + h->root.u.def.section->output_section->vma + h->root.u.def.section->output_offset); + if (debug_relocs) + printf (" processing reloc at 0x%lx for global sym: %s " + "st_value: 0x%lx adj value: 0x%lx\n", (long) + (sec->output_offset + sec->output_section->vma + + irel->r_offset), + h->root.root.string, (long) h->root.u.def.value, + (long) symval); } /* For simplicity of coding, we are going to modify the section @@ -2187,6 +2296,8 @@ msp430_elf_relax_section (bfd * abfd, asection * sec, elf_section_data (sec)->this_hdr.contents = contents; symtab_hdr->contents = (unsigned char *) isymbuf; + if (debug_relocs) + printf (" R_MSP430_RL_PCREL -> "); /* Fix the relocation's type. */ if (uses_msp430x_relocs (abfd)) { @@ -2200,11 +2311,21 @@ msp430_elf_relax_section (bfd * abfd, asection * sec, else { if (rx->labels == 3) /* Handle special cases. */ - irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), - R_MSP430_2X_PCREL); + { + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + R_MSP430_2X_PCREL); + if (debug_relocs) + printf ("R_MSP430_2X_PCREL (shrinking with new opcode" + " 0x%x)\n", rx->t0); + } else - irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), - R_MSP430_10_PCREL); + { + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + R_MSP430_10_PCREL); + if (debug_relocs) + printf ("R_MSP430_10_PCREL (shrinking with new opcode" + " 0x%x)\n", rx->t0); + } } /* Fix the opcode right way. */ @@ -2247,11 +2368,11 @@ msp430_elf_relax_section (bfd * abfd, asection * sec, able to relax. */ if ((long) value < 1016 && (long) value > -1016) { - int code2; + int code1, code2, opcode; /* Get the opcode. */ code2 = bfd_get_16 (abfd, contents + irel->r_offset - 2); - if (code2 != 0x4030) + if (code2 != 0x4030) /* BR -> JMP */ continue; /* FIXME: check r4 and r3 ? */ /* FIXME: Handle 0x4010 as well ? */ @@ -2266,21 +2387,75 @@ msp430_elf_relax_section (bfd * abfd, asection * sec, { irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_MSP430X_10_PCREL); + if (debug_relocs) + printf (" R_MSP430X_16 -> R_MSP430X_10_PCREL "); } else { irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_MSP430_10_PCREL); + if (debug_relocs) + printf (" R_MSP430_16 -> R_MSP430_10_PCREL "); } + /* If we're trying to shrink a BR[A] after previously having + grown a JMP for this reloc, then we have a sequence like + this: + J 1f + BR[A] + 1: + The opcode for J has the target hard-coded as 2 words + ahead of the insn, instead of using a reloc. + This means we cannot rely on any of the helper functions to + update this hard-coded jump destination if we remove the + BR[A] insn, so we must explicitly update it here. + This does mean that we can remove the entire branch + instruction, and invert the conditional jump, saving us 4 + bytes rather than only 2 if we detected this in the normal + way. */ + code1 = bfd_get_16 (abfd, contents + irel->r_offset - 4); + switch (code1) + { + case 0x3802: opcode = 0x3401; break; /* Jl +2 -> Jge +1 */ + case 0x3402: opcode = 0x3801; break; /* Jge +2 -> Jl +1 */ + case 0x2c02: opcode = 0x2801; break; /* Jhs +2 -> Jlo +1 */ + case 0x2802: opcode = 0x2c01; break; /* Jlo +2 -> Jhs +1 */ + case 0x2402: opcode = 0x2001; break; /* Jeq +2 -> Jne +1 */ + case 0x2002: opcode = 0x2401; break; /* jne +2 -> Jeq +1 */ + case 0x3002: /* jn +2 */ + /* FIXME: There is no direct inverse of the Jn insn. */ + continue; + default: + /* The previous opcode does not have a hard-coded jump + that we added when previously relaxing, so relax the + current branch as normal. */ + opcode = 0x3c00; + break; + } + if (debug_relocs) + printf ("(shrinking with new opcode 0x%x)\n", opcode); - /* Fix the opcode right way. */ - bfd_put_16 (abfd, 0x3c00, contents + irel->r_offset - 2); - irel->r_offset -= 2; - - /* Delete bytes. */ - if (!msp430_elf_relax_delete_bytes (abfd, sec, - irel->r_offset + 2, 2)) - goto error_return; + if (opcode != 0x3c00) + { + /* Invert the opcode of the conditional jump. */ + bfd_put_16 (abfd, opcode, contents + irel->r_offset - 4); + irel->r_offset -= 4; + + /* Delete 4 bytes - the full BR insn. */ + if (!msp430_elf_relax_delete_bytes (abfd, sec, + irel->r_offset + 2, 4)) + goto error_return; + } + else + { + /* Fix the opcode right way. */ + bfd_put_16 (abfd, opcode, contents + irel->r_offset - 2); + irel->r_offset -= 2; + + /* Delete bytes. */ + if (!msp430_elf_relax_delete_bytes (abfd, sec, + irel->r_offset + 2, 2)) + goto error_return; + } /* That will change things, so, we should relax again. Note that this is not required, and it may be slow. */ @@ -2318,7 +2493,7 @@ msp430_elf_relax_section (bfd * abfd, asection * sec, return TRUE; -error_return: + error_return: if (isymbuf != NULL && symtab_hdr->contents != (unsigned char *) isymbuf) free (isymbuf); if (contents != NULL