]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - bfd/elf32-ip2k.c
Copyright update for binutils
[thirdparty/binutils-gdb.git] / bfd / elf32-ip2k.c
index 0c7c37c9790ade0a853ffcd0a5566bfb713de6df..97cb633dedd94aa958c57ae38a5246c4708e38ae 100644 (file)
@@ -1,11 +1,11 @@
 /* Ubicom IP2xxx specific support for 32-bit ELF
-   Copyright 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+   Copyright (C) 2000-2016 Free Software Foundation, Inc.
 
    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
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
 
    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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
 
-#include "bfd.h"
 #include "sysdep.h"
+#include "bfd.h"
 #include "libbfd.h"
 #include "elf-bfd.h"
 #include "elf/ip2k.h"
@@ -38,59 +39,12 @@ struct ip2k_opcode
   unsigned short opcode;
   unsigned short mask;
 };
-  
-/* Prototypes.  */
-static reloc_howto_type *ip2k_reloc_type_lookup
-  PARAMS ((bfd *, bfd_reloc_code_real_type));
-static int ip2k_is_opcode
-  PARAMS ((bfd_byte *, const struct ip2k_opcode *));
-static bfd_vma symbol_value
-  PARAMS ((bfd *, Elf_Internal_Shdr *, Elf_Internal_Sym *,
-          Elf_Internal_Rela *));
-static void ip2k_get_mem
-  PARAMS ((bfd *, bfd_byte *, int, bfd_byte *));
-static bfd_vma ip2k_nominal_page_bits
-  PARAMS ((bfd *, asection *, bfd_vma, bfd_byte *));
-static bfd_boolean ip2k_test_page_insn
-  PARAMS ((bfd *, asection *, Elf_Internal_Rela *, struct misc *));
-static bfd_boolean ip2k_delete_page_insn
-  PARAMS ((bfd *, asection *, Elf_Internal_Rela *, bfd_boolean *, struct misc *));
-static int ip2k_is_switch_table_128
-  PARAMS ((bfd *, asection *, bfd_vma, bfd_byte *));
-static bfd_boolean ip2k_relax_switch_table_128
-  PARAMS ((bfd *, asection *, Elf_Internal_Rela *, bfd_boolean *, struct misc *));
-static int ip2k_is_switch_table_256
-  PARAMS ((bfd *, asection *, bfd_vma, bfd_byte *));
-static bfd_boolean ip2k_relax_switch_table_256
-  PARAMS ((bfd *, asection *, Elf_Internal_Rela *, bfd_boolean *, struct misc *));
-static bfd_boolean ip2k_elf_relax_section
-  PARAMS ((bfd *, asection *, struct bfd_link_info *, bfd_boolean *));
-static bfd_boolean ip2k_elf_relax_section_page
-  PARAMS ((bfd *, asection *, bfd_boolean *, struct misc *, unsigned long, unsigned long));
-static void adjust_all_relocations
-  PARAMS ((bfd *, asection *, bfd_vma, bfd_vma, int, int));
-static bfd_boolean ip2k_elf_relax_delete_bytes
-  PARAMS ((bfd *, asection *, bfd_vma, int));
-static void ip2k_info_to_howto_rela
-  PARAMS ((bfd *, arelent *, Elf_Internal_Rela *));
-static bfd_reloc_status_type ip2k_final_link_relocate
-  PARAMS ((reloc_howto_type *, bfd *, asection *, bfd_byte *,
-          Elf_Internal_Rela *, bfd_vma));
-static bfd_boolean ip2k_elf_relocate_section
-  PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
-          Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
-static asection *ip2k_elf_gc_mark_hook
-  PARAMS ((asection *, struct bfd_link_info *, Elf_Internal_Rela *,
-          struct elf_link_hash_entry *, Elf_Internal_Sym *));
-static bfd_boolean ip2k_elf_gc_sweep_hook
-  PARAMS ((bfd *, struct bfd_link_info *, asection *,
-          const Elf_Internal_Rela *));
 
 static bfd_boolean ip2k_relaxed = FALSE;
 
 static const struct ip2k_opcode ip2k_page_opcode[] =
 {
-  {0x0010, 0xFFF8},    /* page */
+  {0x0010, 0xFFF8},    /* Page.  */
   {0x0000, 0x0000},
 };
 
@@ -99,7 +53,7 @@ static const struct ip2k_opcode ip2k_page_opcode[] =
 
 static const struct ip2k_opcode ip2k_jmp_opcode[] =
 {
-  {0xE000, 0xE000},    /* jmp */
+  {0xE000, 0xE000},    /* Jmp.  */
   {0x0000, 0x0000},
 };
 
@@ -108,7 +62,7 @@ static const struct ip2k_opcode ip2k_jmp_opcode[] =
 
 static const struct ip2k_opcode ip2k_snc_opcode[] =
 {
-  {0xA00B, 0xFFFF},    /* snc */
+  {0xA00B, 0xFFFF},    /* Snc.  */
   {0x0000, 0x0000},
 };
 
@@ -117,7 +71,7 @@ static const struct ip2k_opcode ip2k_snc_opcode[] =
 
 static const struct ip2k_opcode ip2k_inc_1sp_opcode[] =
 {
-  {0x2B81, 0xFFFF},    /* inc 1(SP) */
+  {0x2B81, 0xFFFF},    /* Inc 1(SP).  */
   {0x0000, 0x0000},
 };
 
@@ -126,7 +80,7 @@ static const struct ip2k_opcode ip2k_inc_1sp_opcode[] =
 
 static const struct ip2k_opcode ip2k_add_2sp_w_opcode[] =
 {
-  {0x1F82, 0xFFFF},    /* add 2(SP),w */
+  {0x1F82, 0xFFFF},    /* Add 2(SP),w.  */
   {0x0000, 0x0000},
 };
 
@@ -135,8 +89,8 @@ static const struct ip2k_opcode ip2k_add_2sp_w_opcode[] =
 
 static const struct ip2k_opcode ip2k_add_w_wreg_opcode[] =
 {
-  {0x1C0A, 0xFFFF},    /* add w,wreg */
-  {0x1E0A, 0xFFFF},    /* add wreg,w */
+  {0x1C0A, 0xFFFF},    /* Add w,wreg.  */
+  {0x1E0A, 0xFFFF},    /* Add wreg,w.  */
   {0x0000, 0x0000},
 };
 
@@ -145,7 +99,7 @@ static const struct ip2k_opcode ip2k_add_w_wreg_opcode[] =
 
 static const struct ip2k_opcode ip2k_add_pcl_w_opcode[] =
 {
-  {0x1E09, 0xFFFF},    /* add pcl,w */
+  {0x1E09, 0xFFFF},    /* Add pcl,w.  */
   {0x0000, 0x0000},
 };
 
@@ -187,7 +141,7 @@ static reloc_howto_type ip2k_elf_howto_table [] =
           pr)                   /* pcrel_offset */
 
   /* This reloc does nothing.  */
-  IP2K_HOWTO (R_IP2K_NONE, 0,2,32, FALSE, 0, "R_IP2K_NONE", 0, 0),
+  IP2K_HOWTO (R_IP2K_NONE, 0,3,0, FALSE, 0, "R_IP2K_NONE", 0, 0),
   /* A 16 bit absolute relocation.  */
   IP2K_HOWTO (R_IP2K_16, 0,1,16, FALSE, 0, "R_IP2K_16", 0, 0xffff),
   /* A 32 bit absolute relocation.  */
@@ -219,10 +173,10 @@ static reloc_howto_type ip2k_elf_howto_table [] =
 
 
 /* Map BFD reloc types to IP2K ELF reloc types.  */
+
 static reloc_howto_type *
