* elflink.c (_bfd_elf_gc_mark_hook): New function.
[deliverable/binutils-gdb.git] / bfd / elf32-avr.c
index ec42bb335d1a2c7629ec176d160a603b8da9eebc..40cc0830fb7b28302e7f5c7883ced31525ea9e0b 100644 (file)
 #include "libbfd.h"
 #include "elf-bfd.h"
 #include "elf/avr.h"
+#include "elf32-avr.h"
+
+/* Enable debugging printout at stdout with this variable.  */
+static bfd_boolean debug_relax = FALSE;
+
+/* Enable debugging printout at stdout with this variable.  */
+static bfd_boolean debug_stubs = FALSE;
+
+/* Hash table initialization and handling.  Code is taken from the hppa port
+   and adapted to the needs of AVR.  */
+
+/* We use two hash tables to hold information for linking avr objects.
+
+   The first is the elf32_avr_link_hash_tablse which is derived from the
+   stanard ELF linker hash table.  We use this as a place to attach the other
+   hash table and some static information.
+
+   The second is the stub hash table which is derived from the base BFD
+   hash table.  The stub hash table holds the information on the linker
+   stubs.  */
+
+struct elf32_avr_stub_hash_entry
+{
+  /* Base hash table entry structure.  */
+  struct bfd_hash_entry bh_root;
+
+  /* Offset within stub_sec of the beginning of this stub.  */
+  bfd_vma stub_offset;
+
+  /* Given the symbol's value and its section we can determine its final
+     value when building the stubs (so the stub knows where to jump).  */
+  bfd_vma target_value;
+
+  /* This way we could mark stubs to be no longer necessary.  */
+  bfd_boolean is_actually_needed;
+};
+
+struct elf32_avr_link_hash_table
+{
+  /* The main hash table.  */
+  struct elf_link_hash_table etab;
+
+  /* The stub hash table.  */
+  struct bfd_hash_table bstab;
+
+  bfd_boolean no_stubs;
+
+  /* Linker stub bfd.  */
+  bfd *stub_bfd;
+
+  /* The stub section.  */
+  asection *stub_sec;
+
+  /* Usually 0, unless we are generating code for a bootloader.  Will
+     be initialized by elf32_avr_size_stubs to the vma offset of the
+     output section associated with the stub section.  */
+  bfd_vma vector_base;
+
+  /* Assorted information used by elf32_avr_size_stubs.  */
+  unsigned int        bfd_count;
+  int                 top_index;
+  asection **         input_list;
+  Elf_Internal_Sym ** all_local_syms;
+
+  /* Tables for mapping vma beyond the 128k boundary to the address of the
+     corresponding stub.  (AMT)
+     "amt_max_entry_cnt" reflects the number of entries that memory is allocated
+     for in the "amt_stub_offsets" and "amt_destination_addr" arrays.
+     "amt_entry_cnt" informs how many of these entries actually contain
+     useful data.  */
+  unsigned int amt_entry_cnt;
+  unsigned int amt_max_entry_cnt;
+  bfd_vma *    amt_stub_offsets;
+  bfd_vma *    amt_destination_addr;
+};
+
+/* Various hash macros and functions.  */
+#define avr_link_hash_table(p) \
+  ((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[] =
 {
@@ -101,7 +187,8 @@ static reloc_howto_type elf_avr_howto_table[] =
         0xffff,                /* dst_mask */
         FALSE),                /* pcrel_offset */
 
-  /* A 16 bit absolute relocation for command address.  */
+  /* A 16 bit absolute relocation for command address
+     Will be changed when linker stubs are needed.  */
   HOWTO (R_AVR_16_PM,          /* type */
         1,                     /* rightshift */
         1,                     /* size (0 = byte, 1 = short, 2 = long) */
@@ -207,7 +294,7 @@ static reloc_howto_type elf_avr_howto_table[] =
         0xffff,                /* dst_mask */
         FALSE),                /* pcrel_offset */
   /* A low 8 bit absolute relocation of 24 bit program memory address.
-     For LDI command.  */
+     For LDI command.  Will not be changed when linker stubs are needed. */
   HOWTO (R_AVR_LO8_LDI_PM,     /* type */
         1,                     /* rightshift */
         1,                     /* size (0 = byte, 1 = short, 2 = long) */
@@ -221,8 +308,8 @@ static reloc_howto_type elf_avr_howto_table[] =
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
         FALSE),                /* pcrel_offset */
-  /* A high 8 bit absolute relocation of 16 bit program memory address.
-     For LDI command.  */
+  /* A low 8 bit absolute relocation of 24 bit program memory address.
+     For LDI command.  Will not be changed when linker stubs are needed. */
   HOWTO (R_AVR_HI8_LDI_PM,     /* type */
         9,                     /* rightshift */
         1,                     /* size (0 = byte, 1 = short, 2 = long) */
@@ -236,8 +323,8 @@ static reloc_howto_type elf_avr_howto_table[] =
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
         FALSE),                /* pcrel_offset */
-  /* A high 8 bit absolute relocation of 24 bit program memory address.
-     For LDI command.  */
+  /* A low 8 bit absolute relocation of 24 bit program memory address.
+     For LDI command.  Will not be changed when linker stubs are needed. */
   HOWTO (R_AVR_HH8_LDI_PM,     /* type */
         17,                    /* rightshift */
         1,                     /* size (0 = byte, 1 = short, 2 = long) */
@@ -251,8 +338,8 @@ static reloc_howto_type elf_avr_howto_table[] =
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
         FALSE),                /* pcrel_offset */
-  /* A low 8 bit absolute relocation of a negative 24 bit
-     program memory address.  For LDI command.  */
+  /* A low 8 bit absolute relocation of 24 bit program memory address.
+     For LDI command.  Will not be changed when linker stubs are needed. */
   HOWTO (R_AVR_LO8_LDI_PM_NEG, /* type */
         1,                     /* rightshift */
         1,                     /* size (0 = byte, 1 = short, 2 = long) */
@@ -266,8 +353,8 @@ static reloc_howto_type elf_avr_howto_table[] =
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
         FALSE),                /* pcrel_offset */
-  /* A high 8 bit absolute relocation of a negative 16 bit
-     program memory address.  For LDI command.  */
+  /* A low 8 bit absolute relocation of 24 bit program memory address.
+     For LDI command.  Will not be changed when linker stubs are needed. */
   HOWTO (R_AVR_HI8_LDI_PM_NEG, /* type */
         9,                     /* rightshift */
         1,                     /* size (0 = byte, 1 = short, 2 = long) */
@@ -281,8 +368,8 @@ static reloc_howto_type elf_avr_howto_table[] =
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
         FALSE),                /* pcrel_offset */
-  /* A high 8 bit absolute relocation of a negative 24 bit
-     program memory address.  For LDI command.  */
+  /* A low 8 bit absolute relocation of 24 bit program memory address.
+     For LDI command.  Will not be changed when linker stubs are needed. */
   HOWTO (R_AVR_HH8_LDI_PM_NEG, /* type */
         17,                    /* rightshift */
         1,                     /* size (0 = byte, 1 = short, 2 = long) */
@@ -382,7 +469,37 @@ static reloc_howto_type elf_avr_howto_table[] =
         FALSE,                 /* partial_inplace */
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
-        FALSE)                 /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
+  /* A low 8 bit absolute relocation of 24 bit program memory address.
+     For LDI command.  Will be changed when linker stubs are needed. */
+  HOWTO (R_AVR_LO8_LDI_GS,      /* type */
+         1,                     /* rightshift */
+         1,                     /* size (0 = byte, 1 = short, 2 = long) */
+         8,                     /* bitsize */
+         FALSE,                 /* pc_relative */
+         0,                     /* bitpos */
+         complain_overflow_dont, /* complain_on_overflow */
+         bfd_elf_generic_reloc, /* special_function */
+         "R_AVR_LO8_LDI_GS",    /* name */
+         FALSE,                 /* partial_inplace */
+         0xffff,                /* src_mask */
+         0xffff,                /* dst_mask */
+         FALSE),                /* pcrel_offset */
+  /* A low 8 bit absolute relocation of 24 bit program memory address.
+     For LDI command.  Will be changed when linker stubs are needed. */
+  HOWTO (R_AVR_HI8_LDI_GS,      /* type */
+         9,                     /* rightshift */
+         1,                     /* size (0 = byte, 1 = short, 2 = long) */
+         8,                     /* bitsize */
+         FALSE,                 /* pc_relative */
+         0,                     /* bitpos */
+         complain_overflow_dont, /* complain_on_overflow */
+         bfd_elf_generic_reloc, /* special_function */
+         "R_AVR_HI8_LDI_GS",    /* name */
+         FALSE,                 /* partial_inplace */
+         0xffff,                /* src_mask */
+         0xffff,                /* dst_mask */
+         FALSE)                 /* pcrel_offset */
 };
 
 /* Map BFD reloc types to AVR ELF reloc types.  */
@@ -393,7 +510,7 @@ struct avr_reloc_map
   unsigned int elf_reloc_val;
 };
 
