Add IP2k support to BFD and LD
authorNick Clifton <nickc@redhat.com>
Wed, 17 Jul 2002 14:15:52 +0000 (14:15 +0000)
committerNick Clifton <nickc@redhat.com>
Wed, 17 Jul 2002 14:15:52 +0000 (14:15 +0000)
24 files changed:
bfd/ChangeLog
bfd/Makefile.am
bfd/Makefile.in
bfd/archures.c
bfd/bfd-in2.h
bfd/config.bfd
bfd/configure
bfd/configure.in
bfd/cpu-ip2k.c [new file with mode: 0644]
bfd/doc/Makefile.in
bfd/elf32-ip2k.c [new file with mode: 0644]
bfd/libbfd.h
bfd/reloc.c
bfd/targets.c
include/elf/ChangeLog
include/elf/common.h
include/elf/ip2k.h [new file with mode: 0644]
ld/ChangeLog
ld/Makefile.am
ld/Makefile.in
ld/NEWS
ld/configure.tgt
ld/emulparams/elf32ip2k.sh [new file with mode: 0644]
ld/scripttempl/ip2k.sc [new file with mode: 0644]

index f8e4100..235f961 100644 (file)
@@ -1,3 +1,25 @@
+2002-07-15  Denis Chertykov  <denisc@overta.ru>
+           Frank Ch. Eigler  <fche@redhat.com>
+           Ben Elliston  <bje@redhat.com>
+           Alan Lehotsky  <alehotsky@cygnus.com>
+           John Healy  <jhealy@redhat.com>
+           Graham Stott  <grahams@redhat.com>
+           Jeff Johnston  <jjohnstn@redhat.com>
+
+       * Makefile.am: Add support for ip2k.
+       * Makefile.in: Regenerate.
+       * doc/Makefile.in: Regenerate.
+       * archures.c: Add support for ip2k.
+       * config.bfd: Add support for ip2k.
+       * configure.in: Add support for ip2k.
+       * configure: Regenerate.
+       * reloc.c: Add support for ip2k.
+       * targets.c: Add support for ip2k.
+       * bfd-in2.h: Regenerate.
+       * libbfd.h: Regenerate.
+       * cpu-ip2k.c: New file.
+       * elf32-ip2k.c: New file.
+
 2002-07-17  Ian Rickards  <irickard@arm.com>
 
        * dwarf2.c (concat_filename): If we can't establish the directory 
index 0947685..8b416f2 100644 (file)
@@ -67,6 +67,7 @@ ALL_MACHINES = \
        cpu-i386.lo \
        cpu-i860.lo \
        cpu-i960.lo \
+       cpu-ip2k.lo \
        cpu-m32r.lo \
        cpu-m68hc11.lo \
        cpu-m68hc12.lo \
@@ -117,6 +118,7 @@ ALL_MACHINES_CFILES = \
        cpu-i386.c \
        cpu-i860.c \
        cpu-i960.c \
+       cpu-ip2k.c \
        cpu-m32r.c \
        cpu-m68hc11.c \
        cpu-m68hc12.c \
@@ -214,6 +216,7 @@ BFD32_BACKENDS = \
        elf32-i860.lo \
        elf32-i960.lo \
        elf32-ia64.lo \
+       elf32-ip2k.c \
        elf32-m32r.lo \
        elf32-m68hc11.lo \
        elf32-m68hc12.lo \
@@ -370,6 +373,7 @@ BFD32_BACKENDS_CFILES = \
        elf32-i386qnx.c \
        elf32-i860.c \
        elf32-i960.c \
+       elf32-ip2k.c \
        elf32-m32r.c \
        elf32-m68k.c \
        elf32-m68hc11.c \
@@ -901,6 +905,7 @@ cpu-i370.lo: cpu-i370.c $(INCDIR)/filenames.h
 cpu-i386.lo: cpu-i386.c $(INCDIR)/filenames.h
 cpu-i860.lo: cpu-i860.c $(INCDIR)/filenames.h
 cpu-i960.lo: cpu-i960.c $(INCDIR)/filenames.h
+cpu-ip2k.lo: cpu-ip2k.c $(INCDIR)/filenames.h
 cpu-m32r.lo: cpu-m32r.c $(INCDIR)/filenames.h
 cpu-m68hc11.lo: cpu-m68hc11.c $(INCDIR)/filenames.h
 cpu-m68hc12.lo: cpu-m68hc12.c $(INCDIR)/filenames.h
@@ -1149,6 +1154,10 @@ elf32-i960.lo: elf32-i960.c $(INCDIR)/filenames.h elf-bfd.h \
   $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h \
   $(INCDIR)/bfdlink.h $(INCDIR)/elf/i960.h $(INCDIR)/elf/reloc-macros.h \
   elf32-target.h
+elf32-ip2k.lo: elf32-ip2k.c $(INCDIR)/filenames.h elf-bfd.h \
+  $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h \
+  $(INCDIR)/bfdlink.h $(INCDIR)/elf/ip2k.h $(INCDIR)/elf/reloc-macros.h \
+  elf32-target.h
 elf32-m32r.lo: elf32-m32r.c $(INCDIR)/filenames.h elf-bfd.h \
   $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h \
   $(INCDIR)/bfdlink.h $(INCDIR)/elf/m32r.h $(INCDIR)/elf/reloc-macros.h \
index 6ea2e60..e00218f 100644 (file)
@@ -193,6 +193,7 @@ ALL_MACHINES = \
        cpu-i386.lo \
        cpu-i860.lo \
        cpu-i960.lo \
+       cpu-ip2k.lo \
        cpu-m32r.lo \
        cpu-m68hc11.lo \
        cpu-m68hc12.lo \
@@ -244,6 +245,7 @@ ALL_MACHINES_CFILES = \
        cpu-i386.c \
        cpu-i860.c \
        cpu-i960.c \
+       cpu-ip2k.c \
        cpu-m32r.c \
        cpu-m68hc11.c \
        cpu-m68hc12.c \
@@ -342,6 +344,7 @@ BFD32_BACKENDS = \
        elf32-i860.lo \
        elf32-i960.lo \
        elf32-ia64.lo \
+       elf32-ip2k.c \
        elf32-m32r.lo \
        elf32-m68hc11.lo \
        elf32-m68hc12.lo \
@@ -499,6 +502,7 @@ BFD32_BACKENDS_CFILES = \
        elf32-i386qnx.c \
        elf32-i860.c \
        elf32-i960.c \
+       elf32-ip2k.c \
        elf32-m32r.c \
        elf32-m68k.c \
        elf32-m68hc11.c \
@@ -778,7 +782,7 @@ configure.in
 
 DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
 
-TAR = tar
+TAR = gtar
 GZIP_ENV = --best
 SOURCES = $(libbfd_a_SOURCES) $(libbfd_la_SOURCES)
 OBJECTS = $(libbfd_a_OBJECTS) $(libbfd_la_OBJECTS)
@@ -1431,6 +1435,7 @@ cpu-i370.lo: cpu-i370.c $(INCDIR)/filenames.h
 cpu-i386.lo: cpu-i386.c $(INCDIR)/filenames.h
 cpu-i860.lo: cpu-i860.c $(INCDIR)/filenames.h
 cpu-i960.lo: cpu-i960.c $(INCDIR)/filenames.h
+cpu-ip2k.lo: cpu-ip2k.c $(INCDIR)/filenames.h
 cpu-m32r.lo: cpu-m32r.c $(INCDIR)/filenames.h
 cpu-m68hc11.lo: cpu-m68hc11.c $(INCDIR)/filenames.h
 cpu-m68hc12.lo: cpu-m68hc12.c $(INCDIR)/filenames.h
@@ -1679,6 +1684,10 @@ elf32-i960.lo: elf32-i960.c $(INCDIR)/filenames.h elf-bfd.h \
   $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h \
   $(INCDIR)/bfdlink.h $(INCDIR)/elf/i960.h $(INCDIR)/elf/reloc-macros.h \
   elf32-target.h
+elf32-ip2k.lo: elf32-ip2k.c $(INCDIR)/filenames.h elf-bfd.h \
+  $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h \
+  $(INCDIR)/bfdlink.h $(INCDIR)/elf/ip2k.h $(INCDIR)/elf/reloc-macros.h \
+  elf32-target.h
 elf32-m32r.lo: elf32-m32r.c $(INCDIR)/filenames.h elf-bfd.h \
   $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h \
   $(INCDIR)/bfdlink.h $(INCDIR)/elf/m32r.h $(INCDIR)/elf/reloc-macros.h \
index 063e4fb..76094cc 100644 (file)
@@ -258,6 +258,9 @@ DESCRIPTION
 .  bfd_arch_ia64,      {* HP/Intel ia64 *}
 .#define bfd_mach_ia64_elf64   0
 .#define bfd_mach_ia64_elf32   1
+.  bfd_arch_ip2k,      {* Ubicom IP2K microcontrollers. *}
+.#define bfd_mach_ip2022       0
+.#define bfd_mach_ip2022ext    1
 .  bfd_arch_pj,
 .  bfd_arch_avr,       {* Atmel AVR microcontrollers.  *}
 .#define bfd_mach_avr1         1
@@ -331,6 +334,7 @@ extern const bfd_arch_info_type bfd_i386_arch;
 extern const bfd_arch_info_type bfd_i860_arch;
 extern const bfd_arch_info_type bfd_i960_arch;
 extern const bfd_arch_info_type bfd_ia64_arch;
+extern const bfd_arch_info_type bfd_ip2k_arch;
 extern const bfd_arch_info_type bfd_m32r_arch;
 extern const bfd_arch_info_type bfd_m68hc11_arch;
 extern const bfd_arch_info_type bfd_m68hc12_arch;
@@ -386,6 +390,7 @@ static const bfd_arch_info_type * const bfd_archures_list[] =
     &bfd_i860_arch,
     &bfd_i960_arch,
     &bfd_ia64_arch,
+    &bfd_ip2k_arch,
     &bfd_m32r_arch,
     &bfd_m68hc11_arch,
     &bfd_m68hc12_arch,
index fa418e3..401b027 100644 (file)
@@ -1648,6 +1648,9 @@ enum bfd_architecture
   bfd_arch_ia64,      /* HP/Intel ia64 */
 #define bfd_mach_ia64_elf64    0
 #define bfd_mach_ia64_elf32    1
+  bfd_arch_ip2k,      /* Ubicom IP2K microcontrollers. */
+#define bfd_mach_ip2022        0
+#define bfd_mach_ip2022ext     1
   bfd_arch_pj,
   bfd_arch_avr,       /* Atmel AVR microcontrollers.  */
 #define bfd_mach_avr1          1
@@ -2901,6 +2904,40 @@ into 22 bits.  */
 /* 32 bit rel. offset to GOT entry.  */
   BFD_RELOC_390_GOTENT,
 
