Add linker relaxation support for the AVR
authorNick Clifton <nickc@redhat.com>
Fri, 3 Mar 2006 15:25:31 +0000 (15:25 +0000)
committerNick Clifton <nickc@redhat.com>
Fri, 3 Mar 2006 15:25:31 +0000 (15:25 +0000)
bfd/ChangeLog
bfd/bfd-in2.h
bfd/elf32-avr.c
bfd/libbfd.h
bfd/reloc.c
gas/ChangeLog
gas/config/tc-avr.c
include/elf/ChangeLog
include/elf/avr.h
ld/ChangeLog
ld/scripttempl/avr.sc

index f7bb9410c61358ce910f7ab26b0d8cba8b145fd3..4d9052100ff9d7bcb991ea40cf6197135d142453 100644 (file)
@@ -1,3 +1,19 @@
+2006-03-03  Bjoern Haase  <bjoern.m.haase@web.de>
+
+       * elf32-avr.c (avr_reloc_map): Insert BFD_RELOC_AVR_MS8_LDI
+       and R_AVR_MS8_LDI 
+       (bfd_elf_avr_final_write_processing): Set
+       EF_AVR_LINKRELAX_PREPARED in e_flags field.
+       (elf32_avr_relax_section): New function.
+       (elf32_avr_relax_delete_bytes): New function.
+       (elf32_avr_get_relocated_section_contents): New function.
+       (avr_pc_wrap_around): New function.
+       (avr_relative_distance_considering_wrap_around): New function.
+       (avr_final_link_relocate): Handle negative int8t_t immediate for R_AVR_LDI.
+       * reloc.c: Add BFD_RELOC_AVR_MS8_LDI and BFD_RELOC_AVR_LDI_NEG
+       * libbfd.h: Regenerate.
+       * bfd-in2.h: Regenerate.
+
 2006-03-02  DJ Delorie  <dj@redhat.com>
 
        * elf32-m32c.c (m32c_offset_for_reloc): Fix local symbol
index 195e8a005f60e1dadcf7f8da798912ed2d0a299b..cc9eb24344fbb9115e14d691ca246b2735e95a7c 100644 (file)
@@ -8,7 +8,8 @@
 /* Main header file for the bfd library -- portable access to object files.
 
    Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+   Free Software Foundation, Inc.
 
    Contributed by Cygnus Support.
 
@@ -3483,6 +3484,10 @@ of data memory address) into 8 bit immediate value of LDI insn.  */
 of program memory address) into 8 bit immediate value of LDI insn.  */
   BFD_RELOC_AVR_HH8_LDI,
 
+/* This is a 16 bit reloc for the AVR that stores 8 bit value (most high 8 bit
+of 32 bit value) into 8 bit immediate value of LDI insn.  */
+  BFD_RELOC_AVR_MS8_LDI,
+
 /* This is a 16 bit reloc for the AVR that stores negated 8 bit value
 (usually data memory address) into 8 bit immediate value of SUBI insn.  */
   BFD_RELOC_AVR_LO8_LDI_NEG,
@@ -3497,6 +3502,10 @@ SUBI insn.  */
 of LDI or SUBI insn.  */
   BFD_RELOC_AVR_HH8_LDI_NEG,
 
+/* This is a 16 bit reloc for the AVR that stores negated 8 bit value (msb
+of 32 bit value) into 8 bit immediate value of LDI insn.  */
+  BFD_RELOC_AVR_MS8_LDI_NEG,
+
 /* This is a 16 bit reloc for the AVR that stores 8 bit value (usually
 command address) into 8 bit immediate value of LDI insn.  */
   BFD_RELOC_AVR_LO8_LDI_PM,
index 11b5d4f726332b2b9f8f8007a472ab643f4cecf8..32268ccce17a404fdd87041a19ce058905394f9c 100644 (file)
@@ -1,5 +1,5 @@
 /* AVR-specific support for 32-bit ELF
-   Copyright 1999, 2000, 2001, 2002, 2003, 2004
+   Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2006
    Free Software Foundation, Inc.
    Contributed by Denis Chertykov <denisc@overta.ru>
 
@@ -17,7 +17,8 @@
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, 
+   Boston, MA 02110-1301, USA.  */
 
 #include "bfd.h"
 #include "sysdep.h"
@@ -47,6 +48,15 @@ static bfd_boolean elf32_avr_relocate_section
 static void bfd_elf_avr_final_write_processing PARAMS ((bfd *, bfd_boolean));
 static bfd_boolean elf32_avr_object_p PARAMS ((bfd *));
 
