gdb: add target_ops::supports_displaced_step
[deliverable/binutils-gdb.git] / bfd / elf32-ip2k.c
index 4645fa2046b6cadc0d5d91410e57274fe4e196ff..c0c8b8ec9a71b2b381ab5e9adf17ab535459b6f8 100644 (file)
@@ -1,12 +1,11 @@
 /* Ubicom IP2xxx specific support for 32-bit ELF
-   Copyright 2000, 2001, 2002, 2003, 2004, 2005
-   Free Software Foundation, Inc.
+   Copyright (C) 2000-2020 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., 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 "bfd.h"
 #include "libbfd.h"
 #include "elf-bfd.h"
 #include "elf/ip2k.h"
@@ -30,7 +30,7 @@ struct misc
 {
   Elf_Internal_Shdr *  symtab_hdr;
   Elf_Internal_Rela *  irelbase;
-  bfd_byte *           contents;
+  bfd_byte *          contents;
   Elf_Internal_Sym *   isymbuf;
 };
 
@@ -39,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},
 };
 
@@ -100,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},
 };
 
@@ -109,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},
 };
 
@@ -118,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},
 };
 
@@ -127,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},
 };
 
@@ -136,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},
 };
 
@@ -146,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},
 };
 
@@ -173,22 +126,22 @@ static const struct ip2k_opcode ip2k_skip_opcodes[] =
 static reloc_howto_type ip2k_elf_howto_table [] =
 {
 #define IP2K_HOWTO(t,rs,s,bs,pr,bp,name,sm,dm) \
-    HOWTO(t,                    /* type */ \
-          rs,                   /* rightshift */ \
-          s,                    /* size (0 = byte, 1 = short, 2 = long) */ \
-          bs,                   /* bitsize */ \
-          pr,                   /* pc_relative */ \
-          bp,                   /* bitpos */ \
-          complain_overflow_dont,/* complain_on_overflow */ \
-          bfd_elf_generic_reloc,/* special_function */ \
-          name,                 /* name */ \
-          FALSE,                /* partial_inplace */ \
-          sm,                   /* src_mask */ \
-          dm,                   /* dst_mask */ \
-          pr)                   /* pcrel_offset */
+    HOWTO(t,                   /* type */ \
+         rs,                   /* rightshift */ \
+         s,                    /* size (0 = byte, 1 = short, 2 = long) */ \
+         bs,                   /* bitsize */ \
+         pr,                   /* pc_relative */ \
+         bp,                   /* bitpos */ \
+         complain_overflow_dont,/* complain_on_overflow */ \
+         bfd_elf_generic_reloc,/* special_function */ \
+         name,                 /* name */ \
+         FALSE,                /* partial_inplace */ \
+         sm,                   /* src_mask */ \
+         dm,                   /* dst_mask */ \
+         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.  */
@@ -220,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
@@ -269,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];
 
