/* MSP430-specific support for 32-bit ELF
- Copyright (C) 2002-2016 Free Software Foundation, Inc.
+ Copyright (C) 2002-2020 Free Software Foundation, Inc.
Contributed by Dmitry Diky <diwil@mail.ru>
This file is part of BFD, the Binary File Descriptor library.
#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
+
static bfd_reloc_status_type
rl78_sym_diff_handler (bfd * abfd,
arelent * reloc,
char ** error_message ATTRIBUTE_UNUSED)
{
bfd_size_type octets;
- octets = reloc->address * bfd_octets_per_byte (abfd);
+ octets = reloc->address * OCTETS_PER_BYTE (abfd, input_sec);
/* Catch the case where bfd_install_relocation would return
bfd_reloc_outofrange because the SYM_DIFF reloc is being used in a very
FALSE, /* partial_inplace */
0, /* src_mask */
0xffff, /* dst_mask */
- TRUE), /* pcrel_offset */
+ TRUE), /* pcrel_offset */
EMPTY_HOWTO (R_MSP430_EHTYPE),
FALSE, /* partial_inplace */
0x3ff, /* src_mask */
0x3ff, /* dst_mask */
- TRUE), /* pcrel_offset */
+ TRUE), /* pcrel_offset */
/* A 10 bit PC relative relocation for complicated polymorphs. */
HOWTO (R_MSP430X_2X_PCREL, /* type */
static const struct msp430_reloc_map msp430_reloc_map[] =
{
- {BFD_RELOC_NONE, R_MSP430_NONE},
- {BFD_RELOC_32, R_MSP430_32},
- {BFD_RELOC_MSP430_10_PCREL, R_MSP430_10_PCREL},
- {BFD_RELOC_16, R_MSP430_16_BYTE},
- {BFD_RELOC_MSP430_16_PCREL, R_MSP430_16_PCREL},
- {BFD_RELOC_MSP430_16, R_MSP430_16},
+ {BFD_RELOC_NONE, R_MSP430_NONE},
+ {BFD_RELOC_32, R_MSP430_32},
+ {BFD_RELOC_MSP430_10_PCREL, R_MSP430_10_PCREL},
+ {BFD_RELOC_16, R_MSP430_16_BYTE},
+ {BFD_RELOC_MSP430_16_PCREL, R_MSP430_16_PCREL},
+ {BFD_RELOC_MSP430_16, R_MSP430_16},
{BFD_RELOC_MSP430_16_PCREL_BYTE, R_MSP430_16_PCREL_BYTE},
- {BFD_RELOC_MSP430_16_BYTE, R_MSP430_16_BYTE},
- {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_16_BYTE, R_MSP430_16_BYTE},
+ {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}
};
static const struct msp430_reloc_map msp430x_reloc_map[] =
{
- {BFD_RELOC_NONE, R_MSP430_NONE},
- {BFD_RELOC_32, R_MSP430_ABS32},
- {BFD_RELOC_16, R_MSP430_ABS16},
- {BFD_RELOC_8, R_MSP430_ABS8},
- {BFD_RELOC_MSP430_ABS8, R_MSP430_ABS8},
+ {BFD_RELOC_NONE, R_MSP430_NONE},
+ {BFD_RELOC_32, R_MSP430_ABS32},
+ {BFD_RELOC_16, R_MSP430_ABS16},
+ {BFD_RELOC_8, R_MSP430_ABS8},
+ {BFD_RELOC_MSP430_ABS8, R_MSP430_ABS8},
{BFD_RELOC_MSP430X_PCR20_EXT_SRC, R_MSP430X_PCR20_EXT_SRC},
{BFD_RELOC_MSP430X_PCR20_EXT_DST, R_MSP430X_PCR20_EXT_DST},
{BFD_RELOC_MSP430X_PCR20_EXT_ODST, R_MSP430X_PCR20_EXT_ODST},
{BFD_RELOC_MSP430X_ABS20_EXT_ODST, R_MSP430X_ABS20_EXT_ODST},
{BFD_RELOC_MSP430X_ABS20_ADR_SRC, R_MSP430X_ABS20_ADR_SRC},
{BFD_RELOC_MSP430X_ABS20_ADR_DST, R_MSP430X_ABS20_ADR_DST},
- {BFD_RELOC_MSP430X_PCR16, R_MSP430X_PCR16},
+ {BFD_RELOC_MSP430X_PCR16, R_MSP430X_PCR16},
{BFD_RELOC_MSP430X_PCR20_CALL, R_MSP430X_PCR20_CALL},
- {BFD_RELOC_MSP430X_ABS16, R_MSP430X_ABS16},
- {BFD_RELOC_MSP430_ABS_HI16, R_MSP430_ABS_HI16},
- {BFD_RELOC_MSP430_PREL31, R_MSP430_PREL31},
- {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_MSP430X_ABS16, R_MSP430X_ABS16},
+ {BFD_RELOC_MSP430_ABS_HI16, R_MSP430_ABS_HI16},
+ {BFD_RELOC_MSP430_PREL31, R_MSP430_PREL31},
+ {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}
};
static inline bfd_boolean
/* Set the howto pointer for an MSP430 ELF reloc. */
-static void
-msp430_info_to_howto_rela (bfd * abfd ATTRIBUTE_UNUSED,
+static bfd_boolean
+msp430_info_to_howto_rela (bfd * abfd,
arelent * cache_ptr,
Elf_Internal_Rela * dst)
{
{
if (r_type >= (unsigned int) R_MSP430x_max)
{
- _bfd_error_handler (_("%B: invalid MSP430X reloc number: %d"), abfd, r_type);
- r_type = 0;
+ /* xgettext:c-format */
+ _bfd_error_handler (_("%pB: unsupported relocation type %#x"),
+ abfd, r_type);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
}
cache_ptr->howto = elf_msp430x_howto_table + r_type;
- return;
}
-
- if (r_type >= (unsigned int) R_MSP430_max)
+ else if (r_type >= (unsigned int) R_MSP430_max)
{
- _bfd_error_handler (_("%B: invalid MSP430 reloc number: %d"), abfd, r_type);
- r_type = 0;
+ /* xgettext:c-format */
+ _bfd_error_handler (_("%pB: unsupported relocation type %#x"),
+ abfd, r_type);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
}
- cache_ptr->howto = &elf_msp430_howto_table[r_type];
+ else
+ cache_ptr->howto = &elf_msp430_howto_table[r_type];
+
+ return TRUE;
}
/* Look through the relocs for a section during the first phase.
while (h->root.type == bfd_link_hash_indirect
|| h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
-
- /* PR15323, ref flags aren't set for references in the same
- object. */
- h->root.non_ir_ref = 1;
}
}
routines, but a few relocs, we have to do them ourselves. */
static bfd_reloc_status_type
-msp430_final_link_relocate (reloc_howto_type * howto,
- bfd * input_bfd,
- asection * input_section,
- bfd_byte * contents,
- Elf_Internal_Rela * rel,
- bfd_vma relocation,
+msp430_final_link_relocate (reloc_howto_type * howto,
+ bfd * input_bfd,
+ asection * input_section,
+ bfd_byte * contents,
+ Elf_Internal_Rela * rel,
+ bfd_vma relocation,
struct bfd_link_info * info)
{
static asection * sym_diff_section;
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);
{
info->callbacks->warning
(info,
- _("Try enabling relaxation to avoid relocation truncations"),
+ _("try enabling relaxation to avoid relocation truncations"),
NULL, input_bfd, input_section, relocation);
warned = TRUE;
}
{
info->callbacks->warning
(info,
- _("Try enabling relaxation to avoid relocation truncations"),
+ _("try enabling relaxation to avoid relocation truncations"),
NULL, input_bfd, input_section, relocation);
warned = TRUE;
}
name = bfd_elf_string_from_elf_section
(input_bfd, symtab_hdr->sh_link, sym->st_name);
- name = (name == NULL || * name == 0) ? bfd_section_name (input_bfd, sec) : name;
+ name = name == NULL || *name == 0 ? bfd_section_name (sec) : name;
}
else
{
switch (r)
{
case bfd_reloc_overflow:
- r = info->callbacks->reloc_overflow
+ (*info->callbacks->reloc_overflow)
(info, (h ? &h->root : NULL), name, howto->name,
- (bfd_vma) 0, input_bfd, input_section,
- rel->r_offset);
+ (bfd_vma) 0, input_bfd, input_section, rel->r_offset);
break;
case bfd_reloc_undefined:
- r = info->callbacks->undefined_symbol
- (info, name, input_bfd, input_section, rel->r_offset, TRUE);
+ (*info->callbacks->undefined_symbol)
+ (info, name, input_bfd, input_section, rel->r_offset, TRUE);
break;
case bfd_reloc_outofrange:
}
if (msg)
- r = info->callbacks->warning
- (info, msg, name, input_bfd, input_section, rel->r_offset);
-
- if (!r)
- return FALSE;
+ (*info->callbacks->warning) (info, msg, name, input_bfd,
+ input_section, rel->r_offset);
}
}
file. This gets the MSP430 architecture right based on the machine
number. */
-static void
-bfd_elf_msp430_final_write_processing (bfd * abfd,
- bfd_boolean linker ATTRIBUTE_UNUSED)
+static bfd_boolean
+bfd_elf_msp430_final_write_processing (bfd *abfd)
{
unsigned long val;
elf_elfheader (abfd)->e_machine = EM_MSP430;
elf_elfheader (abfd)->e_flags &= ~EF_MSP430_MACH;
elf_elfheader (abfd)->e_flags |= val;
+ return _bfd_elf_final_write_processing (abfd);
}
/* Set the right machine number. */
1. 3 words -> 1 word
- eq == jeq label jne +4; br lab
- ne != jne label jeq +4; br lab
- lt < jl label jge +4; br lab
- ltu < jlo label lhs +4; br lab
- ge >= jge label jl +4; br lab
- geu >= jhs label jlo +4; br lab
+ eq == jeq label jne +4; br lab
+ ne != jne label jeq +4; br lab
+ lt < jl label jge +4; br lab
+ ltu < jlo label lhs +4; br lab
+ ge >= jge label jl +4; br lab
+ geu >= jhs label jlo +4; br lab
2. 4 words -> 1 word
- ltn < jn jn +2; jmp +4; br lab
+ ltn < jn jn +2; jmp +4; br lab
3. 4 words -> 2 words
- gt > jeq +2; jge label jeq +6; jl +4; br label
- gtu > jeq +2; jhs label jeq +6; jlo +4; br label
+ gt > jeq +2; jge label jeq +6; jl +4; br label
+ gtu > jeq +2; jhs label jeq +6; jlo +4; br label
4. 4 words -> 2 words and 2 labels
- leu <= jeq label; jlo label jeq +2; jhs +4; br label
- le <= jeq label; jl label jeq +2; jge +4; br label
+ leu <= jeq label; jlo label jeq +2; jhs +4; br label
+ le <= jeq label; jl label jeq +2; jge +4; br label
=================================================================
codemap for first cases is (labels masked ):
int off; /* Offset from old label for new code. */
int ncl; /* New code length. */
} rcode[] =
-{/* lab,cdx,bs,off,ncl */
+{/* lab,cdx,bs,off,ncl */
{ 0x0000, 0x0000, 0x3c00, 0x0000, 1, 0, 2, 2, 2}, /* jump */
{ 0x0000, 0x2002, 0x2400, 0x0000, 1, 1, 4, 4, 2}, /* eq */
{ 0x0000, 0x2402, 0x2000, 0x0000, 1, 1, 4, 4, 2}, /* ne */
{ 0x2403, 0x2802, 0x2401, 0x2c00, 2, 2, 4, 6, 4}, /* gtu */
{ 0x2401, 0x2c02, 0x2400, 0x2800, 3, 2, 4, 6, 4}, /* leu , 2 labels */
{ 0x2401, 0x2c02, 0x2400, 0x2800, 3, 2, 4, 6, 4}, /* le , 2 labels */
- { 0, 0, 0, 0, 0, 0, 0, 0, 0}
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
/* Return TRUE if a symbol exists at the given address. */
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;
name = bfd_elf_string_from_elf_section
(abfd, symtab_hdr->sh_link, isym->st_name);
- name = (name == NULL || * name == 0) ? bfd_section_name (abfd, sec) : name;
+ name = name == NULL || *name == 0 ? bfd_section_name (sec) : name;
if (isym->st_shndx != sec_shndx)
continue;
&& (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
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;
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. */
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. */
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)
&& 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;
Elf_Internal_Rela * internal_relocs;
Elf_Internal_Rela * irel;
Elf_Internal_Rela * irelend;
- bfd_byte * contents = NULL;
+ bfd_byte * contents = NULL;
Elf_Internal_Sym * isymbuf = NULL;
/* Assume nothing changes. */
|| 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. */
/* 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++)
{
bfd_vma symval;
/* If this isn't something that can be relaxed, then ignore
- this reloc. */
+ this reloc. */
if (uses_msp430x_relocs (abfd)
- && ELF32_R_TYPE (irel->r_info) == (int) R_MSP430X_10_PCREL)
+ && ELF32_R_TYPE (irel->r_info) == (int) R_MSP430X_10_PCREL)
;
else if (! uses_msp430x_relocs (abfd)
- && ELF32_R_TYPE (irel->r_info) == (int) R_MSP430_10_PCREL)
+ && ELF32_R_TYPE (irel->r_info) == (int) R_MSP430_10_PCREL)
;
else
continue;
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
{
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
- contents, the section relocs, and the BFD symbol table. We
- must tell the rest of the code not to free up this
- information. It would be possible to instead create a table
- of changes which have to be made, as is done in coff-mips.c;
- that would be more work, but would require less memory when
- the linker is run. */
+ contents, the section relocs, and the BFD symbol table. We
+ must tell the rest of the code not to free up this
+ information. It would be possible to instead create a table
+ of changes which have to be made, as is done in coff-mips.c;
+ that would be more work, but would require less memory when
+ the linker is run. */
bfd_signed_vma value = symval;
int opcode;
value -= (sec->output_section->vma + sec->output_offset);
value -= irel->r_offset;
value -= 2;
+
/* Scale. */
value >>= 1;
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: */
+ 1: */
switch (opcode & 0xfc00)
{
case 0x3800: opcode = 0x3402; break; /* Jl -> Jge +2 */
case 0x3000: /* jn */
/* There is no direct inverse of the Jn insn.
FIXME: we could do this as:
- Jn 1f
- br 2f
+ Jn 1f
+ br 2f
1: br label
- 2: */
+ 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;
}
/* 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);
}
*again = TRUE;
}
+ if (debug_relocs)
+ printf (" trying code size shrinking relocs\n");
+
for (irel = internal_relocs; irel < irelend; irel++)
{
bfd_vma symval;
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
{
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
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))
{
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. */
/* Try to turn a 16-bit absolute branch into a 10-bit pc-relative
branch. */
- if (uses_msp430x_relocs (abfd)
- && ELF32_R_TYPE (irel->r_info) == R_MSP430X_ABS16)
+ if ((uses_msp430x_relocs (abfd)
+ && ELF32_R_TYPE (irel->r_info) == R_MSP430X_ABS16)
+ || (! uses_msp430x_relocs (abfd)
+ && ELF32_R_TYPE (irel->r_info) == R_MSP430_16))
{
bfd_vma value = symval;
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 ? */
{
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<cond> 1f
+ BR[A]
+ 1:
+ The opcode for J<cond> 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. */
return TRUE;
-error_return:
+ error_return:
if (isymbuf != NULL && symtab_hdr->contents != (unsigned char *) isymbuf)
free (isymbuf);
if (contents != NULL
elf32_msp430_obj_attrs_handle_unknown (bfd *abfd, int tag)
{
_bfd_error_handler
- (_("Warning: %B: Unknown MSPABI object attribute %d"),
+ /* xgettext:c-format */
+ (_("warning: %pB: unknown MSPABI object attribute %d"),
abfd, tag);
return TRUE;
}
}
}
-/* Merge MSPABI object attributes from IBFD into OBFD.
+/* Merge MSPABI and GNU object attributes from IBFD into OBFD.
Raise an error if there are conflicting attributes. */
static bfd_boolean
-elf32_msp430_merge_mspabi_attributes (bfd *ibfd, bfd *obfd)
+elf32_msp430_merge_msp430_attributes (bfd *ibfd, struct bfd_link_info *info)
{
- obj_attribute *in_attr;
- obj_attribute *out_attr;
+ bfd *obfd = info->output_bfd;
+ obj_attribute *in_msp_attr, *in_gnu_attr;
+ obj_attribute *out_msp_attr, *out_gnu_attr;
bfd_boolean result = TRUE;
static bfd * first_input_bfd = NULL;
if (ibfd->flags & BFD_LINKER_CREATED)
return TRUE;
+ /* LTO can create temporary files for linking which may not have an attribute
+ section. */
+ if (ibfd->lto_output
+ && bfd_get_section_by_name (ibfd, ".MSP430.attributes") == NULL)
+ return TRUE;
+
/* If this is the first real object just copy the attributes. */
if (!elf_known_obj_attributes_proc (obfd)[0].i)
{
_bfd_elf_copy_obj_attributes (ibfd, obfd);
- out_attr = elf_known_obj_attributes_proc (obfd);
+ out_msp_attr = elf_known_obj_attributes_proc (obfd);
/* Use the Tag_null value to indicate that
the attributes have been initialized. */
- out_attr[0].i = 1;
+ out_msp_attr[0].i = 1;
first_input_bfd = ibfd;
return TRUE;
}
- in_attr = elf_known_obj_attributes_proc (ibfd);
- out_attr = elf_known_obj_attributes_proc (obfd);
+ in_msp_attr = elf_known_obj_attributes_proc (ibfd);
+ out_msp_attr = elf_known_obj_attributes_proc (obfd);
+ in_gnu_attr = elf_known_obj_attributes (ibfd) [OBJ_ATTR_GNU];
+ out_gnu_attr = elf_known_obj_attributes (obfd) [OBJ_ATTR_GNU];
/* The ISAs must be the same. */
- if (in_attr[OFBA_MSPABI_Tag_ISA].i != out_attr[OFBA_MSPABI_Tag_ISA].i)
+ if (in_msp_attr[OFBA_MSPABI_Tag_ISA].i != out_msp_attr[OFBA_MSPABI_Tag_ISA].i)
{
_bfd_error_handler
- (_("error: %B uses %s instructions but %B uses %s"),
- ibfd, first_input_bfd,
- isa_type (in_attr[OFBA_MSPABI_Tag_ISA].i),
- isa_type (out_attr[OFBA_MSPABI_Tag_ISA].i));
+ /* xgettext:c-format */
+ (_("error: %pB uses %s instructions but %pB uses %s"),
+ ibfd, isa_type (in_msp_attr[OFBA_MSPABI_Tag_ISA].i),
+ first_input_bfd, isa_type (out_msp_attr[OFBA_MSPABI_Tag_ISA].i));
result = FALSE;
}
/* The code models must be the same. */
- if (in_attr[OFBA_MSPABI_Tag_Code_Model].i !=
- out_attr[OFBA_MSPABI_Tag_Code_Model].i)
+ if (in_msp_attr[OFBA_MSPABI_Tag_Code_Model].i
+ != out_msp_attr[OFBA_MSPABI_Tag_Code_Model].i)
{
_bfd_error_handler
- (_("error: %B uses the %s code model whereas %B uses the %s code model"),
- ibfd, first_input_bfd,
- code_model (in_attr[OFBA_MSPABI_Tag_Code_Model].i),
- code_model (out_attr[OFBA_MSPABI_Tag_Code_Model].i));
+ /* xgettext:c-format */
+ (_("error: %pB uses the %s code model whereas %pB uses the %s code model"),
+ ibfd, code_model (in_msp_attr[OFBA_MSPABI_Tag_Code_Model].i),
+ first_input_bfd,
+ code_model (out_msp_attr[OFBA_MSPABI_Tag_Code_Model].i));
result = FALSE;
}
/* The large code model is only supported by the MSP430X. */
- if (in_attr[OFBA_MSPABI_Tag_Code_Model].i == 2
- && out_attr[OFBA_MSPABI_Tag_ISA].i != 2)
+ if (in_msp_attr[OFBA_MSPABI_Tag_Code_Model].i == 2
+ && out_msp_attr[OFBA_MSPABI_Tag_ISA].i != 2)
{
_bfd_error_handler
- (_("error: %B uses the large code model but %B uses MSP430 instructions"),
+ /* xgettext:c-format */
+ (_("error: %pB uses the large code model but %pB uses MSP430 instructions"),
ibfd, first_input_bfd);
result = FALSE;
}
/* The data models must be the same. */
- if (in_attr[OFBA_MSPABI_Tag_Data_Model].i !=
- out_attr[OFBA_MSPABI_Tag_Data_Model].i)
+ if (in_msp_attr[OFBA_MSPABI_Tag_Data_Model].i
+ != out_msp_attr[OFBA_MSPABI_Tag_Data_Model].i)
{
_bfd_error_handler
- (_("error: %B uses the %s data model whereas %B uses the %s data model"),
- ibfd, first_input_bfd,
- data_model (in_attr[OFBA_MSPABI_Tag_Data_Model].i),
- data_model (out_attr[OFBA_MSPABI_Tag_Data_Model].i));
+ /* xgettext:c-format */
+ (_("error: %pB uses the %s data model whereas %pB uses the %s data model"),
+ ibfd, data_model (in_msp_attr[OFBA_MSPABI_Tag_Data_Model].i),
+ first_input_bfd,
+ data_model (out_msp_attr[OFBA_MSPABI_Tag_Data_Model].i));
result = FALSE;
}
/* The small code model requires the use of the small data model. */
- if (in_attr[OFBA_MSPABI_Tag_Code_Model].i == 1
- && out_attr[OFBA_MSPABI_Tag_Data_Model].i != 1)
+ if (in_msp_attr[OFBA_MSPABI_Tag_Code_Model].i == 1
+ && out_msp_attr[OFBA_MSPABI_Tag_Data_Model].i != 1)
{
_bfd_error_handler
- (_("error: %B uses the small code model but %B uses the %s data model"),
+ /* xgettext:c-format */
+ (_("error: %pB uses the small code model but %pB uses the %s data model"),
ibfd, first_input_bfd,
- data_model (out_attr[OFBA_MSPABI_Tag_Data_Model].i));
+ data_model (out_msp_attr[OFBA_MSPABI_Tag_Data_Model].i));
result = FALSE;
}
/* The large data models are only supported by the MSP430X. */
- if (in_attr[OFBA_MSPABI_Tag_Data_Model].i > 1
- && out_attr[OFBA_MSPABI_Tag_ISA].i != 2)
+ if (in_msp_attr[OFBA_MSPABI_Tag_Data_Model].i > 1
+ && out_msp_attr[OFBA_MSPABI_Tag_ISA].i != 2)
{
_bfd_error_handler
- (_("error: %B uses the %s data model but %B only uses MSP430 instructions"),
- ibfd, first_input_bfd,
- data_model (in_attr[OFBA_MSPABI_Tag_Data_Model].i));
+ /* xgettext:c-format */
+ (_("error: %pB uses the %s data model but %pB only uses MSP430 instructions"),
+ ibfd, data_model (in_msp_attr[OFBA_MSPABI_Tag_Data_Model].i),
+ first_input_bfd);
result = FALSE;
}
+ /* Just ignore the data region unless the large memory model is in use.
+ We have already checked that ibfd and obfd use the same memory model. */
+ if ((in_msp_attr[OFBA_MSPABI_Tag_Code_Model].i
+ == OFBA_MSPABI_Val_Code_Model_LARGE)
+ && (in_msp_attr[OFBA_MSPABI_Tag_Data_Model].i
+ == OFBA_MSPABI_Val_Data_Model_LARGE))
+ {
+ /* We cannot allow "lower region only" to be linked with any other
+ values (i.e. ANY or NONE).
+ Before this attribute existed, "ANY" region was the default. */
+ bfd_boolean ibfd_lower_region_used
+ = (in_gnu_attr[Tag_GNU_MSP430_Data_Region].i
+ == Val_GNU_MSP430_Data_Region_Lower);
+ bfd_boolean obfd_lower_region_used
+ = (out_gnu_attr[Tag_GNU_MSP430_Data_Region].i
+ == Val_GNU_MSP430_Data_Region_Lower);
+ if (ibfd_lower_region_used != obfd_lower_region_used)
+ {
+ _bfd_error_handler
+ (_("error: %pB can use the upper region for data, "
+ "but %pB assumes data is exclusively in lower memory"),
+ ibfd_lower_region_used ? obfd : ibfd,
+ ibfd_lower_region_used ? ibfd : obfd);
+ result = FALSE;
+ }
+ }
+
return result;
}
object file when linking. */
static bfd_boolean
-elf32_msp430_merge_private_bfd_data (bfd * ibfd, bfd * obfd)
+elf32_msp430_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
{
+ bfd *obfd = info->output_bfd;
/* Make sure that the machine number reflects the most
advanced version of the MSP architecture required. */
#define max(a,b) ((a) > (b) ? (a) : (b))
max (bfd_get_mach (ibfd), bfd_get_mach (obfd)));
#undef max
- return elf32_msp430_merge_mspabi_attributes (ibfd, obfd);
+ return elf32_msp430_merge_msp430_attributes (ibfd, info);
}
static bfd_boolean
}
static unsigned int
-elf32_msp430_eh_frame_address_size (bfd *abfd, asection *sec ATTRIBUTE_UNUSED)
+elf32_msp430_eh_frame_address_size (bfd *abfd,
+ const asection *sec ATTRIBUTE_UNUSED)
{
return uses_large_model (abfd) ? 4 : 2;
}
#define elf_backend_may_use_rela_p 1
#define elf_backend_default_use_rela_p 1
-#undef elf_backend_obj_attrs_vendor
+#undef elf_backend_obj_attrs_vendor
#define elf_backend_obj_attrs_vendor "mspabi"
-#undef elf_backend_obj_attrs_section
+#undef elf_backend_obj_attrs_section
#define elf_backend_obj_attrs_section ".MSP430.attributes"
-#undef elf_backend_obj_attrs_section_type
+#undef elf_backend_obj_attrs_section_type
#define elf_backend_obj_attrs_section_type SHT_MSP430_ATTRIBUTES
-#define elf_backend_section_from_shdr elf32_msp430_section_from_shdr
-#define elf_backend_obj_attrs_handle_unknown elf32_msp430_obj_attrs_handle_unknown
-#undef elf_backend_obj_attrs_arg_type
+#define elf_backend_section_from_shdr elf32_msp430_section_from_shdr
+#define elf_backend_obj_attrs_handle_unknown elf32_msp430_obj_attrs_handle_unknown
+#undef elf_backend_obj_attrs_arg_type
#define elf_backend_obj_attrs_arg_type elf32_msp430_obj_attrs_arg_type
#define bfd_elf32_bfd_merge_private_bfd_data elf32_msp430_merge_private_bfd_data
#define elf_backend_eh_frame_address_size elf32_msp430_eh_frame_address_size
#define ELF_MAXPAGESIZE 4
#define ELF_OSABI ELFOSABI_STANDALONE
-#define TARGET_LITTLE_SYM msp430_elf32_vec
+#define TARGET_LITTLE_SYM msp430_elf32_vec
#define TARGET_LITTLE_NAME "elf32-msp430"
-#define elf_info_to_howto msp430_info_to_howto_rela
-#define elf_info_to_howto_rel NULL
-#define elf_backend_relocate_section elf32_msp430_relocate_section
-#define elf_backend_check_relocs elf32_msp430_check_relocs
-#define elf_backend_can_gc_sections 1
+#define elf_info_to_howto msp430_info_to_howto_rela
+#define elf_info_to_howto_rel NULL
+#define elf_backend_relocate_section elf32_msp430_relocate_section
+#define elf_backend_check_relocs elf32_msp430_check_relocs
+#define elf_backend_can_gc_sections 1
#define elf_backend_final_write_processing bfd_elf_msp430_final_write_processing
#define elf_backend_object_p elf32_msp430_object_p
#define bfd_elf32_bfd_relax_section msp430_elf_relax_section
#define bfd_elf32_bfd_is_target_special_symbol msp430_elf_is_target_special_symbol
-#undef elf32_bed
+#undef elf32_bed
#define elf32_bed elf32_msp430_bed
#include "elf32-target.h"
/* The TI compiler sets the OSABI field to ELFOSABI_NONE. */
-#undef TARGET_LITTLE_SYM
-#define TARGET_LITTLE_SYM msp430_elf32_ti_vec
+#undef TARGET_LITTLE_SYM
+#define TARGET_LITTLE_SYM msp430_elf32_ti_vec
-#undef elf32_bed
+#undef elf32_bed
#define elf32_bed elf32_msp430_ti_bed
#undef ELF_OSABI
static const struct bfd_elf_special_section msp430_ti_elf_special_sections[] =
{
- /* prefix, prefix_length, suffix_len, type, attributes. */
+ /* prefix, prefix_length, suffix_len, type, attributes. */
{ STRING_COMMA_LEN (".TI.symbol.alias"), 0, SHT_MSP430_SYM_ALIASES, 0 },
{ STRING_COMMA_LEN (".TI.section.flags"), 0, SHT_MSP430_SEC_FLAGS, 0 },
{ STRING_COMMA_LEN ("_TI_build_attrib"), 0, SHT_MSP430_ATTRIBUTES, 0 },
- { NULL, 0, 0, 0, 0 }
+ { NULL, 0, 0, 0, 0 }
};
-#undef elf_backend_special_sections
-#define elf_backend_special_sections msp430_ti_elf_special_sections
+#undef elf_backend_special_sections
+#define elf_backend_special_sections msp430_ti_elf_special_sections
#include "elf32-target.h"