-ip2k_reloc_type_lookup (abfd, code)
-     bfd * abfd ATTRIBUTE_UNUSED;
-     bfd_reloc_code_real_type code;
+ip2k_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED,
+                       bfd_reloc_code_real_type code)
 {
   /* Note that the ip2k_elf_howto_table is indxed by the R_
      constants.  Thus, the order that the howto records appear in the
@@ -268,21 +222,33 @@ ip2k_reloc_type_lookup (abfd, code)
   return NULL;
 }
 
+static reloc_howto_type *
+ip2k_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name)
+{
+  unsigned int i;
+
+  for (i = 0;
+       i < sizeof (ip2k_elf_howto_table) / sizeof (ip2k_elf_howto_table[0]);
+       i++)
+    if (ip2k_elf_howto_table[i].name != NULL
+       && strcasecmp (ip2k_elf_howto_table[i].name, r_name) == 0)
+      return &ip2k_elf_howto_table[i];
+
+  return NULL;
+}
+
 static void
-ip2k_get_mem (abfd, addr, length, ptr)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     bfd_byte *addr;
-     int length;
-     bfd_byte *ptr;
+ip2k_get_mem (bfd *abfd ATTRIBUTE_UNUSED,
+             bfd_byte *addr,
+             int length,
+             bfd_byte *ptr)
 {
   while (length --)
     * ptr ++ = bfd_get_8 (abfd, addr ++);
 }
 
 static bfd_boolean
-ip2k_is_opcode (code, opcodes)
-     bfd_byte *code;
-     const struct ip2k_opcode *opcodes;
+ip2k_is_opcode (bfd_byte *code, const struct ip2k_opcode *opcodes)
 {
   unsigned short insn = (code[0] << 8) | code[1];
 
@@ -305,11 +271,10 @@ ip2k_is_opcode (code, opcodes)
 /* Return the value of the symbol associated with the relocation IREL.  */
 
 static bfd_vma
-symbol_value (abfd, symtab_hdr, isymbuf, irel)
-     bfd *abfd;
-     Elf_Internal_Shdr *symtab_hdr;
-     Elf_Internal_Sym *isymbuf;
-     Elf_Internal_Rela *irel;
+symbol_value (bfd *abfd,
+             Elf_Internal_Shdr *symtab_hdr,
+             Elf_Internal_Sym *isymbuf,
+             Elf_Internal_Rela *irel)
 {
   if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
     {
@@ -345,115 +310,6 @@ symbol_value (abfd, symtab_hdr, isymbuf, irel)
     }
 }
 
-/* Returns the expected page state for the given instruction not including
-   the effect of page instructions.  */
-
-static bfd_vma
-ip2k_nominal_page_bits (abfd, sec, addr, contents)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     asection *sec;
-     bfd_vma addr;
-     bfd_byte *contents;
-{
-  bfd_vma page = PAGENO (BASEADDR (sec) + addr);
-
-  /* Check if section flows into this page. If not then the page
-     bits are assumed to match the PC. This will be true unless
-     the user has a page instruction without a call/jump, in which
-     case they are on their own.  */
-  if (PAGENO (BASEADDR (sec)) == page)
-    return page;
-
-  /* Section flows across page boundary. The page bits should match
-     the PC unless there is a possible flow from the previous page,
-     in which case it is not possible to determine the value of the
-     page bits.  */
-  while (PAGENO (BASEADDR (sec) + addr - 2) == page)
-    {
-      bfd_byte code[2];
-
-      addr -= 2;
-      ip2k_get_mem (abfd, contents + addr, 2, code);
-      if (!IS_PAGE_OPCODE (code))
-       continue;
-
-      /* Found a page instruction, check if jump table.  */
-      if (ip2k_is_switch_table_128 (abfd, sec, addr, contents) != -1)
-       /* Jump table => page is conditional.  */
-       continue;
-
-      if (ip2k_is_switch_table_256 (abfd, sec, addr, contents) != -1)
-       /* Jump table => page is conditional.  */
-       continue;
-
-      /* Found a page instruction, check if conditional.  */
-      if (addr >= 2)
-        {
-         ip2k_get_mem (abfd, contents + addr - 2, 2, code);
-          if (IS_SKIP_OPCODE (code))
-           /* Page is conditional.  */
-           continue;
-        }
-
-      /* Unconditional page instruction => page bits should be correct.  */
-      return page;
-    }
-
-  /* Flow from previous page => page bits are impossible to determine.  */
-  return 0;
-}
-
-static bfd_boolean
-ip2k_test_page_insn (abfd, sec, irel, misc)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     asection *sec;
-     Elf_Internal_Rela *irel;
-     struct misc *misc;
-{
-  bfd_vma symval;
-
-  /* Get the value of the symbol referred to by the reloc.  */
-  symval = symbol_value (abfd, misc->symtab_hdr, misc->isymbuf, irel);
-  if (symval == UNDEFINED_SYMBOL)
-    /* This appears to be a reference to an undefined
-       symbol.  Just ignore it--it will be caught by the
-       regular reloc processing.  */
-    return FALSE;
-
-  /* Test if we can delete this page instruction.  */
-  if (PAGENO (symval + irel->r_addend) !=
-      ip2k_nominal_page_bits (abfd, sec, irel->r_offset, misc->contents))
-    return FALSE;
-
-  return TRUE;
-}
-
-static bfd_boolean
-ip2k_delete_page_insn (abfd, sec, irel, again, misc)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     asection *sec;
-     Elf_Internal_Rela *irel;
-     bfd_boolean *again;
-     struct misc *misc;
-{
-  /* Note that we've changed the relocs, section contents, etc.  */
-  elf_section_data (sec)->relocs = misc->irelbase;
-  elf_section_data (sec)->this_hdr.contents = misc->contents;
-  misc->symtab_hdr->contents = (bfd_byte *) misc->isymbuf;
-
-  /* Fix the relocation's type.  */
-  irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_IP2K_NONE);
-
-  /* Delete the PAGE insn.  */
-  if (!ip2k_elf_relax_delete_bytes (abfd, sec, irel->r_offset, 2))
-    return FALSE;
-       
-  /* Modified => will need to iterate relaxation again.  */
-  *again = TRUE;
-  
-  return TRUE;
-}
-
 /* Determine if the instruction sequence matches that for
    the prologue of a switch dispatch table with fewer than
    128 entries.
@@ -484,15 +340,14 @@ ip2k_delete_page_insn (abfd, sec, irel, again, misc)
           jmp     $nnnN  */
 
 static int
-ip2k_is_switch_table_128 (abfd, sec, addr, contents)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     asection *sec;
-     bfd_vma addr;
-     bfd_byte *contents;
+ip2k_is_switch_table_128 (bfd *abfd ATTRIBUTE_UNUSED,
+                         asection *sec,
+                         bfd_vma addr,
+                         bfd_byte *contents)
 {
   bfd_byte code[4];
-  int index = 0;
-  
+  int table_index = 0;
+
   /* Check current page-jmp.  */
   if (addr + 4 > sec->size)
     return -1;
@@ -502,7 +357,7 @@ ip2k_is_switch_table_128 (abfd, sec, addr, contents)
   if ((! IS_PAGE_OPCODE (code + 0))
       || (! IS_JMP_OPCODE (code + 2)))
     return -1;
-  
+
   /* Search back.  */
   while (1)
     {
@@ -513,89 +368,17 @@ ip2k_is_switch_table_128 (abfd, sec, addr, contents)
       ip2k_get_mem (abfd, contents + addr - 4, 4, code);
       if ((IS_ADD_W_WREG_OPCODE (code + 0))
          && (IS_ADD_PCL_W_OPCODE (code + 2)))
-       return index;
+       return table_index;
 
       if ((! IS_PAGE_OPCODE (code + 0))
          || (! IS_JMP_OPCODE (code + 2)))
        return -1;
 
-      index++;
+      table_index++;
       addr -= 4;
     }
 }
 
