#include "bfd.h"
#include "sysdep.h"
#include "libbfd.h"
#include "elf-bfd.h"
#include "elf/avr.h"
#include "elf32-avr.h"
static bfd_boolean debug_relax = FALSE;
static bfd_boolean debug_stubs = FALSE;
struct elf32_avr_stub_hash_entry
{
struct bfd_hash_entry bh_root;
bfd_vma stub_offset;
bfd_vma target_value;
bfd_boolean is_actually_needed;
};
struct elf32_avr_link_hash_table
{
struct elf_link_hash_table etab;
struct bfd_hash_table bstab;
bfd_boolean no_stubs;
bfd *stub_bfd;
asection *stub_sec;
bfd_vma vector_base;
unsigned int bfd_count;
int top_index;
asection ** input_list;
Elf_Internal_Sym ** all_local_syms;
unsigned int amt_entry_cnt;
unsigned int amt_max_entry_cnt;
bfd_vma * amt_stub_offsets;
bfd_vma * amt_destination_addr;
};
#define avr_link_hash_table(p) \
\
((p)->hash->table.newfunc != elf32_avr_link_hash_newfunc ? NULL : \
((struct elf32_avr_link_hash_table *) ((p)->hash)))
#define avr_stub_hash_entry(ent) \
((struct elf32_avr_stub_hash_entry *)(ent))
#define avr_stub_hash_lookup(table, string, create, copy) \
((struct elf32_avr_stub_hash_entry *) \
bfd_hash_lookup ((table), (string), (create), (copy)))
static reloc_howto_type elf_avr_howto_table[] =
{
HOWTO (R_AVR_NONE,
0,
2,
32,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_AVR_NONE",
FALSE,
0,
0,
FALSE),
HOWTO (R_AVR_32,
0,
2,
32,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_AVR_32",
FALSE,
0xffffffff,
0xffffffff,
FALSE),
HOWTO (R_AVR_7_PCREL,
1,
1,
7,
TRUE,
3,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_AVR_7_PCREL",
FALSE,
0xffff,
0xffff,
TRUE),
HOWTO (R_AVR_13_PCREL,
1,
1,
13,
TRUE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_AVR_13_PCREL",
FALSE,
0xfff,
0xfff,
TRUE),
HOWTO (R_AVR_16,
0,
1,
16,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_AVR_16",
FALSE,
0xffff,
0xffff,
FALSE),
HOWTO (R_AVR_16_PM,
1,
1,
16,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_AVR_16_PM",
FALSE,
0xffff,
0xffff,
FALSE),
HOWTO (R_AVR_LO8_LDI,
0,
1,
8,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_AVR_LO8_LDI",
FALSE,
0xffff,
0xffff,
FALSE),
HOWTO (R_AVR_HI8_LDI,
8,
1,
8,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_AVR_HI8_LDI",
FALSE,
0xffff,
0xffff,
FALSE),
HOWTO (R_AVR_HH8_LDI,
16,
1,
8,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_AVR_HH8_LDI",
FALSE,
0xffff,
0xffff,
FALSE),
HOWTO (R_AVR_LO8_LDI_NEG,
0,
1,
8,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_AVR_LO8_LDI_NEG",
FALSE,
0xffff,
0xffff,
FALSE),
HOWTO (R_AVR_HI8_LDI_NEG,
8,
1,
8,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_AVR_HI8_LDI_NEG",
FALSE,
0xffff,
0xffff,
FALSE),
HOWTO (R_AVR_HH8_LDI_NEG,
16,
1,
8,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_AVR_HH8_LDI_NEG",
FALSE,
0xffff,
0xffff,
FALSE),
HOWTO (R_AVR_LO8_LDI_PM,
1,
1,
8,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_AVR_LO8_LDI_PM",
FALSE,
0xffff,
0xffff,
FALSE),
HOWTO (R_AVR_HI8_LDI_PM,
9,
1,
8,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_AVR_HI8_LDI_PM",
FALSE,
0xffff,
0xffff,
FALSE),
HOWTO (R_AVR_HH8_LDI_PM,
17,
1,
8,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_AVR_HH8_LDI_PM",
FALSE,
0xffff,
0xffff,
FALSE),
HOWTO (R_AVR_LO8_LDI_PM_NEG,
1,
1,
8,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_AVR_LO8_LDI_PM_NEG",
FALSE,
0xffff,
0xffff,
FALSE),
HOWTO (R_AVR_HI8_LDI_PM_NEG,
9,
1,
8,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_AVR_HI8_LDI_PM_NEG",
FALSE,
0xffff,
0xffff,
FALSE),
HOWTO (R_AVR_HH8_LDI_PM_NEG,
17,
1,
8,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_AVR_HH8_LDI_PM_NEG",
FALSE,
0xffff,
0xffff,
FALSE),
HOWTO (R_AVR_CALL,
1,
2,
23,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_AVR_CALL",
FALSE,
0xffffffff,
0xffffffff,
FALSE),
HOWTO (R_AVR_LDI,
0,
1,
16,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_AVR_LDI",
FALSE,
0xffff,
0xffff,
FALSE),
HOWTO (R_AVR_6,
0,
0,
6,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_AVR_6",
FALSE,
0xffff,
0xffff,
FALSE),
HOWTO (R_AVR_6_ADIW,
0,
0,
6,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_AVR_6_ADIW",
FALSE,
0xffff,
0xffff,
FALSE),
HOWTO (R_AVR_MS8_LDI,
24,
1,
8,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_AVR_MS8_LDI",
FALSE,
0xffff,
0xffff,
FALSE),
HOWTO (R_AVR_MS8_LDI_NEG,
24,
1,
8,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_AVR_MS8_LDI_NEG",
FALSE,
0xffff,
0xffff,
FALSE),
HOWTO (R_AVR_LO8_LDI_GS,
1,
1,
8,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_AVR_LO8_LDI_GS",
FALSE,
0xffff,
0xffff,
FALSE),
HOWTO (R_AVR_HI8_LDI_GS,
9,
1,
8,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_AVR_HI8_LDI_GS",
FALSE,
0xffff,
0xffff,
FALSE)
};
struct avr_reloc_map
{
bfd_reloc_code_real_type bfd_reloc_val;
unsigned int elf_reloc_val;
};
static const struct avr_reloc_map avr_reloc_map[] =
{
{ BFD_RELOC_NONE, R_AVR_NONE },
{ BFD_RELOC_32, R_AVR_32 },
{ BFD_RELOC_AVR_7_PCREL, R_AVR_7_PCREL },
{ BFD_RELOC_AVR_13_PCREL, R_AVR_13_PCREL },
{ BFD_RELOC_16, R_AVR_16 },
{ BFD_RELOC_AVR_16_PM, R_AVR_16_PM },
{ BFD_RELOC_AVR_LO8_LDI, R_AVR_LO8_LDI},
{ BFD_RELOC_AVR_HI8_LDI, R_AVR_HI8_LDI },
{ BFD_RELOC_AVR_HH8_LDI, R_AVR_HH8_LDI },
{ BFD_RELOC_AVR_MS8_LDI, R_AVR_MS8_LDI },
{ BFD_RELOC_AVR_LO8_LDI_NEG, R_AVR_LO8_LDI_NEG },
{ BFD_RELOC_AVR_HI8_LDI_NEG, R_AVR_HI8_LDI_NEG },
{ BFD_RELOC_AVR_HH8_LDI_NEG, R_AVR_HH8_LDI_NEG },
{ BFD_RELOC_AVR_MS8_LDI_NEG, R_AVR_MS8_LDI_NEG },
{ BFD_RELOC_AVR_LO8_LDI_PM, R_AVR_LO8_LDI_PM },
{ BFD_RELOC_AVR_LO8_LDI_GS, R_AVR_LO8_LDI_GS },
{ BFD_RELOC_AVR_HI8_LDI_PM, R_AVR_HI8_LDI_PM },
{ BFD_RELOC_AVR_HI8_LDI_GS, R_AVR_HI8_LDI_GS },
{ BFD_RELOC_AVR_HH8_LDI_PM, R_AVR_HH8_LDI_PM },
{ BFD_RELOC_AVR_LO8_LDI_PM_NEG, R_AVR_LO8_LDI_PM_NEG },
{ BFD_RELOC_AVR_HI8_LDI_PM_NEG, R_AVR_HI8_LDI_PM_NEG },
{ BFD_RELOC_AVR_HH8_LDI_PM_NEG, R_AVR_HH8_LDI_PM_NEG },
{ BFD_RELOC_AVR_CALL, R_AVR_CALL },
{ BFD_RELOC_AVR_LDI, R_AVR_LDI },
{ BFD_RELOC_AVR_6, R_AVR_6 },
{ BFD_RELOC_AVR_6_ADIW, R_AVR_6_ADIW }
};
static bfd_vma avr_pc_wrap_around = 0x10000000;
static int avr_replace_call_ret_sequences = 1;
static struct bfd_hash_entry *
stub_hash_newfunc (struct bfd_hash_entry *entry,
struct bfd_hash_table *table,
const char *string)
{
if (entry == NULL)
{
entry = bfd_hash_allocate (table,
sizeof (struct elf32_avr_stub_hash_entry));
if (entry == NULL)
return entry;
}
entry = bfd_hash_newfunc (entry, table, string);
if (entry != NULL)
{
struct elf32_avr_stub_hash_entry *hsh;
hsh = avr_stub_hash_entry (entry);
hsh->stub_offset = 0;
hsh->target_value = 0;
}
return entry;
}
static struct bfd_hash_entry *
elf32_avr_link_hash_newfunc (struct bfd_hash_entry * entry,
struct bfd_hash_table * table,
const char * string)
{
return _bfd_elf_link_hash_newfunc (entry, table, string);
}
static struct bfd_link_hash_table *
elf32_avr_link_hash_table_create (bfd *abfd)
{
struct elf32_avr_link_hash_table *htab;
bfd_size_type amt = sizeof (*htab);
htab = bfd_malloc (amt);
if (htab == NULL)
return NULL;
if (!_bfd_elf_link_hash_table_init (&htab->etab, abfd,
elf32_avr_link_hash_newfunc,
sizeof (struct elf_link_hash_entry)))
{
free (htab);
return NULL;
}
if (!bfd_hash_table_init (&htab->bstab, stub_hash_newfunc,
sizeof (struct elf32_avr_stub_hash_entry)))
return NULL;
htab->stub_bfd = NULL;
htab->stub_sec = NULL;
htab->amt_stub_offsets = NULL;
htab->amt_destination_addr = NULL;
htab->amt_entry_cnt = 0;
htab->amt_max_entry_cnt = 0;
return &htab->etab.root;
}
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;
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_generic_link_hash_table_free (btab);
}
static int
avr_relative_distance_considering_wrap_around (unsigned int distance)
{
unsigned int wrap_around_mask = avr_pc_wrap_around - 1;
int dist_with_wrap_around = distance & wrap_around_mask;
if (dist_with_wrap_around > ((int) (avr_pc_wrap_around >> 1)))
dist_with_wrap_around -= avr_pc_wrap_around;
return dist_with_wrap_around;
}
static reloc_howto_type *
bfd_elf32_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
bfd_reloc_code_real_type code)
{
unsigned int i;
for (i = 0;
i < sizeof (avr_reloc_map) / sizeof (struct avr_reloc_map);
i++)
if (avr_reloc_map[i].bfd_reloc_val == code)
return &elf_avr_howto_table[avr_reloc_map[i].elf_reloc_val];
return NULL;
}
static void
avr_info_to_howto_rela (bfd *abfd ATTRIBUTE_UNUSED,
arelent *cache_ptr,
Elf_Internal_Rela *dst)
{
unsigned int r_type;
r_type = ELF32_R_TYPE (dst->r_info);
BFD_ASSERT (r_type < (unsigned int) R_AVR_max);
cache_ptr->howto = &elf_avr_howto_table[r_type];
}
static bfd_boolean
elf32_avr_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;
const Elf_Internal_Rela *rel;
const Elf_Internal_Rela *rel_end;
if (info->relocatable)
return TRUE;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (abfd);
sym_hashes_end = sym_hashes + symtab_hdr->sh_size / sizeof (Elf32_External_Sym);
if (!elf_bad_symtab (abfd))
sym_hashes_end -= symtab_hdr->sh_info;
rel_end = relocs + sec->reloc_count;
for (rel = relocs; rel < rel_end; rel++)
{
struct elf_link_hash_entry *h;
unsigned long r_symndx;
r_symndx = ELF32_R_SYM (rel->r_info);
if (r_symndx < symtab_hdr->sh_info)
h = NULL;
else
{
h = sym_hashes[r_symndx - symtab_hdr->sh_info];
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;
}
}
return TRUE;
}
static bfd_boolean
avr_stub_is_required_for_16_bit_reloc (bfd_vma relocation)
{
return (relocation >= 0x020000);
}
static bfd_vma
avr_get_stub_addr (bfd_vma srel,
struct elf32_avr_link_hash_table *htab)
{
unsigned int index;
bfd_vma stub_sec_addr =
(htab->stub_sec->output_section->vma +
htab->stub_sec->output_offset);
for (index = 0; index < htab->amt_max_entry_cnt; index ++)
if (htab->amt_destination_addr[index] == srel)
return htab->amt_stub_offsets[index] + stub_sec_addr;
return 0x020000;
}
static bfd_reloc_status_type
avr_final_link_relocate (reloc_howto_type * howto,
bfd * input_bfd,
asection * input_section,
bfd_byte * contents,
Elf_Internal_Rela * rel,
bfd_vma relocation,
struct elf32_avr_link_hash_table * htab)
{
bfd_reloc_status_type r = bfd_reloc_ok;
bfd_vma x;
bfd_signed_vma srel;
bfd_signed_vma reloc_addr;
bfd_boolean use_stubs = FALSE;
bfd_signed_vma base_addr = htab->vector_base;
reloc_addr = rel->r_offset + input_section->output_section->vma
+ input_section->output_offset;
switch (howto->type)
{
case R_AVR_7_PCREL:
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation;
srel += rel->r_addend;
srel -= rel->r_offset;
srel -= 2;
srel -= (input_section->output_section->vma +
input_section->output_offset);
if (srel & 1)
return bfd_reloc_outofrange;
if (srel > ((1 << 7) - 1) || (srel < - (1 << 7)))
return bfd_reloc_overflow;
x = bfd_get_16 (input_bfd, contents);
x = (x & 0xfc07) | (((srel >> 1) << 3) & 0x3f8);
bfd_put_16 (input_bfd, x, contents);
break;
case R_AVR_13_PCREL:
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation;
srel += rel->r_addend;
srel -= rel->r_offset;
srel -= 2;
srel -= (input_section->output_section->vma +
input_section->output_offset);
if (srel & 1)
return bfd_reloc_outofrange;
srel = avr_relative_distance_considering_wrap_around (srel);
srel >>= 1;
if (srel < -2048 || srel > 2047)
{
switch (bfd_get_mach (input_bfd))
{
case bfd_mach_avr2:
case bfd_mach_avr4:
break;
default:
return bfd_reloc_overflow;
}
}
x = bfd_get_16 (input_bfd, contents);
x = (x & 0xf000) | (srel & 0xfff);
bfd_put_16 (input_bfd, x, contents);
break;
case R_AVR_LO8_LDI:
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation + rel->r_addend;
x = bfd_get_16 (input_bfd, contents);
x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
bfd_put_16 (input_bfd, x, contents);
break;
case R_AVR_LDI:
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation + rel->r_addend;
if (((srel > 0) && (srel & 0xffff) > 255)
|| ((srel < 0) && ((-srel) & 0xffff) > 128))
return bfd_reloc_overflow;
x = bfd_get_16 (input_bfd, contents);
x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
bfd_put_16 (input_bfd, x, contents);
break;
case R_AVR_6:
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation + rel->r_addend;
if (((srel & 0xffff) > 63) || (srel < 0))
return bfd_reloc_overflow;
x = bfd_get_16 (input_bfd, contents);
x = (x & 0xd3f8) | ((srel & 7) | ((srel & (3 << 3)) << 7)
| ((srel & (1 << 5)) << 8));
bfd_put_16 (input_bfd, x, contents);
break;
case R_AVR_6_ADIW:
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation + rel->r_addend;
if (((srel & 0xffff) > 63) || (srel < 0))
return bfd_reloc_overflow;
x = bfd_get_16 (input_bfd, contents);
x = (x & 0xff30) | (srel & 0xf) | ((srel & 0x30) << 2);
bfd_put_16 (input_bfd, x, contents);
break;
case R_AVR_HI8_LDI:
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation + rel->r_addend;
srel = (srel >> 8) & 0xff;
x = bfd_get_16 (input_bfd, contents);
x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
bfd_put_16 (input_bfd, x, contents);
break;
case R_AVR_HH8_LDI:
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation + rel->r_addend;
srel = (srel >> 16) & 0xff;
x = bfd_get_16 (input_bfd, contents);
x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
bfd_put_16 (input_bfd, x, contents);
break;
case R_AVR_MS8_LDI:
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation + rel->r_addend;
srel = (srel >> 24) & 0xff;
x = bfd_get_16 (input_bfd, contents);
x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
bfd_put_16 (input_bfd, x, contents);
break;
case R_AVR_LO8_LDI_NEG:
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation + rel->r_addend;
srel = -srel;
x = bfd_get_16 (input_bfd, contents);
x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
bfd_put_16 (input_bfd, x, contents);
break;
case R_AVR_HI8_LDI_NEG:
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation + rel->r_addend;
srel = -srel;
srel = (srel >> 8) & 0xff;
x = bfd_get_16 (input_bfd, contents);
x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
bfd_put_16 (input_bfd, x, contents);
break;
case R_AVR_HH8_LDI_NEG:
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation + rel->r_addend;
srel = -srel;
srel = (srel >> 16) & 0xff;
x = bfd_get_16 (input_bfd, contents);
x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
bfd_put_16 (input_bfd, x, contents);
break;
case R_AVR_MS8_LDI_NEG:
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation + rel->r_addend;
srel = -srel;
srel = (srel >> 24) & 0xff;
x = bfd_get_16 (input_bfd, contents);
x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
bfd_put_16 (input_bfd, x, contents);
break;
case R_AVR_LO8_LDI_GS:
use_stubs = (!htab->no_stubs);
case R_AVR_LO8_LDI_PM:
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation + rel->r_addend;
if (use_stubs
&& avr_stub_is_required_for_16_bit_reloc (srel - base_addr))
{
bfd_vma old_srel = srel;
srel = avr_get_stub_addr (srel, htab);
if (debug_stubs)
printf ("LD: Using jump stub (at 0x%x) with destination 0x%x for "
"reloc at address 0x%x.\n",
(unsigned int) srel,
(unsigned int) old_srel,
(unsigned int) reloc_addr);
if (avr_stub_is_required_for_16_bit_reloc (srel - base_addr))
return bfd_reloc_outofrange;
}
if (srel & 1)
return bfd_reloc_outofrange;
srel = srel >> 1;
x = bfd_get_16 (input_bfd, contents);
x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
bfd_put_16 (input_bfd, x, contents);
break;
case R_AVR_HI8_LDI_GS:
use_stubs = (!htab->no_stubs);
case R_AVR_HI8_LDI_PM:
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation + rel->r_addend;
if (use_stubs
&& avr_stub_is_required_for_16_bit_reloc (srel - base_addr))
{
bfd_vma old_srel = srel;
srel = avr_get_stub_addr (srel, htab);
if (debug_stubs)
printf ("LD: Using jump stub (at 0x%x) with destination 0x%x for "
"reloc at address 0x%x.\n",
(unsigned int) srel,
(unsigned int) old_srel,
(unsigned int) reloc_addr);
if (avr_stub_is_required_for_16_bit_reloc (srel - base_addr))
return bfd_reloc_outofrange;
}
if (srel & 1)
return bfd_reloc_outofrange;
srel = srel >> 1;
srel = (srel >> 8) & 0xff;
x = bfd_get_16 (input_bfd, contents);
x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
bfd_put_16 (input_bfd, x, contents);
break;
case R_AVR_HH8_LDI_PM:
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation + rel->r_addend;
if (srel & 1)
return bfd_reloc_outofrange;
srel = srel >> 1;
srel = (srel >> 16) & 0xff;
x = bfd_get_16 (input_bfd, contents);
x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
bfd_put_16 (input_bfd, x, contents);
break;
case R_AVR_LO8_LDI_PM_NEG:
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation + rel->r_addend;
srel = -srel;
if (srel & 1)
return bfd_reloc_outofrange;
srel = srel >> 1;
x = bfd_get_16 (input_bfd, contents);
x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
bfd_put_16 (input_bfd, x, contents);
break;
case R_AVR_HI8_LDI_PM_NEG:
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation + rel->r_addend;
srel = -srel;
if (srel & 1)
return bfd_reloc_outofrange;
srel = srel >> 1;
srel = (srel >> 8) & 0xff;
x = bfd_get_16 (input_bfd, contents);
x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
bfd_put_16 (input_bfd, x, contents);
break;
case R_AVR_HH8_LDI_PM_NEG:
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation + rel->r_addend;
srel = -srel;
if (srel & 1)
return bfd_reloc_outofrange;
srel = srel >> 1;
srel = (srel >> 16) & 0xff;
x = bfd_get_16 (input_bfd, contents);
x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
bfd_put_16 (input_bfd, x, contents);
break;
case R_AVR_CALL:
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation + rel->r_addend;
if (srel & 1)
return bfd_reloc_outofrange;
srel = srel >> 1;
x = bfd_get_16 (input_bfd, contents);
x |= ((srel & 0x10000) | ((srel << 3) & 0x1f00000)) >> 16;
bfd_put_16 (input_bfd, x, contents);
bfd_put_16 (input_bfd, (bfd_vma) srel & 0xffff, contents+2);
break;
case R_AVR_16_PM:
use_stubs = (!htab->no_stubs);
contents += rel->r_offset;
srel = (bfd_signed_vma) relocation + rel->r_addend;
if (use_stubs
&& avr_stub_is_required_for_16_bit_reloc (srel - base_addr))
{
bfd_vma old_srel = srel;
srel = avr_get_stub_addr (srel,htab);
if (debug_stubs)
printf ("LD: Using jump stub (at 0x%x) with destination 0x%x for "
"reloc at address 0x%x.\n",
(unsigned int) srel,
(unsigned int) old_srel,
(unsigned int) reloc_addr);
if (avr_stub_is_required_for_16_bit_reloc (srel - base_addr))
return bfd_reloc_outofrange;
}
if (srel & 1)
return bfd_reloc_outofrange;
srel = srel >> 1;
bfd_put_16 (input_bfd, (bfd_vma) srel &0x00ffff, contents);
break;
default:
r = _bfd_final_link_relocate (howto, input_bfd, input_section,
contents, rel->r_offset,
relocation, rel->r_addend);
}
return r;
}
static bfd_boolean
elf32_avr_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;
Elf_Internal_Rela * rel;
Elf_Internal_Rela * relend;
struct elf32_avr_link_hash_table * htab = avr_link_hash_table (info);
if (info == NULL || info->relocatable)
return TRUE;
symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (input_bfd);
relend = relocs + input_section->reloc_count;
for (rel = relocs; rel < relend; rel ++)
{
reloc_howto_type * howto;
unsigned long r_symndx;
Elf_Internal_Sym * sym;
asection * sec;
struct elf_link_hash_entry * h;
bfd_vma relocation;
bfd_reloc_status_type r;
const char * name;
int r_type;
r_type = ELF32_R_TYPE (rel->r_info);
r_symndx = ELF32_R_SYM (rel->r_info);
howto = elf_avr_howto_table + ELF32_R_TYPE (rel->r_info);
h = NULL;
sym = NULL;
sec = NULL;
if (r_symndx < symtab_hdr->sh_info)
{
sym = local_syms + r_symndx;
sec = local_sections [r_symndx];
relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
name = bfd_elf_string_from_elf_section
(input_bfd, symtab_hdr->sh_link, sym->st_name);
name = (name == NULL) ? bfd_section_name (input_bfd, sec) : name;
}
else
{
bfd_boolean unresolved_reloc, warned;
RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
r_symndx, symtab_hdr, sym_hashes,
h, sec, relocation,
unresolved_reloc, warned);
name = h->root.root.string;
}
r = avr_final_link_relocate (howto, input_bfd, input_section,
contents, rel, relocation, htab);
if (r != bfd_reloc_ok)
{
const char * msg = (const char *) NULL;
switch (r)
{
case bfd_reloc_overflow:
r = info->callbacks->reloc_overflow
(info, (h ? &h->root : NULL),
name, howto->name, (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);
break;
case bfd_reloc_outofrange:
msg = _("internal error: out of range error");
break;
case bfd_reloc_notsupported:
msg = _("internal error: unsupported relocation error");
break;
case bfd_reloc_dangerous:
msg = _("internal error: dangerous relocation");
break;
default:
msg = _("internal error: unknown error");
break;
}
if (msg)
r = info->callbacks->warning
(info, msg, name, input_bfd, input_section, rel->r_offset);
if (! r)
return FALSE;
}
}
return TRUE;
}
static void
bfd_elf_avr_final_write_processing (bfd *abfd,
bfd_boolean linker ATTRIBUTE_UNUSED)
{
unsigned long val;
switch (bfd_get_mach (abfd))
{
default:
case bfd_mach_avr2:
val = E_AVR_MACH_AVR2;
break;
case bfd_mach_avr1:
val = E_AVR_MACH_AVR1;
break;
case bfd_mach_avr3:
val = E_AVR_MACH_AVR3;
break;
case bfd_mach_avr4:
val = E_AVR_MACH_AVR4;
break;
case bfd_mach_avr5:
val = E_AVR_MACH_AVR5;
break;
case bfd_mach_avr6:
val = E_AVR_MACH_AVR6;
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;
}
static bfd_boolean
elf32_avr_object_p (bfd *abfd)
{
unsigned int e_set = bfd_mach_avr2;
if (elf_elfheader (abfd)->e_machine == EM_AVR
|| elf_elfheader (abfd)->e_machine == EM_AVR_OLD)
{
int e_mach = elf_elfheader (abfd)->e_flags & EF_AVR_MACH;
switch (e_mach)
{
default:
case E_AVR_MACH_AVR2:
e_set = bfd_mach_avr2;
break;
case E_AVR_MACH_AVR1:
e_set = bfd_mach_avr1;
break;
case E_AVR_MACH_AVR3:
e_set = bfd_mach_avr3;
break;
case E_AVR_MACH_AVR4:
e_set = bfd_mach_avr4;
break;
case E_AVR_MACH_AVR5:
e_set = bfd_mach_avr5;
break;
case E_AVR_MACH_AVR6:
e_set = bfd_mach_avr6;
break;
}
}
return bfd_default_set_arch_mach (abfd, bfd_arch_avr,
e_set);
}
static bfd_boolean
elf32_avr_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, *irelend;
Elf_Internal_Rela *irelalign;
Elf_Internal_Sym *isym;
Elf_Internal_Sym *isymbuf = NULL;
Elf_Internal_Sym *isymend;
bfd_vma toaddr;
struct elf_link_hash_entry **sym_hashes;
struct elf_link_hash_entry **end_hashes;
unsigned int symcount;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec);
contents = elf_section_data (sec)->this_hdr.contents;
irelalign = NULL;
toaddr = sec->size;
irel = elf_section_data (sec)->relocs;
irelend = irel + sec->reloc_count;
if (toaddr - addr - count > 0)
memmove (contents + addr, contents + addr + count,
(size_t) (toaddr - addr - count));
sec->size -= count;
for (irel = elf_section_data (sec)->relocs; irel < irelend; irel++)
{
bfd_vma old_reloc_address;
bfd_vma shrinked_insn_address;
old_reloc_address = (sec->output_section->vma
+ sec->output_offset + irel->r_offset);
shrinked_insn_address = (sec->output_section->vma
+ sec->output_offset + addr - count);
if ((irel->r_offset > addr
&& irel->r_offset < toaddr))
{
if (debug_relax)
printf ("Relocation at address 0x%x needs to be moved.\n"
"Old section offset: 0x%x, New section offset: 0x%x \n",
(unsigned int) old_reloc_address,
(unsigned int) irel->r_offset,
(unsigned int) ((irel->r_offset) - count));
irel->r_offset -= count;
}
}
{
struct bfd_section *isec;
for (isec = abfd->sections; isec; isec = isec->next)
{
bfd_vma symval;
bfd_vma shrinked_insn_address;
shrinked_insn_address = (sec->output_section->vma
+ sec->output_offset + addr - count);
irelend = elf_section_data (isec)->relocs + isec->reloc_count;
for (irel = elf_section_data (isec)->relocs;
irel < irelend;
irel++)
{
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)
return FALSE;
}
if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
{
Elf_Internal_Sym *isym;
asection *sym_sec;
isym = isymbuf + ELF32_R_SYM (irel->r_info);
sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
symval = isym->st_value;
if (sym_sec == sec)
{
symval += sym_sec->output_section->vma
+ sym_sec->output_offset;
if (debug_relax)
printf ("Checking if the relocation's "
"addend needs corrections.\n"
"Address of anchor symbol: 0x%x \n"
"Address of relocation target: 0x%x \n"
"Address of relaxed insn: 0x%x \n",
(unsigned int) symval,
(unsigned int) (symval + irel->r_addend),
(unsigned int) shrinked_insn_address);
if (symval <= shrinked_insn_address
&& (symval + irel->r_addend) > shrinked_insn_address)
{
irel->r_addend -= count;
if (debug_relax)
printf ("Relocation's addend needed to be fixed \n");
}
}
}
}
}
}
isym = (Elf_Internal_Sym *) symtab_hdr->contents;
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;
}
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
elf32_avr_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, *irelend;
bfd_byte *contents = NULL;
Elf_Internal_Sym *isymbuf = NULL;
static asection *last_input_section = NULL;
static Elf_Internal_Rela *last_reloc = NULL;
struct elf32_avr_link_hash_table *htab;
htab = avr_link_hash_table (link_info);
if (htab == NULL)
return FALSE;
*again = FALSE;
if ((!htab->no_stubs) && (sec == htab->stub_sec))
{
bfd_size_type last_estimated_stub_section_size = htab->stub_sec->size;
if (debug_relax)
printf ("Relaxing the stub section. Size prior to this pass: %i\n",
(int) last_estimated_stub_section_size);
elf32_avr_size_stubs (htab->stub_sec->output_section->owner,
link_info, FALSE);
if (last_estimated_stub_section_size != htab->stub_sec->size)
*again = TRUE;
if (debug_relax)
printf ("Size of stub section after this pass: %i\n",
(int) htab->stub_sec->size);
return TRUE;
}
if (link_info->relocatable
|| (sec->flags & SEC_RELOC) == 0
|| sec->reloc_count == 0
|| (sec->flags & SEC_CODE) == 0)
return TRUE;
if (!(elf_elfheader (abfd)->e_flags & EF_AVR_LINKRELAX_PREPARED))
return TRUE;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
internal_relocs = (_bfd_elf_link_read_relocs
(abfd, sec, NULL, NULL, link_info->keep_memory));
if (internal_relocs == NULL)
goto error_return;
if (sec != last_input_section)
last_reloc = NULL;
last_input_section = sec;
irelend = internal_relocs + sec->reloc_count;
for (irel = internal_relocs; irel < irelend; irel++)
{
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)
continue;
if (contents == NULL)
{
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;
}
}
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;
}
if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
{
Elf_Internal_Sym *isym;
asection *sym_sec;
isym = isymbuf + ELF32_R_SYM (irel->r_info);
sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
symval = isym->st_value;
if (sym_sec)
symval += sym_sec->output_section->vma
+ sym_sec->output_offset;
}
else
{
unsigned long indx;
struct elf_link_hash_entry *h;
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)
continue;
symval = (h->root.u.def.value
+ h->root.u.def.section->output_section->vma
+ h->root.u.def.section->output_offset);
}
switch (ELF32_R_TYPE (irel->r_info))
{
case R_AVR_CALL:
{
bfd_vma value = symval + irel->r_addend;
bfd_vma dot, gap;
int distance_short_enough = 0;
dot = (sec->output_section->vma
+ sec->output_offset + irel->r_offset);
gap = value - dot;
if ((int) gap >= -4094 && (int) gap <= 4098)
distance_short_enough = 1;
{
int rgap;
int safety_margin;
int assumed_shrink = 600;
if (avr_pc_wrap_around > 0x4000)
assumed_shrink = 900;
safety_margin = 2 * assumed_shrink;
rgap = avr_relative_distance_considering_wrap_around (gap);
if (rgap >= (-4092 + safety_margin)
&& rgap <= (4094 - safety_margin))
distance_short_enough = 1;
}
if (distance_short_enough)
{
unsigned char code_msb;
unsigned char code_lsb;
if (debug_relax)
printf ("shrinking jump/call instruction at address 0x%x"
" in section %s\n\n",
(int) dot, sec->name);
elf_section_data (sec)->relocs = internal_relocs;
elf_section_data (sec)->this_hdr.contents = contents;
symtab_hdr->contents = (unsigned char *) isymbuf;
code_lsb = bfd_get_8 (abfd, contents + irel->r_offset);
code_msb = bfd_get_8 (abfd, contents + irel->r_offset + 1);
code_msb &= 0x94;
code_lsb &= 0x0E;
if (code_msb == 0x94 && code_lsb == 0x0E)
{
bfd_put_8 (abfd, 0x00, contents + irel->r_offset);
bfd_put_8 (abfd, 0xD0, contents + irel->r_offset + 1);
}
else if (code_msb == 0x94 && code_lsb == 0x0C)
{
bfd_put_8 (abfd, 0x00, contents + irel->r_offset);
bfd_put_8 (abfd, 0xC0, contents + irel->r_offset + 1);
}
else
abort ();
irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
R_AVR_13_PCREL);
if (!strcmp (sec->name,".vectors")
|| !strcmp (sec->name,".jumptables"))
{
bfd_put_8 (abfd, 0x00, contents + irel->r_offset + 2);
bfd_put_8 (abfd, 0x00, contents + irel->r_offset + 3);
}
else
{
if (!elf32_avr_relax_delete_bytes (abfd, sec,
irel->r_offset + 2, 2))
goto error_return;
*again = TRUE;
}
}
}
default:
{
unsigned char code_msb;
unsigned char code_lsb;
bfd_vma dot;
code_msb = bfd_get_8 (abfd, contents + irel->r_offset + 1);
code_lsb = bfd_get_8 (abfd, contents + irel->r_offset + 0);
dot = (sec->output_section->vma
+ sec->output_offset + irel->r_offset);
if (((code_msb & 0xf0) == 0xd0)
&& avr_replace_call_ret_sequences)
{
unsigned char next_insn_msb = 0;
unsigned char next_insn_lsb = 0;
if (irel->r_offset + 3 < sec->size)
{
next_insn_msb =
bfd_get_8 (abfd, contents + irel->r_offset + 3);
next_insn_lsb =
bfd_get_8 (abfd, contents + irel->r_offset + 2);
}
if ((0x95 == next_insn_msb) && (0x08 == next_insn_lsb))
{
code_msb &= 0xef;
bfd_put_8 (abfd, code_msb, contents + irel->r_offset + 1);
if (debug_relax)
printf ("converted rcall/ret sequence at address 0x%x"
" into rjmp/ret sequence. Section is %s\n\n",
(int) dot, sec->name);
*again = TRUE;
break;
}
}
else if ((0x94 == (code_msb & 0xfe))
&& (0x0e == (code_lsb & 0x0e))
&& avr_replace_call_ret_sequences)
{
unsigned char next_insn_msb = 0;
unsigned char next_insn_lsb = 0;
if (irel->r_offset + 5 < sec->size)
{
next_insn_msb =
bfd_get_8 (abfd, contents + irel->r_offset + 5);
next_insn_lsb =
bfd_get_8 (abfd, contents + irel->r_offset + 4);
}
if ((0x95 == next_insn_msb) && (0x08 == next_insn_lsb))
{
code_lsb &= 0xfd;
bfd_put_8 (abfd, code_lsb, contents + irel->r_offset);
if (debug_relax)
printf ("converted call/ret sequence at address 0x%x"
" into jmp/ret sequence. Section is %s\n\n",
(int) dot, sec->name);
*again = TRUE;
break;
}
}
else if ((0xc0 == (code_msb & 0xf0))
|| ((0x94 == (code_msb & 0xfe))
&& (0x0c == (code_lsb & 0x0e))))
{
unsigned char next_insn_msb = 0;
unsigned char next_insn_lsb = 0;
int insn_size;
if (0xc0 == (code_msb & 0xf0))
insn_size = 2;
else
insn_size = 4;
if (irel->r_offset + insn_size + 1 < sec->size)
{
next_insn_msb =
bfd_get_8 (abfd, contents + irel->r_offset
+ insn_size + 1);
next_insn_lsb =
bfd_get_8 (abfd, contents + irel->r_offset
+ insn_size);
}
if ((0x95 == next_insn_msb) && (0x08 == next_insn_lsb))
{
int there_is_preceeding_non_skip_insn = 1;
bfd_vma address_of_ret;
address_of_ret = dot + insn_size;
if (debug_relax && (insn_size == 2))
printf ("found rjmp / ret sequence at address 0x%x\n",
(int) dot);
if (debug_relax && (insn_size == 4))
printf ("found jmp / ret sequence at address 0x%x\n",
(int) dot);
if (irel->r_offset >= 2)
{
unsigned char preceeding_msb;
unsigned char preceeding_lsb;
preceeding_msb =
bfd_get_8 (abfd, contents + irel->r_offset - 1);
preceeding_lsb =
bfd_get_8 (abfd, contents + irel->r_offset - 2);
if (0x99 == preceeding_msb)
there_is_preceeding_non_skip_insn = 0;
if (0x9b == preceeding_msb)
there_is_preceeding_non_skip_insn = 0;
if ((0xfc == (preceeding_msb & 0xfe)
&& (0x00 == (preceeding_lsb & 0x08))))
there_is_preceeding_non_skip_insn = 0;
if ((0xfe == (preceeding_msb & 0xfe)
&& (0x00 == (preceeding_lsb & 0x08))))
there_is_preceeding_non_skip_insn = 0;
if (0x10 == (preceeding_msb & 0xfc))
there_is_preceeding_non_skip_insn = 0;
if (there_is_preceeding_non_skip_insn == 0)
if (debug_relax)
printf ("preceeding skip insn prevents deletion of"
" ret insn at addr 0x%x in section %s\n",
(int) dot + 2, sec->name);
}
else
{
there_is_preceeding_non_skip_insn = 0;
}
if (there_is_preceeding_non_skip_insn)
{
int deleting_ret_is_safe = 1;
unsigned int section_offset_of_ret_insn =
irel->r_offset + insn_size;
Elf_Internal_Sym *isym, *isymend;
unsigned int sec_shndx;
sec_shndx =
_bfd_elf_section_from_bfd_section (abfd, sec);
isym = (Elf_Internal_Sym *) symtab_hdr->contents;
isymend = isym + symtab_hdr->sh_info;
for (; isym < isymend; isym++)
{
if (isym->st_value == section_offset_of_ret_insn
&& isym->st_shndx == sec_shndx)
{
deleting_ret_is_safe = 0;
if (debug_relax)
printf ("local label prevents deletion of ret "
"insn at address 0x%x\n",
(int) dot + insn_size);
}
}
{
int symcount;
struct elf_link_hash_entry **sym_hashes;
struct elf_link_hash_entry **end_hashes;
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 == section_offset_of_ret_insn)
{
deleting_ret_is_safe = 0;
if (debug_relax)
printf ("global label prevents deletion of "
"ret insn at address 0x%x\n",
(int) dot + insn_size);
}
}
}
{
Elf_Internal_Rela *irel;
Elf_Internal_Rela *relend;
Elf_Internal_Shdr *symtab_hdr;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
relend = elf_section_data (sec)->relocs
+ sec->reloc_count;
for (irel = elf_section_data (sec)->relocs;
irel < relend; irel++)
{
bfd_vma reloc_target = 0;
bfd_vma symval;
Elf_Internal_Sym *isymbuf = NULL;
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)
break;
}
if (ELF32_R_SYM (irel->r_info)
< symtab_hdr->sh_info)
{
Elf_Internal_Sym *isym;
asection *sym_sec;
isym = isymbuf
+ ELF32_R_SYM (irel->r_info);
sym_sec = bfd_section_from_elf_index
(abfd, isym->st_shndx);
symval = isym->st_value;
if (sym_sec)
{
symval +=
sym_sec->output_section->vma
+ sym_sec->output_offset;
reloc_target = symval + irel->r_addend;
}
else
{
reloc_target = symval + irel->r_addend;
}
}
if (address_of_ret == reloc_target)
{
deleting_ret_is_safe = 0;
if (debug_relax)
printf ("ret from "
"rjmp/jmp ret sequence at address"
" 0x%x could not be deleted. ret"
" is target of a relocation.\n",
(int) address_of_ret);
}
}
}
if (deleting_ret_is_safe)
{
if (debug_relax)
printf ("unreachable ret instruction "
"at address 0x%x deleted.\n",
(int) dot + insn_size);
if (!elf32_avr_relax_delete_bytes (abfd, sec,
irel->r_offset + insn_size, 2))
goto error_return;
*again = TRUE;
break;
}
}
}
}
break;
}
}
}
if (contents != NULL
&& elf_section_data (sec)->this_hdr.contents != contents)
{
if (! link_info->keep_memory)
free (contents);
else
{
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;
}
static bfd_byte *
elf32_avr_get_relocated_section_contents (bfd *output_bfd,
struct bfd_link_info *link_info,
struct bfd_link_order *link_order,
bfd_byte *data,
bfd_boolean relocatable,
asymbol **symbols)
{
Elf_Internal_Shdr *symtab_hdr;
asection *input_section = link_order->u.indirect.section;
bfd *input_bfd = input_section->owner;
asection **sections = NULL;
Elf_Internal_Rela *internal_relocs = NULL;
Elf_Internal_Sym *isymbuf = NULL;
if (relocatable
|| elf_section_data (input_section)->this_hdr.contents == NULL)
return bfd_generic_get_relocated_section_contents (output_bfd, link_info,
link_order, data,
relocatable,
symbols);
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
memcpy (data, elf_section_data (input_section)->this_hdr.contents,
(size_t) input_section->size);
if ((input_section->flags & SEC_RELOC) != 0
&& input_section->reloc_count > 0)
{
asection **secpp;
Elf_Internal_Sym *isym, *isymend;
bfd_size_type amt;
internal_relocs = (_bfd_elf_link_read_relocs
(input_bfd, input_section, NULL, NULL, FALSE));
if (internal_relocs == NULL)
goto error_return;
if (symtab_hdr->sh_info != 0)
{
isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
if (isymbuf == NULL)
isymbuf = bfd_elf_get_elf_syms (input_bfd, symtab_hdr,
symtab_hdr->sh_info, 0,
NULL, NULL, NULL);
if (isymbuf == NULL)
goto error_return;
}
amt = symtab_hdr->sh_info;
amt *= sizeof (asection *);
sections = bfd_malloc (amt);
if (sections == NULL && amt != 0)
goto error_return;
isymend = isymbuf + symtab_hdr->sh_info;
for (isym = isymbuf, secpp = sections; isym < isymend; ++isym, ++secpp)
{
asection *isec;
if (isym->st_shndx == SHN_UNDEF)
isec = bfd_und_section_ptr;
else if (isym->st_shndx == SHN_ABS)
isec = bfd_abs_section_ptr;
else if (isym->st_shndx == SHN_COMMON)
isec = bfd_com_section_ptr;
else
isec = bfd_section_from_elf_index (input_bfd, isym->st_shndx);
*secpp = isec;
}
if (! elf32_avr_relocate_section (output_bfd, link_info, input_bfd,
input_section, data, internal_relocs,
isymbuf, sections))
goto error_return;
if (sections != NULL)
free (sections);
if (isymbuf != NULL
&& symtab_hdr->contents != (unsigned char *) isymbuf)
free (isymbuf);
if (elf_section_data (input_section)->relocs != internal_relocs)
free (internal_relocs);
}
return data;
error_return:
if (sections != NULL)
free (sections);
if (isymbuf != NULL
&& symtab_hdr->contents != (unsigned char *) isymbuf)
free (isymbuf);
if (internal_relocs != NULL
&& elf_section_data (input_section)->relocs != internal_relocs)
free (internal_relocs);
return NULL;
}
static char *
avr_stub_name (const asection *symbol_section,
const bfd_vma symbol_offset,
const Elf_Internal_Rela *rela)
{
char *stub_name;
bfd_size_type len;
len = 8 + 1 + 8 + 1 + 1;
stub_name = bfd_malloc (len);
sprintf (stub_name, "%08x+%08x",
symbol_section->id & 0xffffffff,
(unsigned int) ((rela->r_addend & 0xffffffff) + symbol_offset));
return stub_name;
}
static struct elf32_avr_stub_hash_entry *
avr_add_stub (const char *stub_name,
struct elf32_avr_link_hash_table *htab)
{
struct elf32_avr_stub_hash_entry *hsh;
hsh = avr_stub_hash_lookup (&htab->bstab, stub_name, TRUE, FALSE);
if (hsh == NULL)
{
(*_bfd_error_handler) (_("%B: cannot create stub entry %s"),
NULL, stub_name);
return NULL;
}
hsh->stub_offset = 0;
return hsh;
}
static bfd_boolean
avr_build_one_stub (struct bfd_hash_entry *bh, void *in_arg)
{
struct elf32_avr_stub_hash_entry *hsh;
struct bfd_link_info *info;
struct elf32_avr_link_hash_table *htab;
bfd *stub_bfd;
bfd_byte *loc;
bfd_vma target;
bfd_vma starget;
bfd_vma jmp_insn = 0x0000940c;
hsh = avr_stub_hash_entry (bh);
if (!hsh->is_actually_needed)
return TRUE;
info = (struct bfd_link_info *) in_arg;
htab = avr_link_hash_table (info);
if (htab == NULL)
return FALSE;
target = hsh->target_value;
hsh->stub_offset = htab->stub_sec->size;
loc = htab->stub_sec->contents + hsh->stub_offset;
stub_bfd = htab->stub_sec->owner;
if (debug_stubs)
printf ("Building one Stub. Address: 0x%x, Offset: 0x%x\n",
(unsigned int) target,
(unsigned int) hsh->stub_offset);
if (target & 1)
return FALSE;
starget = target >> 1;
jmp_insn |= ((starget & 0x10000) | ((starget << 3) & 0x1f00000)) >> 16;
bfd_put_16 (stub_bfd, jmp_insn, loc);
bfd_put_16 (stub_bfd, (bfd_vma) starget & 0xffff, loc + 2);
htab->stub_sec->size += 4;
{
unsigned int nr;
nr = htab->amt_entry_cnt + 1;
if (nr <= htab->amt_max_entry_cnt)
{
htab->amt_entry_cnt = nr;
htab->amt_stub_offsets[nr - 1] = hsh->stub_offset;
htab->amt_destination_addr[nr - 1] = target;
}
}
return TRUE;
}
static bfd_boolean
avr_mark_stub_not_to_be_necessary (struct bfd_hash_entry *bh,
void *in_arg)
{
struct elf32_avr_stub_hash_entry *hsh;
struct elf32_avr_link_hash_table *htab;
htab = in_arg;
hsh = avr_stub_hash_entry (bh);
hsh->is_actually_needed = FALSE;
return TRUE;
}
static bfd_boolean
avr_size_one_stub (struct bfd_hash_entry *bh, void *in_arg)
{
struct elf32_avr_stub_hash_entry *hsh;
struct elf32_avr_link_hash_table *htab;
int size;
hsh = avr_stub_hash_entry (bh);
htab = in_arg;
if (hsh->is_actually_needed)
size = 4;
else
size = 0;
htab->stub_sec->size += size;
return TRUE;
}
void
elf32_avr_setup_params (struct bfd_link_info *info,
bfd *avr_stub_bfd,
asection *avr_stub_section,
bfd_boolean no_stubs,
bfd_boolean deb_stubs,
bfd_boolean deb_relax,
bfd_vma pc_wrap_around,
bfd_boolean call_ret_replacement)
{
struct elf32_avr_link_hash_table *htab = avr_link_hash_table (info);
if (htab == NULL)
return;
htab->stub_sec = avr_stub_section;
htab->stub_bfd = avr_stub_bfd;
htab->no_stubs = no_stubs;
debug_relax = deb_relax;
debug_stubs = deb_stubs;
avr_pc_wrap_around = pc_wrap_around;
avr_replace_call_ret_sequences = call_ret_replacement;
}
int
elf32_avr_setup_section_lists (bfd *output_bfd,
struct bfd_link_info *info)
{
bfd *input_bfd;
unsigned int bfd_count;
int top_id, top_index;
asection *section;
asection **input_list, **list;
bfd_size_type amt;
struct elf32_avr_link_hash_table *htab = avr_link_hash_table(info);
if (htab == NULL || htab->no_stubs)
return 0;
for (input_bfd = info->input_bfds, bfd_count = 0, top_id = 0;
input_bfd != NULL;
input_bfd = input_bfd->link_next)
{
bfd_count += 1;
for (section = input_bfd->sections;
section != NULL;
section = section->next)
if (top_id < section->id)
top_id = section->id;
}
htab->bfd_count = bfd_count;
for (section = output_bfd->sections, top_index = 0;
section != NULL;
section = section->next)
if (top_index < section->index)
top_index = section->index;
htab->top_index = top_index;
amt = sizeof (asection *) * (top_index + 1);
input_list = bfd_malloc (amt);
htab->input_list = input_list;
if (input_list == NULL)
return -1;
list = input_list + top_index;
do
*list = bfd_abs_section_ptr;
while (list-- != input_list);
for (section = output_bfd->sections;
section != NULL;
section = section->next)
if ((section->flags & SEC_CODE) != 0)
input_list[section->index] = NULL;
return 1;
}
static int
get_local_syms (bfd *input_bfd, struct bfd_link_info *info)
{
unsigned int bfd_indx;
Elf_Internal_Sym *local_syms, **all_local_syms;
struct elf32_avr_link_hash_table *htab = avr_link_hash_table (info);
if (htab == NULL)
return -1;
bfd_size_type amt = sizeof (Elf_Internal_Sym *) * htab->bfd_count;
all_local_syms = bfd_zmalloc (amt);
htab->all_local_syms = all_local_syms;
if (all_local_syms == NULL)
return -1;
for (bfd_indx = 0;
input_bfd != NULL;
input_bfd = input_bfd->link_next, bfd_indx++)
{
Elf_Internal_Shdr *symtab_hdr;
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
if (symtab_hdr->sh_info == 0)
continue;
local_syms = (Elf_Internal_Sym *) symtab_hdr->contents;
if (local_syms == NULL)
{
local_syms = bfd_elf_get_elf_syms (input_bfd, symtab_hdr,
symtab_hdr->sh_info, 0,
NULL, NULL, NULL);
symtab_hdr->contents = (unsigned char *) local_syms;
}
if (local_syms == NULL)
return -1;
all_local_syms[bfd_indx] = local_syms;
}
return 0;
}
#define ADD_DUMMY_STUBS_FOR_DEBUGGING 0
bfd_boolean
elf32_avr_size_stubs (bfd *output_bfd,
struct bfd_link_info *info,
bfd_boolean is_prealloc_run)
{
struct elf32_avr_link_hash_table *htab;
int stub_changed = 0;
htab = avr_link_hash_table (info);
if (htab == NULL)
return FALSE;
htab->vector_base = htab->stub_sec->output_section->vma;
if (get_local_syms (info->input_bfds, info))
{
if (htab->all_local_syms)
goto error_ret_free_local;
return FALSE;
}
if (ADD_DUMMY_STUBS_FOR_DEBUGGING)
{
struct elf32_avr_stub_hash_entry *test;
test = avr_add_stub ("Hugo",htab);
test->target_value = 0x123456;
test->stub_offset = 13;
test = avr_add_stub ("Hugo2",htab);
test->target_value = 0x84210;
test->stub_offset = 14;
}
while (1)
{
bfd *input_bfd;
unsigned int bfd_indx;
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++)
{
Elf_Internal_Shdr *symtab_hdr;
asection *section;
Elf_Internal_Sym *local_syms;
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
if (symtab_hdr->sh_info == 0)
continue;
local_syms = htab->all_local_syms[bfd_indx];
for (section = input_bfd->sections;
section != NULL;
section = section->next)
{
Elf_Internal_Rela *internal_relocs, *irelaend, *irela;
if ((section->flags & SEC_RELOC) == 0
|| section->reloc_count == 0)
continue;
if (section->output_section == NULL
|| section->output_section->owner != output_bfd)
continue;
internal_relocs
= _bfd_elf_link_read_relocs (input_bfd, section, NULL, NULL,
info->keep_memory);
if (internal_relocs == NULL)
goto error_ret_free_local;
irela = internal_relocs;
irelaend = irela + section->reloc_count;
for (; irela < irelaend; irela++)
{
unsigned int r_type, r_indx;
struct elf32_avr_stub_hash_entry *hsh;
asection *sym_sec;
bfd_vma sym_value;
bfd_vma destination;
struct elf_link_hash_entry *hh;
char *stub_name;
r_type = ELF32_R_TYPE (irela->r_info);
r_indx = ELF32_R_SYM (irela->r_info);
if (!((r_type == R_AVR_16_PM)
|| (r_type == R_AVR_LO8_LDI_GS)
|| (r_type == R_AVR_HI8_LDI_GS)))
continue;
sym_sec = NULL;
sym_value = 0;
destination = 0;
hh = NULL;
if (r_indx < symtab_hdr->sh_info)
{
Elf_Internal_Sym *sym;
Elf_Internal_Shdr *hdr;
sym = local_syms + r_indx;
hdr = elf_elfsections (input_bfd)[sym->st_shndx];
sym_sec = hdr->bfd_section;
if (ELF_ST_TYPE (sym->st_info) != STT_SECTION)
sym_value = sym->st_value;
destination = (sym_value + irela->r_addend
+ sym_sec->output_offset
+ sym_sec->output_section->vma);
}
else
{
int e_indx;
e_indx = r_indx - symtab_hdr->sh_info;
hh = elf_sym_hashes (input_bfd)[e_indx];
while (hh->root.type == bfd_link_hash_indirect
|| hh->root.type == bfd_link_hash_warning)
hh = (struct elf_link_hash_entry *)
(hh->root.u.i.link);
if (hh->root.type == bfd_link_hash_defined
|| hh->root.type == bfd_link_hash_defweak)
{
sym_sec = hh->root.u.def.section;
sym_value = hh->root.u.def.value;
if (sym_sec->output_section != NULL)
destination = (sym_value + irela->r_addend
+ sym_sec->output_offset
+ sym_sec->output_section->vma);
}
else if (hh->root.type == bfd_link_hash_undefweak)
{
if (! info->shared)
continue;
}
else if (hh->root.type == bfd_link_hash_undefined)
{
if (! (info->unresolved_syms_in_objects == RM_IGNORE
&& (ELF_ST_VISIBILITY (hh->other)
== STV_DEFAULT)))
continue;
}
else
{
bfd_set_error (bfd_error_bad_value);
error_ret_free_internal:
if (elf_section_data (section)->relocs == NULL)
free (internal_relocs);
goto error_ret_free_local;
}
}
if (! avr_stub_is_required_for_16_bit_reloc
(destination - htab->vector_base))
{
if (!is_prealloc_run)
continue;
}
stub_name = avr_stub_name (sym_sec, sym_value, irela);
if (!stub_name)
goto error_ret_free_internal;
hsh = avr_stub_hash_lookup (&htab->bstab,
stub_name,
FALSE, FALSE);
if (hsh != NULL)
{
hsh->is_actually_needed = TRUE;
hsh->target_value = destination;
free (stub_name);
continue;
}
hsh = avr_add_stub (stub_name, htab);
if (hsh == NULL)
{
free (stub_name);
goto error_ret_free_internal;
}
hsh->is_actually_needed = TRUE;
hsh->target_value = destination;
if (debug_stubs)
printf ("Adding stub with destination 0x%x to the"
" hash table.\n", (unsigned int) destination);
if (debug_stubs)
printf ("(Pre-Alloc run: %i)\n", is_prealloc_run);
stub_changed = TRUE;
}
if (elf_section_data (section)->relocs == NULL)
free (internal_relocs);
}
}
htab->stub_sec->size = 0;
bfd_hash_traverse (&htab->bstab, avr_size_one_stub, htab);
if (!stub_changed)
break;
stub_changed = FALSE;
}
free (htab->all_local_syms);
return TRUE;
error_ret_free_local:
free (htab->all_local_syms);
return FALSE;
}
bfd_boolean
elf32_avr_build_stubs (struct bfd_link_info *info)
{
asection *stub_sec;
struct bfd_hash_table *table;
struct elf32_avr_link_hash_table *htab;
bfd_size_type total_size = 0;
htab = avr_link_hash_table (info);
if (htab == NULL)
return FALSE;
for (stub_sec = htab->stub_bfd->sections;
stub_sec != NULL;
stub_sec = stub_sec->next)
{
bfd_size_type size;
size = stub_sec->size;
total_size += size;
stub_sec->contents = bfd_zalloc (htab->stub_bfd, size);
if (stub_sec->contents == NULL && size != 0)
return FALSE;
stub_sec->size = 0;
}
htab->amt_entry_cnt = 0;
htab->amt_max_entry_cnt = total_size / 4;
htab->amt_stub_offsets = bfd_malloc (sizeof (bfd_vma)
* htab->amt_max_entry_cnt);
htab->amt_destination_addr = bfd_malloc (sizeof (bfd_vma)
* htab->amt_max_entry_cnt );
if (debug_stubs)
printf ("Allocating %i entries in the AMT\n", htab->amt_max_entry_cnt);
table = &htab->bstab;
bfd_hash_traverse (table, avr_build_one_stub, info);
if (debug_stubs)
printf ("Final Stub section Size: %i\n", (int) htab->stub_sec->size);
return TRUE;
}
#define ELF_ARCH bfd_arch_avr
#define ELF_MACHINE_CODE EM_AVR
#define ELF_MACHINE_ALT1 EM_AVR_OLD
#define ELF_MAXPAGESIZE 1
#define TARGET_LITTLE_SYM bfd_elf32_avr_vec
#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
#define elf_backend_relocate_section elf32_avr_relocate_section
#define elf_backend_check_relocs elf32_avr_check_relocs
#define elf_backend_can_gc_sections 1
#define elf_backend_rela_normal 1
#define elf_backend_final_write_processing \
bfd_elf_avr_final_write_processing
#define elf_backend_object_p elf32_avr_object_p
#define bfd_elf32_bfd_relax_section elf32_avr_relax_section
#define bfd_elf32_bfd_get_relocated_section_contents \
elf32_avr_get_relocated_section_contents
#include "elf32-target.h"