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]

[RFC][AVR][PATCH] Add linker relaxation rev. 2


Hi,

thank you for your support so far. This is the second revision of my 
suggestions concerning linker relaxation support for avr.

It incorporates the following changes:

GAS:

In order to make relaxation work, we need to make gas preserve more
relocs than it used to preserve. 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. The first revision had 
problems for the cases where relocations relative to the beginning of the 
text section were issued in the data sections of the same bfd. These got not 
corrected in the old revision. Now, gas should use local symbols for all of 
these relocs.

Because there was no way to find out from the object file itself if it is
suitable for relaxing or not, I have introduced a new flag bit in the
e_flag portion of the elf header. Bits 0-3 have the same meaning. After the
change bit #7 is used for signalizing: "This elf object file is suitable for
linker relaxation since it preserves the local symbols." (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 not valid but if it is filled one day with the correct value,
the relaxation machine will make use of the wrap-around shortcuts for jumps.
Possibly one could add later on target-specific options for filling
this variable with the target hardware's values.

LD:

In order to fix the --gc-sections bug I have added the required KEEP()
statements in the linker script for avr.

Yours,

Bjoern.


2005-10-15  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:

	md_apply_fix, exp_mod:
	Use BFD_RELOC_HH8_LDI and BFD_RELOC_MS8_LDI for hlo8() and hhi8()
	md_begin:
	include inttypes.h, set linkrelax variable to 1, add cast to (intptr_t)
	avr_ldi_expression: use cast to (intptr_t) instead of (int)

	* 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