- static const struct avr_reloc_map avr_reloc_map[] =
+static const struct avr_reloc_map avr_reloc_map[] =
 {
   { BFD_RELOC_NONE,                 R_AVR_NONE },
   { BFD_RELOC_32,                   R_AVR_32 },
@@ -410,7 +527,9 @@ struct avr_reloc_map
   { 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 },
@@ -429,10 +548,104 @@ struct avr_reloc_map
    that we will never suggest a wrap-around jump during relaxation.
    The logic of the source code later on assumes that in
    avr_pc_wrap_around one single bit is set.  */
+static bfd_vma avr_pc_wrap_around = 0x10000000;
+
+/* If this variable holds a value different from zero, the linker relaxation
+   machine will try to optimize call/ret sequences by a single jump
+   instruction. This option could be switched off by a linker switch.  */
+static int avr_replace_call_ret_sequences = 1;
+\f
+/* Initialize an entry in the stub hash table.  */
+
+static struct bfd_hash_entry *
+stub_hash_newfunc (struct bfd_hash_entry *entry,
+                   struct bfd_hash_table *table,
+                   const char *string)
+{
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (entry == NULL)
+    {
+      entry = bfd_hash_allocate (table,
+                                 sizeof (struct elf32_avr_stub_hash_entry));
+      if (entry == NULL)
+        return entry;
+    }
+
+  /* Call the allocation method of the superclass.  */
+  entry = bfd_hash_newfunc (entry, table, string);
+  if (entry != NULL)
+    {
+      struct elf32_avr_stub_hash_entry *hsh;
+
+      /* Initialize the local fields.  */
+      hsh = avr_stub_hash_entry (entry);
+      hsh->stub_offset = 0;
+      hsh->target_value = 0;
+    }
+
+  return entry;
+}
 
-unsigned int avr_pc_wrap_around = 0x10000000;
+/* 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).  */
+
+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,
+                                      _bfd_elf_link_hash_newfunc,
+                                      sizeof (struct elf_link_hash_entry)))
+    {
+      free (htab);
+      return NULL;
+    }
+
+  /* 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;
+
+  htab->stub_bfd = NULL;
+  htab->stub_sec = NULL;
+
+  /* Initialize the address mapping table.  */
+  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;
+}
+
+/* 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_generic_link_hash_table_free (btab);
+}
 
 /* Calculates the effective distance of a pc relative jump/call.  */