@@ -306,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)
     {
@@ -346,154 +310,44 @@ 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.
 
-          sc
-          page    $nnn0
-          jmp     $nnn0
-          add     w,wreg
-          add     pcl,w
+         sc
+         page    $nnn0
+         jmp     $nnn0
+         add     w,wreg
+         add     pcl,w
   addr=>
-          page    $nnn1
-          jmp     $nnn1
-          page    $nnn2
-          jmp     $nnn2
-          ...
-          page    $nnnN
-          jmp     $nnnN
+         page    $nnn1
+         jmp     $nnn1
+          page    $nnn2
+          jmp     $nnn2
+          ...
+          page    $nnnN
+          jmp     $nnnN
 
   After relaxation.
-          sc
-          page    $nnn0
-          jmp     $nnn0
-          add     pcl,w
+          sc
+          page    $nnn0
+          jmp     $nnn0
+          add     pcl,w
   addr=>
-          jmp     $nnn1
-          jmp     $nnn2
-          ...
-          jmp     $nnnN  */
+          jmp     $nnn1
+          jmp     $nnn2
+          ...
+         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;
@@ -503,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)
     {
@@ -514,139 +368,66 @@ 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.
 
    Before relaxation.
-          push    %lo8insn(label) ; Push address of table
-          push    %hi8insn(label)
-          add     w,wreg          ; index*2 => offset
-          snc                     ; CARRY SET?
-          inc     1(sp)           ; Propagate MSB into table address
-          add     2(sp),w         ; Add low bits of offset to table address
-          snc                     ; and handle any carry-out
-          inc     1(sp)
+         push    %lo8insn(label) ; Push address of table
+         push    %hi8insn(label)
+         add     w,wreg          ; index*2 => offset
+         snc                     ; CARRY SET?
+         inc     1(sp)           ; Propagate MSB into table address
+         add     2(sp),w         ; Add low bits of offset to table address
+         snc                     ; and handle any carry-out
+         inc     1(sp)
    addr=>
-          page    __indjmp        ; Do an indirect jump to that location
-          jmp     __indjmp
-   label:                         ; case dispatch table starts here
-          page    $nnn1
-          jmp     $nnn1
-          page    $nnn2
-          jmp     $nnn2
-          ...
-          page    $nnnN
-          jmp     $nnnN
+         page    __indjmp        ; Do an indirect jump to that location
+         jmp     __indjmp
+   label:                        ; case dispatch table starts here
+          page    $nnn1
+          jmp     $nnn1
+          page    $nnn2
+          jmp     $nnn2
+          ...
+          page    $nnnN
+          jmp     $nnnN
 
   After relaxation.
-          push    %lo8insn(label) ; Push address of table
-          push    %hi8insn(label)
-          add     2(sp),w         ; Add low bits of offset to table address
-          snc                     ; and handle any carry-out
-          inc     1(sp)
+         push    %lo8insn(label) ; Push address of table
+         push    %hi8insn(label)
+         add     2(sp),w         ; Add low bits of offset to table address
+         snc                     ; and handle any carry-out
+         inc     1(sp)
   addr=>
-          page    __indjmp        ; Do an indirect jump to that location
-          jmp     __indjmp
-   label:                         ; case dispatch table starts here
-          jmp     $nnn1
-          jmp     $nnn2
-          ...
-          jmp     $nnnN  */
+         page    __indjmp        ; Do an indirect jump to that location
+         jmp     __indjmp
+   label:                        ; case dispatch table starts here
+         jmp     $nnn1
+         jmp     $nnn2
+         ...
+         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;
@@ -655,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)
     {
@@ -672,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))
@@ -681,392 +462,120 @@ 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)
-    {
-      if (addr + 4 > sec->size)
-       break;
+  /* 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;
 
-      ip2k_get_mem (abfd, misc->contents + addr, 4, code);
+  /* 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];
 
-      if ((! IS_PAGE_OPCODE (code + 0))
-         || (! IS_JMP_OPCODE (code + 2)))
-       break;
+      addr -= 2;
+      ip2k_get_mem (abfd, contents + addr, 2, code);
+      if (!IS_PAGE_OPCODE (code))
+       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;
-        }
+      /* 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 (ireltest->r_offset != addr)
-        {
-          _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 (!ip2k_test_page_insn (abfd, sec, ireltest, misc))
-       /* Un-removable page insn => nothing can be done.  */
-       return TRUE;
+      /* 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;
+       }
 
-      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;
-    }
-
-  return TRUE;
-}
-
-/* This function handles relaxing for the ip2k.
-
-   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.
-
-   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.  */
-
-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;
-{
-  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;
-  asection *stab;
-
-  /* Assume nothing changes.  */
-  *again = FALSE;
-
-  if (first_section == NULL)
-    {
-      ip2k_relaxed = TRUE;
-      first_section = sec;
-    }
-
-  if (first_section == sec)
-    {
-      pass++;
-      new_pass = TRUE;
-    }
-
-  /* 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;
-
-  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;
-
-  /* Make sure the stac.rela stuff gets read in.  */
-  stab = bfd_get_section_by_name (abfd, ".stab");
-
-  if (stab)
-    {
-      /* So stab does exits.  */
-      Elf_Internal_Rela * irelbase;
-
-      irelbase = _bfd_elf_link_read_relocs (abfd, stab, NULL,
-                                           (Elf_Internal_Rela *)NULL,
-                                           link_info->keep_memory);
-    }
-
-  /* Get section contents cached copy if it exists.  */
-  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 symbols cached copy if it exists.  */
-  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;
-    }
-
-  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)
-       {
-         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;
-       }
-
-      /* 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;
-    }
-
-  /* Perform some house keeping after relaxing the section.  */
-
-  if (isymbuf != NULL
-      && symtab_hdr->contents != (unsigned char *) isymbuf)
-    {
-      if (! link_info->keep_memory)
-       free (isymbuf);
-      else
-       symtab_hdr->contents = (unsigned char *) isymbuf;
-    }
-
-  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;
-}
-
-/* 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;
-{
-  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++)
-    {
-      if (ELF32_R_TYPE (irel->r_info) != (int) R_IP2K_PAGE3)
-       /* Ignore non page instructions.  */
-       continue;
-
-      if (BASEADDR (sec) + irel->r_offset < page_start)
-       /* Ignore page instructions on earlier page - they have
-          already been processed. Remember that there is code flow
-          that crosses a page boundary.  */
-       continue;
-
-      if (BASEADDR (sec) + irel->r_offset > page_end)
-       /* Flow beyond end of page => nothing more to do for this page.  */
-       return TRUE;
-
-      /* Detect switch tables.  */
-      switch_table_128 = ip2k_is_switch_table_128 (abfd, sec, irel->r_offset, misc->contents);
-      switch_table_256 = ip2k_is_switch_table_256 (abfd, sec, irel->r_offset, misc->contents);
-
-      if ((switch_table_128 > 0) || (switch_table_256 > 0))
-       /* If the index is greater than 0 then it has already been processed.  */
-       continue;
-
-      if (switch_table_128 == 0)
-       {
-         if (!ip2k_relax_switch_table_128 (abfd, sec, irel, again, misc))
-           return FALSE;
-
-         continue;
-       }
-
-      if (switch_table_256 == 0)
-       {
-         if (!ip2k_relax_switch_table_256 (abfd, sec, irel, again, misc))
-           return FALSE;
-
-         continue;
-       }
-
-      /* Simple relax.  */
-      if (ip2k_test_page_insn (abfd, sec, irel, misc))
-       {
-         if (!ip2k_delete_page_insn (abfd, sec, irel, again, misc))
-           return FALSE;
-
-         continue;
-       }
-    }
+  /* 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;
 }
 
 /* Parts of a Stabs entry.  */
 