+/* Relaxing stuff */
+static bfd_boolean elf32_avr_relax_section
+  PARAMS((bfd *, asection *, struct bfd_link_info *, bfd_boolean *));
+static bfd_boolean elf32_avr_relax_delete_bytes
+  PARAMS((bfd *, asection *, bfd_vma, int));
+static bfd_byte *elf32_avr_get_relocated_section_contents
+  PARAMS((bfd *, struct bfd_link_info *, struct bfd_link_order *,
+          bfd_byte *, bfd_boolean, asymbol **));
+
 static reloc_howto_type elf_avr_howto_table[] =
 {
   HOWTO (R_AVR_NONE,           /* type */
@@ -167,7 +177,8 @@ static reloc_howto_type elf_avr_howto_table[] =
         0xffff,                /* dst_mask */
         FALSE),                /* pcrel_offset */
   /* A high 6 bit absolute relocation of 22 bit address.
-     For LDI command.  */
+     For LDI command.  As well second most significant 8 bit value of 
+     a 32 bit link-time constant.  */
   HOWTO (R_AVR_HH8_LDI,                /* type */
         16,                    /* rightshift */
         1,                     /* size (0 = byte, 1 = short, 2 = long) */
@@ -196,7 +207,7 @@ static reloc_howto_type elf_avr_howto_table[] =
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
         FALSE),                /* pcrel_offset */
-  /* A hegative high 8 bit absolute relocation of 16 bit address.
+  /* A negative high 8 bit absolute relocation of 16 bit address.
      For LDI command.  */
   HOWTO (R_AVR_HI8_LDI_NEG,    /* type */
         8,                     /* rightshift */
@@ -211,7 +222,7 @@ static reloc_howto_type elf_avr_howto_table[] =
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
         FALSE),                /* pcrel_offset */
-  /* A hegative high 6 bit absolute relocation of 22 bit address.
+  /* A negative high 6 bit absolute relocation of 22 bit address.
      For LDI command.  */
   HOWTO (R_AVR_HH8_LDI_NEG,    /* type */
         16,                    /* rightshift */
@@ -374,7 +385,35 @@ static reloc_howto_type elf_avr_howto_table[] =
         FALSE,                 /* partial_inplace */
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
-        FALSE)                 /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
+  /* Most significant 8 bit value of a 32 bit link-time constant.  */
+  HOWTO (R_AVR_MS8_LDI,                /* type */
+        24,                    /* 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_MS8_LDI",       /* name */
+        FALSE,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        FALSE),                /* pcrel_offset */
+  /* Negative most significant 8 bit value of a 32 bit link-time constant.  */
+  HOWTO (R_AVR_MS8_LDI_NEG,    /* type */
+        24,                    /* 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_MS8_LDI_NEG",   /* name */
+        FALSE,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        FALSE)                 /* pcrel_offset */
 };
 
 /* Map BFD reloc types to AVR ELF reloc types.  */
@@ -396,9 +435,11 @@ struct avr_reloc_map
   { 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_HI8_LDI_PM,       R_AVR_HI8_LDI_PM },
   { BFD_RELOC_AVR_HH8_LDI_PM,       R_AVR_HH8_LDI_PM },
@@ -411,6 +452,32 @@ struct avr_reloc_map
   { BFD_RELOC_AVR_6_ADIW,           R_AVR_6_ADIW }
 };
 
+/* Meant to be filled one day with the wrap around address for the
+   specific device.  I.e. should get the value 0x4000 for 16k devices, 
+   0x8000 for 32k devices and so on.
+   
+   We initialize it here with a value of 0x1000000 resulting in
+   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.  */
+   
+unsigned int avr_pc_wrap_around = 0x10000000;
+
+/* Calculates the effective distance of a pc relative jump/call.  */
+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 (abfd, code)
      bfd *abfd ATTRIBUTE_UNUSED;
