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] [PATCH][AVR] Add linker relaxation support. Fix 64-Bit bootstrap failure


Hi,

The following patch adds support for linker relaxation for the avr port.
The most important issue is to choose the shorter variant of the two call/jump
alternatives if the displacement is sufficiently small. 
This small change alone results in up to 3 per cent code size reduction for 
the applications I have checked so far. The patch changes gas and bfd. 
When working on the patch I have stepped over a 64bit-system bug in gas. This 
one is fixed in a separate patch.

BFD:

The method is mainly a cut-and paste from the H8300 approach. Differing from 
the situation there, an additional correction for the relocation addend has 
been newly written.

In order to avoid problems in the interrupt vectors, these are protected from
relaxing as well as a ".jumptables" section that could be used in the (near)
future.

\begin{offtopic}

This ".jumptables" section is meant to be used for table-jump tables for the 
devices with three-byte program counter:
For these devices we will not be happy with 2-byte address tables and 
instead of them we could better use 4 bytes for storing a table of complete
22 bit jump instructions that we could jump to by an ijmp instruction.
For doing this we would need to make sure that relaxing does never change 
the relative offsets within this section.

\end{offtopic}

It would be helpful if someone could have a look at 
elf_avr_relax_delete_bytes.
I took this function from H8300 and it works, but I have no clue what it does
and why it is necessary.

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.

Because there is no way to find out from the object file itself if it is
suitable for relaxing or not, I consider to be it best to make always the
linker make the fixups. Otherwise we would probably need an additional
tag in the object files that prevents undue use. I think the slightly larger
object files (more relocs than previously) are a small price to pay.

Legal issues:

I presently have a copyright assignment only for gcc. Could someone of the 
maintainers please ask the FSF to prepare papers for the other two important 
projects, both, binutils and gdb?

Bjoern.


2005-08-03  Bjoern Haase  <bjoern.m.haase@web.de>

	* gas/config/tc-avr.c:  include <inttypes.h>
	md_begin: replace typecast to int by typecast to intptr_t
	avr_ldi_expression: ditto.


2005-08-03  Bjoern Haase  <bjoern.m.haase@web.de>

	* gas/config/tc-avr.c:  include <inttypes.h>
	md_begin: replace typecast to int by typecast to intptr_t
	avr_ldi_expression: ditto.


2005-08-03  Bjoern Haase  <bjoern.m.haase@web.de>

	* gas/config/tc-avr.h
	TC_VALIDATE_FIX: Add. disable fixups for progmem related relocs.

	* bfd/elf32-avr.c
	elf32_avr_relax_section: add.
	elf32_avr_relax_delete_bytes: add.
	elf32_avr_get_relocated_section_contents: add.
Index: tc-avr.c
===================================================================
RCS file: /cvs/src/src/gas/config/tc-avr.c,v
retrieving revision 1.28
diff -u -2 -0 -r1.28 tc-avr.c
--- tc-avr.c	11 Aug 2005 01:25:25 -0000	1.28
+++ tc-avr.c	3 Oct 2005 19:44:46 -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[] = ";";
@@ -426,41 +427,42 @@
 {
   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);
 }
 
 /* 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"));
 
@@ -1183,41 +1185,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: gas/config/tc-avr.h
===================================================================
RCS file: /cvs/src/src/gas/config/tc-avr.h,v
retrieving revision 1.10
diff -U12 -r1.10 tc-avr.h
--- gas/config/tc-avr.h	11 Aug 2005 01:25:25 -0000	1.10
+++ gas/config/tc-avr.h	3 Oct 2005 15:57:35 -0000
@@ -111,12 +111,28 @@
 
 /* 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 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;                                             \
+  }
Index: bfd/elf32-avr.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-avr.c,v
retrieving revision 1.22
diff -U12 -r1.22 elf32-avr.c
--- bfd/elf32-avr.c	20 Jun 2005 18:12:06 -0000	1.22
+++ bfd/elf32-avr.c	3 Oct 2005 15:57:48 -0000
@@ -8,25 +8,26 @@
    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
@@ -38,24 +39,33 @@
 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 */
@@ -975,33 +985,582 @@
 	  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;
+
+  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;
+
+            /* 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)
+              {
+                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 Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]