+
 static int
 avr_relative_distance_considering_wrap_around (unsigned int distance)
 {
@@ -455,10 +668,8 @@ bfd_elf32_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
   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];
-    }
+    if (avr_reloc_map[i].bfd_reloc_val == code)
+      return &elf_avr_howto_table[avr_reloc_map[i].elf_reloc_val];
 
   return NULL;
 }
@@ -477,48 +688,6 @@ avr_info_to_howto_rela (bfd *abfd ATTRIBUTE_UNUSED,
   cache_ptr->howto = &elf_avr_howto_table[r_type];
 }
 
-static asection *
-elf32_avr_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)
-    {
-      switch (ELF32_R_TYPE (rel->r_info))
-       {
-       default:
-         switch (h->root.type)
-           {
-           case bfd_link_hash_defined:
-           case bfd_link_hash_defweak:
-             return h->root.u.def.section;
-
-           case bfd_link_hash_common:
-             return h->root.u.c.p->section;
-
-           default:
-             break;
-           }
-       }
-    }
-  else
-    return bfd_section_from_elf_index (sec->owner, sym->st_shndx);
-
-  return NULL;
-}
-
-static bfd_boolean
-elf32_avr_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 avr.  */
-  return TRUE;
-}
-
 /* Look through the relocs for a section during the first phase.
    Since we don't do .gots or .plts, we just need to consider the
    virtual table relocs for gc.  */