-#define STRDXOFF  (0)
-#define TYPEOFF   (4)
-#define OTHEROFF  (5)
-#define DESCOFF   (6)
-#define VALOFF    (8)
-#define STABSIZE  (12)
+#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;
+adjust_all_relocations (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;
@@ -1078,40 +587,38 @@ adjust_all_relocations (abfd, sec, addr, endaddr, count, noadj)
 
   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;
+       {
+         /* 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.  */
+             /* A local symbol.  */
              isym = isymbuf + ELF32_R_SYM (irel->r_info);
-              sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
+             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 (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;
-                }
-            }
-        }
+                 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;
+       irel->r_offset += count;
     }
 
   /* Now fix the stab relocations.  */
@@ -1131,8 +638,7 @@ adjust_all_relocations (abfd, sec, addr, endaddr, count, noadj)
        {
          if (!bfd_malloc_and_get_section (abfd, stab, &stabcontents))
            {
-             if (stabcontents != NULL)
-               free (stabcontents);
+             free (stabcontents);
              return;
            }
 
@@ -1150,194 +656,606 @@ adjust_all_relocations (abfd, sec, addr, endaddr, count, noadj)
              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;
+                     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 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);
+
+                             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;
+                                   }
+                               }
+                           }
+                       }
+                   }
+               }
+           }
+       }
+    }
+
+  /* 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++)
+    {
+      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)
+       {
+         if (addr <= sym_hash->root.u.def.value
+             && sym_hash->root.u.def.value < endaddr)
+           sym_hash->root.u.def.value += count;
+       }
+    }
+
+  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)
+    {
+      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;
+}
+
+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;
+
+  /* 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, 2, code);
+  if (IS_PAGE_OPCODE (code))
+    addr = irel->r_offset - 16;
+  else
+    addr = irel->r_offset - 14;
+
+  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;
+    }
+
+  /* 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 (!ip2k_delete_page_insn (abfd, sec, irel, again, misc))
+       return FALSE;
+      irel += 2;
+    }
+
+  return TRUE;
+}
+
+/* This function handles relaxation of a section in a specific page.  */
+
+static bfd_boolean
+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++)
+    {
+      if (ELF32_R_TYPE (irel->r_info) != (int) R_IP2K_PAGE3)
+       /* Ignore non page instructions.  */
+       continue;
+
+      if (BASEADDR (sec) + irel->r_offset < page_start)
+       /* Ignore page instructions on earlier page - they have
+          already been processed. Remember that there is code flow
+          that crosses a page boundary.  */
+       continue;
+
+      if (BASEADDR (sec) + irel->r_offset > page_end)
+       /* Flow beyond end of page => nothing more to do for this page.  */
+       return TRUE;
+
+      /* Detect switch tables.  */
+      switch_table_128 = ip2k_is_switch_table_128 (abfd, sec, irel->r_offset, misc->contents);
+      switch_table_256 = ip2k_is_switch_table_256 (abfd, sec, irel->r_offset, misc->contents);
+
+      if ((switch_table_128 > 0) || (switch_table_256 > 0))
+       /* If the index is greater than 0 then it has already been processed.  */
+       continue;
+
+      if (switch_table_128 == 0)
+       {
+         if (!ip2k_relax_switch_table_128 (abfd, sec, irel, again, misc))
+           return FALSE;
+
+         continue;
+       }
+
+      if (switch_table_256 == 0)
+       {
+         if (!ip2k_relax_switch_table_256 (abfd, sec, irel, again, misc))
+           return FALSE;
+
+         continue;
+       }
+
+      /* Simple relax.  */
+      if (ip2k_test_page_insn (abfd, sec, irel, misc))
+       {
+         if (!ip2k_delete_page_insn (abfd, sec, irel, again, misc))
+           return FALSE;
+
+         continue;
+       }
+    }
 