-------------------------------------------------------
Index: include/elf/avr.h
===================================================================
RCS file: /cvs/src/src/include/elf/avr.h,v
retrieving revision 1.6
diff -U20 -r1.6 avr.h
--- include/elf/avr.h	10 May 2005 10:21:10 -0000	1.6
+++ include/elf/avr.h	15 Oct 2005 14:08:39 -0000
@@ -9,53 +9,59 @@
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
 
    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.  */
 
 #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)
      RELOC_NUMBER (R_AVR_NONE,			0)
      RELOC_NUMBER (R_AVR_32,			1)
      RELOC_NUMBER (R_AVR_7_PCREL,		2)
      RELOC_NUMBER (R_AVR_13_PCREL,		3)
      RELOC_NUMBER (R_AVR_16, 			4)
      RELOC_NUMBER (R_AVR_16_PM, 		5)
      RELOC_NUMBER (R_AVR_LO8_LDI,		6)
      RELOC_NUMBER (R_AVR_HI8_LDI,		7)
      RELOC_NUMBER (R_AVR_HH8_LDI,		8)
      RELOC_NUMBER (R_AVR_LO8_LDI_NEG,		9)
      RELOC_NUMBER (R_AVR_HI8_LDI_NEG,	       10)
      RELOC_NUMBER (R_AVR_HH8_LDI_NEG,	       11)
      RELOC_NUMBER (R_AVR_LO8_LDI_PM,	       12)
      RELOC_NUMBER (R_AVR_HI8_LDI_PM,	       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: bfd/elf32-avr.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-avr.c,v
retrieving revision 1.22
diff -U20 -r1.22 elf32-avr.c
--- bfd/elf32-avr.c	20 Jun 2005 18:12:06 -0000	1.22
+++ bfd/elf32-avr.c	15 Oct 2005 14:08:42 -0000
@@ -1,69 +1,79 @@
 /* AVR-specific support for 32-bit ELF
    Copyright 1999, 2000, 2001, 2002, 2003, 2004
    Free Software Foundation, Inc.
    Contributed by Denis Chertykov <denisc@overta.ru>
 
    This file is part of BFD, the Binary File Descriptor library.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
 
    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
   PARAMS ((bfd *abfd, bfd_reloc_code_real_type code));
 static void avr_info_to_howto_rela
   PARAMS ((bfd *, arelent *, Elf_Internal_Rela *));
 static asection *elf32_avr_gc_mark_hook
   PARAMS ((asection *, struct bfd_link_info *, Elf_Internal_Rela *,
 	   struct elf_link_hash_entry *, Elf_Internal_Sym *));
 static bfd_boolean elf32_avr_gc_sweep_hook
   PARAMS ((bfd *, struct bfd_link_info *, asection *,
 	   const Elf_Internal_Rela *));
 static bfd_boolean elf32_avr_check_relocs
   PARAMS ((bfd *, struct bfd_link_info *, asection *,
 	   const Elf_Internal_Rela *));
 static bfd_reloc_status_type avr_final_link_relocate
   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 */
 	 complain_overflow_bitfield, /* complain_on_overflow */
 	 bfd_elf_generic_reloc,	/* special_function */
 	 "R_AVR_NONE",		/* name */
 	 FALSE,			/* partial_inplace */
 	 0,			/* src_mask */
 	 0,			/* dst_mask */
 	 FALSE),		/* pcrel_offset */
 
   HOWTO (R_AVR_32,		/* type */
 	 0,			/* rightshift */
 	 2,			/* size (0 = byte, 1 = short, 2 = long) */
 	 32,			/* bitsize */
@@ -150,41 +160,42 @@
 	 FALSE,			/* partial_inplace */
 	 0xffff,		/* src_mask */
 	 0xffff,		/* dst_mask */
 	 FALSE),		/* pcrel_offset */
   /* A high 8 bit absolute relocation of 16 bit address.
      For LDI command.  */
   HOWTO (R_AVR_HI8_LDI,		/* 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",	/* 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 */
 	 "R_AVR_HH8_LDI",	/* name */
 	 FALSE,			/* partial_inplace */
 	 0xffff,		/* src_mask */
 	 0xffff,		/* dst_mask */
 	 FALSE),		/* pcrel_offset */
   /* A negative low 8 bit absolute relocation of 16 bit address.
      For LDI command.  */
   HOWTO (R_AVR_LO8_LDI_NEG,	/* type */
 	 0,			/* rightshift */
 	 1,			/* size (0 = byte, 1 = short, 2 = long) */
 	 8,			/* bitsize */
 	 FALSE,			/* pc_relative */
@@ -194,41 +205,41 @@
 	 "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.
      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 */
 	 bfd_elf_generic_reloc,	/* special_function */
 	 "R_AVR_HH8_LDI_NEG",	/* name */
 	 FALSE,			/* partial_inplace */
 	 0xffff,		/* src_mask */
 	 0xffff,		/* dst_mask */
 	 FALSE),		/* pcrel_offset */
   /* A low 8 bit absolute relocation of 24 bit program memory address.
      For LDI command.  */
   HOWTO (R_AVR_LO8_LDI_PM,	/* type */
 	 1,			/* rightshift */
 	 1,			/* size (0 = byte, 1 = short, 2 = long) */
 	 8,			/* bitsize */
@@ -357,77 +368,130 @@
 	 bfd_elf_generic_reloc,	/* special_function */
 	 "R_AVR_6",		/* name */
 	 FALSE,			/* partial_inplace */
 	 0xffff,		/* src_mask */
 	 0xffff,		/* dst_mask */
 	 FALSE),		/* pcrel_offset */
   /* A 6 bit absolute relocation of 6 bit offset.
      For sbiw/adiw command.  */
   HOWTO (R_AVR_6_ADIW,		/* type */
 	 0,			/* rightshift */
 	 0,			/* size (0 = byte, 1 = short, 2 = long) */
 	 6,			/* bitsize */
 	 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;
 };
 
  static const struct avr_reloc_map avr_reloc_map[] =
 {
   { BFD_RELOC_NONE,                 R_AVR_NONE },
   { BFD_RELOC_32,                   R_AVR_32 },
   { BFD_RELOC_AVR_7_PCREL,          R_AVR_7_PCREL },
   { BFD_RELOC_AVR_13_PCREL,         R_AVR_13_PCREL },
   { BFD_RELOC_16,                   R_AVR_16 },
   { BFD_RELOC_AVR_16_PM,            R_AVR_16_PM },
   { BFD_RELOC_AVR_LO8_LDI,          R_AVR_LO8_LDI},
   { BFD_RELOC_AVR_HI8_LDI,          R_AVR_HI8_LDI },
   { BFD_RELOC_AVR_HH8_LDI,          R_AVR_HH8_LDI },
+  { BFD_RELOC_AVR_MS8_LDI,          R_AVR_MS8_LDI },
   { BFD_RELOC_AVR_LO8_LDI_NEG,      R_AVR_LO8_LDI_NEG },
   { BFD_RELOC_AVR_HI8_LDI_NEG,      R_AVR_HI8_LDI_NEG },
   { BFD_RELOC_AVR_HH8_LDI_NEG,      R_AVR_HH8_LDI_NEG },
+  { BFD_RELOC_AVR_MS8_LDI_NEG,      R_AVR_MS8_LDI_NEG },
   { BFD_RELOC_AVR_LO8_LDI_PM,       R_AVR_LO8_LDI_PM },
   { BFD_RELOC_AVR_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. it is meant to be filled one day with
+   0x4000 for 16k devices, 0x8000 for 32k devices and so on.
+   We initialize it here with a value of 0x1000000 resulting in the 
+   fact that we will never detect a wrap-around.  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 = 0x1000000;
+
+/* 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;
        i < sizeof (avr_reloc_map) / sizeof (struct avr_reloc_map);
        i++)
     {
       if (avr_reloc_map[i].bfd_reloc_val == code)
 	return &elf_avr_howto_table[avr_reloc_map[i].elf_reloc_val];
     }
 
   return NULL;
 }
 
 /* Set the howto pointer for an AVR ELF reloc.  */
 
@@ -566,77 +630,83 @@
 	return bfd_reloc_outofrange;
       if (srel > ((1 << 7) - 1) || (srel < - (1 << 7)))
 	return bfd_reloc_overflow;
       x = bfd_get_16 (input_bfd, contents);
       x = (x & 0xfc07) | (((srel >> 1) << 3) & 0x3f8);
       bfd_put_16 (input_bfd, x, contents);
       break;
 
     case R_AVR_13_PCREL:
       contents   += rel->r_offset;
       srel = (bfd_signed_vma) relocation;
       srel += rel->r_addend;
       srel -= rel->r_offset;
       srel -= 2;	/* 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)
 	{
+          printf ("Relative distance too large: %i \n", (int) srel);
+
 	  /* Apply WRAPAROUND if possible.  */
 	  switch (bfd_get_mach (input_bfd))
 	    {
 	    case bfd_mach_avr2:
 	    case bfd_mach_avr4:
 	      break;
 
 	    default:
 	      return bfd_reloc_overflow;
 	    }
 	}
 
       x = bfd_get_16 (input_bfd, contents);
       x = (x & 0xf000) | (srel & 0xfff);
       bfd_put_16 (input_bfd, x, contents);
       break;
 
     case R_AVR_LO8_LDI:
       contents += rel->r_offset;
       srel = (bfd_signed_vma) relocation + rel->r_addend;
       x = bfd_get_16 (input_bfd, contents);
       x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
       bfd_put_16 (input_bfd, x, contents);
       break;
 
     case R_AVR_LDI:
       contents += rel->r_offset;
       srel = (bfd_signed_vma) relocation + rel->r_addend;
-      if ((srel & 0xffff) > 255)
+      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));
       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))