-static bfd_boolean
-ip2k_relax_switch_table_128 (abfd, sec, irel, again, misc)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     asection *sec;
-     Elf_Internal_Rela *irel;
-     bfd_boolean *again;
-     struct misc *misc;
-{
-  Elf_Internal_Rela *irelend = misc->irelbase + sec->reloc_count;
-  Elf_Internal_Rela *ireltest = irel;
-  bfd_byte code[4];
-  bfd_vma addr;
-  
-  /* Test all page instructions.  */
-  addr = irel->r_offset;
-  while (1)
-    {
-      if (addr + 4 > sec->size)
-       break;
-
-      ip2k_get_mem (abfd, misc->contents + addr, 4, code);
-      if ((! IS_PAGE_OPCODE (code + 0))
-         || (! IS_JMP_OPCODE (code + 2)))
-       break;
-
-      /* Validate relocation entry (every entry should have a matching
-          relocation entry).  */
-      if (ireltest >= irelend)
-        {
-         _bfd_error_handler (_("ip2k relaxer: switch table without complete matching relocation information."));
-          return FALSE;
-        }
-
-      if (ireltest->r_offset != addr)
-        {
-         _bfd_error_handler (_("ip2k relaxer: switch table without complete matching relocation information."));
-          return FALSE;
-        }
-
-      if (! ip2k_test_page_insn (abfd, sec, ireltest, misc))
-       /* Un-removable page insn => nothing can be done.  */
-       return TRUE;
-
-      addr += 4;
-      ireltest += 2;
-    }
-
-  /* Relaxable. Adjust table header.  */
-  ip2k_get_mem (abfd, misc->contents + irel->r_offset - 4, 4, code);
-  if ((! IS_ADD_W_WREG_OPCODE (code + 0))
-      || (! IS_ADD_PCL_W_OPCODE (code + 2)))
-    {
-      _bfd_error_handler (_("ip2k relaxer: switch table header corrupt."));
-      return FALSE;
-    }
-
-  if (!ip2k_elf_relax_delete_bytes (abfd, sec, irel->r_offset - 4, 2))
-    return FALSE;
-
-  *again = TRUE;
-
-  /* Delete all page instructions in table.  */
-  while (irel < ireltest)
-    {
-      if (!ip2k_delete_page_insn (abfd, sec, irel, again, misc))
-       return FALSE;
-      irel += 2;
-    }
-
-  return TRUE;
-}
-
 /* Determine if the instruction sequence matches that for
    the prologue switch dispatch table with fewer than
    256 entries but more than 127.
@@ -637,15 +420,14 @@ ip2k_relax_switch_table_128 (abfd, sec, irel, again, misc)
           jmp     $nnnN  */
 
 static int
-ip2k_is_switch_table_256 (abfd, sec, addr, contents)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     asection *sec;
-     bfd_vma addr;
-     bfd_byte *contents;
+ip2k_is_switch_table_256 (bfd *abfd ATTRIBUTE_UNUSED,
+                         asection *sec,
+                         bfd_vma addr,
+                         bfd_byte *contents)
 {
   bfd_byte code[16];
-  int index = 0;
-  
+  int table_index = 0;
+
   /* Check current page-jmp.  */
   if (addr + 4 > sec->size)
     return -1;
@@ -654,7 +436,7 @@ ip2k_is_switch_table_256 (abfd, sec, addr, contents)
   if ((! IS_PAGE_OPCODE (code + 0))
       || (! IS_JMP_OPCODE (code + 2)))
     return -1;
-  
+
   /* Search back.  */
   while (1)
     {
@@ -671,7 +453,7 @@ ip2k_is_switch_table_256 (abfd, sec, addr, contents)
          && (IS_INC_1SP_OPCODE (code + 10))
          && (IS_PAGE_OPCODE (code + 12))
          && (IS_JMP_OPCODE (code + 14)))
-       return index;
+       return table_index;
 
       if ((IS_ADD_W_WREG_OPCODE (code + 2))
          && (IS_SNC_OPCODE (code + 4))
@@ -680,314 +462,539 @@ ip2k_is_switch_table_256 (abfd, sec, addr, contents)
          && (IS_SNC_OPCODE (code + 10))
          && (IS_INC_1SP_OPCODE (code + 12))
          && (IS_JMP_OPCODE (code + 14)))
-       return index;
-      
+       return table_index;
+
       if ((! IS_PAGE_OPCODE (code + 0))
          || (! IS_JMP_OPCODE (code + 2)))
        return -1;
 
-      index++;
+      table_index++;
       addr -= 4;
     }
 }
 
-static bfd_boolean
-ip2k_relax_switch_table_256 (abfd, sec, irel, again, misc)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     asection *sec;
-     Elf_Internal_Rela *irel;
-     bfd_boolean *again;
-     struct misc *misc;
+/* Returns the expected page state for the given instruction not including
+   the effect of page instructions.  */
+
+static bfd_vma
+ip2k_nominal_page_bits (bfd *abfd ATTRIBUTE_UNUSED,
+                       asection *sec,
+                       bfd_vma addr,
+                       bfd_byte *contents)
 {
-  Elf_Internal_Rela *irelend = misc->irelbase + sec->reloc_count;
-  Elf_Internal_Rela *ireltest = irel;
-  bfd_byte code[12];
-  bfd_vma addr;
-  
-  /* Test all page instructions.  */
-  addr = irel->r_offset;
+  bfd_vma page = PAGENO (BASEADDR (sec) + addr);
 
-  while (1)
+  /* Check if section flows into this page. If not then the page
+     bits are assumed to match the PC. This will be true unless
+     the user has a page instruction without a call/jump, in which
+     case they are on their own.  */
+  if (PAGENO (BASEADDR (sec)) == page)
+    return page;
+
+  /* Section flows across page boundary. The page bits should match
+     the PC unless there is a possible flow from the previous page,
+     in which case it is not possible to determine the value of the
+     page bits.  */
+  while (PAGENO (BASEADDR (sec) + addr - 2) == page)
     {
-      if (addr + 4 > sec->size)
-       break;
+      bfd_byte code[2];
 
-      ip2k_get_mem (abfd, misc->contents + addr, 4, code);
+      addr -= 2;
+      ip2k_get_mem (abfd, contents + addr, 2, code);
+      if (!IS_PAGE_OPCODE (code))
+       continue;
 
-      if ((! IS_PAGE_OPCODE (code + 0))
-         || (! IS_JMP_OPCODE (code + 2)))
-       break;
+      /* Found a page instruction, check if jump table.  */
+      if (ip2k_is_switch_table_128 (abfd, sec, addr, contents) != -1)
+       /* Jump table => page is conditional.  */
+       continue;
 
-      /* Validate relocation entry (every entry should have a matching
-          relocation entry).  */
-      if (ireltest >= irelend)
-        {
-          _bfd_error_handler (_("ip2k relaxer: switch table without complete matching relocation information."));
-          return FALSE;
-        }
+      if (ip2k_is_switch_table_256 (abfd, sec, addr, contents) != -1)
+       /* Jump table => page is conditional.  */
+       continue;
 
-      if (ireltest->r_offset != addr)
+      /* Found a page instruction, check if conditional.  */
+      if (addr >= 2)
         {
-          _bfd_error_handler (_("ip2k relaxer: switch table without complete matching relocation information."));
-          return FALSE;
+         ip2k_get_mem (abfd, contents + addr - 2, 2, code);
+          if (IS_SKIP_OPCODE (code))
+           /* Page is conditional.  */
+           continue;
         }
 
-      if (!ip2k_test_page_insn (abfd, sec, ireltest, misc))
-       /* Un-removable page insn => nothing can be done.  */
-       return TRUE;
-
-      addr += 4;
-      ireltest += 2;
+      /* Unconditional page instruction => page bits should be correct.  */
+      return page;
     }
 
-  /* Relaxable. Adjust table header.  */
-  ip2k_get_mem (abfd, misc->contents + irel->r_offset - 4, 2, code);
-  if (IS_PAGE_OPCODE (code))
-    addr = irel->r_offset - 16;
-  else
-    addr = irel->r_offset - 14;
+  /* Flow from previous page => page bits are impossible to determine.  */
+  return 0;
+}
 
-  ip2k_get_mem (abfd, misc->contents + addr, 12, code);
-  if ((!IS_ADD_W_WREG_OPCODE (code + 0))
-      || (!IS_SNC_OPCODE (code + 2))
-      || (!IS_INC_1SP_OPCODE (code + 4))
-      || (!IS_ADD_2SP_W_OPCODE (code + 6))
-      || (!IS_SNC_OPCODE (code + 8))
-      || (!IS_INC_1SP_OPCODE (code + 10)))
-    {
-      _bfd_error_handler (_("ip2k relaxer: switch table header corrupt."));
-      return FALSE;
-    }
+static bfd_boolean
+ip2k_test_page_insn (bfd *abfd ATTRIBUTE_UNUSED,
+                    asection *sec,
+                    Elf_Internal_Rela *irel,
+                    struct misc *misc)
+{
+  bfd_vma symval;
 
-  /* Delete first 3 opcodes.  */
-  if (!ip2k_elf_relax_delete_bytes (abfd, sec, addr + 0, 6))
+  /* Get the value of the symbol referred to by the reloc.  */
+  symval = symbol_value (abfd, misc->symtab_hdr, misc->isymbuf, irel);
+  if (symval == UNDEFINED_SYMBOL)
+    /* This appears to be a reference to an undefined
+       symbol.  Just ignore it--it will be caught by the
+       regular reloc processing.  */
     return FALSE;
 
-  *again = TRUE;
-
-  /* Delete all page instructions in table.  */
-  while (irel < ireltest)
-    {
-      if (!ip2k_delete_page_insn (abfd, sec, irel, again, misc))
-       return FALSE;
-      irel += 2;
-    }
+  /* Test if we can delete this page instruction.  */
+  if (PAGENO (symval + irel->r_addend) !=
+      ip2k_nominal_page_bits (abfd, sec, irel->r_offset, misc->contents))
+    return FALSE;
 
   return TRUE;
 }
 
