aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf32-msp430.c
diff options
context:
space:
mode:
Diffstat (limited to 'bfd/elf32-msp430.c')
-rw-r--r--bfd/elf32-msp430.c667
1 files changed, 569 insertions, 98 deletions
diff --git a/bfd/elf32-msp430.c b/bfd/elf32-msp430.c
index dfe33bd..ba59cf7 100644
--- a/bfd/elf32-msp430.c
+++ b/bfd/elf32-msp430.c
@@ -25,41 +25,6 @@
#include "elf-bfd.h"
#include "elf/msp430.h"
-static reloc_howto_type *bfd_elf32_bfd_reloc_type_lookup
- PARAMS ((bfd *, bfd_reloc_code_real_type));
-
-static void msp430_info_to_howto_rela
- PARAMS ((bfd *, arelent *, Elf_Internal_Rela *));
-
-static asection *elf32_msp430_gc_mark_hook
- PARAMS ((asection *, struct bfd_link_info *, Elf_Internal_Rela *,
- struct elf_link_hash_entry *, Elf_Internal_Sym *));
-
-static bfd_boolean elf32_msp430_gc_sweep_hook
- PARAMS ((bfd *, struct bfd_link_info *, asection *,
- const Elf_Internal_Rela *));
-
-static bfd_boolean elf32_msp430_check_relocs
- PARAMS ((bfd *, struct bfd_link_info *, asection *,
- const Elf_Internal_Rela *));
-
-static bfd_reloc_status_type msp430_final_link_relocate
- PARAMS ((reloc_howto_type *, bfd *, asection *, bfd_byte *,
- Elf_Internal_Rela *, bfd_vma));
-
-static bfd_boolean elf32_msp430_relocate_section
- PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
- Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
-
-static void bfd_elf_msp430_final_write_processing
- PARAMS ((bfd *, bfd_boolean));
-
-static bfd_boolean elf32_msp430_object_p
- PARAMS ((bfd *));
-
-static void elf32_msp430_post_process_headers
- PARAMS ((bfd *, struct bfd_link_info *));
-
/* Use RELA instead of REL. */
#undef USE_REL
@@ -71,7 +36,7 @@ static reloc_howto_type elf_msp430_howto_table[] =
32, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_bitfield, /* complain_on_overflow */
+ complain_overflow_bitfield,/* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MSP430_NONE", /* name */
FALSE, /* partial_inplace */
@@ -85,7 +50,7 @@ static reloc_howto_type elf_msp430_howto_table[] =
32, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_bitfield, /* complain_on_overflow */
+ complain_overflow_bitfield,/* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MSP430_32", /* name */
FALSE, /* partial_inplace */
@@ -100,7 +65,7 @@ static reloc_howto_type elf_msp430_howto_table[] =
10, /* bitsize */
TRUE, /* pc_relative */
0, /* bitpos */
- complain_overflow_bitfield, /* complain_on_overflow */
+ complain_overflow_bitfield,/* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_MSP430_13_PCREL", /* name */
FALSE, /* partial_inplace */
@@ -119,7 +84,7 @@ static reloc_howto_type elf_msp430_howto_table[] =
bfd_elf_generic_reloc, /* special_function */
"R_MSP430_16", /* name */
FALSE, /* partial_inplace */
- 0xffff, /* src_mask */
+ 0, /* src_mask */
0xffff, /* dst_mask */
FALSE), /* pcrel_offset */
@@ -134,7 +99,7 @@ static reloc_howto_type elf_msp430_howto_table[] =
bfd_elf_generic_reloc, /* special_function */
"R_MSP430_16_PCREL", /* name */
FALSE, /* partial_inplace */
- 0xffff, /* src_mask */
+ 0, /* src_mask */
0xffff, /* dst_mask */
TRUE), /* pcrel_offset */
@@ -162,10 +127,40 @@ static reloc_howto_type elf_msp430_howto_table[] =
0, /* bitpos */
complain_overflow_dont,/* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
- "R_MSP430_16_PCREL_BYTE", /* name */
+ "R_MSP430_16_PCREL_BYTE",/* name */
FALSE, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* A 13 bit PC relative relocation for complicated polymorphs. */
+ HOWTO (R_MSP430_2X_PCREL, /* type */
+ 1, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 10, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield,/* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_MSP430_2X_PCREL", /* name */
+ FALSE, /* partial_inplace */
+ 0xfff, /* src_mask */
+ 0xfff, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* A 16 bit relaxable relocation for command address. */
+ HOWTO (R_MSP430_RL_PCREL, /* type */
+ 1, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont,/* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_MSP430_RL_PCREL", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffff, /* dst_mask */
TRUE) /* pcrel_offset */
};
@@ -179,20 +174,21 @@ struct msp430_reloc_map
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_16_BYTE, R_MSP430_16_BYTE},
+ {BFD_RELOC_MSP430_2X_PCREL, R_MSP430_2X_PCREL},
+ {BFD_RELOC_MSP430_RL_PCREL, R_MSP430_RL_PCREL}
};
static reloc_howto_type *
-bfd_elf32_bfd_reloc_type_lookup (abfd, code)
- bfd *abfd ATTRIBUTE_UNUSED;
- bfd_reloc_code_real_type code;
+bfd_elf32_bfd_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED,
+ bfd_reloc_code_real_type code)
{
unsigned int i;
@@ -206,10 +202,9 @@ bfd_elf32_bfd_reloc_type_lookup (abfd, code)
/* Set the howto pointer for an MSP430 ELF reloc. */
static void
-msp430_info_to_howto_rela (abfd, cache_ptr, dst)
- bfd *abfd ATTRIBUTE_UNUSED;
- arelent *cache_ptr;
- Elf_Internal_Rela *dst;
+msp430_info_to_howto_rela (bfd * abfd ATTRIBUTE_UNUSED,
+ arelent * cache_ptr,
+ Elf_Internal_Rela * dst)
{
unsigned int r_type;
@@ -219,12 +214,11 @@ msp430_info_to_howto_rela (abfd, cache_ptr, dst)
}
static asection *
-elf32_msp430_gc_mark_hook (sec, info, rel, h, sym)
- asection *sec;
- struct bfd_link_info *info ATTRIBUTE_UNUSED;
- Elf_Internal_Rela *rel;
- struct elf_link_hash_entry *h;
- Elf_Internal_Sym *sym;
+elf32_msp430_gc_mark_hook (asection * sec,
+ struct bfd_link_info * info ATTRIBUTE_UNUSED,
+ Elf_Internal_Rela * rel,
+ struct elf_link_hash_entry * h,
+ Elf_Internal_Sym * sym)
{
if (h != NULL)
{
@@ -252,11 +246,10 @@ elf32_msp430_gc_mark_hook (sec, info, rel, h, sym)
}
static bfd_boolean
-elf32_msp430_gc_sweep_hook (abfd, info, sec, relocs)
- bfd *abfd ATTRIBUTE_UNUSED;
- struct bfd_link_info *info ATTRIBUTE_UNUSED;
- asection *sec ATTRIBUTE_UNUSED;
- const Elf_Internal_Rela *relocs ATTRIBUTE_UNUSED;
+elf32_msp430_gc_sweep_hook (bfd * abfd ATTRIBUTE_UNUSED,
+ struct bfd_link_info * info ATTRIBUTE_UNUSED,
+ asection * sec ATTRIBUTE_UNUSED,
+ const Elf_Internal_Rela * relocs ATTRIBUTE_UNUSED)
{
/* We don't use got and plt entries for msp430. */
return TRUE;
@@ -267,11 +260,8 @@ elf32_msp430_gc_sweep_hook (abfd, info, sec, relocs)
virtual table relocs for gc. */
static bfd_boolean
-elf32_msp430_check_relocs (abfd, info, sec, relocs)
- bfd *abfd;
- struct bfd_link_info *info;
- asection *sec;
- const Elf_Internal_Rela *relocs;
+elf32_msp430_check_relocs (bfd * abfd, struct bfd_link_info * info,
+ asection * sec, const Elf_Internal_Rela * relocs)
{
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes, **sym_hashes_end;
@@ -308,14 +298,9 @@ elf32_msp430_check_relocs (abfd, info, sec, relocs)
routines, but a few relocs, we have to do them ourselves. */
static bfd_reloc_status_type
-msp430_final_link_relocate (howto, input_bfd, input_section,
- contents, rel, relocation)
- 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)
{
bfd_reloc_status_type r = bfd_reloc_ok;
bfd_vma x;
@@ -347,7 +332,37 @@ msp430_final_link_relocate (howto, input_bfd, input_section,
bfd_put_16 (input_bfd, x, contents);
break;
+ case R_MSP430_2X_PCREL:
+ contents += rel->r_offset;
+ srel = (bfd_signed_vma) relocation;
+ srel += rel->r_addend;
+ srel -= rel->r_offset;
+ srel -= 2; /* Branch instructions add 2 to the PC... */
+ srel -= (input_section->output_section->vma +
+ input_section->output_offset);
+
+ if (srel & 1)
+ return bfd_reloc_outofrange;
+
+ /* MSP430 addresses commands as words. */
+ srel >>= 1;
+
+ /* Check for an overflow. */
+ if (srel < -512 || srel > 511)
+ return bfd_reloc_overflow;
+
+ x = bfd_get_16 (input_bfd, contents);
+ x = (x & 0xfc00) | (srel & 0x3ff);
+ bfd_put_16 (input_bfd, x, contents);
+ /* Handle second jump instruction. */
+ x = bfd_get_16 (input_bfd, contents - 2);
+ srel += 1;
+ x = (x & 0xfc00) | (srel & 0x3ff);
+ bfd_put_16 (input_bfd, x, contents - 2);
+ break;
+
case R_MSP430_16_PCREL:
+ case R_MSP430_RL_PCREL:
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation;
srel += rel->r_addend;
@@ -404,16 +419,14 @@ msp430_final_link_relocate (howto, input_bfd, input_section,
/* Relocate an MSP430 ELF section. */
static bfd_boolean
-elf32_msp430_relocate_section (output_bfd, info, input_bfd, input_section,
- contents, relocs, local_syms, local_sections)
- bfd *output_bfd ATTRIBUTE_UNUSED;
- struct bfd_link_info *info;
- bfd *input_bfd;
- asection *input_section;
- bfd_byte *contents;
- Elf_Internal_Rela *relocs;
- Elf_Internal_Sym *local_syms;
- asection **local_sections;
+elf32_msp430_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED,
+ struct bfd_link_info * info,
+ bfd * input_bfd,
+ asection * input_section,
+ bfd_byte * contents,
+ Elf_Internal_Rela * relocs,
+ Elf_Internal_Sym * local_syms,
+ asection ** local_sections)
{
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
@@ -520,9 +533,8 @@ elf32_msp430_relocate_section (output_bfd, info, input_bfd, input_section,
number. */
static void
-bfd_elf_msp430_final_write_processing (abfd, linker)
- bfd *abfd;
- bfd_boolean linker ATTRIBUTE_UNUSED;
+bfd_elf_msp430_final_write_processing (bfd * abfd,
+ bfd_boolean linker ATTRIBUTE_UNUSED)
{
unsigned long val;
@@ -594,8 +606,7 @@ bfd_elf_msp430_final_write_processing (abfd, linker)
/* Set the right machine number. */
static bfd_boolean
-elf32_msp430_object_p (abfd)
- bfd *abfd;
+elf32_msp430_object_p (bfd * abfd)
{
int e_set = bfd_mach_msp14;
@@ -669,11 +680,10 @@ elf32_msp430_object_p (abfd)
}
static void
-elf32_msp430_post_process_headers (abfd, link_info)
- bfd *abfd;
- struct bfd_link_info *link_info ATTRIBUTE_UNUSED;
+elf32_msp430_post_process_headers (bfd * abfd,
+ struct bfd_link_info * link_info ATTRIBUTE_UNUSED)
{
- Elf_Internal_Ehdr *i_ehdrp; /* ELF file header, internal form. */
+ Elf_Internal_Ehdr * i_ehdrp; /* ELF file header, internal form. */
i_ehdrp = elf_elfheader (abfd);
@@ -684,6 +694,466 @@ elf32_msp430_post_process_headers (abfd, link_info)
i_ehdrp->e_ident[EI_OSABI] = ELFOSABI_STANDALONE;
}
+/* These functions handle relaxing for the msp430.
+ Relaxation required only in two cases:
+ - Bad hand coding like jumps from one section to another or
+ from file to file.
+ - Sibling calls. This will affect onlu 'jump label' polymorph. Without
+ relaxing this enlarges code by 2 bytes. Sibcalls implemented but
+ do not work in gcc's port by the reason I do not know.
+ Anyway, if a relaxation required, user should pass -relax option to the
+ linker.
+
+ There are quite a few relaxing opportunities available on the msp430:
+
+ ================================================================
+
+ 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
+
+ 2. 4 words -> 1 word
+
+ 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
+
+ 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
+ =================================================================
+
+ codemap for first cases is (labels masked ):
+ eq: 0x2002,0x4010,0x0000 -> 0x2400
+ ne: 0x2402,0x4010,0x0000 -> 0x2000
+ lt: 0x3402,0x4010,0x0000 -> 0x3800
+ ltu: 0x2c02,0x4010,0x0000 -> 0x2800
+ ge: 0x3802,0x4010,0x0000 -> 0x3400
+ geu: 0x2802,0x4010,0x0000 -> 0x2c00
+
+ second case:
+ ltn: 0x3001,0x3c02,0x4010,0x0000 -> 0x3000
+
+ third case:
+ gt: 0x2403,0x3802,0x4010,0x0000 -> 0x2401,0x3400
+ gtu: 0x2403,0x2802,0x4010,0x0000 -> 0x2401,0x2c00
+
+ fourth case:
+ leu: 0x2401,0x2c02,0x4010,0x0000 -> 0x2400,0x2800
+ le: 0x2401,0x3402,0x4010,0x0000 -> 0x2400,0x3800
+
+ Unspecified case :)
+ jump: 0x4010,0x0000 -> 0x3c00. */
+
+#define NUMB_RELAX_CODES 12
+static struct rcodes_s
+{
+ int f0, f1; /* From code. */
+ int t0, t1; /* To code. */
+ int labels; /* Position of labels: 1 - one label at first
+ word, 2 - one at second word, 3 - two
+ labels at both. */
+ int cdx; /* Words to match. */
+ int bs; /* Shrink bytes. */
+ int off; /* Offset from old label for new code. */
+ int ncl; /* New code length. */
+} rcode[] =
+{/* 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 */
+ { 0x0000, 0x3402, 0x3800, 0x0000, 1, 1, 4, 4, 2}, /* lt */
+ { 0x0000, 0x2c02, 0x2800, 0x0000, 1, 1, 4, 4, 2}, /* ltu */
+ { 0x0000, 0x3802, 0x3400, 0x0000, 1, 1, 4, 4, 2}, /* ge */
+ { 0x0000, 0x2802, 0x2c00, 0x0000, 1, 1, 4, 4, 2}, /* geu */
+ { 0x3001, 0x3c02, 0x3000, 0x0000, 1, 2, 6, 6, 2}, /* ltn */
+ { 0x2403, 0x3802, 0x2401, 0x3400, 2, 2, 4, 6, 4}, /* gt */
+ { 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}
+};
+
+/* Return TRUE if a symbol exists at the given address. */
+
+static bfd_boolean
+msp430_elf_symbol_address_p (bfd * abfd,
+ asection * sec,
+ Elf_Internal_Sym * isym,
+ bfd_vma addr)
+{
+ Elf_Internal_Shdr *symtab_hdr;
+ unsigned int sec_shndx;
+ Elf_Internal_Sym *isymend;
+ struct elf_link_hash_entry **sym_hashes;
+ struct elf_link_hash_entry **end_hashes;
+ unsigned int symcount;
+
+ sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec);
+
+ /* Examine all the local symbols. */
+ symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+ for (isymend = isym + symtab_hdr->sh_info; isym < isymend; isym++)
+ if (isym->st_shndx == sec_shndx && isym->st_value == addr)
+ return TRUE;
+
+ symcount = (symtab_hdr->sh_size / sizeof (Elf32_External_Sym)
+ - symtab_hdr->sh_info);
+ sym_hashes = elf_sym_hashes (abfd);
+ end_hashes = sym_hashes + symcount;
+ for (; sym_hashes < end_hashes; sym_hashes++)
+ {
+ struct elf_link_hash_entry *sym_hash = *sym_hashes;
+
+ if ((sym_hash->root.type == bfd_link_hash_defined
+ || sym_hash->root.type == bfd_link_hash_defweak)
+ && sym_hash->root.u.def.section == sec
+ && sym_hash->root.u.def.value == addr)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Delete some bytes from a section while relaxing. */
+
+static bfd_boolean
+msp430_elf_relax_delete_bytes (bfd * abfd, asection * sec, bfd_vma addr,
+ int count)
+{
+ Elf_Internal_Shdr *symtab_hdr;
+ unsigned int sec_shndx;
+ bfd_byte *contents;
+ Elf_Internal_Rela *irel;
+ Elf_Internal_Rela *irelend;
+ Elf_Internal_Rela *irelalign;
+ bfd_vma toaddr;
+ Elf_Internal_Sym *isym;
+ Elf_Internal_Sym *isymend;
+ struct elf_link_hash_entry **sym_hashes;
+ struct elf_link_hash_entry **end_hashes;
+ unsigned int symcount;
+
+ sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec);
+
+ contents = elf_section_data (sec)->this_hdr.contents;
+
+ /* The deletion must stop at the next ALIGN reloc for an aligment
+ power larger than the number of bytes we are deleting. */
+
+ irelalign = NULL;
+ toaddr = sec->size;
+
+ irel = elf_section_data (sec)->relocs;
+ irelend = irel + sec->reloc_count;
+
+ /* Actually delete the bytes. */
+ memmove (contents + addr, contents + addr + count,
+ (size_t) (toaddr - addr - count));
+ sec->size -= count;
+
+ /* Adjust all the relocs. */
+ for (irel = elf_section_data (sec)->relocs; irel < irelend; irel++)
+ /* Get the new reloc address. */
+ if ((irel->r_offset > addr && irel->r_offset < toaddr))
+ irel->r_offset -= count;
+
+ /* Adjust the local symbols defined in this section. */
+ symtab_hdr = & elf_tdata (abfd)->symtab_hdr;
+ isym = (Elf_Internal_Sym *) symtab_hdr->contents;
+ for (isymend = isym + symtab_hdr->sh_info; isym < isymend; isym++)
+ if (isym->st_shndx == sec_shndx
+ && isym->st_value > addr && isym->st_value < toaddr)
+ isym->st_value -= count;
+
+ /* Now adjust the global symbols defined in this section. */
+ symcount = (symtab_hdr->sh_size / sizeof (Elf32_External_Sym)
+ - symtab_hdr->sh_info);
+ sym_hashes = elf_sym_hashes (abfd);
+ end_hashes = sym_hashes + symcount;
+ for (; sym_hashes < end_hashes; sym_hashes++)
+ {
+ struct elf_link_hash_entry *sym_hash = *sym_hashes;
+
+ if ((sym_hash->root.type == bfd_link_hash_defined
+ || sym_hash->root.type == bfd_link_hash_defweak)
+ && sym_hash->root.u.def.section == sec
+ && sym_hash->root.u.def.value > addr
+ && sym_hash->root.u.def.value < toaddr)
+ sym_hash->root.u.def.value -= count;
+ }
+
+ return TRUE;
+}
+
+
+static bfd_boolean
+msp430_elf_relax_section (bfd * abfd, asection * sec,
+ struct bfd_link_info * link_info,
+ bfd_boolean * again)
+{
+ Elf_Internal_Shdr * symtab_hdr;
+ Elf_Internal_Rela * internal_relocs;
+ Elf_Internal_Rela * irel;
+ Elf_Internal_Rela * irelend;
+ bfd_byte * contents = NULL;
+ Elf_Internal_Sym * isymbuf = NULL;
+
+ /* Assume nothing changes. */
+ *again = FALSE;
+
+ /* We don't have to do anything for a relocatable link, if
+ this section does not have relocs, or if this is not a
+ code section. */
+ if (link_info->relocatable
+ || (sec->flags & SEC_RELOC) == 0
+ || sec->reloc_count == 0 || (sec->flags & SEC_CODE) == 0)
+ return TRUE;
+
+ symtab_hdr = & elf_tdata (abfd)->symtab_hdr;
+
+ /* Get a copy of the native relocations. */
+ internal_relocs =
+ _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL, link_info->keep_memory);
+ if (internal_relocs == NULL)
+ goto error_return;
+
+ /* Walk through them looking for relaxing opportunities. */
+ irelend = internal_relocs + sec->reloc_count;
+ for (irel = internal_relocs; irel < irelend; irel++)
+ {
+ bfd_vma symval;
+
+ /* If this isn't something that can be relaxed, then ignore
+ this reloc. */
+ if (ELF32_R_TYPE (irel->r_info) != (int) R_MSP430_RL_PCREL)
+ continue;
+
+ /* Get the section contents if we haven't done so already. */
+ if (contents == NULL)
+ {
+ /* Get cached copy if it exists. */
+ if (elf_section_data (sec)->this_hdr.contents != NULL)
+ contents = elf_section_data (sec)->this_hdr.contents;
+ else if (! bfd_malloc_and_get_section (abfd, sec, &contents))
+ goto error_return;
+ }
+
+ /* Read this BFD's local symbols if we haven't done so already. */
+ if (isymbuf == NULL && symtab_hdr->sh_info != 0)
+ {
+ isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
+ if (isymbuf == NULL)
+ isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr,
+ symtab_hdr->sh_info, 0,
+ NULL, NULL, NULL);
+ if (isymbuf == NULL)
+ goto error_return;
+ }
+
+ /* Get the value of the symbol referred to by the reloc. */
+ if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
+ {
+ /* A local symbol. */
+ Elf_Internal_Sym *isym;
+ asection *sym_sec;
+
+ isym = isymbuf + ELF32_R_SYM (irel->r_info);
+ if (isym->st_shndx == SHN_UNDEF)
+ sym_sec = bfd_und_section_ptr;
+ else if (isym->st_shndx == SHN_ABS)
+ sym_sec = bfd_abs_section_ptr;
+ else if (isym->st_shndx == SHN_COMMON)
+ sym_sec = bfd_com_section_ptr;
+ else
+ sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
+ symval = (isym->st_value
+ + sym_sec->output_section->vma + sym_sec->output_offset);
+ }
+ else
+ {
+ unsigned long indx;
+ struct elf_link_hash_entry *h;
+
+ /* An external symbol. */
+ indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info;
+ h = elf_sym_hashes (abfd)[indx];
+ BFD_ASSERT (h != NULL);
+
+ if (h->root.type != bfd_link_hash_defined
+ && h->root.type != bfd_link_hash_defweak)
+ /* This appears to be a reference to an undefined
+ symbol. Just ignore it--it will be caught by the
+ regular reloc processing. */
+ continue;
+
+ symval = (h->root.u.def.value
+ + h->root.u.def.section->output_section->vma
+ + h->root.u.def.section->output_offset);
+ }
+
+ /* 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. */
+
+ /* Try to turn a 16bit pc-relative branch into a 10bit pc-relative
+ branch. */
+ /* Paranoia? paranoia... */
+ if (ELF32_R_TYPE (irel->r_info) == (int) R_MSP430_RL_PCREL)
+ {
+ bfd_vma value = symval;
+
+ /* Deal with pc-relative gunk. */
+ value -= (sec->output_section->vma + sec->output_offset);
+ value -= irel->r_offset;
+ value += irel->r_addend;
+
+ /* See if the value will fit in 10 bits, note the high value is
+ 1016 as the target will be two bytes closer if we are
+ able to relax. */
+ if ((long) value < 1016 && (long) value > -1016)
+ {
+ int code0 = 0, code1 = 0, code2 = 0;
+ int i;
+ struct rcodes_s *rx;
+
+ /* Get the opcode. */
+ if (irel->r_offset >= 6)
+ code0 = bfd_get_16 (abfd, contents + irel->r_offset - 6);
+
+ if (irel->r_offset >= 4)
+ code1 = bfd_get_16 (abfd, contents + irel->r_offset - 4);
+
+ code2 = bfd_get_16 (abfd, contents + irel->r_offset - 2);
+
+ if (code2 != 0x4010)
+ continue;
+
+ /* Check r4 and r3. */
+ for (i = NUMB_RELAX_CODES - 1; i >= 0; i--)
+ {
+ rx = &rcode[i];
+ if (rx->cdx == 2 && rx->f0 == code0 && rx->f1 == code1)
+ break;
+ else if (rx->cdx == 1 && rx->f1 == code1)
+ break;
+ else if (rx->cdx == 0) /* This is an unconditional jump. */
+ break;
+ }
+
+ /* Check labels:
+ .Label0: ; we do not care about this label
+ jeq +6
+ .Label1: ; make sure there is no label here
+ jl +4
+ .Label2: ; make sure there is no label here
+ br .Label_dst
+
+ So, if there is .Label1 or .Label2 we cannot relax this code.
+ This actually should not happen, cause for relaxable
+ instructions we use RL_PCREL reloc instead of 16_PCREL.
+ Will change this in the future. */
+
+ if (rx->cdx > 0
+ && msp430_elf_symbol_address_p (abfd, sec, isymbuf,
+ irel->r_offset - 2))
+ continue;
+ if (rx->cdx > 1
+ && msp430_elf_symbol_address_p (abfd, sec, isymbuf,
+ irel->r_offset - 4))
+ continue;
+
+ /* Note that we've changed the relocs, section contents, etc. */
+ elf_section_data (sec)->relocs = internal_relocs;
+ elf_section_data (sec)->this_hdr.contents = contents;
+ symtab_hdr->contents = (unsigned char *) isymbuf;
+
+ /* Fix the relocation's type. */
+ if (rx->labels == 3) /* Handle special cases. */
+ irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
+ R_MSP430_2X_PCREL);
+ else
+ irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
+ R_MSP430_10_PCREL);
+
+ /* Fix the opcode right way. */
+ bfd_put_16 (abfd, rx->t0, contents + irel->r_offset - rx->off);
+ if (rx->t1)
+ bfd_put_16 (abfd, rx->t1,
+ contents + irel->r_offset - rx->off + 2);
+
+ /* Delete bytes. */
+ if (!msp430_elf_relax_delete_bytes (abfd, sec,
+ irel->r_offset - rx->off +
+ rx->ncl, rx->bs))
+ goto error_return;
+
+ /* Handle unconditional jumps. */
+ if (rx->cdx == 0)
+ irel->r_offset -= 2;
+
+ /* That will change things, so, we should relax again.
+ Note that this is not required, and it may be slow. */
+ *again = TRUE;
+ }
+ }
+ }
+
+ if (isymbuf != NULL && symtab_hdr->contents != (unsigned char *) isymbuf)
+ {
+ if (!link_info->keep_memory)
+ free (isymbuf);
+ else
+ {
+ /* Cache the symbols for elf_link_input_bfd. */
+ symtab_hdr->contents = (unsigned char *) isymbuf;
+ }
+ }
+
+ if (contents != NULL
+ && elf_section_data (sec)->this_hdr.contents != contents)
+ {
+ if (!link_info->keep_memory)
+ free (contents);
+ else
+ {
+ /* Cache the section contents for elf_link_input_bfd. */
+ elf_section_data (sec)->this_hdr.contents = contents;
+ }
+ }
+
+ if (internal_relocs != NULL
+ && elf_section_data (sec)->relocs != internal_relocs)
+ free (internal_relocs);
+
+ return TRUE;
+
+error_return:
+ if (isymbuf != NULL && symtab_hdr->contents != (unsigned char *) isymbuf)
+ free (isymbuf);
+ if (contents != NULL
+ && elf_section_data (sec)->this_hdr.contents != contents)
+ free (contents);
+ if (internal_relocs != NULL
+ && elf_section_data (sec)->relocs != internal_relocs)
+ free (internal_relocs);
+
+ return FALSE;
+}
+
#define ELF_ARCH bfd_arch_msp430
#define ELF_MACHINE_CODE EM_MSP430
@@ -703,5 +1173,6 @@ elf32_msp430_post_process_headers (abfd, link_info)
#define elf_backend_final_write_processing bfd_elf_msp430_final_write_processing
#define elf_backend_object_p elf32_msp430_object_p
#define elf_backend_post_process_headers elf32_msp430_post_process_headers
+#define bfd_elf32_bfd_relax_section msp430_elf_relax_section
#include "elf32-target.h"