@@ -648,69 +718,88 @@
       break;
 
     case R_AVR_HI8_LDI:
       contents += rel->r_offset;
       srel = (bfd_signed_vma) relocation + rel->r_addend;
       srel = (srel >> 8) & 0xff;
       x = bfd_get_16 (input_bfd, contents);
       x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
       bfd_put_16 (input_bfd, x, contents);
       break;
 
     case R_AVR_HH8_LDI:
       contents += rel->r_offset;
       srel = (bfd_signed_vma) relocation + rel->r_addend;
       srel = (srel >> 16) & 0xff;
       x = bfd_get_16 (input_bfd, contents);
       x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
       bfd_put_16 (input_bfd, x, contents);
       break;
 
+    case R_AVR_MS8_LDI:
+      contents += rel->r_offset;
+      srel = (bfd_signed_vma) relocation + rel->r_addend;
+      srel = (srel >> 24) & 0xff;
+      x = bfd_get_16 (input_bfd, contents);
+      x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
+      bfd_put_16 (input_bfd, x, contents);
+      break;
+
     case R_AVR_LO8_LDI_NEG:
       contents += rel->r_offset;
       srel = (bfd_signed_vma) relocation + rel->r_addend;
       srel = -srel;
       x = bfd_get_16 (input_bfd, contents);
       x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
       bfd_put_16 (input_bfd, x, contents);
       break;
 
     case R_AVR_HI8_LDI_NEG:
       contents += rel->r_offset;
       srel = (bfd_signed_vma) relocation + rel->r_addend;
       srel = -srel;
       srel = (srel >> 8) & 0xff;
       x = bfd_get_16 (input_bfd, contents);
       x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
       bfd_put_16 (input_bfd, x, contents);
       break;
 
     case R_AVR_HH8_LDI_NEG:
       contents += rel->r_offset;
       srel = (bfd_signed_vma) relocation + rel->r_addend;
       srel = -srel;
       srel = (srel >> 16) & 0xff;
       x = bfd_get_16 (input_bfd, contents);
       x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
       bfd_put_16 (input_bfd, x, contents);
       break;
 