-/* This function handles relaxing for the ip2k.
+/* Parts of a Stabs entry.  */
 
-   Principle: Start with the first page and remove page instructions that
-   are not require on this first page. By removing page instructions more
-   code will fit into this page - repeat until nothing more can be achieved
-   for this page. Move on to the next page.
+#define STRDXOFF   0
+#define TYPEOFF    4
+#define OTHEROFF   5
+#define DESCOFF    6
+#define VALOFF     8
+#define STABSIZE   12
 
-   Processing the pages one at a time from the lowest page allows a removal
-   only policy to be used - pages can be removed but are never reinserted.  */
+/* Adjust all the relocations entries after adding or inserting instructions.  */
 
-static bfd_boolean
-ip2k_elf_relax_section (abfd, sec, link_info, again)
-     bfd *abfd;
-     asection *sec;
-     struct bfd_link_info *link_info;
-     bfd_boolean *again;
+static void
+adjust_all_relocations (bfd *abfd,
+                       asection *sec,
+                       bfd_vma addr,
+                       bfd_vma endaddr,
+                       int count,
+                       int noadj)
 {
   Elf_Internal_Shdr *symtab_hdr;
-  Elf_Internal_Rela *internal_relocs;
-  bfd_byte *contents = NULL;
-  Elf_Internal_Sym *isymbuf = NULL;
-  static asection * first_section = NULL;
-  static unsigned long search_addr;
-  static unsigned long page_start = 0;
-  static unsigned long page_end = 0;
-  static unsigned int pass = 0;
-  static bfd_boolean new_pass = FALSE;
-  static bfd_boolean changed = FALSE;
-  struct misc misc;
+  Elf_Internal_Sym *isymbuf, *isym, *isymend;
+  unsigned int shndx;
+  Elf_Internal_Rela *irel, *irelend, *irelbase;
+  struct elf_link_hash_entry **sym_hashes;
+  struct elf_link_hash_entry **end_hashes;
+  unsigned int symcount;
   asection *stab;
 
-  /* Assume nothing changes.  */
-  *again = FALSE;
+  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+  isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
 
-  if (first_section == NULL)
-    {
-      ip2k_relaxed = TRUE;
-      first_section = sec;
-    }
+  shndx = _bfd_elf_section_from_bfd_section (abfd, sec);
 
-  if (first_section == sec)
+  irelbase = elf_section_data (sec)->relocs;
+  irelend = irelbase + sec->reloc_count;
+
+  for (irel = irelbase; irel < irelend; irel++)
     {
-      pass++;
-      new_pass = TRUE;
-    }
+      if (ELF32_R_TYPE (irel->r_info) != R_IP2K_NONE)
+        {
+          /* Get the value of the symbol referred to by the reloc.  */
+          if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
+            {
+              asection *sym_sec;
 
-  /* 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;
+              /* A local symbol.  */
+             isym = isymbuf + ELF32_R_SYM (irel->r_info);
+              sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
 
-  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+              if (isym->st_shndx == shndx)
+                {
+                  bfd_vma baseaddr = BASEADDR (sec);
+                  bfd_vma symval = BASEADDR (sym_sec) + isym->st_value
+                                   + irel->r_addend;
 
-  internal_relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL,
-                                              (Elf_Internal_Rela *)NULL,
-                                              link_info->keep_memory);
-  if (internal_relocs == NULL)
-    goto error_return;
+                  if ((baseaddr + addr + noadj) <= symval
+                      && symval < (baseaddr + endaddr))
+                    irel->r_addend += count;
+                }
+            }
+        }
 
-  /* Make sure the stac.rela stuff gets read in.  */
-  stab = bfd_get_section_by_name (abfd, ".stab");
+      /* Do this only for PC space relocations.  */
+      if (addr <= irel->r_offset && irel->r_offset < endaddr)
+        irel->r_offset += count;
+    }
 
+  /* Now fix the stab relocations.  */
+  stab = bfd_get_section_by_name (abfd, ".stab");
   if (stab)
     {
-      /* So stab does exits.  */
-      Elf_Internal_Rela * irelbase;
+      bfd_byte *stabcontents, *stabend, *stabp;
+      bfd_size_type stab_size = stab->rawsize ? stab->rawsize : stab->size;
+
+      irelbase = elf_section_data (stab)->relocs;
+      irelend = irelbase + stab->reloc_count;
+
+      /* Pull out the contents of the stab section.  */
+      if (elf_section_data (stab)->this_hdr.contents != NULL)
+       stabcontents = elf_section_data (stab)->this_hdr.contents;
+      else
+       {
+         if (!bfd_malloc_and_get_section (abfd, stab, &stabcontents))
+           {
+             if (stabcontents != NULL)
+               free (stabcontents);
+             return;
+           }
+
+         /* We need to remember this.  */
+         elf_section_data (stab)->this_hdr.contents = stabcontents;
+       }
+
+      stabend = stabcontents + stab_size;
+
+      for (irel = irelbase; irel < irelend; irel++)
+       {
+         if (ELF32_R_TYPE (irel->r_info) != R_IP2K_NONE)
+           {
+             /* Get the value of the symbol referred to by the reloc.  */
+             if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
+               {
+                 asection *sym_sec;
+
+                 /* A local symbol.  */
+                 isym = isymbuf + ELF32_R_SYM (irel->r_info);
+                 sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
+
+                 if (sym_sec == sec)
+                   {
+                     const char *name;
+                     unsigned char type;
+                     bfd_vma value;
+                     bfd_vma baseaddr = BASEADDR (sec);
+                     bfd_vma symval = BASEADDR (sym_sec) + isym->st_value
+                       + irel->r_addend;
+
+                     if ((baseaddr + addr) <= symval
+                         && symval <= (baseaddr + endaddr))
+                       irel->r_addend += count;
+
+                     /* Go hunt up a function and fix its line info if needed.  */
+                     stabp = stabcontents + irel->r_offset - 8;
+
+                     /* Go pullout the stab entry.  */
+                     type  = bfd_h_get_8 (abfd, stabp + TYPEOFF);
+                     value = bfd_h_get_32 (abfd, stabp + VALOFF);
+
+                     name = bfd_get_stab_name (type);
+
+                     if (strcmp (name, "FUN") == 0)
+                       {
+                         int function_adjusted = 0;
+
+                         if (symval > (baseaddr + addr))
+                           /* Not in this function.  */
+                           continue;
+
+                         /* Hey we got a function hit.  */
+                         stabp += STABSIZE;
+                         for (;stabp < stabend; stabp += STABSIZE)
+                           {
+                             /* Go pullout the stab entry.  */
+                             type  = bfd_h_get_8 (abfd, stabp + TYPEOFF);
+                             value = bfd_h_get_32 (abfd, stabp + VALOFF);
+
+                             name = bfd_get_stab_name (type);
 
-      irelbase = _bfd_elf_link_read_relocs (abfd, stab, NULL,
-                                           (Elf_Internal_Rela *)NULL,
-                                           link_info->keep_memory);
+                             if (strcmp (name, "FUN") == 0)
+                               {
+                                 /* Hit another function entry.  */
+                                 if (function_adjusted)
+                                   {
+                                     /* Adjust the value.  */
+                                     value += count;
+
+                                     /* We need to put it back.  */
+                                     bfd_h_put_32 (abfd, value,stabp + VALOFF);
+                                   }
+
+                                 /* And then bale out.  */
+                                 break;
+                               }
+
+                             if (strcmp (name, "SLINE") == 0)
+                               {
+                                 /* Got a line entry.  */
+                                 if ((baseaddr + addr) <= (symval + value))
+                                   {
+                                     /* Adjust the line entry.  */
+                                     value += count;
+
+                                     /* We need to put it back.  */
+                                     bfd_h_put_32 (abfd, value,stabp + VALOFF);
+                                     function_adjusted = 1;
+                                   }
+                               }
+                           }
+                       }
+                   }
+               }
+           }
+       }
     }
 
