diff options
author | Jozef Lawrynowicz <jozef.l@mittosystems.com> | 2020-09-08 16:13:48 +0100 |
---|---|---|
committer | Jozef Lawrynowicz <jozef.l@mittosystems.com> | 2020-09-08 16:18:38 +0100 |
commit | 7d81bc937cd3949fc3bed8194646d3a4563f94b2 (patch) | |
tree | 6d1300169968fa3dae4c85e0c633f27ed61cb5ca /bfd/elf32-msp430.c | |
parent | f1363b0fb4eb8bbe9ef08f1e78ff6ffa71e07b8b (diff) | |
download | gdb-7d81bc937cd3949fc3bed8194646d3a4563f94b2.zip gdb-7d81bc937cd3949fc3bed8194646d3a4563f94b2.tar.gz gdb-7d81bc937cd3949fc3bed8194646d3a4563f94b2.tar.bz2 |
MSP430: Support relocations for subtract expressions in .uleb128 directives
Link-time relaxations of branches are common for MSP430, given that GCC
can generate pessimal branch instructions, and the
-mcode-region=either/-mdata-region=either options to shuffle sections
can further change the type of branch instruction required.
These relaxations can result in invalid code when .uleb128
directives, used in the .gcc_except_table section, are used to calculate
the distance between two labels. A value for the .uleb128 directive is
calculated at assembly-time, and can't be updated at link-time, even if
relaxation causes the distance between the labels to change.
This patch adds relocations for subtract expressions in .uleb128
directives, to allow the linker to re-calculate the value of these
expressions after relaxation has been performed.
bfd/ChangeLog:
* bfd-in2.h (bfd_reloc_code_real): Add
BFD_RELOC_MSP430_{SET,SUB}_ULEB128.
* elf32-msp430.c (msp430_elf_ignore_reloc): New.
(elf_msp430_howto_table): Add R_MSP430{,X}_GNU_{SET,SUB}_ULEB128.
(msp430_reloc_map): Add R_MSP430_GNU_{SET,SUB}_ULEB128.
(msp430x_reloc_map): Add R_MSP430X_GNU_{SET,SUB}_ULEB128.
(write_uleb128): New.
(msp430_final_link_relocate): Handle R_MSP430{,X}_GNU_{SET,SUB}_ULEB128.
* libbfd.c (_bfd_write_unsigned_leb128): New.
* libbfd.h (_bfd_write_unsigned_leb128): New prototype.
Add BFD_RELOC_MSP430_{SET,SUB}_ULEB128.
* reloc.c: Document BFD_RELOC_MSP430_{SET,SUB}_ULEB128.
binutils/ChangeLog:
* readelf.c (target_specific_reloc_handling): Handle
R_MSP430{,X}_GNU_{SET,SUB}_ULEB128.
gas/ChangeLog:
* config/tc-msp430.c (msp430_insert_uleb128_fixes): New.
(msp430_md_end): Call msp430_insert_uleb128_fixes.
include/ChangeLog:
* elf/msp430.h (elf_msp430_reloc_type): Add
R_MSP430_GNU_{SET,SUB}_ULEB128.
(elf_msp430x_reloc_type): Add R_MSP430X_GNU_{SET,SUB}_ULEB128.
ld/ChangeLog:
* testsuite/ld-msp430-elf/msp430-elf.exp: Run new tests.
* testsuite/ld-msp430-elf/uleb128.s: New test.
* testsuite/ld-msp430-elf/uleb128_430.d: New test.
* testsuite/ld-msp430-elf/uleb128_430x.d: New test.
Diffstat (limited to 'bfd/elf32-msp430.c')
-rw-r--r-- | bfd/elf32-msp430.c | 153 |
1 files changed, 146 insertions, 7 deletions
diff --git a/bfd/elf32-msp430.c b/bfd/elf32-msp430.c index 59e54ec..fd1922b 100644 --- a/bfd/elf32-msp430.c +++ b/bfd/elf32-msp430.c @@ -56,6 +56,20 @@ rl78_sym_diff_handler (bfd * abfd, return bfd_reloc_continue; } +/* Special handler for relocations which don't have to be relocated. + This function just simply returns bfd_reloc_ok. */ +static bfd_reloc_status_type +msp430_elf_ignore_reloc (bfd *abfd ATTRIBUTE_UNUSED, arelent *reloc_entry, + asymbol *symbol ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED, asection *input_section, + bfd *output_bfd, char **error_message ATTRIBUTE_UNUSED) +{ + if (output_bfd != NULL) + reloc_entry->address += input_section->output_offset; + + return bfd_reloc_ok; +} + static reloc_howto_type elf_msp430_howto_table[] = { HOWTO (R_MSP430_NONE, /* type */ @@ -220,7 +234,40 @@ static reloc_howto_type elf_msp430_howto_table[] = FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ - FALSE) /* pcrel_offset */ + FALSE), /* pcrel_offset */ + + /* The length of unsigned-leb128 is variable, just assume the + size is one byte here. */ + HOWTO (R_MSP430_GNU_SET_ULEB128, /* type */ + 0, /* rightshift */ + 0, /* size */ + 0, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + msp430_elf_ignore_reloc, /* special handler. */ + "R_MSP430_GNU_SET_ULEB128", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* The length of unsigned-leb128 is variable, just assume the + size is one byte here. */ + HOWTO (R_MSP430_GNU_SUB_ULEB128, /* type */ + 0, /* rightshift */ + 0, /* size */ + 0, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + msp430_elf_ignore_reloc, /* special handler. */ + "R_MSP430_GNU_SUB_ULEB128", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + FALSE), /* pcrel_offset */ + }; static reloc_howto_type elf_msp430x_howto_table[] = @@ -523,7 +570,40 @@ static reloc_howto_type elf_msp430x_howto_table[] = FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ - FALSE) /* pcrel_offset */ + FALSE), /* pcrel_offset */ + + /* The length of unsigned-leb128 is variable, just assume the + size is one byte here. */ + HOWTO (R_MSP430X_GNU_SET_ULEB128, /* type */ + 0, /* rightshift */ + 0, /* size */ + 0, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + msp430_elf_ignore_reloc, /* special handler. */ + "R_MSP430X_GNU_SET_ULEB128", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* The length of unsigned-leb128 is variable, just assume the + size is one byte here. */ + HOWTO (R_MSP430X_GNU_SUB_ULEB128, /* type */ + 0, /* rightshift */ + 0, /* size */ + 0, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + msp430_elf_ignore_reloc, /* special handler. */ + "R_MSP430X_GNU_SUB_ULEB128", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + FALSE), /* pcrel_offset */ + }; /* Map BFD reloc types to MSP430 ELF reloc types. */ @@ -547,7 +627,9 @@ static const struct msp430_reloc_map msp430_reloc_map[] = {BFD_RELOC_MSP430_2X_PCREL, R_MSP430_2X_PCREL}, {BFD_RELOC_MSP430_RL_PCREL, R_MSP430_RL_PCREL}, {BFD_RELOC_8, R_MSP430_8}, - {BFD_RELOC_MSP430_SYM_DIFF, R_MSP430_SYM_DIFF} + {BFD_RELOC_MSP430_SYM_DIFF, R_MSP430_SYM_DIFF}, + {BFD_RELOC_MSP430_SET_ULEB128, R_MSP430_GNU_SET_ULEB128 }, + {BFD_RELOC_MSP430_SUB_ULEB128, R_MSP430_GNU_SUB_ULEB128 } }; static const struct msp430_reloc_map msp430x_reloc_map[] = @@ -573,7 +655,9 @@ static const struct msp430_reloc_map msp430x_reloc_map[] = {BFD_RELOC_MSP430_10_PCREL, R_MSP430X_10_PCREL}, {BFD_RELOC_MSP430_2X_PCREL, R_MSP430X_2X_PCREL}, {BFD_RELOC_MSP430_RL_PCREL, R_MSP430X_PCR16}, - {BFD_RELOC_MSP430_SYM_DIFF, R_MSP430X_SYM_DIFF} + {BFD_RELOC_MSP430_SYM_DIFF, R_MSP430X_SYM_DIFF}, + {BFD_RELOC_MSP430_SET_ULEB128, R_MSP430X_GNU_SET_ULEB128 }, + {BFD_RELOC_MSP430_SUB_ULEB128, R_MSP430X_GNU_SUB_ULEB128 } }; static inline bfd_boolean @@ -755,6 +839,9 @@ msp430_final_link_relocate (reloc_howto_type * howto, if (uses_msp430x_relocs (input_bfd)) switch (howto->type) { + case R_MSP430X_GNU_SET_ULEB128: + relocation += (!is_rel_reloc ? rel->r_addend : 0); + /* Fall through. */ case R_MSP430_ABS32: /* If we are computing a 32-bit value for the location lists and the result is 0 then we add one to the value. A zero @@ -780,6 +867,9 @@ msp430_final_link_relocate (reloc_howto_type * howto, else switch (howto->type) { + case R_MSP430_GNU_SET_ULEB128: + relocation += (!is_rel_reloc ? rel->r_addend : 0); + /* Fall through. */ case R_MSP430_32: case R_MSP430_16: case R_MSP430_16_BYTE: @@ -794,16 +884,63 @@ msp430_final_link_relocate (reloc_howto_type * howto, sym_diff_section = NULL; } - if (uses_msp430x_relocs (input_bfd)) + if ((uses_msp430x_relocs (input_bfd) + && howto->type == R_MSP430X_GNU_SET_ULEB128) + || (!uses_msp430x_relocs (input_bfd) + && howto->type == R_MSP430_GNU_SET_ULEB128)) + { + unsigned int len, new_len = 0; + bfd_byte *endp, *p; + unsigned int val = relocation; + + _bfd_read_unsigned_leb128 (input_bfd, contents + rel->r_offset, &len); + + /* Clean the contents value to zero. Do not reduce the length. */ + p = contents + rel->r_offset; + endp = (p + len) - 1; + memset (p, 0x80, len - 1); + *(endp) = 0; + + /* Get the length of the new uleb128 value. */ + do + { + new_len++; + val >>= 7; + } while (val); + + if (new_len > len) + { + _bfd_error_handler + (_("error: final size of uleb128 value at offset 0x%lx in %pA " + "from %pB exceeds available space"), + (long) rel->r_offset, input_section, input_bfd); + } + else + { + /* If the number of bytes required to store the new value has + decreased, "right align" the new value within the available space, + so the MSB side is padded with uleb128 zeros (0x80). */ + p = _bfd_write_unsigned_leb128 (p + (len - new_len), endp, + relocation); + /* We checked there is enough space for the new value above, so this + should never be NULL. */ + BFD_ASSERT (p); + } + + return bfd_reloc_ok; + } + else if (uses_msp430x_relocs (input_bfd)) switch (howto->type) { case R_MSP430X_SYM_DIFF: + case R_MSP430X_GNU_SUB_ULEB128: /* Cache the input section and value. The offset is unreliable, since relaxation may have reduced the following reloc's offset. */ BFD_ASSERT (! is_rel_reloc); sym_diff_section = input_section; - sym_diff_value = relocation; + sym_diff_value = relocation + (howto->type == R_MSP430X_GNU_SUB_ULEB128 + ? rel->r_addend : 0); return bfd_reloc_ok; case R_MSP430_ABS16: @@ -1254,11 +1391,13 @@ msp430_final_link_relocate (reloc_howto_type * howto, break; case R_MSP430_SYM_DIFF: + case R_MSP430_GNU_SUB_ULEB128: /* Cache the input section and value. The offset is unreliable, since relaxation may have reduced the following reloc's offset. */ sym_diff_section = input_section; - sym_diff_value = relocation; + sym_diff_value = relocation + (howto->type == R_MSP430_GNU_SUB_ULEB128 + ? rel->r_addend : 0); return bfd_reloc_ok; default: |