+/* Scenix IP2K - 9-bit register number / data address  */
+  BFD_RELOC_IP2K_FR9,
+
+/* Scenix IP2K - 4-bit register/data bank number  */
+  BFD_RELOC_IP2K_BANK,
+
+/* Scenix IP2K - low 13 bits of instruction word address  */
+  BFD_RELOC_IP2K_ADDR16CJP,
+
+/* Scenix IP2K - high 3 bits of instruction word address  */
+  BFD_RELOC_IP2K_PAGE3,
+
+/* Scenix IP2K - ext/low/high 8 bits of data address  */
+  BFD_RELOC_IP2K_LO8DATA,
+  BFD_RELOC_IP2K_HI8DATA,
+  BFD_RELOC_IP2K_EX8DATA,
+
+/* Scenix IP2K - low/high 8 bits of instruction word address  */
+  BFD_RELOC_IP2K_LO8INSN,
+  BFD_RELOC_IP2K_HI8INSN,
+
+/* Scenix IP2K - even/odd PC modifier to modify snb pcl.0  */
+  BFD_RELOC_IP2K_PC_SKIP,
+
+/* Scenix IP2K - 16 bit word address in text section.  */
+  BFD_RELOC_IP2K_TEXT,
+
+/* Scenix IP2K - 7-bit sp or dp offset  */
+  BFD_RELOC_IP2K_FR_OFFSET,
+
+/* Scenix VPE4K coprocessor - data/insn-space addressing  */
+  BFD_RELOC_VPE4KMATH_DATA,
+  BFD_RELOC_VPE4KMATH_INSN,
+
 /* These two relocations are used by the linker to determine which of
 the entries in a C++ virtual function table are actually used.  When
 the --gc-sections option is given, the linker will zero out the entries
index 3fd1551..d5a7b6a 100644 (file)
@@ -518,6 +518,10 @@ case "${targ}" in
     targ_selvecs="icoff_little_vec icoff_big_vec"
     ;;
 
+  ip2k-*-elf)
+    targ_defvec=bfd_elf32_ip2k_vec
+    ;;
+
   m32r-*-*)
     targ_defvec=bfd_elf32_m32r_vec
     ;;
index a0d6a9f..5d07921 100755 (executable)
@@ -6081,6 +6081,7 @@ do
     bfd_elf32_i960_vec)                tb="$tb elf32-i960.lo elf32.lo $elf" ;;
     bfd_elf32_ia64_big_vec)    tb="$tb elf32-ia64.lo elf32.lo $elf" ;;
     bfd_elf32_ia64_hpux_big_vec) tb="$tb elf32-ia64.lo elf32.lo $elf";;
+    bfd_elf32_ip2k_vec)                tb="$tb elf32-ip2k.lo elf32.lo $elf" ;;
     bfd_elf32_little_generic_vec) tb="$tb elf32-gen.lo elf32.lo $elf" ;;
     bfd_elf32_littlearc_vec)   tb="$tb elf32-arc.lo elf32.lo $elf" ;;
     bfd_elf32_littlearm_oabi_vec) tb="$tb elfarm-oabi.lo elf32.lo $elf" ;;
@@ -6321,10 +6322,10 @@ case ${host64}-${target64}-${want64} in
     if test -n "$GCC" ; then
        bad_64bit_gcc=no;
        echo $ac_n "checking for gcc version with buggy 64-bit support""... $ac_c" 1>&6
-echo "configure:6325: checking for gcc version with buggy 64-bit support" >&5
+echo "configure:6326: checking for gcc version with buggy 64-bit support" >&5
        # Add more tests for gcc versions with non-working 64-bit support here.
        cat > conftest.$ac_ext <<EOF
-#line 6328 "configure"
+#line 6329 "configure"
 #include "confdefs.h"
 :__GNUC__:__GNUC_MINOR__:__i386__:
 EOF
@@ -6369,17 +6370,17 @@ for ac_hdr in stdlib.h unistd.h sys/stat.h sys/types.h
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:6373: checking for $ac_hdr" >&5
+echo "configure:6374: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 6378 "configure"
+#line 6379 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:6383: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:6384: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -6408,12 +6409,12 @@ done
 for ac_func in getpagesize
 do
 echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
-echo "configure:6412: checking for $ac_func" >&5
+echo "configure:6413: checking for $ac_func" >&5
 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 6417 "configure"
+#line 6418 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char $ac_func(); below.  */
@@ -6436,7 +6437,7 @@ $ac_func();
 
 ; return 0; }
 EOF
-if { (eval echo configure:6440: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:6441: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_$ac_func=yes"
 else
@@ -6461,7 +6462,7 @@ fi
 done
 
 echo $ac_n "checking for working mmap""... $ac_c" 1>&6
-echo "configure:6465: checking for working mmap" >&5
+echo "configure:6466: checking for working mmap" >&5
 if eval "test \"`echo '$''{'ac_cv_func_mmap_fixed_mapped'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -6469,7 +6470,7 @@ else
   ac_cv_func_mmap_fixed_mapped=no
 else
   cat > conftest.$ac_ext <<EOF
-#line 6473 "configure"
+#line 6474 "configure"
 #include "confdefs.h"
 
 /* Thanks to Mike Haertel and Jim Avera for this test.
@@ -6622,7 +6623,7 @@ main()
 }
 
 EOF
-if { (eval echo configure:6626: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:6627: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   ac_cv_func_mmap_fixed_mapped=yes
 else
@@ -6647,12 +6648,12 @@ fi
 for ac_func in madvise mprotect
 do
 echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
-echo "configure:6651: checking for $ac_func" >&5
+echo "configure:6652: checking for $ac_func" >&5
 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 6656 "configure"
+#line 6657 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char $ac_func(); below.  */
@@ -6675,7 +6676,7 @@ $ac_func();
 
 ; return 0; }
 EOF
-if { (eval echo configure:6679: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:6680: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_$ac_func=yes"
 else
index a2439a4..e2bc1df 100644 (file)
@@ -582,6 +582,7 @@ do
     bfd_elf32_i960_vec)                tb="$tb elf32-i960.lo elf32.lo $elf" ;;
     bfd_elf32_ia64_big_vec)    tb="$tb elf32-ia64.lo elf32.lo $elf" ;;
     bfd_elf32_ia64_hpux_big_vec) tb="$tb elf32-ia64.lo elf32.lo $elf";;
+    bfd_elf32_ip2k_vec)                tb="$tb elf32-ip2k.lo elf32.lo $elf" ;;
     bfd_elf32_little_generic_vec) tb="$tb elf32-gen.lo elf32.lo $elf" ;;
     bfd_elf32_littlearc_vec)   tb="$tb elf32-arc.lo elf32.lo $elf" ;;
     bfd_elf32_littlearm_oabi_vec) tb="$tb elfarm-oabi.lo elf32.lo $elf" ;;
diff --git a/bfd/cpu-ip2k.c b/bfd/cpu-ip2k.c
new file mode 100644 (file)
index 0000000..6afb7fe
--- /dev/null
@@ -0,0 +1,54 @@
+/* BFD support for the Scenix IP2xxx processor.
+   Copyright (C) 2000, 2002 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
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+
+const bfd_arch_info_type bfd_ip2k_nonext_arch =
+{
+  32,                          /* Bits per word - not really true.  */
+  16,                          /* Bits per address.  */
+  8,                           /* Bits per byte.  */
+  bfd_arch_ip2k,               /* Architecture.  */
+  bfd_mach_ip2022,             /* Machine.  */
+  "ip2k",                      /* Architecture name.  */
+  "ip2022",                    /* Machine name.  */
+  1,                           /* Section align power.  */
+  false,                       /* The default ?  */
+  bfd_default_compatible,      /* Architecture comparison fn.  */
+  bfd_default_scan,            /* String to architecture convert fn.  */
+  NULL                         /* Next in list.  */
+};
+
+const bfd_arch_info_type bfd_ip2k_arch =
+{
+  32,                          /* Bits per word - not really true.  */
+  16,                          /* Bits per address.  */
+  8,                           /* Bits per byte.  */
+  bfd_arch_ip2k,               /* Architecture.  */
+  bfd_mach_ip2022ext,          /* Machine.  */
+  "ip2k",                      /* Architecture name.  */
+  "ip2022ext",                 /* Machine name.  */
+  1,                           /* Section align power.  */
+  true,                                /* The default ?  */
+  bfd_default_compatible,      /* Architecture comparison fn.  */
+  bfd_default_scan,            /* String to architecture convert fn.  */
+  & bfd_ip2k_nonext_arch       /* Next in list.  */
+};
index 8daf537..66a19d1 100644 (file)
@@ -244,7 +244,7 @@ DIST_COMMON =  ChangeLog Makefile.am Makefile.in
 
 DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
 
-TAR = tar
+TAR = gtar
 GZIP_ENV = --best
 all: all-redirect
 .SUFFIXES:
diff --git a/bfd/elf32-ip2k.c b/bfd/elf32-ip2k.c
new file mode 100644 (file)
index 0000000..42287f3
--- /dev/null
@@ -0,0 +1,1989 @@
+/* Scenix IP2xxx specific support for 32-bit ELF
+   Copyright 2000, 2001, 2002 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
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+#include "elf-bfd.h"
+#include "elf/ip2k.h"
+
+/* Struct used to pass miscellaneous paramaters which
+   helps to avoid overly long parameter lists.  */
+struct misc
+{
+  Elf_Internal_Shdr *  symtab_hdr;
+  Elf_Internal_Rela *  irelbase;
+  bfd_byte *           contents;
+  bfd_byte *           free_contents;
+  Elf32_External_Sym * extsyms;
+  Elf32_External_Sym * free_extsyms;
+  Elf_Internal_Rela *  free_relocs;
+};
+
+/* Prototypes.  */
+static reloc_howto_type *    ip2k_reloc_type_lookup               PARAMS ((bfd *, bfd_reloc_code_real_type));
+static void                  ip2k_info_to_howto_rela              PARAMS ((bfd *, arelent *, Elf32_Internal_Rela *));
+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 boolean               ip2k_elf_gc_sweep_hook               PARAMS ((bfd *, struct bfd_link_info *, asection *, const Elf_Internal_Rela *));
+static bfd_vma               symbol_value                         PARAMS ((bfd *, Elf_Internal_Shdr *, Elf32_External_Sym *, Elf_Internal_Rela *));
+static void                  adjust_all_relocations               PARAMS ((bfd *, asection *, bfd_vma, bfd_vma, int, int));
+static boolean               ip2k_elf_relax_delete_bytes          PARAMS ((bfd *, asection *, bfd_vma, int));
+static boolean               ip2k_elf_relax_add_bytes             PARAMS ((bfd *, asection *, bfd_vma, const bfd_byte *, int, int));
+static boolean               add_page_insn                        PARAMS ((bfd *, asection *, Elf_Internal_Rela *, struct misc *));
+static boolean               ip2k_elf_relax_section               PARAMS ((bfd *, asection *, struct bfd_link_info *, boolean *));
+static boolean               relax_switch_dispatch_tables_pass1   PARAMS ((bfd *, asection *, bfd_vma, struct misc *));
+static boolean               unrelax_dispatch_table_entries       PARAMS ((bfd *, asection *, bfd_vma, bfd_vma, boolean *, struct misc *));
+static boolean               unrelax_switch_dispatch_tables_passN PARAMS ((bfd *, asection *, bfd_vma, boolean *, struct misc *));
+static boolean               is_switch_128_dispatch_table_p       PARAMS ((bfd *, bfd_vma, boolean, struct misc *));
+static boolean               is_switch_256_dispatch_table_p       PARAMS ((bfd *, bfd_vma, boolean, struct misc *));
+static void                  tidyup_after_error                   PARAMS ((struct misc *));
+static boolean               ip2k_elf_relax_section_pass1         PARAMS ((bfd *, asection *, boolean *, struct misc *));
+static boolean               ip2k_elf_relax_section_passN         PARAMS ((bfd *, asection *, boolean *, boolean *, struct misc *));
+static bfd_reloc_status_type ip2k_final_link_relocate             PARAMS ((reloc_howto_type *, bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, bfd_vma));
+static boolean               ip2k_elf_relocate_section            PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
+
+#define IS_OPCODE(CODE0,CODE1,OPCODE) \
+  ((CODE0) == (OPCODE)[0] && (CODE1) == (OPCODE)[1])
+
+#define PAGE_INSN_0            0x00
+#define PAGE_INSN_1            0x10
+
+static const bfd_byte page_opcode[] =
+{
+   PAGE_INSN_0, PAGE_INSN_1
+};
+
+#define IS_PAGE_OPCODE(CODE0,CODE1) \
+  IS_OPCODE (CODE0, CODE1, page_opcode)
+
+#define JMP_INSN_0             0xE0
+#define JMP_INSN_1             0x00
+
+static const bfd_byte jmp_opcode[] =
+{
+   JMP_INSN_0, JMP_INSN_1
+};
+
+#define IS_JMP_OPCODE(CODE0,CODE1) \
+  IS_OPCODE (CODE0, CODE1, jmp_opcode)
+
+#define CALL_INSN_0            0xC0
+#define CALL_INSN_1            0x00
+
+static const bfd_byte call_opcode[] =
+{
+  CALL_INSN_0, CALL_INSN_1
+};
+
+#define IS_CALL_OPCODE(CODE0,CODE1) \
+  IS_OPCODE (CODE0, CODE1, call_opcode)
+
+#define ADD_PCL_W_INSN_0       0x1E
+#define ADD_PCL_W_INSN_1       0x09
+
+static const bfd_byte add_pcl_w_opcode[] =
+{
+  ADD_PCL_W_INSN_0, ADD_PCL_W_INSN_1
+};
+
+#define IS_ADD_PCL_W_OPCODE(CODE0,CODE1) \
+  IS_OPCODE (CODE0, CODE1, add_pcl_w_opcode)
+
+#define ADD_W_WREG_INSN_0      0x1C
+#define ADD_W_WREG_INSN_1      0x0A
+
+static const bfd_byte add_w_wreg_opcode[] =
+{
+  ADD_W_WREG_INSN_0, ADD_W_WREG_INSN_1
+};
+
+#define IS_ADD_W_WREG_OPCODE(CODE0,CODE1) \
+  IS_OPCODE (CODE0, CODE1, add_w_wreg_opcode)
+
+#define SNC_INSN_0             0xA0
+#define SNC_INSN_1             0x0B
+
+static const bfd_byte snc_opcode[] =
+{
+   SNC_INSN_0, SNC_INSN_1
+};
+
+#define IS_SNC_OPCODE(CODE0,CODE1) \
+  IS_OPCODE (CODE0, CODE1, snc_opcode)
+
+#define INC_1_SP_INSN_0                0x2B
+#define INC_1_SP_INSN_1                0x81
+
+static const bfd_byte inc_1_sp_opcode[] =
+{
+   INC_1_SP_INSN_0, INC_1_SP_INSN_1
+};
+
+#define IS_INC_1_SP_OPCODE(CODE0,CODE1) \
+  IS_OPCODE (CODE0, CODE1, inc_1_sp_opcode)
+
+#define ADD_2_SP_W_INSN_0      0x1F
+#define ADD_2_SP_W_INSN_1      0x82
+
+static const bfd_byte add_2_sp_w_opcode[] =
+{
+   ADD_2_SP_W_INSN_0, ADD_2_SP_W_INSN_1
+};
+
+#define IS_ADD_2_SP_W_OPCODE(CODE0,CODE1) \
+  IS_OPCODE (CODE0, CODE1, add_2_sp_w_opcode)
+
+/* Relocation tables. */
+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 */
+
+  /* This reloc does nothing. */
+  IP2K_HOWTO (R_IP2K_NONE, 0,2,32, 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.  */
+  IP2K_HOWTO (R_IP2K_32, 0,2,32, false, 0, "R_IP2K_32", 0, 0xffffffff),
+  /* A 8-bit data relocation for the FR9 field.  Ninth bit is computed specially.  */
+  IP2K_HOWTO (R_IP2K_FR9, 0,1,9, false, 0, "R_IP2K_FR9", 0, 0x00ff),
+  /* A 4-bit data relocation.  */
+  IP2K_HOWTO (R_IP2K_BANK, 8,1,4, false, 0, "R_IP2K_BANK", 0, 0x000f),
+  /* A 13-bit insn relocation - word address => right-shift 1 bit extra.  */
+  IP2K_HOWTO (R_IP2K_ADDR16CJP, 1,1,13, false, 0, "R_IP2K_ADDR16CJP", 0, 0x1fff),
+  /* A 3-bit insn relocation - word address => right-shift 1 bit extra.  */
+  IP2K_HOWTO (R_IP2K_PAGE3, 14,1,3, false, 0, "R_IP2K_PAGE3", 0, 0x0007),
+  /* Two 8-bit data relocations.  */
+  IP2K_HOWTO (R_IP2K_LO8DATA, 0,1,8, false, 0, "R_IP2K_LO8DATA", 0, 0x00ff),
+  IP2K_HOWTO (R_IP2K_HI8DATA, 8,1,8, false, 0, "R_IP2K_HI8DATA", 0, 0x00ff),
+  /* Two 8-bit insn relocations.  word address => right-shift 1 bit extra.  */
+  IP2K_HOWTO (R_IP2K_LO8INSN, 1,1,8, false, 0, "R_IP2K_LO8INSN", 0, 0x00ff),
+  IP2K_HOWTO (R_IP2K_HI8INSN, 9,1,8, false, 0, "R_IP2K_HI8INSN", 0, 0x00ff),
+
+  /* Special 1 bit relocation for SKIP instructions.  */
+  IP2K_HOWTO (R_IP2K_PC_SKIP, 1,1,1, false, 12, "R_IP2K_PC_SKIP", 0xfffe, 0x1000),
+  /* 16 bit word address.  */
+  IP2K_HOWTO (R_IP2K_TEXT, 1,1,16, false, 0, "R_IP2K_TEXT", 0, 0xffff),
+  /* A 7-bit offset relocation for the FR9 field.  Eigth and ninth bit comes from insn.  */
+  IP2K_HOWTO (R_IP2K_FR_OFFSET, 0,1,9, false, 0, "R_IP2K_FR_OFFSET", 0x180, 0x007f),
+  /* Bits 23:16 of an address.  */
+  IP2K_HOWTO (R_IP2K_EX8DATA, 16,1,8, false, 0, "R_IP2K_EX8DATA", 0, 0x00ff),
+};
+
+
+/* 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;
+{
+  /* Note that the ip2k_elf_howto_table is indxed by the R_
+     constants.  Thus, the order that the howto records appear in the
+     table *must* match the order of the relocation types defined in
+     include/elf/ip2k.h. */
+
+  switch (code)
+    {
+    case BFD_RELOC_NONE:
+      return &ip2k_elf_howto_table[ (int) R_IP2K_NONE];
+    case BFD_RELOC_16:
+      return &ip2k_elf_howto_table[ (int) R_IP2K_16];
+    case BFD_RELOC_32:
+      return &ip2k_elf_howto_table[ (int) R_IP2K_32];
+    case BFD_RELOC_IP2K_FR9:
+      return &ip2k_elf_howto_table[ (int) R_IP2K_FR9];
+    case BFD_RELOC_IP2K_BANK:
+      return &ip2k_elf_howto_table[ (int) R_IP2K_BANK];
+    case BFD_RELOC_IP2K_ADDR16CJP:
+      return &ip2k_elf_howto_table[ (int) R_IP2K_ADDR16CJP];
+    case BFD_RELOC_IP2K_PAGE3:
+      return &ip2k_elf_howto_table[ (int) R_IP2K_PAGE3];
+    case BFD_RELOC_IP2K_LO8DATA:
+      return &ip2k_elf_howto_table[ (int) R_IP2K_LO8DATA];
+    case BFD_RELOC_IP2K_HI8DATA:
+      return &ip2k_elf_howto_table[ (int) R_IP2K_HI8DATA];
+    case BFD_RELOC_IP2K_LO8INSN:
+      return &ip2k_elf_howto_table[ (int) R_IP2K_LO8INSN];
+    case BFD_RELOC_IP2K_HI8INSN:
+      return &ip2k_elf_howto_table[ (int) R_IP2K_HI8INSN];
+    case BFD_RELOC_IP2K_PC_SKIP:
+      return &ip2k_elf_howto_table[ (int) R_IP2K_PC_SKIP];
+    case BFD_RELOC_IP2K_TEXT:
+      return &ip2k_elf_howto_table[ (int) R_IP2K_TEXT];
+    case BFD_RELOC_IP2K_FR_OFFSET:
+      return &ip2k_elf_howto_table[ (int) R_IP2K_FR_OFFSET];
+    case BFD_RELOC_IP2K_EX8DATA:
+      return &ip2k_elf_howto_table[ (int) R_IP2K_EX8DATA];
+    default:
+      /* Pacify gcc -Wall. */
+      return NULL;
+    }
+  return NULL;
+}
+
+#define PAGENO(ABSADDR) ((ABSADDR) & 0x1C000)
+#define BASEADDR(SEC)  ((SEC)->output_section->vma + (SEC)->output_offset)
+
+#define UNDEFINED_SYMBOL (~(bfd_vma)0)
+
+/* Return the value of the symbol associated with the relocation IREL.  */
+
+static bfd_vma
+symbol_value (abfd, symtab_hdr, extsyms, irel)
+     bfd *abfd;
+     Elf_Internal_Shdr *symtab_hdr;
+     Elf32_External_Sym *extsyms;
+     Elf_Internal_Rela *irel;   
+{
+  if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
+    {
+      Elf_External_Sym_Shndx *sym_shndx;
+      Elf_Internal_Shdr *shndx_hdr;
+      Elf_Internal_Sym isym;
+      asection *sym_sec;
+
+      shndx_hdr = &elf_tdata (abfd)->symtab_shndx_hdr;
+      sym_shndx = (Elf_External_Sym_Shndx *) shndx_hdr->contents;
+      sym_shndx = sym_shndx ? sym_shndx + ELF32_R_SYM (irel->r_info) : NULL;
+      bfd_elf32_swap_symbol_in (abfd, extsyms + ELF32_R_SYM (irel->r_info),
+                               sym_shndx, &isym);
+      if (isym.st_shndx == SHN_UNDEF)
+       sym_sec = bfd_und_section_ptr;
+      else if (isym.st_shndx == SHN_ABS)
+       sym_sec = bfd_abs_section_ptr;
+      else if (isym.st_shndx == SHN_COMMON)
+       sym_sec = bfd_com_section_ptr;
+      else
+       sym_sec = bfd_section_from_elf_index (abfd, isym.st_shndx);
+
+      return isym.st_value + BASEADDR (sym_sec);
+    }
+  else
+    {
+      unsigned long indx;
+      struct elf_link_hash_entry *h;
+
+      indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info;
+      h = elf_sym_hashes (abfd)[indx];
+      BFD_ASSERT (h != NULL);
+
+      if (h->root.type != bfd_link_hash_defined
+         && h->root.type != bfd_link_hash_defweak)
+       return UNDEFINED_SYMBOL;
+
+      return (h->root.u.def.value + BASEADDR (h->root.u.def.section));
+    }
+}
+
+/* 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
+  addr=>
+          page    $nnn1
+          jmp     $nnn1
+          page    $nnn2
+          jmp     $nnn2
+          ...
+          page    $nnnN
+          jmp     $nnnN
+  After relaxation.
+          sc
+          page    $nnn0
+          jmp     $nnn0
+          add     pcl,w
+  addr=>
+          jmp     $nnn1
+          jmp     $nnn2
+          ...
+          jmp     $nnnN  */
+
+static boolean 
+is_switch_128_dispatch_table_p (abfd, addr, relaxed, misc)
+     bfd *abfd ATTRIBUTE_UNUSED;                
+     bfd_vma addr;
+     boolean relaxed;
+     struct misc *misc;
+{
+  bfd_byte code0, code1;
+
+  if (addr < (3 * 2))
+    return false;
+
+  code0 = bfd_get_8 (abfd, misc->contents + addr - 2);
+  code1 = bfd_get_8 (abfd, misc->contents + addr - 1);
+
+  /* Is it ADD PCL,W */
+  if (! IS_ADD_PCL_W_OPCODE (code0, code1))
+    return false;
+
+  code0 = bfd_get_8 (abfd, misc->contents + addr - 4);
+  code1 = bfd_get_8 (abfd, misc->contents + addr - 3);
+
+  if (relaxed)
+    /* Is it ADD W,WREG  */
+    return ! IS_ADD_W_WREG_OPCODE (code0, code1);
+
+  else
+    {
+      /* Is it ADD W,WREG  */
+      if (! IS_ADD_W_WREG_OPCODE (code0, code1))
+       return false;
+
+      code0 = bfd_get_8 (abfd, misc->contents + addr - 6);
+      code1 = bfd_get_8 (abfd, misc->contents + addr - 5);
+
+      /* Is it JMP $nnnn  */
+      if (! IS_JMP_OPCODE (code0, code1))
+        return false;
+    }
+
+  /* It looks like we've found the prologue for
+     a 1-127 entry switch dispatch table.  */
+  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)
+   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
+  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)
+  addr=>
+          page    __indjmp        ; Do an indirect jump to that location
+          jmp     __indjmp
+   label:                         ; case dispatch table starts here
+          jmp     $nnn1
+          jmp     $nnn2
+          ...
+          jmp     $nnnN  */
+
+static boolean 
+is_switch_256_dispatch_table_p (abfd, addr, relaxed,  misc)
+     bfd *abfd ATTRIBUTE_UNUSED;
+     bfd_vma addr;
+     boolean relaxed;
+     struct misc *misc;
+{
+  bfd_byte code0, code1;
+
+  if (addr < (8 * 2))
+    return false;
+
+  code0 = bfd_get_8 (abfd, misc->contents + addr - 2);
+  code1 = bfd_get_8 (abfd, misc->contents + addr - 1);
+
+  /* Is it INC 1(SP).  */
+  if (! IS_INC_1_SP_OPCODE (code0, code1))
+    return false;
+
+  code0 = bfd_get_8 (abfd, misc->contents + addr - 4);
+  code1 = bfd_get_8 (abfd, misc->contents + addr - 3);
+
+  /* Is it SNC.  */
+  if (! IS_SNC_OPCODE (code0, code1))
+    return false;
+
+  code0 = bfd_get_8 (abfd, misc->contents + addr - 6);
+  code1 = bfd_get_8 (abfd, misc->contents + addr - 5);
+
+  /* Is it ADD 2(SP),W.  */
+  if (! IS_ADD_2_SP_W_OPCODE (code0, code1))
+    return false;
+
+  code0 = bfd_get_8 (abfd, misc->contents + addr - 8);
+  code1 = bfd_get_8 (abfd, misc->contents + addr - 7);
+
+  if (relaxed)
+    /* Is it INC 1(SP).  */
+    return ! IS_INC_1_SP_OPCODE (code0, code1);
+
+  else
+    {
+      /* Is it INC 1(SP).  */
+      if (! IS_INC_1_SP_OPCODE (code0, code1))
+       return false;
+
+      code0 = bfd_get_8 (abfd, misc->contents + addr - 10);
+      code1 = bfd_get_8 (abfd, misc->contents + addr - 9);
+      /* Is it SNC.  */
+      if (! IS_SNC_OPCODE (code0, code1))
+        return false;
+
+      code0 = bfd_get_8 (abfd, misc->contents + addr - 12);
+      code1 = bfd_get_8 (abfd, misc->contents + addr - 11);
+
+      /* Is it ADD W,WREG.  */
+      if (! IS_ADD_W_WREG_OPCODE (code0, code1))
+       return false;
+    }
+
+  /* It looks like we've found the prologue for
+     a 128-255 entry switch dispatch table.  */
+  return true;
+}
+
+static boolean
+relax_switch_dispatch_tables_pass1 (abfd, sec, addr, misc)
+     bfd *abfd;
+     asection *sec;
+     bfd_vma addr;
+     struct misc *misc;
+{
+  if (addr + 3 < sec->_cooked_size)
+    {
+      bfd_byte code0 = bfd_get_8 (abfd, misc->contents + addr + 2);
+      bfd_byte code1 = bfd_get_8 (abfd, misc->contents + addr + 3);
+
+      if (IS_JMP_OPCODE (code0, code1)
+         && is_switch_128_dispatch_table_p (abfd, addr, false, misc))
+       {
+         /* Delete ADD W,WREG from prologue.  */
+         ip2k_elf_relax_delete_bytes (abfd, sec, addr - (2 * 2), (1 * 2));
+         return true;
+       }
+
+      if (IS_JMP_OPCODE (code0, code1)
+         && is_switch_256_dispatch_table_p (abfd, addr, false, misc))
+       {
+         /* Delete ADD W,WREG; SNC ; INC 1(SP) from prologue.  */
+         ip2k_elf_relax_delete_bytes (abfd, sec, addr - 6 * 2, 3 * 2);
+         return true;
+       }
+    }
+  return true;
+}
+
+static boolean
+unrelax_dispatch_table_entries (abfd, sec, first, last, changed, misc)
+     bfd *abfd;
+     asection *sec;
+     bfd_vma first;
+     bfd_vma last;
+     boolean *changed;
+     struct misc *misc;
+{
+  bfd_vma addr = first;
+
+  while (addr < last)
+    {
+      bfd_byte code0 = bfd_get_8 (abfd, misc->contents + addr);
+      bfd_byte code1 = bfd_get_8 (abfd, misc->contents + addr + 1);
+
+      /* We are only expecting to find PAGE or JMP insns
+         in the dispatch table. If we find anything else
+         something has gone wrong failed the relaxation
+         which will cause the link to be aborted.  */
+
+      if (IS_PAGE_OPCODE (code0, code1))
+       /* Skip the PAGE and JMP insns.  */
+        addr += 4;
+      else if (IS_JMP_OPCODE (code0, code1))
+         {
+            Elf_Internal_Rela * irelend = misc->irelbase
+                                         + sec->reloc_count;
+            Elf_Internal_Rela * irel;
+
+            /* Find the relocation entry.  */
+            for (irel = misc->irelbase; irel < irelend; irel++)
+               {
+                  if (irel->r_offset == addr
+                      && ELF32_R_TYPE (irel->r_info) == R_IP2K_ADDR16CJP)
+                    {
+                      if (! add_page_insn (abfd, sec, irel, misc))
+                       /* Something has gone wrong.  */
+                        return false;
+
+                     *changed = true;
+                     break;
+                    }
+               }
+
+           /* If we fell off the end something has gone wrong.  */
+           if (irel >= irelend)
+             /* Something has gone wrong.  */
+             return false;
+
+           /* Skip the PAGE and JMP isns.  */
+           addr += 4;
+           /* Acount for the new PAGE insn.  */
+            last += 2;
+          }
+       else
+        /* Something has gone wrong.  */
+        return false;
+    }
+
+  return true;
+}
+
+static boolean 
+unrelax_switch_dispatch_tables_passN (abfd, sec, addr, changed, misc)
+     bfd *abfd;
+     asection *sec;
+     bfd_vma addr;
+     boolean *changed;
+     struct misc *misc;
+{
+  if (2 <= addr && (addr + 3) < sec->_cooked_size)
+    {
+      bfd_byte code0 = bfd_get_8 (abfd, misc->contents + addr - 2);
+      bfd_byte code1 = bfd_get_8 (abfd, misc->contents + addr - 1);
+
+      if (IS_PAGE_OPCODE (code0, code1))
+       {
+         addr -= 2;
+         code0 = bfd_get_8 (abfd, misc->contents + addr + 2);
+          code1 = bfd_get_8 (abfd, misc->contents + addr + 3);
+       }
+      else
+       {
+         code0 = bfd_get_8 (abfd, misc->contents + addr);
+         code1 = bfd_get_8 (abfd, misc->contents + addr + 1);
+       }
+
+      if (IS_JMP_OPCODE (code0, code1)
+          && is_switch_128_dispatch_table_p (abfd, addr, true, misc))
+        {
+         bfd_vma first = addr;
+         bfd_vma last  = first;
+         boolean relaxed = true;
+
+         /* On the final pass we must check if *all* entries in the
+            dispatch table are relaxed. If *any* are not relaxed
+            then we must unrelax *all* the entries in the dispach
+            table and also unrelax the dispatch table prologue.  */
+
+         /* Find the last entry in the dispach table.  */
+         while (last < sec->_cooked_size)
+            {
+               code0 = bfd_get_8 (abfd, misc->contents + last);
+               code1 = bfd_get_8 (abfd, misc->contents + last + 1);
+
+               if (IS_PAGE_OPCODE (code0, code1))
+                 relaxed = false;
+               else if (! IS_JMP_OPCODE (code0, code1))
+                   break;
+
+               last += 2;
+            }
+
+         /* We should have found the end of the dispatch table
+            before reaching the end of the section. If we've have
+            reached the end then fail the relaxation which will
+            cause the link to be aborted.  */
+         if (last >= sec->_cooked_size)
+           /* Something has gone wrong.  */
+           return false;
+
+         /* If we found an unrelaxed entry then
+            unlrelax all the switch table entries.  */
+         if (! relaxed )
+           {
+             if (! unrelax_dispatch_table_entries (abfd, sec, first,
+                                                   last, changed, misc))
+               /* Something has gone wrong.  */
+               return false;
+
+             if (! is_switch_128_dispatch_table_p (abfd, addr, true, misc))
+               /* Something has gone wrong.  */
+               return false;
+               
+              /* Unrelax the prologue.  */
+
+              /* Insert an ADD W,WREG insnstruction.  */
+              if (! ip2k_elf_relax_add_bytes (abfd, sec,
+                                             addr - 2,
+                                             add_w_wreg_opcode,
+                                             sizeof (add_w_wreg_opcode),
+                                             0))
+               /* Something has gone wrong.  */
+                return false;
+           }
+
+          return true;
+        }
+
+      if (IS_JMP_OPCODE (code0, code1)
+          && is_switch_256_dispatch_table_p (abfd, addr, true, misc))
+        {
+          bfd_vma first = addr;
+          bfd_vma last;
+          boolean relaxed = true;
+
+          /* On the final pass we must check if *all* entries in the
+             dispatch table are relaxed. If *any* are not relaxed
+             then we must unrelax *all* the entries in the dispach
+             table and also unrelax the dispatch table prologue.  */
+
+         /* Note the 1st PAGE/JMP instructions are part of the
+            prologue and can safely be relaxed.  */
+
+          code0 = bfd_get_8 (abfd, misc->contents + first);
+          code1 = bfd_get_8 (abfd, misc->contents + first + 1);
+
+         if (IS_PAGE_OPCODE (code0, code1))
+           {
+             first += 2;
+              code0 = bfd_get_8 (abfd, misc->contents + first);
+              code1 = bfd_get_8 (abfd, misc->contents + first + 1);
+           }
+
+          if (! IS_JMP_OPCODE (code0, code1))
+           /* Something has gone wrong.  */
+           return false;
+
+          first += 2;
+         last = first; 
+
+          /* Find the last entry in the dispach table.  */
+          while (last < sec->_cooked_size)
+             {
+                code0 = bfd_get_8 (abfd, misc->contents + last);
+                code1 = bfd_get_8 (abfd, misc->contents + last + 1);
+
+                if (IS_PAGE_OPCODE (code0, code1))
+                  relaxed = false;
+                else if (! IS_JMP_OPCODE (code0, code1))
+                    break;
+
+                last += 2;
+             }
+
+          /* We should have found the end of the dispatch table
+             before reaching the end of the section. If we have
+             reached the end of the section then fail the
+            relaxation.  */
+          if (last >= sec->_cooked_size)
+            return false;
+
+          /* If we found an unrelaxed entry then
+              unrelax all the switch table entries.  */
+          if (! relaxed)
+           {
+             if (! unrelax_dispatch_table_entries (abfd, sec, first,
+                                                   last, changed, misc))
+               return false;
+
+              if (! is_switch_256_dispatch_table_p (abfd, addr, true, misc))
+               return false;
+
+              /* Unrelax the prologue.  */
+
+              /* Insert an INC 1(SP) insnstruction.  */
+              if (! ip2k_elf_relax_add_bytes (abfd, sec,
+                                              addr - 6,
+                                              inc_1_sp_opcode,
+                                              sizeof (inc_1_sp_opcode),
+                                             0))
+               return false;
+
+              /* Insert an SNC insnstruction.  */
+              if (! ip2k_elf_relax_add_bytes (abfd, sec,
+                                             addr - 6,
+                                             snc_opcode,
+                                             sizeof (snc_opcode),
+                                             0))
+               return false;
+
+             /* Insert an ADD W,WREG insnstruction.  */
+              if (! ip2k_elf_relax_add_bytes (abfd, sec,
+                                            addr - 6,
+                                            add_w_wreg_opcode,
+                                            sizeof (add_w_wreg_opcode),
+                                            0))
+               return false;
+           }
+
+          return true;
+        }
+    }
+
+  return true;
+}
+
+/* This function handles relaxing for the ip2k.  */
+
+static boolean
+ip2k_elf_relax_section (abfd, sec, link_info, again)
+     bfd *abfd;
+     asection *sec;
+     struct bfd_link_info *link_info;
+     boolean *again;
+{
+  Elf_External_Sym_Shndx *shndx_buf;
+  Elf_Internal_Shdr *shndx_hdr;
+  static asection * first_section = NULL;
+  static asection * last_section = NULL;
+  static boolean changed = false;
+  static boolean final_pass = false;
+  static unsigned int pass = 0;
+  struct misc misc;
+  asection *stab;
+
+  /* Assume nothing changes.  */
+  *again = false;
+
+  if (first_section == NULL)
+    first_section = sec;
+
+  if (first_section == sec)
+    {
+      changed = false;
+      pass++;
+    }
+
+  /* If we make too many passes then it's a sign that
+     something is wrong and we fail the relaxation.
+     Note if everything is working correctly then the
+     relaxation should converge reasonably quickly.  */
+  if (pass == 4096)
+    return false;
+
+  /* We don't have to do anything for a relocatable link,
+     if this section does not have relocs, or if this is
+     not a code section.  */
+  if (link_info->relocateable
+      || (sec->flags & SEC_RELOC) == 0
+      || sec->reloc_count == 0
+      || (sec->flags & SEC_CODE) == 0)
+    return true;
+
+  if (pass == 1)
+    last_section = sec;
+
+  misc.symtab_hdr = NULL;
+  misc.irelbase = NULL;
+  misc.contents = NULL;
+  misc.free_contents = NULL;
+  misc.extsyms = NULL;
+  misc.free_extsyms = NULL;
+  misc.free_relocs = NULL;
+
+  /* If this is the first time we have been called
+      for this section, initialise the cooked size.  */
+  if (sec->_cooked_size == 0)
+    sec->_cooked_size = sec->_raw_size;
+
+  misc.symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+  shndx_hdr = &elf_tdata (abfd)->symtab_shndx_hdr;
+
+  misc.irelbase = _bfd_elf32_link_read_relocs (abfd, sec, NULL,
+                                            (Elf_Internal_Rela *)NULL,
+                                            link_info->keep_memory);
+  if (misc.irelbase == NULL)
+    {
+      tidyup_after_error (&misc);
+      return false;
+    }
+
+  if (! link_info->keep_memory)
+    misc.free_relocs = misc.irelbase;
+
+  /* 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_elf32_link_read_relocs (abfd, stab, NULL,
+                                             (Elf_Internal_Rela *)NULL,
+                                             link_info->keep_memory);
+    }
+
+  /* Get section contents cached copy if it exists.  */
+  if (elf_section_data (sec)->this_hdr.contents != NULL)
+    misc.contents = elf_section_data (sec)->this_hdr.contents;
+  else
+    {
+      /* Go get them of disk.  */
+      misc.contents = (bfd_byte *) bfd_malloc (sec->_raw_size);
+      if (misc.contents == NULL)
+       {
+         tidyup_after_error (&misc);
+         return false;
+       }
+
+      misc.free_contents = misc.contents;
+      if (! bfd_get_section_contents (abfd, sec, misc.contents,
+                                     (file_ptr)0,
+                                     sec->_raw_size))
+       {
+         tidyup_after_error (&misc);
+         return false;
+       }
+    }
+  
+  /* Read this BFD's symbols cached copy if it exists.  */
+  if (misc.symtab_hdr->contents != NULL)
+    misc.extsyms = (Elf32_External_Sym *) misc.symtab_hdr->contents;
+  else
+    {
+      /* Go get them off disk.  */
+      misc.extsyms = ((Elf32_External_Sym *)bfd_malloc (misc.symtab_hdr->sh_size));
+      if (misc.extsyms == NULL)
+       {
+         tidyup_after_error (&misc);
+         return false;
+       }
+
+      misc.free_extsyms = misc.extsyms;
+      if (bfd_seek (abfd, misc.symtab_hdr->sh_offset, SEEK_SET) != 0
+          || (bfd_read (misc.extsyms, 1, misc.symtab_hdr->sh_size, abfd)
+          != misc.symtab_hdr->sh_size))
+       {
+         tidyup_after_error (&misc);
+         return false;
+       }
+    }
+
+  if (shndx_hdr->sh_size != 0)
+    {
+      bfd_size_type amt;
+
+      amt = misc.symtab_hdr->sh_info * sizeof (Elf_External_Sym_Shndx);
+      shndx_buf = (Elf_External_Sym_Shndx *) bfd_malloc (amt);
+      if (shndx_buf == NULL)
+       {
+         tidyup_after_error (&misc);
+         return false;
+       }
+      if (bfd_seek (abfd, shndx_hdr->sh_offset, SEEK_SET) != 0
+         || bfd_bread ((PTR) shndx_buf, amt, abfd) != amt)
+       {
+         tidyup_after_error (&misc);
+         return false;
+       }
+      shndx_hdr->contents = (PTR) shndx_buf;
+    }
+
+  /* This is where all the relaxation actually get done.  */
+
+  if (pass == 1)
+    {
+      /* On the first pass we remove *all* page instructions and
+         relax the prolog for switch dispatch tables. This gets
+        us to the starting point for subsequent passes where
+        we add page instructions back in as needed.  */
+
+      if (! ip2k_elf_relax_section_pass1 (abfd, sec, again, &misc))
+        {
+         tidyup_after_error (&misc);
+         return false;
+        }
+
+      changed |= *again;
+    }
+  else
+    {
+      /* Add page instructions back in as needed but we ignore 
+        the issue with sections (functions) crossing a page
+        boundary until we have converged to an approximate
+        solution (i.e. nothing has changed on this relaxation
+        pass) and we then know roughly where the page boundaries
+        will end up.
+
+        After we have have converged to an approximate solution
+        we set the final pass flag and continue relaxing. On these
+        final passes if a section (function) cross page boundary
+        we will add *all* the page instructions back into such
+        sections.
+
+        After adding *all* page instructions back into a section
+        which crosses a page bounbdary we reset the final pass flag
+        so the we will again interate until we find a new approximate
+        solution which is closer to the final solution.  */
+
+      if (! ip2k_elf_relax_section_passN (abfd, sec, again,
+                                        &final_pass,  &misc))
+       {
+         tidyup_after_error (&misc);
+         return false;
+       }
+
+      changed |= *again;
+
+      /* If nothing has changed on this relaxation
+         pass restart the final relaxaton pass.  */
+      if (! changed && last_section == sec)
+       {
+         /* If this was the final pass and we didn't reset 
+            the final pass flag then we are done, otherwise
+            do another final pass.  */
+         if (! final_pass)
+           {
+             final_pass = true;
+             *again = true;
+           }
+       }
+    }
+
+  /* Perform some house keeping after relaxing the section.  */  
+
+  if (misc.free_relocs != NULL)
+    {
+      free (misc.free_relocs);
+      misc.free_relocs = NULL;
+    }
+
+  if (misc.free_contents != NULL)
+    {
+      if (! link_info->keep_memory)
+       free (misc.free_contents);
+      else
+       {
+         /* Cache the section contents for elf_link_input_bfd.  */
+         elf_section_data (sec)->this_hdr.contents = misc.contents;
+       }
+
+      misc.free_contents = NULL;
+    }
+
+  if (misc.free_extsyms != NULL)
+    {
+      if (! link_info->keep_memory)
+       free (misc.free_extsyms);
+      else
+       {
+         /* Cache the symbols for elf_link_input_bfd.  */
+         misc.symtab_hdr->contents = misc.extsyms;
+       }
+
+      misc.free_extsyms = NULL;
+    }
+
+  return true;
+}
+
+static void
+tidyup_after_error (misc)
+     struct misc *misc;
+{
+  if (misc->free_relocs != NULL)
+    {
+      free (misc->free_relocs);
+      misc->free_relocs = NULL;
+    }
+
+  if (misc->free_contents != NULL)
+    {
+      free (misc->free_contents);
+      misc->free_contents = NULL;
+    }
+
+  if (misc->free_extsyms != NULL)
+    {
+      free (misc->free_extsyms);
+      misc->free_extsyms = NULL;
+    }
+
+  return;
+}
+
+/* This function handles relaxation during the first pass.  */
+
+static boolean
+ip2k_elf_relax_section_pass1 (abfd, sec, again, misc)
+     bfd *abfd;
+     asection *sec;
+     boolean *again;
+     struct misc * misc;
+{
+  Elf_Internal_Rela *irelend = misc->irelbase + sec->reloc_count;
+  Elf_Internal_Rela *irel;
+
+  /* Walk thru the section looking for relaxation opertunities.  */
+  for (irel = misc->irelbase; irel < irelend; irel++)
+    {
+      if (ELF32_R_TYPE (irel->r_info) == (int) R_IP2K_PAGE3)
+      {
+       bfd_byte code0 = bfd_get_8 (abfd,
+                                   misc->contents + irel->r_offset);
+       bfd_byte code1 = bfd_get_8 (abfd,
+                                   misc->contents + irel->r_offset + 1);
+
+        /* Verify that this is the PAGE opcode.  */
+        if (IS_PAGE_OPCODE (code0, code1))
+         {
+           /* Note that we've changed the relocs, section contents, etc.  */
+           elf_section_data (sec)->relocs = misc->irelbase;
+           misc->free_relocs = NULL;
+
+           elf_section_data (sec)->this_hdr.contents = misc->contents;
+           misc->free_contents = NULL;
+
+           misc->symtab_hdr->contents = (bfd_byte *) misc->extsyms;
+           misc->free_extsyms = NULL;
+
+           /* Handle switch dispatch tables/prologues.  */
+           if (!  relax_switch_dispatch_tables_pass1 (abfd, sec,
+                                                      irel->r_offset, misc))
+             return false;
+           
+           /* 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,
+                                              sizeof (page_opcode)))
+             return false;
+
+           /* That will change things, so, we should relax again.
+              Note that this is not required, and it may be slow.  */
+           *again = true;
+         }
+      }
+    }
+
+  return true;
+}
+
+/* This function handles relaxation for 2nd and subsequent passes.  */
+
+static boolean
+ip2k_elf_relax_section_passN (abfd, sec, again, final_pass, misc)
+     bfd *abfd;
+     asection *sec;
+     boolean *again;
+     boolean *final_pass;
+     struct misc * misc;
+{
+  Elf_Internal_Rela *irelend = misc->irelbase + sec->reloc_count;
+  Elf_Internal_Rela *irel;
+  boolean add_all;
+
+  /* If we are on the final relaxation pass and the section crosses
+     then set a flag to indicate that *all* page instructions need
+     to be added back into this section.  */
+  if (*final_pass)
+    {
+      add_all = (PAGENO (BASEADDR (sec))
+                != PAGENO (BASEADDR (sec) + sec->_cooked_size));
+
+      /* If this section crosses a page boundary set the crossed
+        page boundary flag.  */
+      if (add_all)
+       sec->userdata = sec;
+      else
+       {
+         /* If the section had previously crossed a page boundary
+            but on this pass does not then reset crossed page
+            boundary flag and rerun the 1st relaxation pass on
+            this section.  */
+         if (sec->userdata)
+           {
+             sec->userdata = NULL;
+             if (! ip2k_elf_relax_section_pass1 (abfd, sec, again, misc))
+               return false;
+           }
+       }
+    }
+  else
+    add_all = false;
+
+  /* Walk thru the section looking for call/jmp
+      instructions which need a page instruction.  */
+  for (irel = misc->irelbase; irel < irelend; irel++)
+    {
+      if (ELF32_R_TYPE (irel->r_info) == (int) R_IP2K_ADDR16CJP)
+      {
+        /* Get the value of the symbol referred to by the reloc.  */
+        bfd_vma symval = symbol_value (abfd, misc->symtab_hdr, misc->extsyms,
+                                      irel);
+       bfd_byte code0, code1;
+
+        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.  */
+           continue;
+         }
+
+        /* For simplicity of coding, we are going to modify the section
+          contents, the section relocs, and the BFD symbol table.  We
+          must tell the rest of the code not to free up this
+          information.  It would be possible to instead create a table
+          of changes which have to be made, as is done in coff-mips.c;
+          that would be more work, but would require less memory when
+          the linker is run.  */
+
+       /* Get the opcode.  */
+       code0 = bfd_get_8 (abfd, misc->contents + irel->r_offset);
+       code1 = bfd_get_8 (abfd, misc->contents + irel->r_offset + 1);
+
+       if (IS_JMP_OPCODE (code0, code1) || IS_CALL_OPCODE (code0, code1))
+         {
+           if (*final_pass)
+             {
+               if (! unrelax_switch_dispatch_tables_passN (abfd, sec,
+                                                           irel->r_offset,
+                                                            again, misc))
+                 return false;
+
+                if (*again)
+                 add_all = false;
+             }
+
+           code0 = bfd_get_8 (abfd, misc->contents + irel->r_offset - 2);
+           code1 = bfd_get_8 (abfd, misc->contents + irel->r_offset - 1);
+
+           if (! IS_PAGE_OPCODE (code0, code1))
+             {
+               bfd_vma value = symval + irel->r_addend;
+               bfd_vma addr  = BASEADDR (sec) + irel->r_offset;
+
+               if (add_all || PAGENO (addr) != PAGENO (value))
+                 {
+                   if (! add_page_insn (abfd, sec, irel, misc))
+                     return false;
+
+                   /* That will have changed things, so,  we must relax again.  */
+                   *again = true;
+                 }
+              }
+          }
+        }
+    }
+      
+  /* If anything changed reset the final pass flag.  */
+  if (*again)
+    *final_pass = 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)
+
+/* 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;
+  Elf32_External_Sym *extsyms;
+  int shndx, index;
+  bfd_byte *contents;
+  Elf_Internal_Rela *irel, *irelend, *irelbase;
+  Elf32_External_Sym *esym, *esymend;
+  asection *stab;
+  bfd_byte *stabp, *stabend, *stabcontents;
+  Elf_Internal_Shdr *shndx_hdr;
+  Elf_External_Sym_Shndx *sym_shndx;
+    
+  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+  extsyms = (Elf32_External_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)
+            {
+              Elf_Internal_Sym isym;
+              asection *sym_sec;
+             Elf_External_Sym_Shndx *sym_shndx;
+             Elf_Internal_Shdr *shndx_hdr;
+
+              /* A local symbol.  */
+             
+             shndx_hdr = &elf_tdata (abfd)->symtab_shndx_hdr;
+             sym_shndx = (Elf_External_Sym_Shndx *) shndx_hdr->contents;
+             sym_shndx = (sym_shndx
+                          ? sym_shndx + ELF32_R_SYM (irel->r_info) : NULL);
+              bfd_elf32_swap_symbol_in (abfd,
+                                       extsyms + ELF32_R_SYM (irel->r_info),
+                                       sym_shndx, &isym);
+
+             if (isym.st_shndx == SHN_UNDEF)
+               sym_sec = bfd_und_section_ptr;
+             else if (isym.st_shndx == SHN_ABS)
+               sym_sec = bfd_abs_section_ptr;
+             else if (isym.st_shndx == SHN_COMMON)
+               sym_sec = bfd_com_section_ptr;
+             else
+               sym_sec = bfd_section_from_elf_index (abfd, isym.st_shndx);
+
+              if (sym_sec == sec)
+                {
+                  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)
+    {
+      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
+       {
+         stabcontents = (bfd_byte *) bfd_alloc (abfd, stab->_raw_size);
+         if (stabcontents == NULL)
+           return;
+         if (! bfd_get_section_contents (abfd, stab, stabcontents,
+                                         (file_ptr) 0, stab->_raw_size))
+           return;
+
+         /* We need to remember this.  */
+         elf_section_data (stab)->this_hdr.contents = stabcontents;
+       }
+
+      stabend = stabcontents + stab->_raw_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)
+               {
+                 Elf_Internal_Sym isym;
+                 asection *sym_sec;
+                 Elf_External_Sym_Shndx *sym_shndx;
+                 Elf_Internal_Shdr *shndx_hdr;
+                 
+                 /* A local symbol.  */
+                 shndx_hdr = &elf_tdata (abfd)->symtab_shndx_hdr;
+                 sym_shndx = (Elf_External_Sym_Shndx *) shndx_hdr->contents;
+                 sym_shndx = (sym_shndx
+                              ? sym_shndx + ELF32_R_SYM (irel->r_info)
+                              : NULL);
+                 
+                 bfd_elf32_swap_symbol_in (abfd,
+                                           (extsyms
+                                            + ELF32_R_SYM (irel->r_info)),
+                                           sym_shndx, &isym);
+                 
+                 if (isym.st_shndx == SHN_UNDEF)
+                   sym_sec = bfd_und_section_ptr;
+                 else if (isym.st_shndx == SHN_ABS)
+                   sym_sec = bfd_abs_section_ptr;
+                 else if (isym.st_shndx == SHN_COMMON)
+                   sym_sec = bfd_com_section_ptr;
+                 else
+                   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;
+
+                         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.  */
+                             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)
+                               {
+                                 /* 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.  */
+  shndx_hdr = &elf_tdata (abfd)->symtab_shndx_hdr;
+  sym_shndx = (Elf_External_Sym_Shndx *) shndx_hdr->contents;
+  esym = extsyms;
+  esymend = esym + symtab_hdr->sh_info;
+  for (; esym < esymend; esym++, sym_shndx = (sym_shndx ? sym_shndx + 1: NULL))
+    {
+      Elf_Internal_Sym isym;
+      Elf_External_Sym_Shndx dummy;
+
+      bfd_elf32_swap_symbol_in (abfd, esym, sym_shndx, &isym);
+
+      if (isym.st_shndx == shndx)
+        {
+          if (addr <= isym.st_value && isym.st_value < endaddr)
+            {
+              isym.st_value += count;
+              bfd_elf32_swap_symbol_out (abfd, &isym, esym, &dummy);
+            }
+        }
+    }
+
+  /* Now adjust the global symbols defined in this section.  */
+  shndx_hdr = &elf_tdata (abfd)->symtab_shndx_hdr;
+  sym_shndx = (Elf_External_Sym_Shndx *) shndx_hdr->contents;
+  esym = extsyms + symtab_hdr->sh_info;
+  esymend = extsyms + (symtab_hdr->sh_size / sizeof (Elf32_External_Sym));
+  for (index = 0; esym < esymend;
+       esym++, index++, sym_shndx = (sym_shndx ? sym_shndx + 1: NULL))
+    {
+      Elf_Internal_Sym isym;
+      struct elf_link_hash_entry *sym_hash;
+
+      bfd_elf32_swap_symbol_in (abfd, esym, sym_shndx, &isym);
+      sym_hash = elf_sym_hashes (abfd)[index];
+
+      if (isym.st_shndx == shndx
+          && (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)
+            {
+             Elf_External_Sym_Shndx dummy;
+
+             sym_hash->root.u.def.value += count;
+              bfd_elf32_swap_symbol_out (abfd, &isym, esym, &dummy);
+            }
+        }
+    }
+
+  return;
+}
+
+static boolean
+add_page_insn (abfd, sec, irel, misc)
+      bfd *abfd;
+      asection *sec;
+      Elf_Internal_Rela *irel;
+      struct misc *misc;
+{
+  /* Note that we've changed the relocs, section contents, etc.  */
+  elf_section_data (sec)->relocs = misc->irelbase;
+  misc->free_relocs = NULL;
+
+  elf_section_data (sec)->this_hdr.contents = misc->contents;
+  misc->free_contents = NULL;
+
+  misc->symtab_hdr->contents = (bfd_byte *) misc->extsyms;
+  misc->free_extsyms = NULL;
+
+  /* Add the PAGE insn.  */
+  if (! ip2k_elf_relax_add_bytes (abfd, sec, irel->r_offset,
+                                  page_opcode,
+                                  sizeof (page_opcode),
+                                 sizeof (page_opcode)))
+    return false;
+  else
+    {
+       Elf32_Internal_Rela * jrel = irel - 1;
+
+       /* Add relocation for PAGE insn added.  */
+       if (ELF32_R_TYPE (jrel->r_info) != R_IP2K_NONE)
+        {
+          bfd_byte code0, code1;
+          char *msg = NULL;
+          
+          /* Get the opcode.  */
+          code0 = bfd_get_8 (abfd, misc->contents + irel->r_offset);
+          code1 = bfd_get_8 (abfd, misc->contents + irel->r_offset + 1);
+
+          if (IS_JMP_OPCODE (code0, code1))
+            msg = "\tJMP instruction missing a preceeding PAGE instruction in %s\n\n";
+
+          else if (IS_CALL_OPCODE (code0, code1))
+            msg = "\tCALL instruction missing a preceeding PAGE instruction in %s\n\n";
+
+          if (msg)
+            {
+              fprintf (stderr, "\n\t *** LINKER RELAXATION failure ***\n");
+              fprintf (stderr, msg, sec->owner->filename);
+            }
+
+          return false;
+        }
+
+       jrel->r_addend = irel->r_addend;
+       jrel->r_offset = irel->r_offset - sizeof (page_opcode);
+       jrel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
+                                    R_IP2K_PAGE3);
+     }
+
+   return true;
+}
+
+/* Insert bytes into a section while relaxing.  */
+
+static boolean
+ip2k_elf_relax_add_bytes (abfd, sec, addr, bytes, count, noadj)
+     bfd *abfd;
+     asection *sec;
+     bfd_vma addr;
+     const bfd_byte *bytes;
+     int count;
+     int noadj;
+{
+  bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
+  bfd_vma endaddr = sec->_cooked_size;
+
+  /* Make room to insert the bytes.  */
+  memmove (contents + addr + count, contents + addr, endaddr - addr);
+
+  /* Insert the bytes into the section.  */
+  memcpy  (contents + addr, bytes, count);
+  
+  sec->_cooked_size += count;
+
+  adjust_all_relocations (abfd, sec, addr, endaddr, count, noadj);
+  return true;
+}
+
+/* Delete some bytes from a section while relaxing.  */
+
+static 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->_cooked_size;
+
+  /* Actually delete the bytes.  */
+  memmove (contents + addr, contents + addr + count,
+          endaddr - addr - count);
+
+  sec->_cooked_size -= count;
+
+  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.  */
+
+/* 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;
+     Elf32_Internal_Rela * dst;
+{
+  unsigned int r_type;
+
+  r_type = ELF32_R_TYPE (dst->r_info);
+  switch (r_type)
+    {
+    default:
+      cache_ptr->howto = & ip2k_elf_howto_table [r_type];
+      break;
+    }
+}
+
+/* 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;
+{
+  bfd_reloc_status_type r = bfd_reloc_ok;
+
+  switch (howto->type)
+    {
+      /* Handle data space relocations.  */
+    case R_IP2K_FR9:
+    case R_IP2K_BANK:
+      if ((relocation & IP2K_DATA_MASK) == IP2K_DATA_VALUE)
+       relocation &= ~IP2K_DATA_MASK;
+      else
+       r = bfd_reloc_notsupported;
+      break;
+
+    case R_IP2K_LO8DATA:
+    case R_IP2K_HI8DATA:
+    case R_IP2K_EX8DATA:
+      break;
+
+      /* Handle insn space relocations.  */
+    case R_IP2K_ADDR16CJP:
+    case R_IP2K_PAGE3:
+    case R_IP2K_LO8INSN:
+    case R_IP2K_HI8INSN:
+    case R_IP2K_PC_SKIP:
+      if ((relocation & IP2K_INSN_MASK) == IP2K_INSN_VALUE)
+       relocation &= ~IP2K_INSN_MASK;
+      else
+       r = bfd_reloc_notsupported;
+      break;
+
+    case R_IP2K_16:
+      /* If this is a relocation involving a TEXT
+        symbol, reduce it to a word address.  */
+      if ((relocation & IP2K_INSN_MASK) == IP2K_INSN_VALUE)
+       howto = &ip2k_elf_howto_table[ (int) R_IP2K_TEXT];
+      break;
+
+      /* Pass others through.  */
+    default:
+      break;
+    }
+
+  /* Only install relocation if above tests did not disqualify it.  */
+  if (r == bfd_reloc_ok)
+    r = _bfd_final_link_relocate (howto, input_bfd, input_section,
+                                 contents, rel->r_offset,
+                                 relocation, rel->r_addend);
+
+  return r;
+}
+
+/* Relocate a IP2K ELF section.
+   There is some attempt to make this function usable for many architectures,
+   both USE_REL and USE_RELA ['twould be nice if such a critter existed],
+   if only to serve as a learning tool.
+
+   The RELOCATE_SECTION function is called by the new ELF backend linker
+   to handle the relocations for a section.
+
+   The relocs are always passed as Rela structures; if the section
+   actually uses Rel structures, the r_addend field will always be
+   zero.
+
+   This function is responsible for adjusting the section contents as
+   necessary, and (if using Rela relocs and generating a relocateable
+   output file) adjusting the reloc addend as necessary.
+
+   This function does not have to worry about setting the reloc
+   address or the reloc symbol index.
+
+   LOCAL_SYMS is a pointer to the swapped in local symbols.
+
+   LOCAL_SECTIONS is an array giving the section in the input file
+   corresponding to the st_shndx field of each local symbol.
+
+   The global hash table entry for the global symbols can be found
+   via elf_sym_hashes (input_bfd).
+
+   When generating relocateable output, this function must handle
+   STB_LOCAL/STT_SECTION symbols specially.  The output symbol is
+   going to be the section symbol corresponding to the output
+   section, which means that the addend must be adjusted
+   accordingly.  */
+
+static 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;
+{
+  Elf_Internal_Shdr *           symtab_hdr;
+  struct elf_link_hash_entry ** sym_hashes;
+  Elf_Internal_Rela *           rel;
+  Elf_Internal_Rela *           relend;
+
+  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;
+      struct elf_link_hash_entry * h;
+      bfd_vma                      relocation;
+      bfd_reloc_status_type        r;
+      const char *                 name = NULL;
+      int                          r_type;
+      
+      r_type = ELF32_R_TYPE (rel->r_info);
+
+      r_symndx = ELF32_R_SYM (rel->r_info);
+
+      if (info->relocateable)
+       {
+         /* This is a relocateable link.  We don't have to change
+             anything, unless the reloc is against a section symbol,
+             in which case we have to adjust according to where the
+             section symbol winds up in the output section.  */
+         if (r_symndx < symtab_hdr->sh_info)
+           {
+             sym = local_syms + r_symndx;
+             
+             if (ELF_ST_TYPE (sym->st_info) == STT_SECTION)
+               {
+                 sec = local_sections [r_symndx];
+                 rel->r_addend += sec->output_offset + sym->st_value;
+               }
+           }
+
+         continue;
+       }
+
+      /* This is a final link.  */
+      howto  = ip2k_elf_howto_table + ELF32_R_TYPE (rel->r_info);
+      h      = NULL;
+      sym    = NULL;
+      sec    = NULL;
+      
+      if (r_symndx < symtab_hdr->sh_info)
+       {
+         sym = local_syms + r_symndx;
+         sec = local_sections [r_symndx];
+         relocation = BASEADDR (sec) + sym->st_value;
+         
+         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;
+       }
+      else
+       {
+         h = sym_hashes [r_symndx - symtab_hdr->sh_info];
+         
+         while (h->root.type == bfd_link_hash_indirect
+                || h->root.type == bfd_link_hash_warning)
+           h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+         name = h->root.root.string;
+         
+         if (h->root.type == bfd_link_hash_defined
+             || h->root.type == bfd_link_hash_defweak)
+           {
+             sec = h->root.u.def.section;
+             relocation = h->root.u.def.value + BASEADDR (sec);
+           }
+         else if (h->root.type == bfd_link_hash_undefweak)
+           {
+             relocation = 0;
+           }
+         else
+           {
+             if (! ((*info->callbacks->undefined_symbol)
+                    (info, h->root.root.string, input_bfd,
+                     input_section, rel->r_offset,
+                    (! info->shared || info->no_undefined))))
+               return false;
+             relocation = 0;
+           }
+       }
+
+      /* 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;
+
+         switch (r)
+           {
+           case bfd_reloc_overflow:
+             r = info->callbacks->reloc_overflow
+               (info, name, howto->name, (bfd_vma) 0,
+                input_bfd, input_section, rel->r_offset);
+             break;
+             
+           case bfd_reloc_undefined:
+             r = info->callbacks->undefined_symbol
+               (info, name, input_bfd, input_section, rel->r_offset, true);
+             break;
+             
+           case bfd_reloc_outofrange:
+             msg = _("internal error: out of range error");
+             break;
+
+             /* This is how ip2k_final_link_relocate tells us of a non-kosher
+                 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");
+             break;
+
+           case bfd_reloc_dangerous:
+             msg = _("internal error: dangerous relocation");
+             break;
+
+           default:
+             msg = _("internal error: unknown error");
+             break;
+           }
+
+         if (msg)
+           r = info->callbacks->warning
+             (info, msg, name, input_bfd, input_section, rel->r_offset);
+
+         if (! r)
+           return false;
+       }
+    }
+
+  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))
+      {
+#if 0 
+      case R_IP2K_GNU_VTINHERIT:
+      case R_IP2K_GNU_VTENTRY:
+        break;
+#endif
+
+      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 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_NAME  "elf32-ip2k"
+
+#define ELF_ARCH        bfd_arch_ip2k
+#define ELF_MACHINE_CODE EM_IP2K
+#define ELF_MAXPAGESIZE  1 /* No pages on the IP2K */
+
+#undef USE_REL
+#define USE_RELA
+
+#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_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_relax_section            ip2k_elf_relax_section
+
+
+#include "elf32-target.h"
+
index bec8004..f7c8cc1 100644 (file)
@@ -1120,6 +1120,20 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_390_GOT64",
   "BFD_RELOC_390_PLT64",
   "BFD_RELOC_390_GOTENT",
+  "BFD_RELOC_IP2K_FR9",
+  "BFD_RELOC_IP2K_BANK",
+  "BFD_RELOC_IP2K_ADDR16CJP",
+  "BFD_RELOC_IP2K_PAGE3",
+  "BFD_RELOC_IP2K_LO8DATA",
+  "BFD_RELOC_IP2K_HI8DATA",
+  "BFD_RELOC_IP2K_EX8DATA",
+  "BFD_RELOC_IP2K_LO8INSN",
+  "BFD_RELOC_IP2K_HI8INSN",
+  "BFD_RELOC_IP2K_PC_SKIP",
+  "BFD_RELOC_IP2K_TEXT",
+  "BFD_RELOC_IP2K_FR_OFFSET",
+  "BFD_RELOC_VPE4KMATH_DATA",
+  "BFD_RELOC_VPE4KMATH_INSN",
   "BFD_RELOC_VTABLE_INHERIT",
   "BFD_RELOC_VTABLE_ENTRY",
   "BFD_RELOC_IA64_IMM14",
index 2e10dce..5f76db1 100644 (file)
@@ -3123,6 +3123,55 @@ ENUMDOC
   32 bit rel. offset to GOT entry.
 
 ENUM
+  BFD_RELOC_IP2K_FR9
+ENUMDOC
+  Scenix IP2K - 9-bit register number / data address
+ENUM
+  BFD_RELOC_IP2K_BANK
+ENUMDOC
+  Scenix IP2K - 4-bit register/data bank number
+ENUM
+  BFD_RELOC_IP2K_ADDR16CJP
+ENUMDOC
+  Scenix IP2K - low 13 bits of instruction word address
+ENUM
+  BFD_RELOC_IP2K_PAGE3
+ENUMDOC
+  Scenix IP2K - high 3 bits of instruction word address
+ENUM
+  BFD_RELOC_IP2K_LO8DATA
+ENUMX
+  BFD_RELOC_IP2K_HI8DATA
+ENUMX
+  BFD_RELOC_IP2K_EX8DATA
+ENUMDOC
+  Scenix IP2K - ext/low/high 8 bits of data address
+ENUM
+  BFD_RELOC_IP2K_LO8INSN
+ENUMX
+  BFD_RELOC_IP2K_HI8INSN
+ENUMDOC
+  Scenix IP2K - low/high 8 bits of instruction word address
+ENUM
+  BFD_RELOC_IP2K_PC_SKIP
+ENUMDOC
+  Scenix IP2K - even/odd PC modifier to modify snb pcl.0
+ENUM
+  BFD_RELOC_IP2K_TEXT
+ENUMDOC
+  Scenix IP2K - 16 bit word address in text section.
+ENUM
+  BFD_RELOC_IP2K_FR_OFFSET
+ENUMDOC
+  Scenix IP2K - 7-bit sp or dp offset
+ENUM
+  BFD_RELOC_VPE4KMATH_DATA
+ENUMX
+  BFD_RELOC_VPE4KMATH_INSN
+ENUMDOC
+  Scenix VPE4K coprocessor - data/insn-space addressing
+
+ENUM
   BFD_RELOC_VTABLE_INHERIT
 ENUMX
   BFD_RELOC_VTABLE_ENTRY
index d03eb02..13415ad 100644 (file)
@@ -526,6 +526,7 @@ extern const bfd_target bfd_elf32_i860_vec;
 extern const bfd_target bfd_elf32_i960_vec;
 extern const bfd_target bfd_elf32_ia64_big_vec;
 extern const bfd_target bfd_elf32_ia64_hpux_big_vec;
+extern const bfd_target bfd_elf32_ip2k_vec;
 extern const bfd_target bfd_elf32_little_generic_vec;
 extern const bfd_target bfd_elf32_littlearc_vec;
 extern const bfd_target bfd_elf32_littlearm_oabi_vec;
@@ -790,6 +791,7 @@ static const bfd_target * const _bfd_target_vector[] = {
        &bfd_elf32_ia64_big_vec,
 #endif
        &bfd_elf32_ia64_hpux_big_vec,
+       &bfd_elf32_ip2k_vec,
        &bfd_elf32_little_generic_vec,
        &bfd_elf32_littlearc_vec,
        &bfd_elf32_littlearm_oabi_vec,
index 3e64a32..e7b8b76 100644 (file)
@@ -1,3 +1,15 @@
+2002-07-15  Denis Chertykov  <denisc@overta.ru>
+           Frank Ch. Eigler  <fche@redhat.com>
+           Ben Elliston  <bje@redhat.com>
+           Alan Lehotsky  <alehotsky@cygnus.com>
+           John Healy  <jhealy@redhat.com>
+           Graham Stott  <grahams@redhat.com>
+           Jeff Johnston  <jjohnstn@redhat.com>
+
+        * common.h (EM_IP2K): New macro.
+       (EM_IP2K_OLD): New macro.
+        * ip2k.h: New file.
+
 2002-07-01  Matt Thomas  <matt@3am-software.com>
 
        * vax.h: Rename EF_* to EF_VAX_*.
index 4e1b502..86b82fe 100644 (file)
@@ -176,6 +176,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #define EM_OPENRISC    92      /* OpenRISC 32-bit embedded processor */
 #define EM_ARC_A5      93      /* ARC Cores Tangent-A5 */
 #define EM_XTENSA      94      /* Tensilica Xtensa Architecture */
+#define EM_IP2K               101      /* Ubicom IP2022 micro controller */
 
 /* If it is necessary to assign new unofficial EM_* values, please pick large
    random numbers (0x8523, 0xa7f2, etc.) to minimize the chances of collision
diff --git a/include/elf/ip2k.h b/include/elf/ip2k.h
new file mode 100644 (file)
index 0000000..c331b72
--- /dev/null
@@ -0,0 +1,62 @@
+/* IP2xxx ELF support for BFD.
+   Copyright (C) 2000, 2002 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
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifndef _ELF_IP2K_H
+#define _ELF_IP2K_H
+
+#include "elf/reloc-macros.h"
+
+/* Relocations.  */
+START_RELOC_NUMBERS (elf_ip2k_reloc_type)
+  RELOC_NUMBER (R_IP2K_NONE, 0)
+  RELOC_NUMBER (R_IP2K_16, 1)
+  RELOC_NUMBER (R_IP2K_32, 2)
+  RELOC_NUMBER (R_IP2K_FR9, 3)
+  RELOC_NUMBER (R_IP2K_BANK, 4)
+  RELOC_NUMBER (R_IP2K_ADDR16CJP, 5)
+  RELOC_NUMBER (R_IP2K_PAGE3, 6)
+  RELOC_NUMBER (R_IP2K_LO8DATA, 7)
+  RELOC_NUMBER (R_IP2K_HI8DATA, 8)
+  RELOC_NUMBER (R_IP2K_LO8INSN, 9)
+  RELOC_NUMBER (R_IP2K_HI8INSN, 10)
+  RELOC_NUMBER (R_IP2K_PC_SKIP, 11)
+  RELOC_NUMBER (R_IP2K_TEXT, 12)
+  RELOC_NUMBER (R_IP2K_FR_OFFSET, 13)
+  RELOC_NUMBER (R_IP2K_EX8DATA, 14)
+END_RELOC_NUMBERS(R_IP2K_max)
+
+
+/* Define the data & instruction memory discriminator.  In a linked
+   executable, an symbol should be deemed to point to an instruction
+   if ((address & IP2K_INSN_MASK) == IP2K_INSN_VALUE), and similarly
+   for the data space.  See also `ld/emulparams/elf32ip2k.sh'.  */
+/* ??? Consider extending the _MASK values to include all the
+   intermediate bits that must be zero due to the limited physical
+   memory size on the IP2K.  */
+
+#define IP2K_DATA_MASK   0xff000000
+#define IP2K_DATA_VALUE  0x01000000
+#define IP2K_INSN_MASK   0xff000000
+#define IP2K_INSN_VALUE  0x02000000
+
+/* The location of the memory mapped hardware stack.  */
+#define IP2K_STACK_VALUE 0x0f000000
+#define IP2K_STACK_SIZE  0x20
+
+#endif /* _ELF_IP2K_H */
index e14b2f4..876110f 100644 (file)
@@ -1,3 +1,15 @@
+2002-07-16  Denis Chertykov  <denisc@overta.ru>
+           Nick Clifton  <nickc@redhat.com>
+           Frank Ch. Eigler  <fche@redhat.com>
+           John Healy  <jhealy@redhat.com>
+
+       * configure.tgt: Add support for ip2k-elf.
+       * Makefile.am: Add support for ip2k-elf.
+       * Makefile.in: Regenerate.
+       * emulparams/elf32ip2k.sh: New file.
+       * scripttempl/ip2k.sc: New file
+       * NEWS: Mention support for new port.
+
 2002-07-16  Nick Clifton  <nickc@cambridge.redhat.com>
 
        * NEWS: Add 'Changes in 2.13'.
index 8247d25..fbdb946 100644 (file)
@@ -161,6 +161,7 @@ ALL_EMULATIONS = \
        eelf32fr30.o \
        eelf32frv.o \
        eelf32i370.o \
+       eelf32ip2k.o \
        eelf32l4300.o \
        eelf32lmip.o \
        eelf32lppc.o \
@@ -626,6 +627,9 @@ eelf64lppc.c: $(srcdir)/emulparams/elf64lppc.sh \
 eelf32i370.c: $(srcdir)/emulparams/elf32i370.sh \
   $(srcdir)/emultempl/elf32.em $(srcdir)/scripttempl/elfi370.sc ${GEN_DEPENDS}
        ${GENSCRIPTS} elf32i370 "$(tdir_elf32i370)"
+eelf32ip2k.c: $(srcdir)/emulparams/elf32ip2k.sh \
+  $(srcdir)/emultempl/elf32.em $(srcdir)/scripttempl/ip2k.sc ${GEN_DEPENDS}
+       ${GENSCRIPTS} elf32ip2k "$(tdir_ip2k)"
 eelf64alpha.c: $(srcdir)/emulparams/elf64alpha.sh \
   $(srcdir)/emultempl/elf32.em $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
        ${GENSCRIPTS} elf64alpha "$(tdir_elf64alpha)"
index f9d9fa4..079d334 100644 (file)
@@ -272,6 +272,7 @@ ALL_EMULATIONS = \
        eelf32fr30.o \
        eelf32frv.o \
        eelf32i370.o \
+       eelf32ip2k.o \
        eelf32l4300.o \
        eelf32lmip.o \
        eelf32lppc.o \
@@ -526,7 +527,7 @@ deffilep.c ldgram.c ldlex.c
 
 DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
 
-TAR = tar
+TAR = gtar
 GZIP_ENV = --best
 SOURCES = $(ld_new_SOURCES) $(EXTRA_ld_new_SOURCES)
 OBJECTS = $(ld_new_OBJECTS)
@@ -1349,6 +1350,9 @@ eelf64lppc.c: $(srcdir)/emulparams/elf64lppc.sh \
 eelf32i370.c: $(srcdir)/emulparams/elf32i370.sh \
   $(srcdir)/emultempl/elf32.em $(srcdir)/scripttempl/elfi370.sc ${GEN_DEPENDS}
        ${GENSCRIPTS} elf32i370 "$(tdir_elf32i370)"
+eelf32ip2k.c: $(srcdir)/emulparams/elf32ip2k.sh \
+  $(srcdir)/emultempl/elf32.em $(srcdir)/scripttempl/ip2k.sc ${GEN_DEPENDS}
+       ${GENSCRIPTS} elf32ip2k "$(tdir_ip2k)"
 eelf64alpha.c: $(srcdir)/emulparams/elf64alpha.sh \
   $(srcdir)/emultempl/elf32.em $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
        ${GENSCRIPTS} elf64alpha "$(tdir_elf64alpha)"
diff --git a/ld/NEWS b/ld/NEWS
index 383bd8c..533041c 100644 (file)
--- a/ld/NEWS
+++ b/ld/NEWS
@@ -1,4 +1,7 @@
 -*- text -*-
+
+* IP2K support added by Denis Chertykov.
+
 Changes in 2.13:
 
 * Support for the Fujitsu FRV architecture added by Red Hat. Models for FR400
index c1d7620..f923346 100644 (file)
@@ -494,6 +494,7 @@ mcore-*-elf)                targ_emul=elf32mcore ;;
 s390x-*-linux*)         targ_emul=elf64_s390 ;;
 s390-*-linux*)          targ_emul=elf_s390 ;;
 *-*-ieee*)             targ_emul=vanilla ;;