-  /* Get section contents cached copy if it exists.  */
-  if (contents == NULL)
+  /* When adding an instruction back it is sometimes necessary to move any
+     global or local symbol that was referencing the first instruction of
+     the moved block to refer to the first instruction of the inserted block.
+
+     For example adding a PAGE instruction before a CALL or JMP requires
+     that any label on the CALL or JMP is moved to the PAGE insn.  */
+  addr += noadj;
+
+  /* Adjust the local symbols defined in this section.  */
+  isymend = isymbuf + symtab_hdr->sh_info;
+  for (isym = isymbuf; isym < isymend; isym++)
     {
-      /* Get cached copy if it exists.  */
-      if (elf_section_data (sec)->this_hdr.contents != NULL)
-       contents = elf_section_data (sec)->this_hdr.contents;
-      else
+      if (isym->st_shndx == shndx
+         && addr <= isym->st_value
+         && isym->st_value < endaddr)
+       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)
        {
-         /* Go get them off disk.  */
-         if (!bfd_malloc_and_get_section (abfd, sec, &contents))
-           goto error_return;
+          if (addr <= sym_hash->root.u.def.value
+              && sym_hash->root.u.def.value < endaddr)
+           sym_hash->root.u.def.value += count;
        }
     }
 
-  /* Read this BFD's symbols cached copy if it exists.  */
-  if (isymbuf == NULL && symtab_hdr->sh_info != 0)
+  return;
+}
+
+/* Delete some bytes from a section while relaxing.  */
+
+static bfd_boolean
+ip2k_elf_relax_delete_bytes (bfd *abfd,
+                            asection *sec,
+                            bfd_vma addr,
+                            int count)
+{
+  bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
+  bfd_vma endaddr = sec->size;
+
+  /* Actually delete the bytes.  */
+  memmove (contents + addr, contents + addr + count,
+          endaddr - addr - count);
+
+  sec->size -= count;
+
+  adjust_all_relocations (abfd, sec, addr + count, endaddr, -count, 0);
+  return TRUE;
+}
+
+static bfd_boolean
+ip2k_delete_page_insn (bfd *abfd ATTRIBUTE_UNUSED,
+                      asection *sec,
+                      Elf_Internal_Rela *irel,
+                      bfd_boolean *again,
+                      struct misc *misc)
+{
+  /* Note that we've changed the relocs, section contents, etc.  */
+  elf_section_data (sec)->relocs = misc->irelbase;
+  elf_section_data (sec)->this_hdr.contents = misc->contents;
+  misc->symtab_hdr->contents = (bfd_byte *) misc->isymbuf;
+
+  /* Fix the relocation's type.  */
+  irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_IP2K_NONE);
+
+  /* Delete the PAGE insn.  */
+  if (!ip2k_elf_relax_delete_bytes (abfd, sec, irel->r_offset, 2))
+    return FALSE;
+
+  /* Modified => will need to iterate relaxation again.  */
+  *again = TRUE;
+
+  return TRUE;
+}
+
+static bfd_boolean
+ip2k_relax_switch_table_128 (bfd *abfd ATTRIBUTE_UNUSED,
+                            asection *sec,
+                            Elf_Internal_Rela *irel,
+                            bfd_boolean *again,
+                            struct misc *misc)
+{
+  Elf_Internal_Rela *irelend = misc->irelbase + sec->reloc_count;
+  Elf_Internal_Rela *ireltest = irel;
+  bfd_byte code[4];
+  bfd_vma addr;
+
+  /* Test all page instructions.  */
+  addr = irel->r_offset;
+  while (1)
     {
-      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;
+      if (addr + 4 > sec->size)
+       break;
+
+      ip2k_get_mem (abfd, misc->contents + addr, 4, code);
+      if ((! IS_PAGE_OPCODE (code + 0))
+         || (! IS_JMP_OPCODE (code + 2)))
+       break;
+
+      /* Validate relocation entry (every entry should have a matching
+          relocation entry).  */
+      if (ireltest >= irelend)
+        {
+         _bfd_error_handler (_("ip2k relaxer: switch table without complete matching relocation information."));
+          return FALSE;
+        }
+
+      if (ireltest->r_offset != addr)
+        {
+         _bfd_error_handler (_("ip2k relaxer: switch table without complete matching relocation information."));
+          return FALSE;
+        }
+
+      if (! ip2k_test_page_insn (abfd, sec, ireltest, misc))
+       /* Un-removable page insn => nothing can be done.  */
+       return TRUE;
+
+      addr += 4;
+      ireltest += 2;
+    }
+
+  /* Relaxable. Adjust table header.  */
+  ip2k_get_mem (abfd, misc->contents + irel->r_offset - 4, 4, code);
+  if ((! IS_ADD_W_WREG_OPCODE (code + 0))
+      || (! IS_ADD_PCL_W_OPCODE (code + 2)))
+    {
+      _bfd_error_handler (_("ip2k relaxer: switch table header corrupt."));
+      return FALSE;
+    }
+
+  if (!ip2k_elf_relax_delete_bytes (abfd, sec, irel->r_offset - 4, 2))
+    return FALSE;
+
+  *again = TRUE;
+
+  /* Delete all page instructions in table.  */
+  while (irel < ireltest)
+    {
+      if (!ip2k_delete_page_insn (abfd, sec, irel, again, misc))
+       return FALSE;
+      irel += 2;
     }
 
-  misc.symtab_hdr = symtab_hdr;
-  misc.isymbuf = isymbuf;
-  misc.irelbase = internal_relocs;
-  misc.contents = contents;
+  return TRUE;
+}
 
-  /* This is where all the relaxation actually get done.  */
-  if ((pass == 1) || (new_pass && !changed))
-    {
-      /* On the first pass we simply search for the lowest page that
-         we havn't relaxed yet. Note that the pass count is reset
-         each time a page is complete in order to move on to the next page.
-         If we can't find any more pages then we are finished.  */
-      if (new_pass)
-       {
-         pass = 1;
-         new_pass = FALSE;
-         changed = TRUE; /* Pre-initialize to break out of pass 1.  */
-         search_addr = 0xFFFFFFFF;
-       }
+static bfd_boolean
+ip2k_relax_switch_table_256 (bfd *abfd ATTRIBUTE_UNUSED,
+                            asection *sec,
+                            Elf_Internal_Rela *irel,
+                            bfd_boolean *again,
+                            struct misc *misc)
+{
+  Elf_Internal_Rela *irelend = misc->irelbase + sec->reloc_count;
+  Elf_Internal_Rela *ireltest = irel;
+  bfd_byte code[12];
+  bfd_vma addr;
 
-      if ((BASEADDR (sec) + sec->size < search_addr)
-         && (BASEADDR (sec) + sec->size > page_end))
-       {
-         if (BASEADDR (sec) <= page_end)
-           search_addr = page_end + 1;
-         else
-           search_addr = BASEADDR (sec);
+  /* Test all page instructions.  */
+  addr = irel->r_offset;
 
-         /* Found a page => more work to do.  */
-         *again = TRUE;
-       }
-    }
-  else
+  while (1)
     {
-      if (new_pass)
-       {
-         new_pass = FALSE;
-         changed = FALSE;
-         page_start = PAGENO (search_addr);
-         page_end = page_start | 0x00003FFF;
-       }
+      if (addr + 4 > sec->size)
+       break;
 
-      /* Only process sections in range.  */
-      if ((BASEADDR (sec) + sec->size >= page_start)
-         && (BASEADDR (sec) <= page_end))
-       {
-          if (!ip2k_elf_relax_section_page (abfd, sec, &changed, &misc, page_start, page_end))
-           return FALSE;
-       }
-      *again = TRUE;
+      ip2k_get_mem (abfd, misc->contents + addr, 4, code);
+
+      if ((! IS_PAGE_OPCODE (code + 0))
+         || (! IS_JMP_OPCODE (code + 2)))
+       break;
+
+      /* Validate relocation entry (every entry should have a matching
+          relocation entry).  */
+      if (ireltest >= irelend)
+        {
+          _bfd_error_handler (_("ip2k relaxer: switch table without complete matching relocation information."));
+          return FALSE;
+        }
+
+      if (ireltest->r_offset != addr)
+        {
+          _bfd_error_handler (_("ip2k relaxer: switch table without complete matching relocation information."));
+          return FALSE;
+        }
+
+      if (!ip2k_test_page_insn (abfd, sec, ireltest, misc))
+       /* Un-removable page insn => nothing can be done.  */
+       return TRUE;
+
+      addr += 4;
+      ireltest += 2;
     }
 
-  /* Perform some house keeping after relaxing the section.  */
+  /* Relaxable. Adjust table header.  */
+  ip2k_get_mem (abfd, misc->contents + irel->r_offset - 4, 2, code);
+  if (IS_PAGE_OPCODE (code))
+    addr = irel->r_offset - 16;
+  else
+    addr = irel->r_offset - 14;
 
-  if (isymbuf != NULL
-      && symtab_hdr->contents != (unsigned char *) isymbuf)
+  ip2k_get_mem (abfd, misc->contents + addr, 12, code);
+  if ((!IS_ADD_W_WREG_OPCODE (code + 0))
+      || (!IS_SNC_OPCODE (code + 2))
+      || (!IS_INC_1SP_OPCODE (code + 4))
+      || (!IS_ADD_2SP_W_OPCODE (code + 6))
+      || (!IS_SNC_OPCODE (code + 8))
+      || (!IS_INC_1SP_OPCODE (code + 10)))
     {
-      if (! link_info->keep_memory)
-       free (isymbuf);
-      else
-       symtab_hdr->contents = (unsigned char *) isymbuf;
+      _bfd_error_handler (_("ip2k relaxer: switch table header corrupt."));
+      return FALSE;
     }
 
-  if (contents != NULL
-      && elf_section_data (sec)->this_hdr.contents != contents)
+  /* Delete first 3 opcodes.  */
+  if (!ip2k_elf_relax_delete_bytes (abfd, sec, addr + 0, 6))
+    return FALSE;
+
+  *again = TRUE;
+
+  /* Delete all page instructions in table.  */
+  while (irel < ireltest)
     {
-      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 (!ip2k_delete_page_insn (abfd, sec, irel, again, misc))
+       return FALSE;
+      irel += 2;
     }
 
-  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;
 }
 
 /* This function handles relaxation of a section in a specific page.  */
 
 static bfd_boolean