-                     /* 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 (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 (symtab_hdr->contents != (unsigned char *) isymbuf)
+    free (isymbuf);
+  if (elf_section_data (sec)->this_hdr.contents != contents)
+    free (contents);
+  if (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;
+static bfd_boolean
+ip2k_info_to_howto_rela (bfd * abfd,
+                        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;
+      /* xgettext:c-format */
+      _bfd_error_handler (_("%pB: unsupported relocation type %#x"),
+                         abfd, r_type);
+      bfd_set_error (bfd_error_bad_value);
+      return FALSE;
     }
+  cache_ptr->howto = & ip2k_elf_howto_table [r_type];
+  return TRUE;
 }
 
 /* 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;
 
@@ -1373,25 +1291,33 @@ ip2k_final_link_relocate (howto, input_bfd, input_section, contents, rel,
          /* No preceding page instruction, verify that it isn't needed.  */
          if (PAGENO (relocation + rel->r_addend) !=
              ip2k_nominal_page_bits (input_bfd, input_section,
-                                     rel->r_offset, contents))
-           _bfd_error_handler (_("ip2k linker: missing page instruction at 0x%08lx (dest = 0x%08lx)."),
-                               BASEADDR (input_section) + rel->r_offset,
-                               relocation + rel->r_addend);
-        }
+                                     rel->r_offset, contents))
+           /* xgettext:c-format */
+           _bfd_error_handler
+             (_("ip2k linker: missing page instruction "
+                "at %#" PRIx64 " (dest = %#" PRIx64 ")"),
+              (uint64_t) (BASEADDR (input_section) + rel->r_offset),
+              (uint64_t) (relocation + rel->r_addend));
+       }
       else if (ip2k_relaxed)