@@ -583,13 +650,17 @@ avr_final_link_relocate (howto, input_bfd, input_section,
       if (srel & 1)
        return bfd_reloc_outofrange;
 
+      srel = avr_relative_distance_considering_wrap_around (srel);
+
       /* AVR addresses commands as words.  */
       srel >>= 1;
 
       /* Check for overflow.  */
       if (srel < -2048 || srel > 2047)
        {
-         /* Apply WRAPAROUND if possible.  */
+          /* Relative distance is too large.  */
+
+         /* Always apply WRAPAROUND for avr2 and avr4.  */
          switch (bfd_get_mach (input_bfd))
            {
            case bfd_mach_avr2:
@@ -617,9 +688,11 @@ avr_final_link_relocate (howto, input_bfd, input_section,
     case R_AVR_LDI:
       contents += rel->r_offset;
       srel = (bfd_signed_vma) relocation + rel->r_addend;
-      if ((srel & 0xffff) > 255)
-       /* Remove offset for data/eeprom section.  */
-       return bfd_reloc_overflow;
+      if ( ((srel > 0) && (srel & 0xffff) > 255)
+           || ((srel < 0) && ( (-srel) & 0xffff) > 128))
+        /* Remove offset for data/eeprom section.  */
+        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);
@@ -632,7 +705,8 @@ avr_final_link_relocate (howto, input_bfd, input_section,
        /* Remove offset for data/eeprom section.  */
        return bfd_reloc_overflow;
       x = bfd_get_16 (input_bfd, contents);
-      x = (x & 0xd3f8) | ((srel & 7) | ((srel & (3 << 3)) << 7) | ((srel & (1 << 5)) << 8));
+      x = (x & 0xd3f8) | ((srel & 7) | ((srel & (3 << 3)) << 7) 
+                       | ((srel & (1 << 5)) << 8));
       bfd_put_16 (input_bfd, x, contents);
       break;
 
@@ -665,6 +739,15 @@ avr_final_link_relocate (howto, input_bfd, input_section,
       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;
@@ -694,6 +777,16 @@ avr_final_link_relocate (howto, input_bfd, input_section,
       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_PM:
       contents += rel->r_offset;
       srel = (bfd_signed_vma) relocation + rel->r_addend;
@@ -943,6 +1036,7 @@ bfd_elf_avr_final_write_processing (abfd, linker)
   elf_elfheader (abfd)->e_machine = EM_AVR;
   elf_elfheader (abfd)->e_flags &= ~ EF_AVR_MACH;
   elf_elfheader (abfd)->e_flags |= val;
+  elf_elfheader (abfd)->e_flags |= EF_AVR_LINKRELAX_PREPARED;
 }
 
 /* Set the right machine number.  */
@@ -984,6 +1078,933 @@ elf32_avr_object_p (abfd)
                                    e_set);
 }
 
+
+/* Enable debugging printout at stdout with a value of 1.  */
+#define DEBUG_RELAX 0
+
+/* This function handles relaxing for the avr.
+   Many important relaxing opportunities within functions are already
+   realized by the compiler itself.
+   Here we try to replace  call (4 bytes) ->  rcall (2 bytes)
+   and jump -> rjmp (safes also 2 bytes).  
+   As well we now optimize seqences of 
+     - call/rcall function
+     - ret
+   to yield
+     - jmp/rjmp function
+     - ret
+   . In case that within a sequence
+     - jmp/rjmp label
+     - ret
+   the ret could no longer be reached it is optimized away. In order
+   to check if the ret is no longer needed, it is checked that the ret's address
+   is not the target of a branch or jump within the same section, it is checked
+   that there is no skip instruction before the jmp/rjmp and that there
+   is no local or global label place at the address of the ret.
+   
+   We refrain from relaxing within sections ".vectors" and
+   ".jumptables" in order to maintain the position of the instructions.  
+   There, however, we substitute jmp/call by a sequence rjmp,nop/rcall,nop
+   if possible. (In future one could possibly use the space of the nop 
+   for the first instruction of the irq service function.
+
+   The .jumptables sections is meant to be used for a future tablejump variant
+   for the devices with 3-byte program counter where the table itself
+   contains 4-byte jump instructions whose relative offset must not 
+   be changed.  */
+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;
+
+  /* Assume nothing changes.  */
+  *again = FALSE;
+
+  /* We don't have to do anything for a relocatable link, if
+     this section does not have relocs, or if this is not a
+     code section.  */
+  if (link_info->relocatable
+      || (sec->flags & SEC_RELOC) == 0
+      || sec->reloc_count == 0
+      || (sec->flags & SEC_CODE) == 0)
+    return TRUE;
+  /* Check if the object file to relax uses internal symbols so that we
+     could fix up the relocations.  */
+  if (!(elf_elfheader (abfd)->e_flags & EF_AVR_LINKRELAX_PREPARED))
+    return TRUE;
+  
+
+  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+
+  /* Get a copy of the native relocations.  */
+  internal_relocs = (_bfd_elf_link_read_relocs
+                     (abfd, sec, (PTR) NULL, (Elf_Internal_Rela *) NULL,
+                      link_info->keep_memory));
+  if (internal_relocs == NULL)
+    goto error_return;
+
+  if (sec != last_input_section)
+    last_reloc = NULL;
+
+  last_input_section = sec;
+
+  /* Walk through the relocs looking for relaxing opportunities.  */
+  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;
+           
+      /* Get the section contents if we haven't done so already.  */
+      if (contents == NULL)
+        {
+          /* Get cached copy if it exists.  */
+          if (elf_section_data (sec)->this_hdr.contents != NULL)
+            contents = elf_section_data (sec)->this_hdr.contents;
+          else
+            {
+              /* Go get them off disk.  */
+              if (!bfd_malloc_and_get_section (abfd, sec, &contents))
+                goto error_return;
+            }
+        }
+
+     /* Read this BFD's local symbols if we haven't done so already.  */
+      if (isymbuf == NULL && symtab_hdr->sh_info != 0)
+        {
+          isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
+          if (isymbuf == NULL)
+            isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr,
+                                            symtab_hdr->sh_info, 0,
+                                            NULL, NULL, NULL);
+          if (isymbuf == NULL)
+            goto error_return;
+        }
+
+
+      /* Get the value of the symbol referred to by the reloc.  */
+      if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
+        {
+          /* A local symbol.  */
+          Elf_Internal_Sym *isym;
+          asection *sym_sec;
+
+          isym = isymbuf + ELF32_R_SYM (irel->r_info);
+          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;
+        }
+      else
+        {
+          unsigned long indx;
+          struct elf_link_hash_entry *h;
+
+          /* An external symbol.  */
+          indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info;
+          h = elf_sym_hashes (abfd)[indx];
+          BFD_ASSERT (h != NULL);
+          if (h->root.type != bfd_link_hash_defined
+              && h->root.type != bfd_link_hash_defweak)
+            {
+              /* This appears to be a reference to an undefined
+                 symbol.  Just ignore it--it will be caught by the
+                 regular reloc processing.  */
+              continue;
+            }
+          symval = (h->root.u.def.value
+                    + h->root.u.def.section->output_section->vma
+                    + h->root.u.def.section->output_offset);
+        }
+
+      /* For simplicity of coding, we are going to modify the section
+         contents, the section relocs, and the BFD symbol table.  We
+         must tell the rest of the code not to free up this
+         information.  It would be possible to instead create a table
+         of changes which have to be made, as is done in coff-mips.c;
+         that would be more work, but would require less memory when
+         the linker is run.  */
+      switch (ELF32_R_TYPE (irel->r_info))
+        {
+         /* Try to turn a 22-bit absolute call/jump into an 13-bit
+            pc-relative rcall/rjmp.  */
+         case R_AVR_CALL:
+          {
+            bfd_vma value = symval + irel->r_addend;
+            bfd_vma dot, gap;
+            int distance_short_enough = 0;
+
+            /* Get the address of this instruction.  */
+            dot = (sec->output_section->vma
+                   + sec->output_offset + irel->r_offset);
+
+            /* Compute the distance from this insn to the branch target.  */
+            gap = value - dot;
+
+            /* If the distance is within -4094..+4098 inclusive, then we can
+               relax this jump/call.  +4098 because the call/jump target
+               will be closer after the relaxation.  */ 
+            if ((int) gap >= -4094 && (int) gap <= 4098)
+              distance_short_enough = 1;
+
+            /* Here we handle the wrap-around case.  E.g. for a 16k device
+               we could use a rjmp to jump from address 0x100 to 0x3d00!  
+               In order to make this work properly, we need to fill the
+               vaiable avr_pc_wrap_around with the appropriate value.
+               I.e. 0x4000 for a 16k device.  */
+            {
+               /* Shrinking the code size makes the gaps larger in the
+                  case of wrap-arounds.  So we use a heuristical safety
+                  margin to avoid that during relax the distance gets
+                  again too large for the short jumps.  Let's assume
+                  a typical code-size reduction due to relax for a
+                  16k device of 600 bytes.  So let's use twice the
+                  typical value as safety margin.  */ 
+
+               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);
+
+                /* Note that we've changed the relocs, section contents,
+                   etc.  */
+                elf_section_data (sec)->relocs = internal_relocs;
+                elf_section_data (sec)->this_hdr.contents = contents;
+                symtab_hdr->contents = (unsigned char *) isymbuf;
+
+                /* Get the instruction code for relaxing.  */
+                code_lsb = bfd_get_8 (abfd, contents + irel->r_offset);
+                code_msb = bfd_get_8 (abfd, contents + irel->r_offset + 1);
+
+                /* Mask out the relocation bits.  */
+                code_msb &= 0x94;
+                code_lsb &= 0x0E;
+                if (code_msb == 0x94 && code_lsb == 0x0E)
+                  {
+                    /* we are changing call -> rcall .  */
+                    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)
+                  {
+                    /* we are changeing jump -> rjmp.  */
+                    bfd_put_8 (abfd, 0x00, contents + irel->r_offset);
+                    bfd_put_8 (abfd, 0xC0, contents + irel->r_offset + 1);
+                  }
+                else 
+                  abort ();
+
+                /* Fix the relocation's type.  */
+                irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
+                                             R_AVR_13_PCREL);
+
+                /* Check for the vector section. There we don't want to
+                   modify the ordering!  */
+
+                if (!strcmp (sec->name,".vectors")
+                    || !strcmp (sec->name,".jumptables"))
+                  {
+                    /* Let's insert a nop.  */
+                    bfd_put_8 (abfd, 0x00, contents + irel->r_offset + 2);
+                    bfd_put_8 (abfd, 0x00, contents + irel->r_offset + 3);
+                  }
+                else
+                  {
+                    /* Delete two bytes of data.  */
+                    if (!elf32_avr_relax_delete_bytes (abfd, sec,
+                                                       irel->r_offset + 2, 2))
+                      goto error_return;
+
+                    /* That will change things, so, we should relax again.
+                       Note that this is not required, and it may be slow.  */
+                    *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);
+
+            /* Get the address of this instruction.  */
+            dot = (sec->output_section->vma
+                   + 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))
+              {
+                /* This insn is a rcall.  */
+                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))
+                  {
+                    /* The next insn is a ret. We now convert the rcall insn
+                       into a rjmp instruction.  */
+                    
+                    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)))
+              {
+                /* This insn is a call.  */
+                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))
+                  {
+                    /* The next insn is a ret. We now convert the call insn
+                       into a jmp instruction.  */
+
+                    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))))
+              {
+                /* this insn is a rjmp or a jmp.  */
+                unsigned char next_insn_msb = 0;
+                unsigned char next_insn_lsb = 0;
+                int insn_size;
+
+                if (0xc0 == (code_msb & 0xf0))
+                  insn_size = 2; /* rjmp insn */
+                else
+                  insn_size = 4; /* jmp insn */
+
+                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))
+                  {
+                    /* The next insn is a ret. We possibly could delete
+                       this ret. First we need to check for preceeding
+                       sbis/sbic/sbrs or cpse "skip" instructions.  */
+
+                    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);
+
+                    /* We have to make sure that there is a preceeding insn.  */
+                    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);
+
+                        /* sbic.  */
+                        if (0x99 == preceeding_msb)    
+                          there_is_preceeding_non_skip_insn = 0;
+
+                        /* sbis.  */
+                        if (0x9b == preceeding_msb)    
+                          there_is_preceeding_non_skip_insn = 0;
+
+                        /* sbrc */
+                        if ((0xfc == (preceeding_msb & 0xfe)
+                            && (0x00 == (preceeding_lsb & 0x08))))
+                          there_is_preceeding_non_skip_insn = 0;
+
+                        /* sbrs */ 
+                        if ((0xfe == (preceeding_msb & 0xfe)
+                            && (0x00 == (preceeding_lsb & 0x08))))
+                          there_is_preceeding_non_skip_insn = 0;
+                       
+                        /* cpse */
+                        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 no previous instruction.  */
+                        there_is_preceeding_non_skip_insn = 0;
+                      } 
+
+                    if (there_is_preceeding_non_skip_insn)
+                      {
+                        /* We now only have to make sure that there is no
+                           local label defined at the address of the ret
+                           instruction and that there is no local relocation
+                           in this section pointing to the ret.  */
+
+                        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);
+
+                        /* Check for local symbols.  */
+                        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);
+                             }
+                         }
+
+                         /* Now check for global symbols.  */
+                         {
+                           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);
+                                }
+                            }
+                         }
+                         /* Now we check for relocations pointing to ret.  */
+                         {
+                           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;
+                               
+                               /* 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)
+                                     break;
+                                  }
+                             
+                               /* 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;
+                                       reloc_target = symval + irel->r_addend;
+                                     }
+                                   else
+                                     {
+                                       reloc_target = symval + irel->r_addend;
+                                       /* reference symbol is absolute.  */
+                                     }
+                                 }
+                               else
+                                 {
+                                   /* reference symbol is extern.  */
+                                 }
+                                 
+                               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);
+                    
+                             /* Delete two bytes of data.  */
+                             if (!elf32_avr_relax_delete_bytes (abfd, sec,
+                                        irel->r_offset + insn_size, 2))
+                               goto error_return;
+
+                             /* That will change things, so, we should relax 
+                                again. Note that this is not required, and it 
+                                may be slow.  */
+                       
+                             *again = TRUE;
+                             break;
+                           }
+                      }
+                        
+                  }                    
+              } 
+            break;
+          }
+        }
+    }
+
+  if (contents != NULL
+      && elf_section_data (sec)->this_hdr.contents != contents)
+    {
+      if (! link_info->keep_memory)
+        free (contents);
+      else
+        {
+          /* Cache the section contents for elf_link_input_bfd.  */
+          elf_section_data (sec)->this_hdr.contents = contents;
+        }
+    }
+
+  if (internal_relocs != NULL
+      && elf_section_data (sec)->relocs != internal_relocs)
+    free (internal_relocs);
+
+  return TRUE;
+
+ error_return:
+  if (isymbuf != NULL
+      && symtab_hdr->contents != (unsigned char *) isymbuf)
+    free (isymbuf);
+  if (contents != NULL
+      && elf_section_data (sec)->this_hdr.contents != contents)
+    free (contents);
+  if (internal_relocs != NULL
+      && elf_section_data (sec)->relocs != internal_relocs)
+    free (internal_relocs);
+
+  return FALSE;  
+}
+
+/* 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
+   byte just behind the original unshrinked instruction.  */
+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;
+
+  /* The deletion must stop at the next ALIGN reloc for an aligment
+     power larger than the number of bytes we are deleting.  */
+
+  irelalign = NULL;
+  toaddr = sec->size;
+
+  irel = elf_section_data (sec)->relocs;
+  irelend = irel + sec->reloc_count;
+
+  /* Actually delete the bytes.  */
+  if (toaddr - addr - count > 0)
+    memmove (contents + addr, contents + addr + count,
+             (size_t) (toaddr - addr - count));
+  sec->size -= count;
+
+  /* Adjust all the relocs.  */
+  for (irel = elf_section_data (sec)->relocs; irel < irelend; irel++)
+    {
+      bfd_vma symval;
+      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);
+
+      /* Get the new reloc address.  */
+      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;
+        }
+
+      /* 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)
+                 {
+                   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);
+                 }
+            }
+          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;
+  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;
+    }
+
+  /* Now adjust the global symbols defined in this section.  */
+  symcount = (symtab_hdr->sh_size / sizeof (Elf32_External_Sym)
+              - symtab_hdr->sh_info);
+  sym_hashes = elf_sym_hashes (abfd);
+  end_hashes = sym_hashes + symcount;
+  for (; sym_hashes < end_hashes; sym_hashes++)
+    {
+      struct elf_link_hash_entry *sym_hash = *sym_hashes;
+      if ((sym_hash->root.type == bfd_link_hash_defined
+           || sym_hash->root.type == bfd_link_hash_defweak)
+          && sym_hash->root.u.def.section == sec
+          && sym_hash->root.u.def.value > addr
+          && sym_hash->root.u.def.value < toaddr)
+        {
+          sym_hash->root.u.def.value -= count;
+        }
+    }
+
+  return TRUE;
+}
+
+/* This is a version of bfd_generic_get_relocated_section_contents
+   which uses elf32_avr_relocate_section.  
+
+   For avr it's essentially a cut and paste taken from the H8300 port. 
+   The author of the relaxation support patch for avr had absolutely no
+   clue what is happening here but found out that this part of the code 
+   seems to be important.  */
+
+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;
+
+  /* We only need to handle the case of relaxing, or of having a
+     particular set of section contents, specially.  */
+  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, (PTR) NULL,
+                          (Elf_Internal_Rela *) 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 = (asection **) 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;
+}
+
+
 #define ELF_ARCH               bfd_arch_avr
 #define ELF_MACHINE_CODE       EM_AVR
 #define ELF_MACHINE_ALT1       EM_AVR_OLD