@@ -564,20 +733,57 @@ elf32_avr_check_relocs (bfd *abfd,
   return TRUE;
 }
 
+static bfd_boolean
+avr_stub_is_required_for_16_bit_reloc (bfd_vma relocation)
+{
+  return (relocation >= 0x020000);
+}
+
+/* Returns the address of the corresponding stub if there is one.
+   Returns otherwise an address above 0x020000.  This function
+   could also be used, if there is no knowledge on the section where
+   the destination is found.  */
+
+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 an address that could not be reached by 16 bit relocs.  */
+  return 0x020000;
+}
+
 /* Perform a single relocation.  By default we use the standard BFD
    routines, but a few relocs, we have to do them ourselves.  */
 
 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)
+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;
+  /* Usually is 0, unless we are generating code for a bootloader.  */
+  bfd_signed_vma        base_addr = htab->vector_base;
+
+  /* Absolute addr of the reloc in the final excecutable.  */
+  reloc_addr = rel->r_offset + input_section->output_section->vma
+              + input_section->output_offset;
 
   switch (howto->type)
     {
@@ -748,9 +954,31 @@ avr_final_link_relocate (reloc_howto_type *  howto,
       bfd_put_16 (input_bfd, x, contents);
       break;
 
+    case R_AVR_LO8_LDI_GS:
+      use_stubs = (!htab->no_stubs);
+      /* Fall through.  */
     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;
+
+          /* We need to use the address of the stub instead.  */
+          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;
@@ -759,9 +987,31 @@ avr_final_link_relocate (reloc_howto_type *  howto,
       bfd_put_16 (input_bfd, x, contents);
       break;
 
+    case R_AVR_HI8_LDI_GS:
+      use_stubs = (!htab->no_stubs);
+      /* Fall through.  */
     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;
+
+          /* We need to use the address of the stub instead.  */
+          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;
@@ -833,6 +1083,35 @@ avr_final_link_relocate (reloc_howto_type *  howto,
       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;
+
+          /* We need to use the address of the stub instead.  */
+          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,
@@ -858,6 +1137,7 @@ elf32_avr_relocate_section (bfd *output_bfd ATTRIBUTE_UNUSED,
   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->relocatable)
     return TRUE;
@@ -909,7 +1189,7 @@ elf32_avr_relocate_section (bfd *output_bfd ATTRIBUTE_UNUSED,
        }
 
       r = avr_final_link_relocate (howto, input_bfd, input_section,
-                                  contents, rel, relocation);
+                                  contents, rel, relocation, htab);
 
       if (r != bfd_reloc_ok)
        {
@@ -990,6 +1270,10 @@ bfd_elf_avr_final_write_processing (bfd *abfd,
     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;
@@ -1032,6 +1316,10 @@ elf32_avr_object_p (bfd *abfd)
        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,
@@ -1039,9 +1327,6 @@ elf32_avr_object_p (bfd *abfd)
 }
 
 
-/* Enable debugging printout at stdout with a value of 1.  */
-#define DEBUG_RELAX 0
-
 /* Delete some bytes from a section while changing the size of an instruction.
    The parameter "addr" denotes the section-relative offset pointing just
    behind the shrinked instruction. "addr+count" point at the first
@@ -1049,9 +1334,9 @@ elf32_avr_object_p (bfd *abfd)
 
 static bfd_boolean
 elf32_avr_relax_delete_bytes (bfd *abfd,
-                             asection *sec,
+                              asection *sec,
                               bfd_vma addr,
-                             int count)
+                              int count)
 {
   Elf_Internal_Shdr *symtab_hdr;
   unsigned int sec_shndx;
@@ -1085,10 +1370,9 @@ elf32_avr_relax_delete_bytes (bfd *abfd,
              (size_t) (toaddr - addr - count));
   sec->size -= count;
 
-  /* Adjust all the relocs.  */
+  /* Adjust all the reloc addresses.  */
   for (irel = elf_section_data (sec)->relocs; irel < irelend; irel++)
     {
-      bfd_vma symval;
       bfd_vma old_reloc_address;
       bfd_vma shrinked_insn_address;
 
@@ -1101,7 +1385,7 @@ elf32_avr_relax_delete_bytes (bfd *abfd,
       if ((irel->r_offset > addr
            && irel->r_offset < toaddr))
         {
-          if (DEBUG_RELAX)
+          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,
@@ -1111,69 +1395,91 @@ elf32_avr_relax_delete_bytes (bfd *abfd,
           irel->r_offset -= count;
         }
 
-      /* The reloc's own addresses are now ok. However, we need to readjust
-         the reloc's addend if two conditions are met:
-         1.) the reloc is relative to a symbol in this section that
-             is located in front of the shrinked instruction
-         2.) symbol plus addend end up behind the shrinked instruction.
-
-         This should happen only for local symbols that are progmem related.  */
-
-      /* 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)
-             return FALSE;
-         }
-
-      /* 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);
-          sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
-          symval = isym->st_value;
-          /* If the reloc is absolute, it will not have
-             a symbol or section associated with it.  */
-          if (sym_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)
+   /* The reloc's own addresses are now ok. However, we need to readjust
+      the reloc's addend, i.e. the reloc's value if two conditions are met:
+      1.) the reloc is relative to a symbol in this section that
+          is located in front of the shrinked instruction
+      2.) symbol plus addend end up behind the shrinked instruction.
+
+      The most common case where this happens are relocs relative to
+      the section-start symbol.
+
+      This step needs to be done for all of the sections of the bfd.  */
+
+  {
+    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++)
+         {
+           /* 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)
+                 return FALSE;
+             }
+
+           /* 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);
+               sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
+               symval = isym->st_value;
+               /* If the reloc is absolute, it will not have
+                  a symbol or section associated with it.  */
+               if (sym_sec == sec)
                  {
-                   irel->r_addend -= count;
-
-                   if (DEBUG_RELAX)
-                     printf ("Anchor symbol and relocation target bracket "
-                             "shrinked insn address.\n"
-                             "Need for new addend : 0x%x\n",
-                             (unsigned int) irel->r_addend);
+                   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");
+                     }
                  }
-            }
-         /* else ... Reference symbol is absolute.  No adjustment needed.  */
-        }
-      /* else ... Reference symbol is extern. No need for adjusting the addend.  */
-    }
+              /* else...Reference symbol is absolute.  No adjustment needed.  */
+            }
+          /* else...Reference symbol is extern.  No need for adjusting
+             the addend.  */
+        }
+     }
+  }
 
   /* Adjust the local symbols defined in this section.  */
   isym = (Elf_Internal_Sym *) symtab_hdr->contents;