+    case R_AVR_MS8_LDI_NEG:
+      contents += rel->r_offset;
+      srel = (bfd_signed_vma) relocation + rel->r_addend;
+      srel = -srel;
+      srel = (srel >> 24) & 0xff;
+      x = bfd_get_16 (input_bfd, contents);
+      x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
+      bfd_put_16 (input_bfd, x, contents);
+      break;
+
     case R_AVR_LO8_LDI_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);
       bfd_put_16 (input_bfd, x, contents);
       break;
 
     case R_AVR_HI8_LDI_PM:
       contents += rel->r_offset;
       srel = (bfd_signed_vma) relocation + rel->r_addend;
       if (srel & 1)
 	return bfd_reloc_outofrange;
       srel = srel >> 1;
       srel = (srel >> 8) & 0xff;
       x = bfd_get_16 (input_bfd, contents);
       x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00);
@@ -926,82 +1015,673 @@
     case bfd_mach_avr1:
       val = E_AVR_MACH_AVR1;
       break;
 
     case bfd_mach_avr3:
       val = E_AVR_MACH_AVR3;
       break;
 
     case bfd_mach_avr4:
       val = E_AVR_MACH_AVR4;
       break;
 
     case bfd_mach_avr5:
       val = E_AVR_MACH_AVR5;
       break;
     }
 
   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;
 {
   unsigned int e_set = bfd_mach_avr2;
   if (elf_elfheader (abfd)->e_machine == EM_AVR
       || elf_elfheader (abfd)->e_machine == EM_AVR_OLD)
     {
       int e_mach = elf_elfheader (abfd)->e_flags & EF_AVR_MACH;
+
       switch (e_mach)
 	{
 	default:
 	case E_AVR_MACH_AVR2:
 	  e_set = bfd_mach_avr2;
 	  break;
 
 	case E_AVR_MACH_AVR1:
 	  e_set = bfd_mach_avr1;
 	  break;
 
 	case E_AVR_MACH_AVR3:
 	  e_set = bfd_mach_avr3;
 	  break;
 
 	case E_AVR_MACH_AVR4:
 	  e_set = bfd_mach_avr4;
 	  break;
 
 	case E_AVR_MACH_AVR5:
 	  e_set = bfd_mach_avr5;
 	  break;
 	}
     }
   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 only try to replace  call (4 bytes) ->  rcall (2 bytes)
+   and jump -> rjmp (safes also 2 bytes).  
+   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_7_PCREL
+          && ELF32_R_TYPE (irel->r_info) != R_AVR_13_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 to 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 assumed_shrink = 600;
+               if (avr_pc_wrap_around > 0x4000)
+                 assumed_shrink = 900;
+  
+               int safety_margin = 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)
+              {
+                if (DEBUG_RELAX)
+                  printf ("shrinking jump/call instruction at address 0x%x"
+                          " in section %s\n\n",
+                          (int) dot, sec->name);
+
+                unsigned char code_msb;
+                unsigned char code_lsb;
+
+                /* 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 changeing 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;
+                  }
+              }
+            break;
+          }
+
+        default:
+          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.  */
+  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_h8_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"
 
 #define elf_info_to_howto	             avr_info_to_howto_rela
 #define elf_info_to_howto_rel	             NULL
 #define elf_backend_relocate_section         elf32_avr_relocate_section
 #define elf_backend_gc_mark_hook             elf32_avr_gc_mark_hook
 #define elf_backend_gc_sweep_hook            elf32_avr_gc_sweep_hook
 #define elf_backend_check_relocs             elf32_avr_check_relocs
 #define elf_backend_can_gc_sections          1
 #define elf_backend_rela_normal		     1
 #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/bfd-in2.h
===================================================================
RCS file: /cvs/src/src/bfd/bfd-in2.h,v
retrieving revision 1.367
diff -U20 -r1.367 bfd-in2.h
--- bfd/bfd-in2.h	30 Sep 2005 15:36:35 -0000	1.367
+++ bfd/bfd-in2.h	15 Oct 2005 14:08:53 -0000
@@ -3429,54 +3429,63 @@
 /* This is a 16 bit reloc for the AVR that stores 13 bit pc relative
 short offset into 12 bits.  */
   BFD_RELOC_AVR_13_PCREL,
 
 /* This is a 16 bit reloc for the AVR that stores 17 bit value (usually
 program memory address) into 16 bits.  */
   BFD_RELOC_AVR_16_PM,
 
 /* This is a 16 bit reloc for the AVR that stores 8 bit value (usually
 data memory address) into 8 bit immediate value of LDI insn.  */
   BFD_RELOC_AVR_LO8_LDI,
 
 /* 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,
 
 /* This is a 16 bit reloc for the AVR that stores 8 bit value (most high 8 bit
 of command address) into 8 bit immediate value of LDI insn.  */
   BFD_RELOC_AVR_HH8_LDI_PM,
 
 /* This is a 16 bit reloc for the AVR that stores negated 8 bit value
 (usually command address) into 8 bit immediate value of SUBI insn.  */
   BFD_RELOC_AVR_LO8_LDI_PM_NEG,
 
 /* This is a 16 bit reloc for the AVR that stores negated 8 bit value
 (high 8 bit of 16 bit command address) into 8 bit immediate value
 of SUBI insn.  */
   BFD_RELOC_AVR_HI8_LDI_PM_NEG,
Index: bfd/libbfd.h
===================================================================
RCS file: /cvs/src/src/bfd/libbfd.h,v
retrieving revision 1.158
diff -U20 -r1.158 libbfd.h
--- bfd/libbfd.h	30 Sep 2005 15:36:40 -0000	1.158
+++ bfd/libbfd.h	15 Oct 2005 14:08:56 -0000
@@ -1456,43 +1456,45 @@
   "BFD_RELOC_MMIX_PUSHJ_1",
   "BFD_RELOC_MMIX_PUSHJ_2",
   "BFD_RELOC_MMIX_PUSHJ_3",
   "BFD_RELOC_MMIX_PUSHJ_STUBBABLE",
   "BFD_RELOC_MMIX_JMP",
   "BFD_RELOC_MMIX_JMP_1",
   "BFD_RELOC_MMIX_JMP_2",
   "BFD_RELOC_MMIX_JMP_3",
   "BFD_RELOC_MMIX_ADDR19",
   "BFD_RELOC_MMIX_ADDR27",
   "BFD_RELOC_MMIX_REG_OR_BYTE",
   "BFD_RELOC_MMIX_REG",
   "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",
   "BFD_RELOC_AVR_6",
   "BFD_RELOC_AVR_6_ADIW",
   "BFD_RELOC_390_12",
   "BFD_RELOC_390_GOT12",
   "BFD_RELOC_390_PLT32",
   "BFD_RELOC_390_COPY",
   "BFD_RELOC_390_GLOB_DAT",
   "BFD_RELOC_390_JMP_SLOT",
   "BFD_RELOC_390_RELATIVE",
   "BFD_RELOC_390_GOTPC",
   "BFD_RELOC_390_GOT16",
   "BFD_RELOC_390_PC16DBL",
Index: bfd/reloc.c
===================================================================
RCS file: /cvs/src/src/bfd/reloc.c,v
retrieving revision 1.140
diff -U20 -r1.140 reloc.c
--- bfd/reloc.c	5 Oct 2005 21:24:23 -0000	1.140
+++ bfd/reloc.c	15 Oct 2005 14:09:03 -0000
@@ -3574,57 +3574,67 @@
   BFD_RELOC_AVR_16_PM
 ENUMDOC
   This is a 16 bit reloc for the AVR that stores 17 bit value (usually
   program memory address) into 16 bits.
 ENUM
   BFD_RELOC_AVR_LO8_LDI
 ENUMDOC
   This is a 16 bit reloc for the AVR that stores 8 bit value (usually
   data memory address) into 8 bit immediate value of LDI insn.
 ENUM
   BFD_RELOC_AVR_HI8_LDI
 ENUMDOC
   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
   (high 8 bit of data memory address) into 8 bit immediate value of
   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
   of command address) into 8 bit immediate value of LDI insn.
 ENUM
   BFD_RELOC_AVR_HH8_LDI_PM
 ENUMDOC
   This is a 16 bit reloc for the AVR that stores 8 bit value (most high 8 bit
   of command address) into 8 bit immediate value of LDI insn.
 ENUM
   BFD_RELOC_AVR_LO8_LDI_PM_NEG
 ENUMDOC
   This is a 16 bit reloc for the AVR that stores negated 8 bit value
   (usually command address) into 8 bit immediate value of SUBI insn.
 ENUM