@@ -1004,4 +2025,8 @@ elf32_avr_object_p (abfd)
                                        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"
index 0b13b6e1ef3856302a53dd86f4f187440f1d868e..03e2fa527c1e954891497208f1797ebd098de06d 100644 (file)
@@ -1474,9 +1474,11 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_AVR_LO8_LDI",
   "BFD_RELOC_AVR_HI8_LDI",
   "BFD_RELOC_AVR_HH8_LDI",
+  "BFD_RELOC_AVR_MS8_LDI",
   "BFD_RELOC_AVR_LO8_LDI_NEG",
   "BFD_RELOC_AVR_HI8_LDI_NEG",
   "BFD_RELOC_AVR_HH8_LDI_NEG",
+  "BFD_RELOC_AVR_MS8_LDI_NEG",
   "BFD_RELOC_AVR_LO8_LDI_PM",
   "BFD_RELOC_AVR_HI8_LDI_PM",
   "BFD_RELOC_AVR_HH8_LDI_PM",
index b621217ef56cdc8978d8d78c3661e60c7660fb83..31314e7246f6f8bc4236f00b6c4e56a5b81c5592 100644 (file)
@@ -3585,6 +3585,11 @@ ENUM
 ENUMDOC
   This is a 16 bit reloc for the AVR that stores 8 bit value (most high 8 bit
   of program memory address) into 8 bit immediate value of LDI insn.