-        {
-          /* 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)))
-           _bfd_error_handler (_("ip2k linker: redundant page instruction at 0x%08lx (dest = 0x%08lx)."),
-                               page_addr,
-                               relocation + rel->r_addend);
-        }
+       {
+         /* 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)))
+           /* xgettext:c-format */
+           _bfd_error_handler
+             (_("ip2k linker: redundant page instruction "
+                "at %#" PRIx64 " (dest = %#" PRIx64 ")"),
+              (uint64_t) page_addr,
+              (uint64_t) (relocation + rel->r_addend));
+       }
       if ((relocation & IP2K_INSN_MASK) == IP2K_INSN_VALUE)
        relocation &= ~IP2K_INSN_MASK;
       else
@@ -1459,45 +1385,39 @@ 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;
 
   for (rel = relocs; rel < relend; rel ++)
     {
-      reloc_howto_type *           howto;
-      unsigned long                r_symndx;
-      Elf_Internal_Sym *           sym;
-      asection *                   sec;
+      reloc_howto_type *          howto;
+      unsigned long               r_symndx;
+      Elf_Internal_Sym *          sym;
+      asection *                  sec;
       struct elf_link_hash_entry * h;
-      bfd_vma                      relocation;
-      bfd_reloc_status_type        r;
-      const char *                 name = NULL;
-      int                          r_type;
+      bfd_vma                     relocation;
+      bfd_reloc_status_type       r;
+      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;
@@ -1510,39 +1430,46 @@ ip2k_elf_relocate_section (output_bfd, info, input_bfd, input_section,
 
          name = bfd_elf_string_from_elf_section
            (input_bfd, symtab_hdr->sh_link, sym->st_name);
-         name = (name == NULL) ? bfd_section_name (input_bfd, sec) : name;
+         name = name == NULL ? bfd_section_name (sec) : name;
        }
       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)
            {
            case bfd_reloc_overflow:
-             r = info->callbacks->reloc_overflow
+             (*info->callbacks->reloc_overflow)
                (info, (h ? &h->root : NULL), name, howto->name,
                 (bfd_vma) 0, input_bfd, input_section, rel->r_offset);
              break;
 
            case bfd_reloc_undefined:
-             r = info->callbacks->undefined_symbol
+             (*info->callbacks->undefined_symbol)
                (info, name, input_bfd, input_section, rel->r_offset, TRUE);
              break;
 
@@ -1551,10 +1478,10 @@ ip2k_elf_relocate_section (output_bfd, info, input_bfd, input_section,
              break;
 
              /* This is how ip2k_final_link_relocate tells us of a non-kosher
-                 reference between insn & data address spaces.  */
+                reference between insn & data address spaces.  */
            case bfd_reloc_notsupported:
-              if (sym != NULL) /* Only if it's not an unresolved symbol.  */
-                msg = _("unsupported relocation between data/insn address spaces");
+             if (sym != NULL) /* Only if it's not an unresolved symbol.  */
+                msg = _("unsupported relocation between data/insn address spaces");
              break;
 
            case bfd_reloc_dangerous:
@@ -1567,67 +1494,15 @@ ip2k_elf_relocate_section (output_bfd, info, input_bfd, input_section,
            }
 
          if (msg)
-           r = info->callbacks->warning
-             (info, msg, name, input_bfd, input_section, rel->r_offset);
-
-         if (! r)
-           return FALSE;
+           (*info->callbacks->warning) (info, msg, name, input_bfd,
+                                        input_section, rel->r_offset);
        }
     }
 
   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
@@ -1638,14 +1513,13 @@ ip2k_elf_gc_sweep_hook (abfd, info, sec, relocs)
 #define elf_info_to_howto_rel                  NULL
 #define elf_info_to_howto                      ip2k_info_to_howto_rela
 
-#define elf_backend_can_gc_sections            1
+#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"
This page took 0.048799 seconds and 4 git commands to generate.