@@ -1238,7 +1544,7 @@ elf32_avr_relax_delete_bytes (bfd *abfd,
    contains 4-byte jump instructions whose relative offset must not
    be changed.  */
 
-static  bfd_boolean
+static bfd_boolean
 elf32_avr_relax_section (bfd *abfd,
                         asection *sec,
                          struct bfd_link_info *link_info,
@@ -1251,10 +1557,37 @@ elf32_avr_relax_section (bfd *abfd,
   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);
 
   /* Assume nothing changes.  */
   *again = FALSE;
 
+  if ((!htab->no_stubs) && (sec == htab->stub_sec))
+    {
+      /* We are just relaxing the stub section.
+        Let's calculate the size needed again.  */
+      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);
+
+      /* Check if the number of trampolines changed.  */
+      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;
+    }
+
   /* 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.  */
@@ -1421,7 +1754,7 @@ elf32_avr_relax_section (bfd *abfd,
                 unsigned char code_msb;
                 unsigned char code_lsb;
 
-                if (DEBUG_RELAX)
+                if (debug_relax)
                   printf ("shrinking jump/call instruction at address 0x%x"
                           " in section %s\n\n",
                           (int) dot, sec->name);
@@ -1496,8 +1829,9 @@ elf32_avr_relax_section (bfd *abfd,
                    + sec->output_offset + irel->r_offset);
 
             /* Here we look for rcall/ret or call/ret sequences that could be
-               safely replaced by rjmp/ret or jmp/ret */
-            if (0xd0 == (code_msb & 0xf0))
+               safely replaced by rjmp/ret or jmp/ret.  */
+            if (((code_msb & 0xf0) == 0xd0)
+                && avr_replace_call_ret_sequences)
               {
                 /* This insn is a rcall.  */
                 unsigned char next_insn_msb = 0;
@@ -1517,7 +1851,7 @@ elf32_avr_relax_section (bfd *abfd,
                        into a rjmp instruction.  */
                     code_msb &= 0xef;
                     bfd_put_8 (abfd, code_msb, contents + irel->r_offset + 1);
-                    if (DEBUG_RELAX)
+                    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);
@@ -1526,7 +1860,8 @@ elf32_avr_relax_section (bfd *abfd,
                   }
               }
             else if ((0x94 == (code_msb & 0xfe))
-                      && (0x0e == (code_lsb & 0x0e)))
+                    && (0x0e == (code_lsb & 0x0e))
+                    && avr_replace_call_ret_sequences)
               {
                 /* This insn is a call.  */
                 unsigned char next_insn_msb = 0;
@@ -1547,7 +1882,7 @@ elf32_avr_relax_section (bfd *abfd,
 
                     code_lsb &= 0xfd;
                     bfd_put_8 (abfd, code_lsb, contents + irel->r_offset);
-                    if (DEBUG_RELAX)
+                    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);
@@ -1590,10 +1925,10 @@ elf32_avr_relax_section (bfd *abfd,
 
                     address_of_ret = dot + insn_size;
 
-                    if (DEBUG_RELAX && (insn_size == 2))
+                    if (debug_relax && (insn_size == 2))
                       printf ("found rjmp / ret sequence at address 0x%x\n",
                               (int) dot);
-                    if (DEBUG_RELAX && (insn_size == 4))
+                    if (debug_relax && (insn_size == 4))
                       printf ("found jmp / ret sequence at address 0x%x\n",
                               (int) dot);
 
@@ -1630,7 +1965,7 @@ elf32_avr_relax_section (bfd *abfd,
                           there_is_preceeding_non_skip_insn = 0;
 
                         if (there_is_preceeding_non_skip_insn == 0)
-                          if (DEBUG_RELAX)
+                          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);
@@ -1666,7 +2001,7 @@ elf32_avr_relax_section (bfd *abfd,
                                && isym->st_shndx == sec_shndx)
                              {
                                deleting_ret_is_safe = 0;
-                               if (DEBUG_RELAX)
+                               if (debug_relax)
                                  printf ("local label prevents deletion of ret "
                                          "insn at address 0x%x\n",
                                          (int) dot + insn_size);
@@ -1695,7 +2030,7 @@ elf32_avr_relax_section (bfd *abfd,
                                   && sym_hash->root.u.def.value == section_offset_of_ret_insn)
                                 {
                                   deleting_ret_is_safe = 0;
-                                  if (DEBUG_RELAX)
+                                  if (debug_relax)
                                     printf ("global label prevents deletion of "
                                             "ret insn at address 0x%x\n",
                                             (int) dot + insn_size);
@@ -1772,7 +2107,7 @@ elf32_avr_relax_section (bfd *abfd,
                                if (address_of_ret == reloc_target)
                                  {
                                    deleting_ret_is_safe = 0;
-                                   if (DEBUG_RELAX)
+                                   if (debug_relax)
                                      printf ("ret from "
                                              "rjmp/jmp ret sequence at address"
                                              " 0x%x could not be deleted. ret"
@@ -1784,7 +2119,7 @@ elf32_avr_relax_section (bfd *abfd,
 
                          if (deleting_ret_is_safe)
                            {
-                             if (DEBUG_RELAX)
+                             if (debug_relax)
                                printf ("unreachable ret instruction "
                                        "at address 0x%x deleted.\n",
                                        (int) dot + insn_size);
@@ -1952,6 +2287,614 @@ elf32_avr_get_relocated_section_contents (bfd *output_bfd,
 }
 
 
+/* Determines the hash entry name for a particular reloc. It consists of
+   the identifier of the symbol section and the added reloc addend and
+   symbol offset relative to the section the symbol is attached to.  */
+
+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;
+}
+
+
+/* Add a new stub entry to the stub hash.  Not all fields of the new
+   stub entry are initialised.  */
+
+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;
+
+  /* Enter this entry into the linker stub hash table.  */
+  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;
+}
+
+/* We assume that there is already space allocated for the stub section
+   contents and that before building the stubs the section size is
+   initialized to 0.  We assume that within the stub hash table entry,
+   the absolute position of the jmp target has been written in the
+   target_value field.  We write here the offset of the generated jmp insn
+   relative to the trampoline section start to the stub_offset entry in
+   the stub hash table entry.  */
+
+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;
+
+  /* Basic opcode */
+  bfd_vma jmp_insn = 0x0000940c;
+
+  /* Massage our args to the form they really have.  */
+  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);
+
+  target = hsh->target_value;
+
+  /* Make a note of the offset within the stubs for this entry.  */
+  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);
+
+  /* We now have to add the information on the jump target to the bare
+     opcode bits already set in jmp_insn.  */
+
+  /* Check for the alignment of the address.  */
+  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;
+
+  /* Now add the entries in the address mapping table if there is still
+     space left.  */
+  {
+    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;
+
+  /* Massage our args to the form they really have.  */
+  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);
+
+  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;
+}
+
+
+/* Set up various things so that we can make a list of input sections
+   for each output section included in the link.  Returns -1 on error,
+   0 when no stubs will be needed, and 1 on success.  It also sets
+   information on the stubs bfd and the stub section in the info
+   struct.  */
+
+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->no_stubs)
+    return 0;
+
+  /* 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)
+    {
+      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;
+
+  /* We can't use output_bfd->section_count here to find the top output
+     section index as some sections may have been removed, and
+     strip_excluded_output_sections doesn't renumber the indices.  */
+  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;
+
+  /* For sections we aren't interested in, mark their entries with a
+     value we can check later.  */
+  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;
+}
+
+
+/* Read in all local syms for all input bfds, and create hash entries
+   for export stubs if we are building a multi-subspace shared lib.
+   Returns -1 on error, 0 otherwise.  */
+
+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);
+
+  /* We want to read in symbol extension records only once.  To do this
+     we need to read in the local symbols in parallel and save them for
+     later use; so hold pointers to the local symbols in an array.  */
+  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;
+
+  /* Walk over all the input BFDs, swapping in local symbols.
+     If we are creating a shared library, create hash entries for the
+     export stubs.  */
+  for (bfd_indx = 0;
+       input_bfd != NULL;
+       input_bfd = input_bfd->link_next, bfd_indx++)
+    {
+      Elf_Internal_Shdr *symtab_hdr;
+
+      /* We'll need the symbol table in a second.  */
+      symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+      if (symtab_hdr->sh_info == 0)
+       continue;
+
+      /* We need an array of the local symbols attached to the input bfd.  */
+      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);
+         /* Cache them for elf_link_input_bfd.  */
+         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);
+
+ /* At this point we initialize htab->vector_base
+    To the start of the text output section.  */
+ 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;
+
+      /* We will have to re-generate the stub hash table each time anything
+         in memory has changed.  */
+
+      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;
+
+          /* We'll need the symbol table in a second.  */
+          symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+          if (symtab_hdr->sh_info == 0)
+            continue;
+
+          local_syms = htab->all_local_syms[bfd_indx];
+
+          /* Walk over each section attached to the input bfd.  */
+          for (section = input_bfd->sections;
+               section != NULL;
+               section = section->next)
+            {
+              Elf_Internal_Rela *internal_relocs, *irelaend, *irela;
+
+              /* If there aren't any relocs, then there's nothing more
+                 to do.  */
+              if ((section->flags & SEC_RELOC) == 0
+                  || section->reloc_count == 0)
+                continue;
+
+              /* If this section is a link-once section that will be
+                 discarded, then don't create any stubs.  */
+              if (section->output_section == NULL
+                  || section->output_section->owner != output_bfd)
+                continue;
+
+              /* Get the relocs.  */
+              internal_relocs
+                = _bfd_elf_link_read_relocs (input_bfd, section, NULL, NULL,
+                                             info->keep_memory);
+              if (internal_relocs == NULL)
+                goto error_ret_free_local;
+
+              /* Now examine each relocation.  */
+              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);
+
+                  /* Only look for 16 bit GS relocs. No other reloc will need a
+                     stub.  */
+                  if (!((r_type == R_AVR_16_PM)
+                        || (r_type == R_AVR_LO8_LDI_GS)
+                        || (r_type == R_AVR_HI8_LDI_GS)))
+                    continue;
+
+                  /* Now determine the call target, its name, value,
+                     section.  */
+                  sym_sec = NULL;
+                  sym_value = 0;
+                  destination = 0;
+                  hh = NULL;
+                  if (r_indx < symtab_hdr->sh_info)
+                    {
+                      /* It's a local symbol.  */
+                      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
+                    {
+                      /* It's an external symbol.  */
+                      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)
+                       /* We are having a reloc that does't need a stub.  */
+                       continue;
+
+                     /* We don't right now know if a stub will be needed.
+                        Let's rather be on the safe side.  */
+                    }
+
+                  /* Get the name of this stub.  */
+                  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)
+                    {
+                      /* The proper stub has already been created.  Mark it
+                         to be used and write the possibly changed destination
+                         value.  */
+                      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;
+                }
+
+              /* We're done with the internal relocs, free them.  */
+              if (elf_section_data (section)->relocs == NULL)
+                free (internal_relocs);
+            }
+        }
+
+      /* Re-Calculate the number of needed stubs.  */
+      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;
+}
+
+
+/* Build all the stubs associated with the current output file.  The
+   stubs are kept in a hash table attached to the main linker hash
+   table.  We also set up the .plt entries for statically linked PIC
+   functions here.  This function is called via hppaelf_finish in the
+   linker.  */
+
+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);
+
+  /* In case that there were several stub sections:  */
+  for (stub_sec = htab->stub_bfd->sections;
+       stub_sec != NULL;
+       stub_sec = stub_sec->next)
+    {
+      bfd_size_type size;
+
+      /* Allocate memory to hold the linker stubs.  */
+      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;
+    }
+
+  /* Allocate memory for the adress mapping table.  */
+  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);
+
+  /* Build the stubs as directed by the stub hash table.  */
+  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
@@ -1960,11 +2903,12 @@ elf32_avr_get_relocated_section_contents (bfd *output_bfd,
 #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_gc_mark_hook             elf32_avr_gc_mark_hook
-#define elf_backend_gc_sweep_hook            elf32_avr_gc_sweep_hook
 #define elf_backend_check_relocs             elf32_avr_check_relocs
 #define elf_backend_can_gc_sections          1
 #define elf_backend_rela_normal                     1
This page took 0.040672 seconds and 4 git commands to generate.