-ip2k_elf_relax_section_page (abfd, sec, again, misc, page_start, page_end)
-     bfd *abfd;
-     asection *sec;
-     bfd_boolean *again;
-     struct misc *misc;
-     unsigned long page_start;
-     unsigned long page_end;
+ip2k_elf_relax_section_page (bfd *abfd,
+                            asection *sec,
+                            bfd_boolean *again,
+                            struct misc *misc,
+                            unsigned long page_start,
+                            unsigned long page_end)
 {
   Elf_Internal_Rela *irelend = misc->irelbase + sec->reloc_count;
   Elf_Internal_Rela *irel;
   int switch_table_128;
   int switch_table_256;
-  
+
   /* Walk thru the section looking for relaxation opportunities.  */
   for (irel = misc->irelbase; irel < irelend; irel++)
     {
@@ -1036,307 +1043,220 @@ ip2k_elf_relax_section_page (abfd, sec, again, misc, page_start, page_end)
            return FALSE;
 
          continue;
-       }
-    }
-
-  return TRUE;
-}
-
-/* Parts of a Stabs entry.  */
-
-#define STRDXOFF  (0)
-#define TYPEOFF   (4)
-#define OTHEROFF  (5)
-#define DESCOFF   (6)
-#define VALOFF    (8)
-#define STABSIZE  (12)
-
-/* Adjust all the relocations entries after adding or inserting instructions.  */
-
-static void
-adjust_all_relocations (abfd, sec, addr, endaddr, count, noadj)
-     bfd *abfd;
-     asection *sec;
-     bfd_vma addr;
-     bfd_vma endaddr;
-     int count;
-     int noadj;
-{
-  Elf_Internal_Shdr *symtab_hdr;
-  Elf_Internal_Sym *isymbuf, *isym, *isymend;
-  unsigned int shndx;
-  bfd_byte *contents;
-  Elf_Internal_Rela *irel, *irelend, *irelbase;
-  struct elf_link_hash_entry **sym_hashes;
-  struct elf_link_hash_entry **end_hashes;
-  unsigned int symcount;
-  asection *stab;
-
-  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
-  isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
-
-  shndx = _bfd_elf_section_from_bfd_section (abfd, sec);
-
-  contents = elf_section_data (sec)->this_hdr.contents;
-
-  irelbase = elf_section_data (sec)->relocs;
-  irelend = irelbase + sec->reloc_count;
-
-  for (irel = irelbase; irel < irelend; irel++)
-    {
-      if (ELF32_R_TYPE (irel->r_info) != R_IP2K_NONE)
-        {
-          /* Get the value of the symbol referred to by the reloc.  */
-          if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
-            {
-              asection *sym_sec;
-
-              /* A local symbol.  */
-             isym = isymbuf + ELF32_R_SYM (irel->r_info);
-              sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
-
-              if (isym->st_shndx == shndx)
-                {
-                  bfd_vma baseaddr = BASEADDR (sec);
-                  bfd_vma symval = BASEADDR (sym_sec) + isym->st_value
-                                   + irel->r_addend;
-
-                  if ((baseaddr + addr + noadj) <= symval
-                      && symval < (baseaddr + endaddr))
-                    irel->r_addend += count;
-                }
-            }
-        }
-
-      /* Do this only for PC space relocations.  */
-      if (addr <= irel->r_offset && irel->r_offset < endaddr)
-        irel->r_offset += count;
-    }
-
-  /* Now fix the stab relocations.  */
-  stab = bfd_get_section_by_name (abfd, ".stab");
-  if (stab)
-    {
-      bfd_byte *stabcontents, *stabend, *stabp;
-      bfd_size_type stab_size = stab->rawsize ? stab->rawsize : stab->size;
-
-      irelbase = elf_section_data (stab)->relocs;
-      irelend = irelbase + stab->reloc_count;
-
-      /* Pull out the contents of the stab section.  */
-      if (elf_section_data (stab)->this_hdr.contents != NULL)
-       stabcontents = elf_section_data (stab)->this_hdr.contents;
-      else
-       {
-         if (!bfd_malloc_and_get_section (abfd, stab, &stabcontents))
-           {
-             if (stabcontents != NULL)
-               free (stabcontents);
-             return;
-           }
-
-         /* We need to remember this.  */
-         elf_section_data (stab)->this_hdr.contents = stabcontents;
-       }
-
-      stabend = stabcontents + stab_size;
-
-      for (irel = irelbase; irel < irelend; irel++)
-       {
-         if (ELF32_R_TYPE (irel->r_info) != R_IP2K_NONE)
-           {
-             /* Get the value of the symbol referred to by the reloc.  */
-             if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
-               {
-                 asection *sym_sec;
-                 
-                 /* A local symbol.  */
-                 isym = isymbuf + ELF32_R_SYM (irel->r_info);
-                 sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
-                 
-                 if (sym_sec == sec)
-                   {
-                     const char *name;
-                     unsigned long strx;
-                     unsigned char type, other;
-                     unsigned short desc;
-                     bfd_vma value;
-                     bfd_vma baseaddr = BASEADDR (sec);
-                     bfd_vma symval = BASEADDR (sym_sec) + isym->st_value
-                       + irel->r_addend;
-                     
-                     if ((baseaddr + addr) <= symval
-                         && symval <= (baseaddr + endaddr))
-                       irel->r_addend += count;
-
-                     /* Go hunt up a function and fix its line info if needed.  */
-                     stabp = stabcontents + irel->r_offset - 8; 
+       }
+    }
 
-                     /* Go pullout the stab entry.  */
-                     strx  = bfd_h_get_32 (abfd, stabp + STRDXOFF);
-                     type  = bfd_h_get_8 (abfd, stabp + TYPEOFF);
-                     other = bfd_h_get_8 (abfd, stabp + OTHEROFF);
-                     desc  = bfd_h_get_16 (abfd, stabp + DESCOFF);
-                     value = bfd_h_get_32 (abfd, stabp + VALOFF);
-                     
-                     name = bfd_get_stab_name (type);
-                     
-                     if (strcmp (name, "FUN") == 0)
-                       {
-                         int function_adjusted = 0;
+  return TRUE;
+}
 
-                         if (symval > (baseaddr + addr))
-                           /* Not in this function.  */
-                           continue;
+/* This function handles relaxing for the ip2k.
 
-                         /* Hey we got a function hit.  */
-                         stabp += STABSIZE;
-                         for (;stabp < stabend; stabp += STABSIZE)
-                           {
-                             /* Go pullout the stab entry.  */
-                             strx  = bfd_h_get_32 (abfd, stabp + STRDXOFF);
-                             type  = bfd_h_get_8 (abfd, stabp + TYPEOFF);
-                             other = bfd_h_get_8 (abfd, stabp + OTHEROFF);
-                             desc  = bfd_h_get_16 (abfd, stabp + DESCOFF);
-                             value = bfd_h_get_32 (abfd, stabp + VALOFF);
+   Principle: Start with the first page and remove page instructions that
+   are not require on this first page. By removing page instructions more
+   code will fit into this page - repeat until nothing more can be achieved
+   for this page. Move on to the next page.
 
-                             name = bfd_get_stab_name (type);
+   Processing the pages one at a time from the lowest page allows a removal
+   only policy to be used - pages can be removed but are never reinserted.  */
 
-                             if (strcmp (name, "FUN") == 0)
-                               {
-                                 /* Hit another function entry.  */
-                                 if (function_adjusted)
-                                   {
-                                     /* Adjust the value.  */
-                                     value += count;
-                                 
-                                     /* We need to put it back.  */
-                                     bfd_h_put_32 (abfd, value,stabp + VALOFF);
-                                   }
+static bfd_boolean
+ip2k_elf_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;
+  bfd_byte *contents = NULL;
+  Elf_Internal_Sym *isymbuf = NULL;
+  static asection * first_section = NULL;
+  static unsigned long search_addr;
+  static unsigned long page_start = 0;
+  static unsigned long page_end = 0;
+  static unsigned int pass = 0;
+  static bfd_boolean new_pass = FALSE;
+  static bfd_boolean changed = FALSE;
+  struct misc misc;
 
-                                 /* And then bale out.  */
-                                 break;
-                               }
+  /* Assume nothing changes.  */
+  *again = FALSE;
 
-                             if (strcmp (name, "SLINE") == 0)
-                               {
-                                 /* Got a line entry.  */
-                                 if ((baseaddr + addr) <= (symval + value))
-                                   {
-                                     /* Adjust the line entry.  */
-                                     value += count;
+  if (first_section == NULL)
+    {
+      ip2k_relaxed = TRUE;
+      first_section = sec;
+    }
 
-                                     /* We need to put it back.  */
-                                     bfd_h_put_32 (abfd, value,stabp + VALOFF);
-                                     function_adjusted = 1;
-                                   }
-                               }
-                           }
-                       }
-                   }
-               }
-           }
-       }
+  if (first_section == sec)
+    {
+      pass++;
+      new_pass = TRUE;
     }
 
-  /* When adding an instruction back it is sometimes necessary to move any
-     global or local symbol that was referencing the first instruction of
-     the moved block to refer to the first instruction of the inserted block.
+  /* 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 (bfd_link_relocatable (link_info)
+      || (sec->flags & SEC_RELOC) == 0
+      || sec->reloc_count == 0
+      || (sec->flags & SEC_CODE) == 0)
+    return TRUE;
 
-     For example adding a PAGE instruction before a CALL or JMP requires
-     that any label on the CALL or JMP is moved to the PAGE insn.  */
-  addr += noadj;
+  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
 
-  /* Adjust the local symbols defined in this section.  */
-  isymend = isymbuf + symtab_hdr->sh_info;
-  for (isym = isymbuf; isym < isymend; isym++)
+  internal_relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL,
+                                              link_info->keep_memory);
+  if (internal_relocs == NULL)
+    goto error_return;
+
+  /* Get section contents cached copy if it exists.  */
+  if (contents == NULL)
     {
-      if (isym->st_shndx == shndx
-         && addr <= isym->st_value
-         && isym->st_value < endaddr)
-       isym->st_value += count;
+      /* 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;
+       }
     }
 
-    /* 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++)
+  /* Read this BFD's symbols cached copy if it exists.  */
+  if (isymbuf == NULL && symtab_hdr->sh_info != 0)
     {
-      struct elf_link_hash_entry *sym_hash = *sym_hashes;
+      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;
+    }
 
-      if ((sym_hash->root.type == bfd_link_hash_defined
-          || sym_hash->root.type == bfd_link_hash_defweak)
-         && sym_hash->root.u.def.section == sec)
+  misc.symtab_hdr = symtab_hdr;
+  misc.isymbuf = isymbuf;
+  misc.irelbase = internal_relocs;
+  misc.contents = contents;
+
+  /* This is where all the relaxation actually get done.  */
+  if ((pass == 1) || (new_pass && !changed))
+    {
+      /* On the first pass we simply search for the lowest page that
+         we havn't relaxed yet. Note that the pass count is reset
+         each time a page is complete in order to move on to the next page.
+         If we can't find any more pages then we are finished.  */
+      if (new_pass)
        {
-          if (addr <= sym_hash->root.u.def.value
-              && sym_hash->root.u.def.value < endaddr)
-           sym_hash->root.u.def.value += count;
+         pass = 1;
+         new_pass = FALSE;
+         changed = TRUE; /* Pre-initialize to break out of pass 1.  */
+         search_addr = 0xFFFFFFFF;
+       }
+
+      if ((BASEADDR (sec) + sec->size < search_addr)
+         && (BASEADDR (sec) + sec->size > page_end))
+       {
+         if (BASEADDR (sec) <= page_end)
+           search_addr = page_end + 1;
+         else
+           search_addr = BASEADDR (sec);
+
+         /* Found a page => more work to do.  */
+         *again = TRUE;
        }
     }
+  else
+    {
+      if (new_pass)
+       {
+         new_pass = FALSE;
+         changed = FALSE;
+         page_start = PAGENO (search_addr);
+         page_end = page_start | 0x00003FFF;
+       }
 
-  return;
-}
+      /* Only process sections in range.  */
+      if ((BASEADDR (sec) + sec->size >= page_start)
+         && (BASEADDR (sec) <= page_end))
+       {
+          if (!ip2k_elf_relax_section_page (abfd, sec, &changed, &misc, page_start, page_end))
+           return FALSE;
+       }
+      *again = TRUE;
+    }
 
-/* Delete some bytes from a section while relaxing.  */
+  /* Perform some house keeping after relaxing the section.  */
 
-static bfd_boolean
-ip2k_elf_relax_delete_bytes (abfd, sec, addr, count)
-     bfd *abfd;
-     asection *sec;
-     bfd_vma addr;
-     int count;
-{
-  bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
-  bfd_vma endaddr = sec->size;
+  if (isymbuf != NULL
+      && symtab_hdr->contents != (unsigned char *) isymbuf)
+    {
+      if (! link_info->keep_memory)
+       free (isymbuf);
+      else
+       symtab_hdr->contents = (unsigned char *) isymbuf;
+    }
 
-  /* Actually delete the bytes.  */
-  memmove (contents + addr, contents + addr + count,
-          endaddr - addr - count);
+  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;
+       }
+    }
 
-  sec->size -= count;
+  if (internal_relocs != NULL
+      && elf_section_data (sec)->relocs != internal_relocs)
+    free (internal_relocs);
 
-  adjust_all_relocations (abfd, sec, addr + count, endaddr, -count, 0);
   return TRUE;
-}
 
-/* -------------------------------------------------------------------- */
-
-/* XXX: The following code is the result of a cut&paste.  This unfortunate
-   practice is very widespread in the various target back-end files.  */
+ 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;
+}
 
 /* Set the howto pointer for a IP2K ELF reloc.  */
 
 static void
-ip2k_info_to_howto_rela (abfd, cache_ptr, dst)
-     bfd * abfd ATTRIBUTE_UNUSED;
-     arelent * cache_ptr;
-     Elf_Internal_Rela * dst;
+ip2k_info_to_howto_rela (bfd * abfd ATTRIBUTE_UNUSED,
+                        arelent * cache_ptr,
+                        Elf_Internal_Rela * dst)
 {
   unsigned int r_type;
 
   r_type = ELF32_R_TYPE (dst->r_info);
-  switch (r_type)
+  if (r_type >= (unsigned int) R_IP2K_max)
     {
-    default:
-      cache_ptr->howto = & ip2k_elf_howto_table [r_type];
-      break;
+      _bfd_error_handler (_("%B: invalid IP2K reloc number: %d"), abfd, r_type);
+      r_type = 0;
     }
+  cache_ptr->howto = & ip2k_elf_howto_table [r_type];
 }
 
 /* Perform a single relocation.
    By default we use the standard BFD routines.  */
 
 static bfd_reloc_status_type
-ip2k_final_link_relocate (howto, input_bfd, input_section, contents, rel,
-                         relocation)
-     reloc_howto_type *  howto;
-     bfd *               input_bfd;
-     asection *          input_section;
-     bfd_byte *          contents;
-     Elf_Internal_Rela * rel;
-     bfd_vma             relocation;
+ip2k_final_link_relocate (reloc_howto_type *  howto,
+                         bfd *               input_bfd,
+                         asection *          input_section,
+                         bfd_byte *          contents,
+                         Elf_Internal_Rela * rel,
+                         bfd_vma             relocation)
 {
   static bfd_vma page_addr = 0;
 
@@ -1382,11 +1302,13 @@ ip2k_final_link_relocate (howto, input_bfd, input_section, contents, rel,
           /* Preceding page instruction. Verify that the page instruction is
              really needed. One reason for the relaxation to miss a page is if
              the section is not marked as executable.  */
-         if (!ip2k_is_switch_table_128 (input_bfd, input_section, rel->r_offset - 2, contents) &&
-             !ip2k_is_switch_table_256 (input_bfd, input_section, rel->r_offset - 2, contents) &&
-             (PAGENO (relocation + rel->r_addend) ==
-              ip2k_nominal_page_bits (input_bfd, input_section,
-                                     rel->r_offset - 2, contents)))
+         if (!ip2k_is_switch_table_128 (input_bfd, input_section,
+                                        rel->r_offset - 2, contents)
+             && !ip2k_is_switch_table_256 (input_bfd, input_section,
+                                           rel->r_offset - 2, contents)
+             && (PAGENO (relocation + rel->r_addend) ==
+                 ip2k_nominal_page_bits (input_bfd, input_section,
+                                         rel->r_offset - 2, contents)))
            _bfd_error_handler (_("ip2k linker: redundant page instruction at 0x%08lx (dest = 0x%08lx)."),
                                page_addr,
                                relocation + rel->r_addend);
@@ -1458,25 +1380,20 @@ ip2k_final_link_relocate (howto, input_bfd, input_section, contents, rel,
    accordingly.  */
 
 static bfd_boolean
-ip2k_elf_relocate_section (output_bfd, info, input_bfd, input_section,
-                          contents, relocs, local_syms, local_sections)
-     bfd *output_bfd ATTRIBUTE_UNUSED;
-     struct bfd_link_info *info;
-     bfd *input_bfd;
-     asection *input_section;
-     bfd_byte *contents;
-     Elf_Internal_Rela *relocs;
-     Elf_Internal_Sym *local_syms;
-     asection **local_sections;
+ip2k_elf_relocate_section (bfd *output_bfd ATTRIBUTE_UNUSED,
+                          struct bfd_link_info *info,
+                          bfd *input_bfd,
+                          asection *input_section,
+                          bfd_byte *contents,
+                          Elf_Internal_Rela *relocs,
+                          Elf_Internal_Sym *local_syms,
+                          asection **local_sections)
 {
   Elf_Internal_Shdr *symtab_hdr;
   struct elf_link_hash_entry **sym_hashes;
   Elf_Internal_Rela *rel;
   Elf_Internal_Rela *relend;
 
-  if (info->relocatable)
-    return TRUE;
-
   symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (input_bfd);
   relend     = relocs + input_section->reloc_count;
@@ -1493,10 +1410,9 @@ ip2k_elf_relocate_section (output_bfd, info, input_bfd, input_section,
       const char *                 name = NULL;
       int                          r_type;
 
-      /* This is a final link.  */
       r_type = ELF32_R_TYPE (rel->r_info);
       r_symndx = ELF32_R_SYM (rel->r_info);
-      howto  = ip2k_elf_howto_table + ELF32_R_TYPE (rel->r_info);
+      howto  = ip2k_elf_howto_table + r_type;
       h      = NULL;
       sym    = NULL;
       sec    = NULL;
@@ -1513,24 +1429,31 @@ ip2k_elf_relocate_section (output_bfd, info, input_bfd, input_section,
        }
       else
        {
-         bfd_boolean warned;
+         bfd_boolean warned, ignored;
          bfd_boolean unresolved_reloc;
 
          RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
                                   r_symndx, symtab_hdr, sym_hashes,
                                   h, sec, relocation,
-                                  unresolved_reloc, warned);
+                                  unresolved_reloc, warned, ignored);
 
          name = h->root.root.string;
        }
 
+      if (sec != NULL && discarded_section (sec))
+       RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
+                                        rel, 1, relend, howto, 0, contents);
+
+      if (bfd_link_relocatable (info))
+       continue;
+
       /* Finally, the sole IP2K-specific part.  */
       r = ip2k_final_link_relocate (howto, input_bfd, input_section,
                                     contents, rel, relocation);
 
       if (r != bfd_reloc_ok)
        {
-         const char * msg = (const char *) NULL;
+         const char * msg = NULL;
 
          switch (r)
            {
@@ -1577,56 +1500,7 @@ ip2k_elf_relocate_section (output_bfd, info, input_bfd, input_section,
   return TRUE;
 }
 
-static asection *
-ip2k_elf_gc_mark_hook (sec, info, rel, h, sym)
-     asection *sec;
-     struct bfd_link_info *info ATTRIBUTE_UNUSED;
-     Elf_Internal_Rela *rel;
-     struct elf_link_hash_entry *h;
-     Elf_Internal_Sym *sym;
-{
-  if (h != NULL)
-    {
-      switch (ELF32_R_TYPE (rel->r_info))
-      {
-      default:
-        switch (h->root.type)
-          {
-          case bfd_link_hash_defined:
-          case bfd_link_hash_defweak:
-            return h->root.u.def.section;
-
-          case bfd_link_hash_common:
-            return h->root.u.c.p->section;
-
-          default:
-            break;
-          }
-       }
-     }
-   else
-     {
-       if (!(elf_bad_symtab (sec->owner)
-            && ELF_ST_BIND (sym->st_info) != STB_LOCAL)
-          && ! ((sym->st_shndx <= 0 || sym->st_shndx >= SHN_LORESERVE)
-                && sym->st_shndx != SHN_COMMON))
-        return bfd_section_from_elf_index (sec->owner, sym->st_shndx);
-      }
-  return NULL;
-}
-
-static bfd_boolean
-ip2k_elf_gc_sweep_hook (abfd, info, sec, relocs)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     struct bfd_link_info *info ATTRIBUTE_UNUSED;
-     asection *sec ATTRIBUTE_UNUSED;
-     const Elf_Internal_Rela *relocs ATTRIBUTE_UNUSED;
-{
-  /* We don't use got and plt entries for ip2k.  */
-  return TRUE;
-}
-
-#define TARGET_BIG_SYM  bfd_elf32_ip2k_vec
+#define TARGET_BIG_SYM  ip2k_elf32_vec
 #define TARGET_BIG_NAME  "elf32-ip2k"
 
 #define ELF_ARCH        bfd_arch_ip2k
@@ -1639,12 +1513,11 @@ ip2k_elf_gc_sweep_hook (abfd, info, sec, relocs)
 
 #define elf_backend_can_gc_sections            1
 #define elf_backend_rela_normal                        1
-#define elf_backend_gc_mark_hook                ip2k_elf_gc_mark_hook
-#define elf_backend_gc_sweep_hook               ip2k_elf_gc_sweep_hook
 #define elf_backend_relocate_section           ip2k_elf_relocate_section
 
 #define elf_symbol_leading_char                        '_'
 #define bfd_elf32_bfd_reloc_type_lookup                ip2k_reloc_type_lookup
+#define bfd_elf32_bfd_reloc_name_lookup        ip2k_reloc_name_lookup
 #define bfd_elf32_bfd_relax_section            ip2k_elf_relax_section
 
 #include "elf32-target.h"