+ENUM
+  BFD_RELOC_AVR_MS8_LDI
+ENUMDOC
+  This is a 16 bit reloc for the AVR that stores 8 bit value (most high 8 bit
+  of 32 bit value) into 8 bit immediate value of LDI insn.
 ENUM
   BFD_RELOC_AVR_LO8_LDI_NEG
 ENUMDOC
@@ -3602,6 +3607,11 @@ ENUMDOC
   This is a 16 bit reloc for the AVR that stores negated 8 bit value
   (most high 8 bit of program memory address) into 8 bit immediate value
   of LDI or SUBI insn.
+ENUM
+  BFD_RELOC_AVR_MS8_LDI_NEG
+ENUMDOC
+  This is a 16 bit reloc for the AVR that stores negated 8 bit value (msb
+  of 32 bit value) into 8 bit immediate value of LDI insn.
 ENUM
   BFD_RELOC_AVR_LO8_LDI_PM
 ENUMDOC
index b9f53e0e7083a2768f760325e25f568abee0107c..1347fae069659af6ab7182c963b93c5f13734a07 100644 (file)
@@ -1,3 +1,16 @@
+2006-03-03  Bjoern Haase  <bjoern.m.haase@web.de>
+
+       * config/tc-avr.c (avr_mod_hash_value): New function.
+       (md_apply_fix, exp_mod): Use BFD_RELOC_HH8_LDI and
+       BFD_RELOC_MS8_LDI for hlo8() and hhi8() 
+       (md_begin): Set linkrelax variable to 1, use avr_mod_hash_value
+       instead of int avr_ldi_expression: use avr_mod_hash_value instead
+       of (int).
+       (tc_gen_reloc): Handle substractions of symbols, if possible do
+       fixups, abort otherwise.        
+       * config/tc-avr.h (TC_LINKRELAX_FIXUP, TC_VALIDATE_FIX,
+       tc_fix_adjustable): Define.
+       
 2006-03-02  James E Wilson  <wilson@specifix.com>
 
        * config/tc-ia64.c (emit_one_bundle): For IA64_OPCODE_LAST, if we
