This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] add linker relaxation support for avr port rev. 6


Hello,

this is now the sixth (and hopefully acceptable) version of the linker 
relaxation patch for avr. The remaining problem with the last patch were two 
new failures for the gas testsuite. With this patch the gas testsuite no 
longer exhibits regressions.

In total this patch incorporates the following changes:

GAS:

In order to make relaxation work, we need to make gas generate relocs instead
of fixups at various places. The issue is that all the relative instruction
offsets no longer must be calculated at assembly time but at linke time.
For this purpose the patch disables the fixups for all of the program-space
relative relocs by setting linkrelax to the value 1.

Because there was no way to find out from the object file itself if it is
suitable for relaxing or not, It is necessary to avoid that ld attempts to
run relaxing optimization on old object files. In order to guarantee this,
I have introduced a new flag bit in the e_flag portion of the elf header. 
Bits 0-3 have the same meaning (new define symbol EF_AVR_LINKRELAX_PREPARED).

In order to be able to leave the fixup for the higher two bytes of 32 bit 
values to the linker, I have changed the present workaround. Before my change 
gas internally made use of negated BFD enum values for these higher bytes.
I replaced this by adding proper new relocs to bfd and the elf files 
(BFD_RELOC_AVR_MS8_LDI and R_AVR_MS8_LDI as well as the negated variants) for 
the most significant byte of 32 bit values.

BFD:

I first had to implement a couple of changes in bfd for supporting the new 
relocs for the most significant bytes.

Three functions for implementing linker relaxation support are now new. Before 
the relaxation machine starts, it is checked that the bfd has the 
EF_AVR_LINKRELAX_PREPARED flag set in the elf header so that we could safely 
continue to link old object files.

In the ".jumptables" and ".vectors" sections only a reduced relaxation machine 
runs: Jumps and Calls are replaced by the shorter variants without changing 
the relative addresses.

In elf32-avr.c I then have introduced a new global variable determinating the 
wrap-around value of the avr program memory space. This value is presently 
initialized with a value signalizing that no wraparound could occur, but if 
it is filled one day with the correct value, the relaxation machine will make 
use of the wrap-around shortcuts for jumps.
Since this is interesting only for targets with 16k and 32k of program memory, 
I thought about adding a target-specific options for ld 
(-muse-16k-wraparound-jumps) or (-muse-32k-wraparound-jumps).

If this patch happens to be inside the source tree one day, I plan to prepare 
a follow up patch that incorporates this addition.

LD:

In order to fix the --gc-sections bug I have added the required KEEP() 
statements in the linker script for avr.
The contents of .data* , .rodata and .rodata* are now also linked into 
the .data output section so that gcc could be used with the -fdata-sections 
switch.

Yours,

Bjoern.

2006-02-28  Bjoern Haase  <bjoern.m.haase@web.de>

	* include/elf/avr.h: 

	R_AVR_MS8_LDI,R_AVR_MS8_LDI_NEG: Add.
	EF_AVR_LINKRELAX_PREPARED: Add.

	* bfd/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: add.
        elf32_avr_relax_delete_bytes: add.
        elf32_avr_get_relocated_section_contents: add.
	avr_pc_wrap_around: add.
	avr_relative_distance_considering_wrap_around: add.
	avr_final_link_relocate: Handle negative int8t_t immediate for R_AVR_LDI
        
        * gas/config/tc-avr.c: 

	avr_mod_hash_value: Add.
	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
	
	* gas/config/tc-avr.h:
	TC_LINKRELAX_FIXUP, TC_VALIDATE_FIX, tc_fix_adjustable: add.
	
	* bfd/bfd-in2.h: Add BFD_RELOC_AVR_MS8_LDI and BFD_RELOC_AVR_LDI_NEG
	* bfd/libbfd.h: Ditto.
	* bfd/reloc.c: Ditto.

	* ld/scripttempl/avr.sc:
	add *(.jumptables) *(.lowtext), add KEEP() directives
	add *(.data*) *(.rodata) and *(.rodata*) and *(.bss*) to .data and .bss
	output sections.
? 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.  */

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]