Index: gas/config/tc-avr.h
===================================================================
RCS file: /cvs/src/src/gas/config/tc-avr.h,v
retrieving revision 1.10
diff -U20 -r1.10 tc-avr.h
--- gas/config/tc-avr.h	11 Aug 2005 01:25:25 -0000	1.10
+++ gas/config/tc-avr.h	15 Oct 2005 14:09:04 -0000
@@ -44,40 +44,41 @@
 /* If you define this macro, GAS will warn about the use of
    nonstandard escape sequences in a string.  */
 #define ONLY_STANDARD_ESCAPES
 
 /* GAS will call this function for any expression that can not be
    recognized.  When the function is called, `input_line_pointer'
    will point to the start of the expression.  */
 #define md_operand(x)
 
 /* You may define this macro to parse an expression used in a data
    allocation pseudo-op such as `.word'.  You can use this to
    recognize relocation directives that may appear in such directives.  */
 #define TC_PARSE_CONS_EXPRESSION(EXPR,N) avr_parse_cons_expression (EXPR,N)
 void avr_parse_cons_expression (expressionS *exp, int nbytes);
 
 /* You may define this macro to generate a fixup for a data
    allocation pseudo-op.  */
 #define TC_CONS_FIX_NEW(FRAG,WHERE,N,EXP) avr_cons_fix_new(FRAG,WHERE,N,EXP)
 void avr_cons_fix_new(fragS *frag,int where, int nbytes, expressionS *exp);
 