index 2ceaa1fffef33cc3033c29d1441a74356ace0784..d6294f3c5a41154dbfd20b0b9afe3191fbc3f052 100644 (file)
@@ -170,8 +170,8 @@ static struct exp_mod_s exp_mod[] =
   {"pm_hi8", BFD_RELOC_AVR_HI8_LDI_PM, BFD_RELOC_AVR_HI8_LDI_PM_NEG, 0},
   {"lo8",    BFD_RELOC_AVR_LO8_LDI,    BFD_RELOC_AVR_LO8_LDI_NEG,    1},
   {"pm_lo8", BFD_RELOC_AVR_LO8_LDI_PM, BFD_RELOC_AVR_LO8_LDI_PM_NEG, 0},
-  {"hlo8",   -BFD_RELOC_AVR_LO8_LDI,   -BFD_RELOC_AVR_LO8_LDI_NEG,   0},
-  {"hhi8",   -BFD_RELOC_AVR_HI8_LDI,   -BFD_RELOC_AVR_HI8_LDI_NEG,   0},
+  {"hlo8",   BFD_RELOC_AVR_HH8_LDI,    BFD_RELOC_AVR_HH8_LDI_NEG,    0},
+  {"hhi8",   BFD_RELOC_AVR_MS8_LDI,    BFD_RELOC_AVR_MS8_LDI_NEG,    0},
 };
 
 /* A union used to store indicies into the exp_mod[] array
@@ -1081,15 +1081,11 @@ md_apply_fix (fixS *fixP, valueT * valP, segT seg)
          bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value), where);
          break;
 
-       case -BFD_RELOC_AVR_LO8_LDI:
-         bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 16), where);
-         break;
-
        case BFD_RELOC_AVR_HI8_LDI:
          bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 8), where);
          break;
 
-       case -BFD_RELOC_AVR_HI8_LDI:
+       case BFD_RELOC_AVR_MS8_LDI:
          bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 24), where);
          break;
 
@@ -1101,15 +1097,11 @@ md_apply_fix (fixS *fixP, valueT * valP, segT seg)
          bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value), where);
          break;
 
-       case -BFD_RELOC_AVR_LO8_LDI_NEG:
-         bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 16), where);
-         break;
-
        case BFD_RELOC_AVR_HI8_LDI_NEG:
          bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 8), where);
          break;
 
-       case -BFD_RELOC_AVR_HI8_LDI_NEG:
+       case BFD_RELOC_AVR_MS8_LDI_NEG:
          bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 24), where);
          break;
 
@@ -1195,6 +1187,32 @@ tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED,
 {
   arelent *reloc;
 
+  if (fixp->fx_addsy && fixp->fx_subsy)
+    {
+      long value = 0;
+
+      if ((S_GET_SEGMENT (fixp->fx_addsy) != S_GET_SEGMENT (fixp->fx_subsy))
+          || S_GET_SEGMENT (fixp->fx_addsy) == undefined_section)
+        {
+          as_bad_where (fixp->fx_file, fixp->fx_line,
+              "Difference of symbols in different sections is not supported");
+          return NULL;
+        }
+
+      /* We are dealing with two symbols defined in the same section. 
+         Let us fix-up them here.  */
+      value += S_GET_VALUE (fixp->fx_addsy);
+      value -= S_GET_VALUE (fixp->fx_subsy);
+
+      /* When fx_addsy and fx_subsy both are zero, md_apply_fix
+         only takes it's second operands for the fixup value.  */
+      fixp->fx_addsy = NULL;
+      fixp->fx_subsy = NULL;
+      md_apply_fix (fixp, (valueT *) &value, NULL);
+
+      return NULL;
+    }
+
   reloc = xmalloc (sizeof (arelent));
 
   reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
index 9012392ed0d615b7a8a97f314884092a8b6ca835..27665852b0d1fd37ce7381aa517316c50e2535a8 100644 (file)
@@ -1,3 +1,8 @@
+2006-03-03  Bjoern Haase  <bjoern.m.haase@web.de>
+
+       * avr.h (R_AVR_MS8_LDI,R_AVR_MS8_LDI_NEG): Add.
+       (EF_AVR_LINKRELAX_PREPARED): Add.
+
 2006-03-02  Ben Elliston  <bje@au.ibm.com>
 
        Import from the GCC tree:
index 68b4d9539d1394526e3cf92f6298f814af38c2a2..f16043805eb6a653bdf0bff188d611d4ccd021cd 100644 (file)
 /* Processor specific flags for the ELF header e_flags field.  */
 #define EF_AVR_MACH 0xf
 
+/* If bit #7 is set, it is assumed that the elf file uses local symbols
+   as reference for the relocations so that linker relaxation is possible.  */
+#define EF_AVR_LINKRELAX_PREPARED 0x80
+
 #define E_AVR_MACH_AVR1 1
 #define E_AVR_MACH_AVR2 2
 #define E_AVR_MACH_AVR3 3
@@ -56,6 +60,8 @@ START_RELOC_NUMBERS (elf_avr_reloc_type)
      RELOC_NUMBER (R_AVR_LDI,                  19)
      RELOC_NUMBER (R_AVR_6,                    20)
      RELOC_NUMBER (R_AVR_6_ADIW,               21)
+     RELOC_NUMBER (R_AVR_MS8_LDI,              22)
+     RELOC_NUMBER (R_AVR_MS8_LDI_NEG,          23)
 END_RELOC_NUMBERS (R_AVR_max)
 
 #endif /* _ELF_AVR_H */
index 8ab52a4d8b5a18b69f821a4b6c2c447ed3fdc06f..9bdfcc4c2010b97684efa73eb5e9408f26419fb3 100644 (file)
@@ -1,3 +1,10 @@
+2006-03-03  Bjoern Haase  <bjoern.m.haase@web.de>
+
+       * scripttempl/avr.sc:  Add *(.jumptables) *(.lowtext) sections.
+       Add KEEP() directives.
+       Add *(.data*) *(.rodata) and *(.rodata*) and *(.bss*) to .data and
+       .bss output sections.
+
 2006-03-03  Richard Sandiford  <richard@codesourcery.com>
 
        * emulparams/vxworks.sh (VXWORKS_BASE_EM_FILE): New variable.
index 69211cc58372ea228b88a7d189868612fe8bf278..d297b4a70298a498174b7caf6fc1fa8bc57f13f3 100644 (file)
@@ -75,6 +75,7 @@ SECTIONS
   .text :
   {
     *(.vectors)
+    KEEP(*(.vectors))
 
     ${CONSTRUCTING+ __ctors_start = . ; }
     ${CONSTRUCTING+ *(.ctors) }
@@ -82,34 +83,65 @@ SECTIONS
     ${CONSTRUCTING+ __dtors_start = . ; }
     ${CONSTRUCTING+ *(.dtors) }
     ${CONSTRUCTING+ __dtors_end = . ; }
+    KEEP(SORT(*)(.ctors))
+    KEEP(SORT(*)(.dtors))
 
+    /* For data that needs to reside in the lower 64k of progmem */
     *(.progmem.gcc*)
     *(.progmem*)
     ${RELOCATING+. = ALIGN(2);}
+    
+    /* for future tablejump instruction arrays for 3 byte pc devices */
+    *(.jumptables) 
+    *(.jumptables*) 
+    /* for code that needs to reside in the lower 128k progmem */
+    *(.lowtext)
+    *(.lowtext*)  
+
     *(.init0)  /* Start here after reset.  */
+    KEEP (*(.init0))
     *(.init1)
+    KEEP (*(.init1))
     *(.init2)  /* Clear __zero_reg__, set up stack pointer.  */
+    KEEP (*(.init2))
     *(.init3)
+    KEEP (*(.init3))
     *(.init4)  /* Initialize data and BSS.  */
+    KEEP (*(.init4))
     *(.init5)
+    KEEP (*(.init5))
     *(.init6)  /* C++ constructors.  */
+    KEEP (*(.init6))
     *(.init7)
+    KEEP (*(.init7))
     *(.init8)
+    KEEP (*(.init8))
     *(.init9)  /* Call main().  */
+    KEEP (*(.init9))
     *(.text)
     ${RELOCATING+. = ALIGN(2);}
     *(.text.*)
     ${RELOCATING+. = ALIGN(2);}
     *(.fini9)  /* _exit() starts here.  */
+    KEEP (*(.fini9))
     *(.fini8)
+    KEEP (*(.fini8))
     *(.fini7)
+    KEEP (*(.fini7))
     *(.fini6)  /* C++ destructors.  */
+    KEEP (*(.fini6))
     *(.fini5)
+    KEEP (*(.fini5))
     *(.fini4)
+    KEEP (*(.fini4))
     *(.fini3)
+    KEEP (*(.fini3))
     *(.fini2)
+    KEEP (*(.fini2))
     *(.fini1)
+    KEEP (*(.fini1))
     *(.fini0)  /* Infinite loop after program termination.  */
+    KEEP (*(.fini0))
     ${RELOCATING+ _etext = . ; }
   } ${RELOCATING+ > text}
 
@@ -117,6 +149,9 @@ SECTIONS
   {
     ${RELOCATING+ PROVIDE (__data_start = .) ; }
     *(.data)
+    *(.data*)
+    *(.rodata)  /* We need to include .rodata here if gcc is used */
+    *(.rodata*) /* with -fdata-sections.  */
     *(.gnu.linkonce.d*)
     ${RELOCATING+. = ALIGN(2);}
     ${RELOCATING+ _edata = . ; }
@@ -127,6 +162,7 @@ SECTIONS
   {
     ${RELOCATING+ PROVIDE (__bss_start = .) ; }
     *(.bss)
+    *(.bss*)
     *(COMMON)
     ${RELOCATING+ PROVIDE (__bss_end = .) ; }
   } ${RELOCATING+ > data}