/* AVR-specific support for 32-bit ELF
- Copyright (C) 1999-2014 Free Software Foundation, Inc.
+ Copyright (C) 1999-2015 Free Software Foundation, Inc.
Contributed by Denis Chertykov <denisc@overta.ru>
This file is part of BFD, the Binary File Descriptor library.
static bfd_boolean debug_stubs = FALSE;
static bfd_reloc_status_type
-bfd_elf_avr_diff_reloc (bfd *abfd,
- arelent *reloc_entry,
- asymbol *symbol,
- void *data,
- asection *input_section,
- bfd *output_bfd,
- char **error_message);
+bfd_elf_avr_diff_reloc (bfd *, arelent *, asymbol *, void *,
+ asection *, bfd *, char **);
/* Hash table initialization and handling. Code is taken from the hppa port
and adapted to the needs of AVR. */
{
HOWTO (R_AVR_NONE, /* type */
0, /* rightshift */
- 2, /* size (0 = byte, 1 = short, 2 = long) */
- 32, /* bitsize */
+ 3, /* size (0 = byte, 1 = short, 2 = long) */
+ 0, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_bitfield, /* complain_on_overflow */
+ complain_overflow_dont, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_AVR_NONE", /* name */
FALSE, /* partial_inplace */
0xffffff, /* src_mask */
0xffffff, /* dst_mask */
FALSE), /* pcrel_offset */
- HOWTO (R_AVR_DIFF8, /* type */
- 0, /* rightshift */
- 0, /* size (0 = byte, 1 = short, 2 = long) */
- 8, /* bitsize */
- FALSE, /* pc_relative */
- 0, /* bitpos */
- complain_overflow_bitfield, /* complain_on_overflow */
- bfd_elf_avr_diff_reloc, /* special_function */
- "R_AVR_DIFF8", /* name */
- FALSE, /* partial_inplace */
- 0, /* src_mask */
- 0xff, /* dst_mask */
- FALSE), /* pcrel_offset */
- HOWTO (R_AVR_DIFF16, /* type */
- 0, /* rightshift */
- 1, /* size (0 = byte, 1 = short, 2 = long) */
- 16, /* bitsize */
- FALSE, /* pc_relative */
- 0, /* bitpos */
- complain_overflow_bitfield, /* complain_on_overflow */
- bfd_elf_avr_diff_reloc, /* special_function */
- "R_AVR_DIFF16", /* name */
- FALSE, /* partial_inplace */
- 0, /* src_mask */
- 0xffff, /* dst_mask */
- FALSE), /* pcrel_offset */
- HOWTO (R_AVR_DIFF32, /* type */
- 0, /* rightshift */
- 2, /* size (0 = byte, 1 = short, 2 = long) */
- 32, /* bitsize */
- FALSE, /* pc_relative */
- 0, /* bitpos */
- complain_overflow_bitfield, /* complain_on_overflow */
- bfd_elf_avr_diff_reloc, /* special_function */
- "R_AVR_DIFF32", /* name */
- FALSE, /* partial_inplace */
- 0, /* src_mask */
- 0xffffffff, /* dst_mask */
- FALSE) /* pcrel_offset */
+ HOWTO (R_AVR_DIFF8, /* type */
+ 0, /* rightshift */
+ 0, /* size (0 = byte, 1 = short, 2 = long) */
+ 8, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ bfd_elf_avr_diff_reloc, /* special_function */
+ "R_AVR_DIFF8", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+ HOWTO (R_AVR_DIFF16, /* type */
+ 0, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ bfd_elf_avr_diff_reloc,/* special_function */
+ "R_AVR_DIFF16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+ HOWTO (R_AVR_DIFF32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ bfd_elf_avr_diff_reloc,/* special_function */
+ "R_AVR_DIFF32", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+ /* 7 bit immediate for LDS/STS in Tiny core. */
+ HOWTO (R_AVR_LDS_STS_16, /* type */
+ 0, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ 7, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont,/* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_AVR_LDS_STS_16", /* name */
+ FALSE, /* partial_inplace */
+ 0xffff, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_AVR_PORT6, /* type */
+ 0, /* rightshift */
+ 0, /* size (0 = byte, 1 = short, 2 = long) */
+ 6, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont,/* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_AVR_PORT6", /* name */
+ FALSE, /* partial_inplace */
+ 0xffffff, /* src_mask */
+ 0xffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+ HOWTO (R_AVR_PORT5, /* type */
+ 0, /* rightshift */
+ 0, /* size (0 = byte, 1 = short, 2 = long) */
+ 5, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont,/* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_AVR_PORT5", /* name */
+ FALSE, /* partial_inplace */
+ 0xffffff, /* src_mask */
+ 0xffffff, /* dst_mask */
+ FALSE) /* pcrel_offset */
};
/* Map BFD reloc types to AVR ELF reloc types. */
{ BFD_RELOC_AVR_8_HLO, R_AVR_8_HLO8 },
{ BFD_RELOC_AVR_DIFF8, R_AVR_DIFF8 },
{ BFD_RELOC_AVR_DIFF16, R_AVR_DIFF16 },
- { BFD_RELOC_AVR_DIFF32, R_AVR_DIFF32 }
+ { BFD_RELOC_AVR_DIFF32, R_AVR_DIFF32 },
+ { BFD_RELOC_AVR_LDS_STS_16, R_AVR_LDS_STS_16},
+ { BFD_RELOC_AVR_PORT6, R_AVR_PORT6},
+ { BFD_RELOC_AVR_PORT5, R_AVR_PORT5}
};
/* Meant to be filled one day with the wrap around address for the
return _bfd_elf_link_hash_newfunc (entry, table, string);
}
+/* Free the derived linker hash table. */
+
+static void
+elf32_avr_link_hash_table_free (bfd *obfd)
+{
+ struct elf32_avr_link_hash_table *htab
+ = (struct elf32_avr_link_hash_table *) obfd->link.hash;
+
+ /* Free the address mapping table. */
+ if (htab->amt_stub_offsets != NULL)
+ free (htab->amt_stub_offsets);
+ if (htab->amt_destination_addr != NULL)
+ free (htab->amt_destination_addr);
+
+ bfd_hash_table_free (&htab->bstab);
+ _bfd_elf_link_hash_table_free (obfd);
+}
+
/* Create the derived linker hash table. The AVR ELF port uses the derived
hash table to keep information specific to the AVR ELF linker (without
using static variables). */
/* Init the stub hash table too. */
if (!bfd_hash_table_init (&htab->bstab, stub_hash_newfunc,
sizeof (struct elf32_avr_stub_hash_entry)))
- return NULL;
+ {
+ _bfd_elf_link_hash_table_free (abfd);
+ return NULL;
+ }
+ htab->etab.root.hash_table_free = elf32_avr_link_hash_table_free;
return &htab->etab.root;
}
-/* Free the derived linker hash table. */
-
-static void
-elf32_avr_link_hash_table_free (struct bfd_link_hash_table *btab)
-{
- struct elf32_avr_link_hash_table *htab
- = (struct elf32_avr_link_hash_table *) btab;
-
- /* Free the address mapping table. */
- if (htab->amt_stub_offsets != NULL)
- free (htab->amt_stub_offsets);
- if (htab->amt_destination_addr != NULL)
- free (htab->amt_destination_addr);
-
- bfd_hash_table_free (&htab->bstab);
- _bfd_elf_link_hash_table_free (btab);
-}
-
/* Calculates the effective distance of a pc relative jump/call. */
static int
unsigned int r_type;
r_type = ELF32_R_TYPE (dst->r_info);
- BFD_ASSERT (r_type < (unsigned int) R_AVR_max);
+ if (r_type >= (unsigned int) R_AVR_max)
+ {
+ _bfd_error_handler (_("%A: invalid AVR reloc number: %d"), abfd, r_type);
+ r_type = 0;
+ }
cache_ptr->howto = &elf_avr_howto_table[r_type];
}
r = bfd_reloc_ok;
break;
+ case R_AVR_LDS_STS_16:
+ contents += rel->r_offset;
+ srel = (bfd_signed_vma) relocation + rel->r_addend;
+ if ((srel & 0xFFFF) < 0x40 || (srel & 0xFFFF) > 0xbf)
+ return bfd_reloc_outofrange;
+ srel = srel & 0x7f;
+ x = bfd_get_16 (input_bfd, contents);
+ x |= (srel & 0x0f) | ((srel & 0x30) << 5) | ((srel & 0x40) << 2);
+ bfd_put_16 (input_bfd, x, contents);
+ break;
+
+ case R_AVR_PORT6:
+ contents += rel->r_offset;
+ srel = (bfd_signed_vma) relocation + rel->r_addend;
+ if ((srel & 0xffff) > 0x3f)
+ return bfd_reloc_outofrange;
+ x = bfd_get_16 (input_bfd, contents);
+ x = (x & 0xf9f0) | ((srel & 0x30) << 5) | (srel & 0x0f);
+ bfd_put_16 (input_bfd, x, contents);
+ break;
+
+ case R_AVR_PORT5:
+ contents += rel->r_offset;
+ srel = (bfd_signed_vma) relocation + rel->r_addend;
+ if ((srel & 0xffff) > 0x1f)
+ return bfd_reloc_outofrange;
+ x = bfd_get_16 (input_bfd, contents);
+ x = (x & 0xff07) | ((srel & 0x1f) << 3);
+ bfd_put_16 (input_bfd, x, contents);
+ break;
+
default:
r = _bfd_final_link_relocate (howto, input_bfd, input_section,
contents, rel->r_offset,
case bfd_mach_avrxmega7:
val = E_AVR_MACH_XMEGA7;
break;
+
+ case bfd_mach_avrtiny:
+ val = E_AVR_MACH_AVRTINY;
+ break;
}
elf_elfheader (abfd)->e_machine = EM_AVR;
elf_elfheader (abfd)->e_flags &= ~ EF_AVR_MACH;
elf_elfheader (abfd)->e_flags |= val;
- elf_elfheader (abfd)->e_flags |= EF_AVR_LINKRELAX_PREPARED;
}
/* Set the right machine number. */
case E_AVR_MACH_XMEGA7:
e_set = bfd_mach_avrxmega7;
break;
+
+ case E_AVR_MACH_AVRTINY:
+ e_set = bfd_mach_avrtiny;
+ break;
}
}
return bfd_default_set_arch_mach (abfd, bfd_arch_avr,
|| ELF32_R_TYPE (irel->r_info) == R_AVR_DIFF32);
}
-/* Reduce the diff value written in the section by count if the shrinked
- insn address happens to fall between the two symbols for which this
- diff reloc was emitted. */
+/* Reduce the diff value written in the section by count if the shrinked
+ insn address happens to fall between the two symbols for which this
+ diff reloc was emitted. */
static void
elf32_avr_adjust_diff_reloc_value (bfd *abfd,
bfd_vma end_address = symval + irel->r_addend;
bfd_vma start_address = end_address - x;
- /* Reduce the diff value by count bytes and write it back into section
+ /* Reduce the diff value by count bytes and write it back into section
contents. */
- if (shrinked_insn_address >= start_address &&
- shrinked_insn_address <= end_address)
+ if (shrinked_insn_address >= start_address
+ && shrinked_insn_address <= end_address)
{
switch (ELF32_R_TYPE (irel->r_info))
{
isymend = isym + symtab_hdr->sh_info;
for (; isym < isymend; isym++)
{
- if (isym->st_shndx == sec_shndx
- && isym->st_value > addr
- && isym->st_value < toaddr)
- isym->st_value -= count;
+ if (isym->st_shndx == sec_shndx)
+ {
+ if (isym->st_value > addr
+ && isym->st_value <= toaddr)
+ isym->st_value -= count;
+
+ if (isym->st_value <= addr
+ && isym->st_value + isym->st_size > addr)
+ {
+ /* If this assert fires then we have a symbol that ends
+ part way through an instruction. Does that make
+ sense? */
+ BFD_ASSERT (isym->st_value + isym->st_size >= addr + count);
+ isym->st_size -= count;
+ }
+ }
}
}
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.section == sec)
{
- sym_hash->root.u.def.value -= count;
+ if (sym_hash->root.u.def.value > addr
+ && sym_hash->root.u.def.value <= toaddr)
+ sym_hash->root.u.def.value -= count;
+
+ if (sym_hash->root.u.def.value <= addr
+ && (sym_hash->root.u.def.value + sym_hash->size > addr))
+ {
+ /* If this assert fires then we have a symbol that ends
+ part way through an instruction. Does that make
+ sense? */
+ BFD_ASSERT (sym_hash->root.u.def.value + sym_hash->size
+ >= addr + count);
+ sym_hash->size -= count;
+ }
}
}
bfd_vma symval;
if ( ELF32_R_TYPE (irel->r_info) != R_AVR_13_PCREL
- && ELF32_R_TYPE (irel->r_info) != R_AVR_7_PCREL
- && ELF32_R_TYPE (irel->r_info) != R_AVR_CALL)
+ && ELF32_R_TYPE (irel->r_info) != R_AVR_7_PCREL
+ && ELF32_R_TYPE (irel->r_info) != R_AVR_CALL)
continue;
/* Get the section contents if we haven't done so already. */
{
Elf_Internal_Rela *rel;
Elf_Internal_Rela *relend;
-
+
rel = elf_section_data (isec)->relocs;
if (rel == NULL)
rel = _bfd_elf_link_read_relocs (abfd, isec, NULL, NULL, TRUE);
/* Count the number of input BFDs and find the top input section id. */
for (input_bfd = info->input_bfds, bfd_count = 0, top_id = 0;
input_bfd != NULL;
- input_bfd = input_bfd->link_next)
+ input_bfd = input_bfd->link.next)
{
bfd_count += 1;
for (section = input_bfd->sections;
export stubs. */
for (bfd_indx = 0;
input_bfd != NULL;
- input_bfd = input_bfd->link_next, bfd_indx++)
+ input_bfd = input_bfd->link.next, bfd_indx++)
{
Elf_Internal_Shdr *symtab_hdr;
bfd_hash_traverse (&htab->bstab, avr_mark_stub_not_to_be_necessary, htab);
for (input_bfd = info->input_bfds, bfd_indx = 0;
input_bfd != NULL;
- input_bfd = input_bfd->link_next, bfd_indx++)
+ input_bfd = input_bfd->link.next, bfd_indx++)
{
Elf_Internal_Shdr *symtab_hdr;
asection *section;
#define TARGET_LITTLE_NAME "elf32-avr"
#define bfd_elf32_bfd_link_hash_table_create elf32_avr_link_hash_table_create
-#define bfd_elf32_bfd_link_hash_table_free elf32_avr_link_hash_table_free
#define elf_info_to_howto avr_info_to_howto_rela
#define elf_info_to_howto_rel NULL