+
 /* This should just call either `number_to_chars_bigendian' or
    `number_to_chars_littleendian', whichever is appropriate.  On
    targets like the MIPS which support options to change the
    endianness, which function to call is a runtime decision.  On
    other targets, `md_number_to_chars' can be a simple macro.  */
 #define md_number_to_chars number_to_chars_littleendian
 
 /* `md_short_jump_size'
    `md_long_jump_size'
    `md_create_short_jump'
    `md_create_long_jump'
    If `WORKING_DOT_WORD' is defined, GAS will not do broken word
    processing (*note Broken words::.).  Otherwise, you should set
    `md_short_jump_size' to the size of a short jump (a jump that is
    just long enough to jump around a long jmp) and
    `md_long_jump_size' to the size of a long jump (a jump that can go
    anywhere in the function), You should define
    `md_create_short_jump' to create a short jump around a long jump,
    and define `md_create_long_jump' to create a long jump.  */
 #define WORKING_DOT_WORD
@@ -103,20 +104,43 @@
 #define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section(FIX, SEC)
 extern long md_pcrel_from_section PARAMS ((struct fix *, segT));
 
 /* The number of bytes to put into a word in a listing.  This affects
    the way the bytes are clumped together in the listing.  For
    example, a value of 2 might print `1234 5678' where a value of 1
    would print `12 34 56 78'.  The default value is 4.  */
 #define LISTING_WORD_SIZE 2
 
 /* AVR port uses `$' as a logical line separator */
 #define LEX_DOLLAR 0
 
 /* An `.lcomm' directive with no explicit alignment parameter will
    use this macro to set P2VAR to the alignment that a request for
    SIZE bytes will have.  The alignment is expressed as a power of
    two.  If no alignment should take place, the macro definition
    should do nothing.  Some targets define a `.bss' directive that is
    also affected by this macro.  The default definition will set
    P2VAR to the truncated power of two of sizes up to eight bytes.  */
 #define TC_IMPLICIT_LCOMM_ALIGNMENT(SIZE, P2VAR) (P2VAR) = 0
