aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf32-msp430.c
diff options
context:
space:
mode:
authorJozef Lawrynowicz <jozef.l@mittosystems.com>2020-09-08 16:13:48 +0100
committerJozef Lawrynowicz <jozef.l@mittosystems.com>2020-09-08 16:18:38 +0100
commit7d81bc937cd3949fc3bed8194646d3a4563f94b2 (patch)
tree6d1300169968fa3dae4c85e0c633f27ed61cb5ca /bfd/elf32-msp430.c
parentf1363b0fb4eb8bbe9ef08f1e78ff6ffa71e07b8b (diff)
downloadgdb-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.c153
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: