? avr_linkrelax_patch.rev5
? avr_linkrelax_patch.rev6
? bfd/differenz
? bfd/elf32-avr.c.neu
? bfd/doc/bfd.info
? bfd/doc/bfd.info-1
? binutils/doc/binutils.info
? binutils/doc/binutils.info-1
? gas/config/tc-avr.c.alt
? gas/config/tc-avr.c.neu
? gas/config/tc-avr.h.neu
? gas/config/tc.avr.h
? gas/doc/as.info
? gas/doc/as.info-1
? ld/ld.info
? ld/ld.info-1
Index: bfd/bfd-in2.h
===================================================================
RCS file: /cvs/src/src/bfd/bfd-in2.h,v
retrieving revision 1.381
diff -U8 -r1.381 bfd-in2.h
--- bfd/bfd-in2.h	24 Feb 2006 22:10:35 -0000	1.381
+++ bfd/bfd-in2.h	28 Feb 2006 11:26:12 -0000
@@ -3478,30 +3478,39 @@
 /* This is a 16 bit reloc for the AVR that stores 8 bit value (high 8 bit
 of data memory address) into 8 bit immediate value of LDI insn.  */
   BFD_RELOC_AVR_HI8_LDI,
 
 /* 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.  */
   BFD_RELOC_AVR_HH8_LDI,
 
+/* This is a 16 bit reloc for the AVR that stores 8 bit value (most high 8 bit
+of 32bit 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,
 
 /* This is a 16 bit reloc for the AVR that stores negated 8 bit value
 (high 8 bit of data memory address) into 8 bit immediate value of
 SUBI insn.  */
   BFD_RELOC_AVR_HI8_LDI_NEG,
 
 /* 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.  */
   BFD_RELOC_AVR_HH8_LDI_NEG,
 
+/* This is a 16 bit reloc for the AVR that stores negated 8 bit value
+(most significant 8 bit of 32 bit value) into 8 bit immediate value
+of LDI or SUBI 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,
 
 /* This is a 16 bit reloc for the AVR that stores 8 bit value (high 8 bit
 of command address) into 8 bit immediate value of LDI insn.  */
   BFD_RELOC_AVR_HI8_LDI_PM,
 
Index: bfd/elf32-avr.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-avr.c,v
retrieving revision 1.22
diff -U8 -r1.22 elf32-avr.c
--- bfd/elf32-avr.c	20 Jun 2005 18:12:06 -0000	1.22
+++ bfd/elf32-avr.c	28 Feb 2006 11:26:16 -0000
@@ -12,17 +12,18 @@
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
    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"
 #include "libbfd.h"
 #include "elf-bfd.h"
 #include "elf/avr.h"
 
 static reloc_howto_type *bfd_elf32_bfd_reloc_type_lookup
@@ -42,16 +43,25 @@
   PARAMS ((reloc_howto_type *, bfd *, asection *, bfd_byte *,
 	   Elf_Internal_Rela *, bfd_vma));
 static bfd_boolean elf32_avr_relocate_section
   PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
 	   Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
 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 */
 	 0,			/* rightshift */
 	 2,			/* size (0 = byte, 1 = short, 2 = long) */
 	 32,			/* bitsize */
 	 FALSE,			/* pc_relative */
 	 0,			/* bitpos */
@@ -162,17 +172,18 @@
 	 complain_overflow_dont, /* complain_on_overflow */
 	 bfd_elf_generic_reloc,	/* special_function */
 	 "R_AVR_HI8_LDI",	/* name */
 	 FALSE,			/* partial_inplace */
 	 0xffff,		/* src_mask */
 	 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) */
 	 8,			/* bitsize */
 	 FALSE,			/* pc_relative */
 	 0,			/* bitpos */
 	 complain_overflow_dont, /* complain_on_overflow */
 	 bfd_elf_generic_reloc,	/* special_function */
@@ -191,32 +202,32 @@
 	 0,			/* bitpos */
 	 complain_overflow_dont, /* complain_on_overflow */
 	 bfd_elf_generic_reloc,	/* special_function */
 	 "R_AVR_LO8_LDI_NEG",	/* name */
 	 FALSE,			/* partial_inplace */
 	 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 */
 	 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_NEG",	/* name */
 	 FALSE,			/* partial_inplace */
 	 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 */
 	 1,			/* size (0 = byte, 1 = short, 2 = long) */
 	 8,			/* bitsize */
 	 FALSE,			/* pc_relative */
 	 0,			/* bitpos */
 	 complain_overflow_dont, /* complain_on_overflow */
@@ -369,17 +380,45 @@
 	 FALSE,			/* pc_relative */
 	 0,			/* bitpos */
 	 complain_overflow_dont,/* complain_on_overflow */
 	 bfd_elf_generic_reloc,	/* special_function */
 	 "R_AVR_6_ADIW",	/* name */
 	 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.  */
 
 struct avr_reloc_map
 {
   bfd_reloc_code_real_type bfd_reloc_val;
   unsigned int elf_reloc_val;
@@ -391,31 +430,59 @@
   { BFD_RELOC_32,                   R_AVR_32 },
   { BFD_RELOC_AVR_7_PCREL,          R_AVR_7_PCREL },
   { BFD_RELOC_AVR_13_PCREL,         R_AVR_13_PCREL },
   { BFD_RELOC_16,                   R_AVR_16 },
   { BFD_RELOC_AVR_16_PM,            R_AVR_16_PM },
   { BFD_RELOC_AVR_LO8_LDI,          R_AVR_LO8_LDI},
   { BFD_RELOC_AVR_HI8_LDI,          R_AVR_HI8_LDI },
   { BFD_RELOC_AVR_HH8_LDI,          R_AVR_HH8_LDI },
+  { BFD_RELOC_AVR_MS8_LDI,          R_AVR_MS8_LDI },
   { BFD_RELOC_AVR_LO8_LDI_NEG,      R_AVR_LO8_LDI_NEG },
   { BFD_RELOC_AVR_HI8_LDI_NEG,      R_AVR_HI8_LDI_NEG },
   { BFD_RELOC_AVR_HH8_LDI_NEG,      R_AVR_HH8_LDI_NEG },
+  { BFD_RELOC_AVR_MS8_LDI_NEG,      R_AVR_MS8_LDI_NEG },
   { BFD_RELOC_AVR_LO8_LDI_PM,       R_AVR_LO8_LDI_PM },
   { BFD_RELOC_AVR_HI8_LDI_PM,       R_AVR_HI8_LDI_PM },
   { BFD_RELOC_AVR_HH8_LDI_PM,       R_AVR_HH8_LDI_PM },
   { BFD_RELOC_AVR_LO8_LDI_PM_NEG,   R_AVR_LO8_LDI_PM_NEG },
   { BFD_RELOC_AVR_HI8_LDI_PM_NEG,   R_AVR_HI8_LDI_PM_NEG },
   { BFD_RELOC_AVR_HH8_LDI_PM_NEG,   R_AVR_HH8_LDI_PM_NEG },
   { BFD_RELOC_AVR_CALL,             R_AVR_CALL },
   { BFD_RELOC_AVR_LDI,              R_AVR_LDI  },
   { BFD_RELOC_AVR_6,                R_AVR_6    },
   { BFD_RELOC_AVR_6_ADIW,           R_AVR_6_ADIW }
 };
 
+/* 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;
      bfd_reloc_code_real_type code;
 {
   unsigned int i;
 
   for (i = 0;
@@ -578,23 +645,27 @@
       srel -= rel->r_offset;
       srel -= 2;	/* Branch instructions add 2 to the PC...  */
       srel -= (input_section->output_section->vma +
 	       input_section->output_offset);
 
       if (srel & 1)
 	return bfd_reloc_outofrange;
 
+      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:
 	    case bfd_mach_avr4:
 	      break;
 
 	    default:
 	      return bfd_reloc_overflow;
@@ -612,32 +683,35 @@
       x = bfd_get_16 (input_bfd, contents);
       x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
       bfd_put_16 (input_bfd, x, contents);
       break;
 
     case R_AVR_LDI:
       contents += rel->r_offset;
       srel = (bfd_signed_vma) relocation + rel->r_addend;
-      if ((srel & 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);
       break;
 
     case R_AVR_6:
       contents += rel->r_offset;
       srel = (bfd_signed_vma) relocation + rel->r_addend;
       if (((srel & 0xffff) > 63) || (srel < 0))
 	/* 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;
 
     case R_AVR_6_ADIW:
       contents += rel->r_offset;
       srel = (bfd_signed_vma) relocation + rel->r_addend;
       if (((srel & 0xffff) > 63) || (srel < 0))
 	/* Remove offset for data/eeprom section.  */
@@ -660,16 +734,25 @@
       contents += rel->r_offset;
       srel = (bfd_signed_vma) relocation + rel->r_addend;
       srel = (srel >> 16) & 0xff;
       x = bfd_get_16 (input_bfd, contents);
       x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
       bfd_put_16 (input_bfd, x, contents);
       break;
 
+    case R_AVR_MS8_LDI:
+      contents += rel->r_offset;
+      srel = (bfd_signed_vma) relocation + rel->r_addend;
+      srel = (srel >> 24) & 0xff;
+      x = bfd_get_16 (input_bfd, contents);
+      x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
+      bfd_put_16 (input_bfd, x, contents);
+      break;
+
     case R_AVR_LO8_LDI_NEG:
       contents += rel->r_offset;
       srel = (bfd_signed_vma) relocation + rel->r_addend;
       srel = -srel;
       x = bfd_get_16 (input_bfd, contents);
       x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
       bfd_put_16 (input_bfd, x, contents);
       break;
@@ -689,16 +772,26 @@
       srel = (bfd_signed_vma) relocation + rel->r_addend;
       srel = -srel;
       srel = (srel >> 16) & 0xff;
       x = bfd_get_16 (input_bfd, contents);
       x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
       bfd_put_16 (input_bfd, x, contents);
       break;
 
+    case R_AVR_MS8_LDI_NEG:
+      contents += rel->r_offset;
+      srel = (bfd_signed_vma) relocation + rel->r_addend;
+      srel = -srel;
+      srel = (srel >> 24) & 0xff;
+      x = bfd_get_16 (input_bfd, contents);
+      x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
+      bfd_put_16 (input_bfd, x, contents);
+      break;
+
     case R_AVR_LO8_LDI_PM:
       contents += rel->r_offset;
       srel = (bfd_signed_vma) relocation + rel->r_addend;
       if (srel & 1)
 	return bfd_reloc_outofrange;
       srel = srel >> 1;
       x = bfd_get_16 (input_bfd, contents);
       x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
@@ -938,16 +1031,17 @@
     case bfd_mach_avr5:
       val = E_AVR_MACH_AVR5;
       break;
     }
 
   elf_elfheader (abfd)->e_machine = EM_AVR;
   elf_elfheader (abfd)->e_flags &= ~ EF_AVR_MACH;
   elf_elfheader (abfd)->e_flags |= val;
+  elf_elfheader (abfd)->e_flags |= EF_AVR_LINKRELAX_PREPARED;
 }
 
 /* Set the right machine number.  */
 
 static bfd_boolean
 elf32_avr_object_p (abfd)
      bfd *abfd;
 {
@@ -979,16 +1073,943 @@
 	  e_set = bfd_mach_avr5;
 	  break;
 	}
     }
   return bfd_default_set_arch_mach (abfd, bfd_arch_avr,
 				    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
 #define ELF_MAXPAGESIZE		1
 
 #define TARGET_LITTLE_SYM       bfd_elf32_avr_vec
 #define TARGET_LITTLE_NAME	"elf32-avr"
 
@@ -999,9 +2020,13 @@
 #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
 #define elf_backend_final_write_processing \
 					bfd_elf_avr_final_write_processing
 #define elf_backend_object_p		elf32_avr_object_p
 
+#define bfd_elf32_bfd_relax_section elf32_avr_relax_section
+#define bfd_elf32_bfd_get_relocated_section_contents \
+                                        elf32_avr_get_relocated_section_contents
+
 #include "elf32-target.h"
Index: bfd/libbfd.h
===================================================================
RCS file: /cvs/src/src/bfd/libbfd.h,v
retrieving revision 1.173
diff -U8 -r1.173 libbfd.h
--- bfd/libbfd.h	24 Feb 2006 22:10:35 -0000	1.173
+++ bfd/libbfd.h	28 Feb 2006 11:26:20 -0000
@@ -1469,19 +1469,21 @@
   "BFD_RELOC_MMIX_BASE_PLUS_OFFSET",
   "BFD_RELOC_MMIX_LOCAL",
   "BFD_RELOC_AVR_7_PCREL",
   "BFD_RELOC_AVR_13_PCREL",
   "BFD_RELOC_AVR_16_PM",
   "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",
   "BFD_RELOC_AVR_LO8_LDI_PM_NEG",
   "BFD_RELOC_AVR_HI8_LDI_PM_NEG",
   "BFD_RELOC_AVR_HH8_LDI_PM_NEG",
   "BFD_RELOC_AVR_CALL",
   "BFD_RELOC_AVR_LDI",
Index: bfd/reloc.c
===================================================================
RCS file: /cvs/src/src/bfd/reloc.c,v
retrieving revision 1.148
diff -U8 -r1.148 reloc.c
--- bfd/reloc.c	24 Feb 2006 22:10:35 -0000	1.148
+++ bfd/reloc.c	28 Feb 2006 11:26:29 -0000
@@ -3581,16 +3581,21 @@
   This is a 16 bit reloc for the AVR that stores 8 bit value (high 8 bit
   of data memory address) into 8 bit immediate value of LDI insn.
 ENUM
   BFD_RELOC_AVR_HH8_LDI
 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
   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.
 ENUM
   BFD_RELOC_AVR_HI8_LDI_NEG
 ENUMDOC
   This is a 16 bit reloc for the AVR that stores negated 8 bit value
@@ -3598,16 +3603,21 @@
   SUBI insn.
 ENUM
   BFD_RELOC_AVR_HH8_LDI_NEG
 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
   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.
 ENUM
   BFD_RELOC_AVR_HI8_LDI_PM
 ENUMDOC
   This is a 16 bit reloc for the AVR that stores 8 bit value (high 8 bit
Index: gas/config/tc-avr.c
===================================================================
RCS file: /cvs/src/src/gas/config/tc-avr.c,v
retrieving revision 1.30
diff -U8 -r1.30 tc-avr.c
--- gas/config/tc-avr.c	11 Jan 2006 17:39:49 -0000	1.30
+++ gas/config/tc-avr.c	28 Feb 2006 11:26:31 -0000
@@ -165,18 +165,18 @@
 static struct exp_mod_s exp_mod[] =
 {
   {"hh8",    BFD_RELOC_AVR_HH8_LDI,    BFD_RELOC_AVR_HH8_LDI_NEG,    1},
   {"pm_hh8", BFD_RELOC_AVR_HH8_LDI_PM, BFD_RELOC_AVR_HH8_LDI_PM_NEG, 0},
   {"hi8",    BFD_RELOC_AVR_HI8_LDI,    BFD_RELOC_AVR_HI8_LDI_NEG,    1},
   {"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
    in a hash table which expects void * data types.  */
 typedef union
 {
   void * ptr;
   int    index;
@@ -1076,45 +1076,37 @@
 			  _("operand out of range: %ld"), value);
 	  bfd_putl16 ((bfd_vma) insn | (value & 0xf) | ((value & 0x30) << 2), where);
 	  break;
 
 	case BFD_RELOC_AVR_LO8_LDI:
 	  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;
 
 	case BFD_RELOC_AVR_HH8_LDI:
 	  bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 16), where);
 	  break;
 
 	case BFD_RELOC_AVR_LO8_LDI_NEG:
 	  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;
 
 	case BFD_RELOC_AVR_HH8_LDI_NEG:
 	  bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 16), where);
 	  break;
 
 	case BFD_RELOC_AVR_LO8_LDI_PM:
@@ -1190,16 +1182,42 @@
    then it is done here.  */
 
 arelent *
 tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED,
 	      fixS *fixp)
 {
   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 *));
   *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
 
   reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
   reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
   if (reloc->howto == (reloc_howto_type *) NULL)
Index: include/elf/avr.h
===================================================================
RCS file: /cvs/src/src/include/elf/avr.h,v
retrieving revision 1.6
diff -U8 -r1.6 avr.h
--- include/elf/avr.h	10 May 2005 10:21:10 -0000	1.6
+++ include/elf/avr.h	28 Feb 2006 11:26:41 -0000
@@ -21,16 +21,20 @@
 #ifndef _ELF_AVR_H
 #define _ELF_AVR_H
 
 #include "elf/reloc-macros.h"
 
 /* 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
 #define E_AVR_MACH_AVR4 4
 #define E_AVR_MACH_AVR5 5
 
 /* Relocations.  */
 START_RELOC_NUMBERS (elf_avr_reloc_type)
@@ -51,11 +55,13 @@
      RELOC_NUMBER (R_AVR_HH8_LDI_PM,	       14)
      RELOC_NUMBER (R_AVR_LO8_LDI_PM_NEG,       15)
      RELOC_NUMBER (R_AVR_HI8_LDI_PM_NEG,       16)
      RELOC_NUMBER (R_AVR_HH8_LDI_PM_NEG,       17)
      RELOC_NUMBER (R_AVR_CALL,		       18)
      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: ld/scripttempl/avr.sc
===================================================================
RCS file: /cvs/src/src/ld/scripttempl/avr.sc,v
retrieving revision 1.3
diff -U8 -r1.3 avr.sc
--- ld/scripttempl/avr.sc	8 May 2004 21:52:56 -0000	1.3
+++ ld/scripttempl/avr.sc	28 Feb 2006 11:26:43 -0000
@@ -70,68 +70,104 @@
   .rela.bss    ${RELOCATING-0} : { *(.rela.bss)		}
   .rel.plt     ${RELOCATING-0} : { *(.rel.plt)		}
   .rela.plt    ${RELOCATING-0} : { *(.rela.plt)		}
 
   /* Internal text space or external memory */
   .text :
   {
     *(.vectors)
+    KEEP(*(.vectors))
 
     ${CONSTRUCTING+ __ctors_start = . ; }
     ${CONSTRUCTING+ *(.ctors) }
     ${CONSTRUCTING+ __ctors_end = . ; }
     ${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}
 
   .data	${RELOCATING-0} : ${RELOCATING+AT (ADDR (.text) + SIZEOF (.text))}
   {
     ${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 = . ; }
     ${RELOCATING+ PROVIDE (__data_end = .) ; }
   } ${RELOCATING+ > data}
 
   .bss ${RELOCATING+ SIZEOF(.data) + ADDR(.data)} :
   {
     ${RELOCATING+ PROVIDE (__bss_start = .) ; }
     *(.bss)
+    *(.bss*)
     *(COMMON)
     ${RELOCATING+ PROVIDE (__bss_end = .) ; }
   } ${RELOCATING+ > data}
 
   ${RELOCATING+ __data_load_start = LOADADDR(.data); }
   ${RELOCATING+ __data_load_end = __data_load_start + SIZEOF(.data); }
 
   /* Global data not cleared after reset.  */