+ip2k-*-elf)            targ_emul=elf32ip2k ;;
 
 *)
   echo 2>&1 "*** ld does not support target ${targ}"
diff --git a/ld/emulparams/elf32ip2k.sh b/ld/emulparams/elf32ip2k.sh
new file mode 100644 (file)
index 0000000..be2ce33
--- /dev/null
@@ -0,0 +1,19 @@
+MACHINE=
+SCRIPT_NAME=elf
+OUTPUT_FORMAT="elf32-ip2k"
+# See also `include/elf/ip2k.h'
+DATA_ADDR=0x01000100
+TEXT_START_ADDR=0x02010000
+GPR_START_ADDR=0x1000080
+GPR_SIZE=0x7F
+GRP_MEMORY=gpr
+ARCH=ip2k
+ENTRY=__start
+EMBEDDED=yes
+ELFSIZE=32
+MAXPAGESIZE=256
+WRITABLE_RODATA=
+OTHER_RELOCATING_SECTIONS='PROVIDE (__stack = 0x01000FFF);'
+OTHER_READONLY_SECTIONS='.pram 0x2000000 : { *(.pram) } .reset 0x201FFE0 : { *(.reset) }'
+OTHER_READWRITE_SECTIONS='.gpr 0x1000080 : { *(.gpr) }'
+
diff --git a/ld/scripttempl/ip2k.sc b/ld/scripttempl/ip2k.sc
new file mode 100644 (file)
index 0000000..d7826ca
--- /dev/null
@@ -0,0 +1,150 @@
+cat << EOF
+OUTPUT_FORMAT("elf32-ip2k", "elf32-ip2k", "elf32-ip2k")
+OUTPUT_ARCH(ip2k)
+ENTRY(_start)
+SEARCH_DIR(.);
+
+/* IP2022 default linker script.  */
+
+MEMORY
+{
+       D_GPR    : org = 0x01000080, len = 128
+        D_RAM    : org = 0x01000100, len = 4K - 256
+        P_RAM    : org = 0x02000000, len = 16K
+        P_ROM    : org = 0x02010000, len = 64K - 32
+        P_RESET  : org = 0x0201FFE0, len = 32
+        P_CONFIG : org = 0x02020000, len = 128
+}
+
+SECTIONS
+{
+       /* Allocated memory end markers
+          (initialized to start of appropiate memory address).  */
+       __data_end  = 0x01000100;
+       __pram_end  = 0x02000000;
+       __flash_end = 0x02010000;
+
+       /* Global general purpose registers in direct addressing range.  */
+       .gpr 0x01000080 :
+       {
+               *(.gpr)
+       } >D_GPR
+
+       /* Pre-allocated, pre-initialized data memory.  */
+       __data_run_begin = __data_end;
+       __data_load_begin = (__flash_end + 1) & 0xFFFFFFFE;
+       .data __data_run_begin : AT (__data_load_begin)
+       {
+               * (.data);
+               * (.rodata)
+       } >D_RAM
+       __data_run_end  = __data_run_begin  + SIZEOF(.data);
+       __data_load_end = __data_load_begin + SIZEOF(.data);
+       __data_end      = __data_run_end;
+       __flash_end     = __data_load_end;
+
+       /* Pre-allocated, uninitialized data memory.  */
+       __bss_begin = __data_end;
+       .bss __bss_begin :
+       {
+               * (.bss)
+       } >D_RAM
+       __bss_end  = __bss_begin + SIZEOF(.bss);
+       __data_end = __bss_end;
+
+       /* Pre-allocated PRAM data memory.  */
+       __pram_data_begin = (__pram_end + 1) & 0xFFFFFFFE;
+       .pram_data __pram_data_begin :
+       {
+               * (.pram_data)
+       } >P_RAM
+       __pram_data_end = __pram_data_begin + SIZEOF(.pram_data);
+       __pram_end      = __pram_data_end;
+
+       /* PRAM code.  */
+       __pram_run_begin  = (__pram_end + 1) & 0xFFFFFFFE;
+       __pram_load_begin = (__flash_end + 1) & 0xFFFFFFFE;
+       .pram __pram_run_begin : AT (__pram_load_begin)
+       {
+               * (.pram)
+       } >P_RAM
+       __pram_run_end  = __pram_run_begin  + SIZEOF(.pram);
+       __pram_load_end = __pram_load_begin + SIZEOF(.pram);
+
+       __pram_load_shift = ((__pram_run_begin - __pram_load_begin) & 0x1FFFF) | 0x02000000;
+       __pram_end  = __pram_run_end;
+       __flash_end = __pram_load_end;
+
+       /* PRAM overlay code.  */
+       __pram_overlay_run_start  = (__pram_end  + 1) & 0xFFFFFFFE;
+       __pram_overlay_load_start = (__flash_end + 1) & 0xFFFFFFFE;     
+       OVERLAY __pram_overlay_run_start : AT (__pram_overlay_load_start)
+       {
+               .pram1 { */overlay1/* (.pram); * (.pram1) }
+               .pram2 { */overlay2/* (.pram); * (.pram2) }
+       } >P_RAM
+       __pram_overlay_run_end = .;
+       __pram_overlay_load_end = __pram_overlay_load_start + SIZEOF(.pram1) + SIZEOF(.pram2);
+       __pram_end  = __pram_overlay_run_end;
+       __flash_end = __pram_overlay_load_end;
+
+       /* Flash code.  */
+       __text_begin = (__flash_end + 1) & 0xFFFFFFFE;
+       .text __text_begin :
+       {
+               * (.text);
+               * (.text.libgcc)
+       } >P_ROM = 0xffff
+       __text_end  = __text_begin + SIZEOF(.text);
+       __flash_end = __text_end;
+
+       /* Strings.  */
+       __strings_begin = (__flash_end + 1) & 0xFFFFFFFE;
+       .strings __strings_begin :
+       {
+               * (strings);
+               * (.progmem.data)
+       } >P_ROM = 0xffff
+       __strings_end = __strings_begin + SIZEOF (.strings);
+       __flash_end   = __strings_end;
+
+       .ctors : { * (.ctors) } > P_ROM
+       .dtors : { * (.dtors) } > P_ROM
+
+       /* Reset code.  */
+       .reset  : { * (.reset)  } >P_RESET  = 0xffff
+
+       /* Configuration block.  */
+       .config : { * (.config) } >P_CONFIG = 0xffff
+
+       /* Stack.  */
+       PROVIDE (__stack = 0x01000FFF);
+
+       /* Stabs debugging sections.  */
+       .stab           0 : { *(.stab) }
+       .stabstr        0 : { *(.stabstr) }
+       .stab.excl      0 : { *(.stab.excl) }
+       .stab.exclstr   0 : { *(.stab.exclstr) }
+       .stab.index     0 : { *(.stab.index) }
+       .stab.indexstr  0 : { *(.stab.indexstr) }
+       .comment        0 : { *(.comment) }
+
+       /* DWARF 1.  */
+       .debug          0 : { *(.debug) }
+       .line           0 : { *(.line) }
+       /* GNU DWARF 1 extensions.  */
+       .debug_srcinfo  0 : { *(.debug_srcinfo) }
+       .debug_sfnames  0 : { *(.debug_sfnames) }
+       /* DWARF 1.1 and DWARF 2.  */
+       .debug_aranges  0 : { *(.debug_aranges) }
+       .debug_pubnames 0 : { *(.debug_pubnames) }
+       /* DWARF 2.  */
+       .debug_info     0 : { *(.debug_info) *(.gnu.linkonce.wi.*) }
+       .debug_abbrev   0 : { *(.debug_abbrev) }
+       .debug_line     0 : { *(.debug_line) }
+       .debug_frame    0 : { *(.debug_frame) }
+       .debug_str      0 : { *(.debug_str) }
+       .debug_loc      0 : { *(.debug_loc) }
+       .debug_macinfo  0 : { *(.debug_macinfo) }
+}
+EOF