+
+/* We don't want gas to fixup anything because auf relaxing.  */
+#define TC_LINKRELAX_FIXUP(seg) 1
+
+/* We don't want gas to fixup the following program memory related relocations.
+   We will need them in case that we want to do linker relaxation.
+   We could in principle keep these fixups in gas when not relaxing.
+   However, there is no serious performance penilty when making the linker
+   make the fixup work. */ 
+#define TC_VALIDATE_FIX(FIXP,SEG,SKIP)                      \
+if (FIXP->fx_r_type == BFD_RELOC_AVR_7_PCREL                \
+    || FIXP->fx_r_type == BFD_RELOC_AVR_13_PCREL            \
+    || FIXP->fx_r_type == BFD_RELOC_AVR_LO8_LDI_PM          \
+    || FIXP->fx_r_type == BFD_RELOC_AVR_HI8_LDI_PM          \
+    || FIXP->fx_r_type == BFD_RELOC_AVR_HH8_LDI_PM          \
+    || FIXP->fx_r_type == BFD_RELOC_AVR_16_PM)              \
+  {                                                         \
+     goto SKIP;                                             \
+  }
+
+/* We do not want to adjust any relocations to make implementation of
+   linker relaxations easier.  */
+#define tc_fix_adjustable(FIXP) 0
Index: gas/config/tc-avr.c
===================================================================
RCS file: /cvs/src/src/gas/config/tc-avr.c,v
retrieving revision 1.28
diff -U20 -r1.28 tc-avr.c
--- gas/config/tc-avr.c	11 Aug 2005 01:25:25 -0000	1.28
+++ gas/config/tc-avr.c	15 Oct 2005 14:09:05 -0000
@@ -8,40 +8,41 @@
 
    GAS is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2, or (at your option)
    any later version.
 
    GAS 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 GAS; see the file COPYING.  If not, write to
    the Free Software Foundation, 51 Franklin Street - Fifth Floor,
    Boston, MA 02110-1301, USA.  */
 
 #include <stdio.h>
 #include "as.h"
 #include "safe-ctype.h"
 #include "subsegs.h"
+#include <inttypes.h>
 
 struct avr_opcodes_s
 {
   char *name;
   char *constraints;
   int insn_size;		/* In words.  */
   int isa;
   unsigned int bin_opcode;
 };
 
 #define AVR_INSN(NAME, CONSTR, OPCODE, SIZE, ISA, BIN) \
 {#NAME, CONSTR, SIZE, ISA, BIN},
 
 struct avr_opcodes_s avr_opcodes[] =
 {
   #include "opcode/avr.h"
   {NULL, NULL, 0, 0, 0}
 };
 
 const char comment_chars[] = ";";
@@ -161,42 +162,42 @@
 #define EXP_MOD_RELOC(i) exp_mod[i].reloc
 #define EXP_MOD_NEG_RELOC(i) exp_mod[i].neg_reloc
 #define HAVE_PM_P(i) exp_mod[i].have_pm
 
 struct exp_mod_s
 {
   char *name;
   bfd_reloc_code_real_type reloc;
   bfd_reloc_code_real_type neg_reloc;
   int have_pm;
 };
 
 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},
 };
 
 /* Opcode hash table.  */
 static struct hash_control *avr_hash;
 
 /* Reloc modifiers hash control (hh8,hi8,lo8,pm_xx).  */
 static struct hash_control *avr_mod_hash;
 
 #define OPTION_MMCU 'm'
 #define OPTION_ALL_OPCODES (OPTION_MD_BASE + 1)
 #define OPTION_NO_SKIP_BUG (OPTION_MD_BASE + 2)
 #define OPTION_NO_WRAP     (OPTION_MD_BASE + 3)
 
 struct option md_longopts[] =
 {
   { "mmcu",   required_argument, NULL, OPTION_MMCU        },
   { "mall-opcodes", no_argument, NULL, OPTION_ALL_OPCODES },
   { "mno-skip-bug", no_argument, NULL, OPTION_NO_SKIP_BUG },
   { "mno-wrap",     no_argument, NULL, OPTION_NO_WRAP     },
   { NULL, no_argument, NULL, 0 }
@@ -426,43 +427,46 @@
 {
   abort ();
 }
 
 void
 md_begin ()
 {
   unsigned int i;
   struct avr_opcodes_s *opcode;
   avr_hash = hash_new ();
 
   /* Insert unique names into hash table.  This hash table then provides a
      quick index to the first opcode with a particular name in the opcode
      table.  */
   for (opcode = avr_opcodes; opcode->name; opcode++)
     hash_insert (avr_hash, opcode->name, (char *) opcode);
 
   avr_mod_hash = hash_new ();
 
   for (i = 0; i < sizeof (exp_mod) / sizeof (exp_mod[0]); ++i)
-    hash_insert (avr_mod_hash, EXP_MOD_NAME (i), (void *) (i + 10));
+    hash_insert (avr_mod_hash, EXP_MOD_NAME (i), 
+                 (void *) ((intptr_t) (i + 10)));
 
   bfd_set_arch_mach (stdoutput, TARGET_ARCH, avr_mcu->mach);
+  
+  linkrelax = 1;
 }
 
 /* Resolve STR as a constant expression and return the result.
    If result greater than MAX then error.  */
 
 static unsigned int
 avr_get_constant (str, max)
      char *str;
      int max;
 {
   expressionS ex;
   str = skip_space (str);
   input_line_pointer = str;
   expression (&ex);
 
   if (ex.X_op != O_constant)
     as_bad (_("constant value required"));
 
   if (ex.X_add_number > max || ex.X_add_number < 0)
     as_bad (_("number must be less than %d"), max + 1);
@@ -969,69 +973,61 @@
 	  break;
 
 	case BFD_RELOC_AVR_6:
 	  if ((value > 63) || (value < 0))
 	    as_bad_where (fixP->fx_file, fixP->fx_line,
 			  _("operand out of range: %ld"), value);
 	  bfd_putl16 ((bfd_vma) insn | ((value & 7) | ((value & (3 << 3)) << 7) | ((value & (1 << 5)) << 8)), where);
 	  break;
 
 	case BFD_RELOC_AVR_6_ADIW:
 	  if ((value > 63) || (value < 0))
 	    as_bad_where (fixP->fx_file, fixP->fx_line,
 			  _("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:
 	  bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 1), where);
 	  break;
 
 	case BFD_RELOC_AVR_HI8_LDI_PM:
 	  bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 9), where);
 	  break;
 
 	case BFD_RELOC_AVR_HH8_LDI_PM:
 	  bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 17), where);
 	  break;
 
 	case BFD_RELOC_AVR_LO8_LDI_PM_NEG:
@@ -1183,41 +1179,41 @@
    xx8 (address)
    xx8 (-address)
    pm_xx8 (address)
    pm_xx8 (-address)
    where xx is: hh, hi, lo.  */
 
 static bfd_reloc_code_real_type
 avr_ldi_expression (exp)
      expressionS *exp;
 {
   char *str = input_line_pointer;
   char *tmp;
   char op[8];
   int mod;
   tmp = str;
 
   str = extract_word (str, op, sizeof (op));
 
   if (op[0])
     {
-      mod = (int) hash_find (avr_mod_hash, op);
+      mod = (intptr_t) hash_find (avr_mod_hash, op);
 
       if (mod)
 	{
 	  int closes = 0;
 
 	  mod -= 10;
 	  str = skip_space (str);
 
 	  if (*str == '(')
 	    {
 	      int neg_p = 0;
 
 	      ++str;
 
 	      if (strncmp ("pm(", str, 3) == 0
 		  || strncmp ("-(pm(", str, 5) == 0)
 		{
 		  if (HAVE_PM_P (mod))
 		    {
 		      ++mod;
Index: ld/scripttempl/avr.sc
===================================================================
RCS file: /cvs/src/src/ld/scripttempl/avr.sc,v
retrieving revision 1.3
diff -U20 -r1.3 avr.sc
--- ld/scripttempl/avr.sc	8 May 2004 21:52:56 -0000	1.3
+++ ld/scripttempl/avr.sc	15 Oct 2005 14:09:06 -0000
@@ -58,75 +58,107 @@
     {
       *(.rela.data)
       ${RELOCATING+*(.rela.data.*)}
       ${RELOCATING+*(.rela.gnu.linkonce.d*)}
     }
   .rel.ctors   ${RELOCATING-0} : { *(.rel.ctors)	}
   .rela.ctors  ${RELOCATING-0} : { *(.rela.ctors)	}
   .rel.dtors   ${RELOCATING-0} : { *(.rel.dtors)	}
   .rela.dtors  ${RELOCATING-0} : { *(.rela.dtors)	}
   .rel.got     ${RELOCATING-0} : { *(.rel.got)		}
   .rela.got    ${RELOCATING-0} : { *(.rela.got)		}
   .rel.bss     ${RELOCATING-0} : { *(.rel.bss)		}
   .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)
     *(.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)
     *(COMMON)
     ${RELOCATING+ PROVIDE (__bss_end = .) ; }
   } ${RELOCATING+ > data}

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