* configure.in: Add xcofflink.o to pmac_xcoff_vec.
authorStan Shebs <shebs@codesourcery.com>
Wed, 25 Oct 1995 00:54:55 +0000 (00:54 +0000)
committerStan Shebs <shebs@codesourcery.com>
Wed, 25 Oct 1995 00:54:55 +0000 (00:54 +0000)
* configure: Rebuild.
* mpw-config.in: Add xcofflink.c.o to powerpc-apple-macos.
* coff-pmac.c: Include coff-rs6000.c instead of duplicating its
contents.
(pmac_xcoff_vec): Update to use new xcoff support.
* coff-rs6000.c (xcoff_generic_stat_arch_elt): Make static.
(xcoff_write_armap): Declare buf as unsigned char.
* xcofflink.c (xcoff_link_add_symbols): Declare a local as PTR.

* mpw-make.sed: Generalize subdir_do edit.

bfd/ChangeLog
bfd/coff-pmac.c [new file with mode: 0644]
bfd/coff-rs6000.c
bfd/configure
bfd/configure.in
bfd/mpw-config.in
bfd/mpw-make.sed
bfd/xcofflink.c [new file with mode: 0644]

index 0ae98413f56f7350af26d87669b93073716343d8..cd81ba34a4c8d27053d2bdc12933206650fdd74a 100644 (file)
@@ -1,3 +1,17 @@
+Tue Oct 24 17:44:20 1995  Stan Shebs  <shebs@andros.cygnus.com>
+
+       * configure.in: Add xcofflink.o to pmac_xcoff_vec.
+       * configure: Rebuild.
+       * mpw-config.in: Add xcofflink.c.o to powerpc-apple-macos.
+       * coff-pmac.c: Include coff-rs6000.c instead of duplicating its
+       contents.
+       (pmac_xcoff_vec): Update to use new xcoff support.
+       * coff-rs6000.c (xcoff_generic_stat_arch_elt): Make static.
+       (xcoff_write_armap): Declare buf as unsigned char.
+       * xcofflink.c (xcoff_link_add_symbols): Declare a local as PTR.
+
+       * mpw-make.sed: Generalize subdir_do edit.
+       
 Tue Oct 24 10:25:01 1995  Jeffrey A Law  (law@cygnus.com)
 
        * hppabsd-core.c (make_bfd_asection): Initialize asect->filepos
diff --git a/bfd/coff-pmac.c b/bfd/coff-pmac.c
new file mode 100644 (file)
index 0000000..cc2c0b7
--- /dev/null
@@ -0,0 +1,79 @@
+/* BFD back-end for Apple et al PowerPC Mac "XCOFF" files.
+   Copyright 1990, 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+   FIXME: Can someone provide a transliteration of this name into ASCII?
+   Using the following chars caused a compiler warning on HIUX (so I replaced
+   them with octal escapes), and isn't useful without an understanding of what
+   character set it is.
+   Written by Metin G. Ozisik, Mimi Ph\373\364ng-Th\345o V\365, 
+     and John Gilmore.
+   Contributed by IBM Corporation and Cygnus Support.
+
+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.  */
+
+/* (eventually lose this) */
+/* coffcode.h modifies itself based on this flag.  */
+#define RS6000COFF_C 1
+
+/* Tweak coffcode.h based on this being a PowerMac instead of RS/6000. */
+
+#define POWERMAC
+
+#include "coff-rs6000.c"
+
+\f
+const bfd_target pmac_xcoff_vec =
+{
+  "xcoff-powermac",            /* name */
+  bfd_target_coff_flavour,     
+  true,                                /* data byte order is big */
+  true,                                /* header byte order is big */
+
+  (HAS_RELOC | EXEC_P |                /* object flags */
+   HAS_LINENO | HAS_DEBUG | DYNAMIC |
+   HAS_SYMS | HAS_LOCALS | WP_TEXT),
+
+  (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC), /* section flags */
+  0,                           /* leading char */
+  '/',                         /* ar_pad_char */
+  15,                          /* ar_max_namelen??? FIXMEmgo */
+
+  bfd_getb64, bfd_getb_signed_64, bfd_putb64,
+     bfd_getb32, bfd_getb_signed_32, bfd_putb32,
+     bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* data */
+  bfd_getb64, bfd_getb_signed_64, bfd_putb64,
+     bfd_getb32, bfd_getb_signed_32, bfd_putb32,
+     bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* hdrs */
+
+  {_bfd_dummy_target, coff_object_p,   /* bfd_check_format */
+     xcoff_archive_p, _bfd_dummy_target},
+  {bfd_false, coff_mkobject, _bfd_generic_mkarchive, /* bfd_set_format */
+     bfd_false},
+  {bfd_false, coff_write_object_contents,      /* bfd_write_contents */
+     xcoff_write_archive_contents, bfd_false},
+
+     BFD_JUMP_TABLE_GENERIC (coff),
+     BFD_JUMP_TABLE_COPY (coff),
+     BFD_JUMP_TABLE_CORE (_bfd_nocore),
+     BFD_JUMP_TABLE_ARCHIVE (xcoff),
+     BFD_JUMP_TABLE_SYMBOLS (coff),
+     BFD_JUMP_TABLE_RELOCS (coff),
+     BFD_JUMP_TABLE_WRITE (coff),
+     BFD_JUMP_TABLE_LINK (_bfd_xcoff),
+     BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
+
+  COFF_SWAP_TABLE,
+};
index e7766ec1ad7377327ae43e582f957e875a3bbd79..acdd5045c3e078251fdb6902aa81fd63aded16ab 100644 (file)
@@ -36,6 +36,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #include "coff/rs6000.h"
 #include "libcoff.h"
 
+/* The main body of code is in coffcode.h.  */
+
+static boolean xcoff_mkobject PARAMS ((bfd *));
+static boolean xcoff_copy_private_bfd_data PARAMS ((bfd *, bfd *));
+static void xcoff_rtype2howto
+  PARAMS ((arelent *, struct internal_reloc *));
+static reloc_howto_type *xcoff_reloc_type_lookup
+  PARAMS ((bfd *, bfd_reloc_code_real_type));
 static boolean xcoff_slurp_armap PARAMS ((bfd *));
 static const bfd_target *xcoff_archive_p PARAMS ((bfd *));
 static PTR xcoff_read_ar_hdr PARAMS ((bfd *));
@@ -45,18 +53,72 @@ static const char *normalize_filename PARAMS ((bfd *));
 static boolean xcoff_write_armap
   PARAMS ((bfd *, unsigned int, struct orl *, unsigned int, int));
 static boolean xcoff_write_archive_contents PARAMS ((bfd *));
+\f
+/* We use our own tdata type.  Its first field is the COFF tdata type,
+   so the COFF routines are compatible.  */
 
-/* The main body of code is in coffcode.h.  */
+static boolean
+xcoff_mkobject (abfd)
+     bfd *abfd;
+{
+  coff_data_type *coff;
 
-#define COFF_DEFAULT_SECTION_ALIGNMENT_POWER (3)
+  abfd->tdata.xcoff_obj_data =
+    ((struct xcoff_tdata *)
+     bfd_zalloc (abfd, sizeof (struct xcoff_tdata)));
+  if (abfd->tdata.xcoff_obj_data == NULL)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      return false;
+    }
+  coff = coff_data (abfd);
+  coff->symbols = (coff_symbol_type *) NULL;
+  coff->conversion_table = (unsigned int *) NULL;
+  coff->raw_syments = (struct coff_ptr_struct *) NULL;
+  coff->relocbase = 0;
+
+  xcoff_data (abfd)->modtype = ('1' << 8) | 'L';
+
+  /* We set cputype to -1 to indicate that it has not been
+     initialized.  */
+  xcoff_data (abfd)->cputype = -1;
+
+  xcoff_data (abfd)->csects = NULL;
+  xcoff_data (abfd)->debug_indices = NULL;
+
+  return true;
+}
 
+/* Copy XCOFF data from one BFD to another.  */
+
+static boolean
+xcoff_copy_private_bfd_data (ibfd, obfd)
+     bfd *ibfd;
+     bfd *obfd;
+{
+  struct xcoff_tdata *ix, *ox;
+
+  if (ibfd->xvec != obfd->xvec)
+    return true;
+  ix = xcoff_data (ibfd);
+  ox = xcoff_data (obfd);
+  ox->toc = ix->toc;
+  ox->text_align_power = ix->text_align_power;
+  ox->data_align_power = ix->data_align_power;
+  ox->modtype = ix->modtype;
+  ox->cputype = ix->cputype;
+  ox->maxdata = ix->maxdata;
+  ox->maxstack = ix->maxstack;
+  return true;
+}
+\f
 /* The XCOFF reloc table.  Actually, XCOFF relocations specify the
    bitsize and whether they are signed or not, along with a
    conventional type.  This table is for the types, which are used for
    different algorithms for putting in the reloc.  Many of these
    relocs need special_function entries, which I have not written.  */
 
-static reloc_howto_type rs6000coff_howto_table[] =
+static reloc_howto_type xcoff_howto_table[] =
 {
   /* Standard 32 bit relocation.  */
   HOWTO (0,                    /* type */                                 
@@ -110,7 +172,7 @@ static reloc_howto_type rs6000coff_howto_table[] =
         16,                    /* bitsize */                   
         false,                 /* pc_relative */                          
         0,                     /* bitpos */                               
-        complain_overflow_signed, /* complain_on_overflow */
+        complain_overflow_bitfield, /* complain_on_overflow */
         0,                     /* special_function */                     
         "R_TOC",               /* name */                                 
         true,                  /* partial_inplace */                      
@@ -400,17 +462,12 @@ static reloc_howto_type rs6000coff_howto_table[] =
         false)                 /* pcrel_offset */
 };
 
-#define RTYPE2HOWTO(cache_ptr, dst) rs6000coff_rtype2howto (cache_ptr, dst)
-
-static void rs6000coff_rtype2howto PARAMS ((arelent *,
-                                           struct internal_reloc *));
-
 static void
-rs6000coff_rtype2howto (relent, internal)
+xcoff_rtype2howto (relent, internal)
      arelent *relent;
      struct internal_reloc *internal;
 {
-  relent->howto = rs6000coff_howto_table + internal->r_type;
+  relent->howto = xcoff_howto_table + internal->r_type;
 
   /* The r_size field of an XCOFF reloc encodes the bitsize of the
      relocation, as well as indicating whether it is signed or not.
@@ -426,26 +483,21 @@ rs6000coff_rtype2howto (relent, internal)
 #endif
 }
 
-#define coff_bfd_reloc_type_lookup rs6000coff_reloc_type_lookup
-
-static reloc_howto_type *rs6000coff_reloc_type_lookup
-  PARAMS ((bfd *, bfd_reloc_code_real_type));
-
 static reloc_howto_type *
-rs6000coff_reloc_type_lookup (abfd, code)
+xcoff_reloc_type_lookup (abfd, code)
      bfd *abfd;
      bfd_reloc_code_real_type code;
 {
   switch (code)
     {
     case BFD_RELOC_PPC_B26:
-      return &rs6000coff_howto_table[0xa];
+      return &xcoff_howto_table[0xa];
     case BFD_RELOC_PPC_BA26:
-      return &rs6000coff_howto_table[8];
+      return &xcoff_howto_table[8];
     case BFD_RELOC_PPC_TOC16:
-      return &rs6000coff_howto_table[3];
+      return &xcoff_howto_table[3];
     case BFD_RELOC_32:
-      return &rs6000coff_howto_table[0];
+      return &xcoff_howto_table[0];
     default:
       return NULL;
     }
@@ -460,9 +512,18 @@ rs6000coff_reloc_type_lookup (abfd, code)
        : 0)                                                            \
        | (howto->bitsize - 1));                                                \
   }
+\f
+#define COFF_DEFAULT_SECTION_ALIGNMENT_POWER (3)
 
 #define COFF_LONG_FILENAMES
 
+#define RTYPE2HOWTO(cache_ptr, dst) xcoff_rtype2howto (cache_ptr, dst)
+
+#define coff_mkobject xcoff_mkobject
+#define coff_bfd_copy_private_bfd_data xcoff_copy_private_bfd_data
+#define coff_bfd_reloc_type_lookup xcoff_reloc_type_lookup
+#define coff_relocate_section _bfd_ppc_xcoff_relocate_section
+
 #include "coffcode.h"
 \f
 /* XCOFF archive support.  The original version of this code was by
@@ -826,7 +887,7 @@ xcoff_openr_next_archived_file (archive, last_file)
 
 /* Stat an element in an XCOFF archive.  */
 
-int
+static int
 xcoff_generic_stat_arch_elt (abfd, s)
      bfd *abfd;
      struct stat *s;
@@ -881,7 +942,7 @@ xcoff_write_armap (abfd, elength, map, orl_count, stridx)
 {
   struct xcoff_ar_hdr hdr;
   char *p;
-  char buf[4];
+  unsigned char buf[4];
   bfd *sub;
   file_ptr fileoff;
   unsigned int i;
@@ -1262,6 +1323,12 @@ extern int lynx_core_file_failing_signal PARAMS ((bfd *abfd));
 
 #endif /* LYNX_CORE */
 
+#define _bfd_xcoff_sizeof_headers coff_sizeof_headers
+#define _bfd_xcoff_bfd_get_relocated_section_contents \
+  coff_bfd_get_relocated_section_contents
+#define _bfd_xcoff_bfd_relax_section coff_bfd_relax_section
+#define _bfd_xcoff_bfd_link_split_section coff_bfd_link_split_section
+
 /* The transfer vector that leads the outside world to all of the above. */
 
 const bfd_target rs6000coff_vec =
@@ -1272,7 +1339,7 @@ const bfd_target rs6000coff_vec =
   true,                                /* header byte order is big */
 
   (HAS_RELOC | EXEC_P |                /* object flags */
-   HAS_LINENO | HAS_DEBUG |
+   HAS_LINENO | HAS_DEBUG | DYNAMIC |
    HAS_SYMS | HAS_LOCALS | WP_TEXT),
 
   (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC), /* section flags */
@@ -1301,7 +1368,7 @@ const bfd_target rs6000coff_vec =
      BFD_JUMP_TABLE_SYMBOLS (coff),
      BFD_JUMP_TABLE_RELOCS (coff),
      BFD_JUMP_TABLE_WRITE (coff),
-     BFD_JUMP_TABLE_LINK (coff),
+     BFD_JUMP_TABLE_LINK (_bfd_xcoff),
      BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
 
   COFF_SWAP_TABLE,
index e40c296467b581269ef2cd3486a435b4c06548df..9ce2cbc1cd3728315ef0e6e41966b6f771c8c703 100755 (executable)
@@ -837,7 +837,7 @@ else
 fi
 done
 
-for ac_func in fcntl
+for ac_func in fcntl getpagesize
 do
 echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
@@ -1124,11 +1124,9 @@ EOF
 
        ;;
   rs6000-*-lynx*)      COREFILE=lynx-core.o ;;
-  rs6000-*-aix4*)      COREFILE=rs6000-core.o
-                       COREFLAG=-DALTERNATE_AIX_CORE_FORMAT ;;
+  rs6000-*-aix4*)      COREFILE=rs6000-core.o ;;
   rs6000-*-*)          COREFILE=rs6000-core.o ;;
-  powerpc-*-aix4*)     COREFILE=rs6000-core.o
-                       COREFLAG=-DALTERNATE_AIX_CORE_FORMAT ;;
+  powerpc-*-aix4*)     COREFILE=rs6000-core.o ;;
   powerpc-*-aix*)      COREFILE=rs6000-core.o ;;
   tahoe-*-*)
        COREFILE=trad-core.o
@@ -1180,7 +1178,7 @@ EOF
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 1184 "configure"
+#line 1182 "configure"
 #include "confdefs.h"
 #include <sys/procfs.h>
 int main() { return 0; }
@@ -1366,8 +1364,8 @@ do
     nlm32_powerpc_vec)         tb="$tb nlm32-ppc.o nlm32.o nlm.o" ;;
     pc532netbsd_vec)           tb="$tb ns32knetbsd.o aout-ns32k.o stab-syms.o" ;;
     pc532machaout_vec)         tb="$tb pc532-mach.o aout-ns32k.o stab-syms.o" ;;
-    pmac_xcoff_vec)            tb="$tb coff-pmac.o cofflink.o" ;;
-    rs6000coff_vec)            tb="$tb coff-rs6000.o" ;;
+    pmac_xcoff_vec)            tb="$tb coff-pmac.o xcofflink.o" ;;
+    rs6000coff_vec)            tb="$tb coff-rs6000.o xcofflink.o" ;;
     bfd_powerpc_pe_vec)         tb="$tb pe-ppc.o cofflink.o" ;;  
     bfd_powerpcle_pe_vec)       tb="$tb pe-ppc.o cofflink.o" ;;  
     bfd_powerpc_pei_vec)        tb="$tb pei-ppc.o cofflink.o" ;;
index 7fbfbafe097e809eb40fd0a8cafa35e780c94b81..4121c88797dd578961fc2a485f2768a5a717fe1d 100644 (file)
@@ -54,7 +54,7 @@ BFD_CC_FOR_BUILD
 
 AC_CHECK_HEADERS(stddef.h string.h strings.h stdlib.h time.h unistd.h)
 AC_CHECK_HEADERS(fcntl.h sys/file.h)
-AC_CHECK_FUNCS(fcntl)
+AC_CHECK_FUNCS(fcntl getpagesize)
 
 BFD_BINARY_FOPEN
 
@@ -203,11 +203,9 @@ changequote([,])dnl
        AC_DEFINE(TRAD_HEADER,"hosts/pc532mach.h")
        ;;
   rs6000-*-lynx*)      COREFILE=lynx-core.o ;;
-  rs6000-*-aix4*)      COREFILE=rs6000-core.o
-                       COREFLAG=-DALTERNATE_AIX_CORE_FORMAT ;;
+  rs6000-*-aix4*)      COREFILE=rs6000-core.o ;;
   rs6000-*-*)          COREFILE=rs6000-core.o ;;
-  powerpc-*-aix4*)     COREFILE=rs6000-core.o
-                       COREFLAG=-DALTERNATE_AIX_CORE_FORMAT ;;
+  powerpc-*-aix4*)     COREFILE=rs6000-core.o ;;
   powerpc-*-aix*)      COREFILE=rs6000-core.o ;;
   tahoe-*-*)
        COREFILE=trad-core.o
@@ -411,7 +409,8 @@ do
     nlm32_powerpc_vec)         tb="$tb nlm32-ppc.o nlm32.o nlm.o" ;;
     pc532netbsd_vec)           tb="$tb ns32knetbsd.o aout-ns32k.o stab-syms.o" ;;
     pc532machaout_vec)         tb="$tb pc532-mach.o aout-ns32k.o stab-syms.o" ;;
-    rs6000coff_vec)            tb="$tb coff-rs6000.o" ;;
+    pmac_xcoff_vec)            tb="$tb coff-pmac.o xcofflink.o" ;;
+    rs6000coff_vec)            tb="$tb coff-rs6000.o xcofflink.o" ;;
     bfd_powerpc_pe_vec)         tb="$tb pe-ppc.o cofflink.o" ;;  
     bfd_powerpcle_pe_vec)       tb="$tb pe-ppc.o cofflink.o" ;;  
     bfd_powerpc_pei_vec)        tb="$tb pei-ppc.o cofflink.o" ;;
index e2bba625ff1f87463a3fb3f49bfa122e1b85daea..c36a1346b0b2fce476d21f53baf1774fa20ce919 100644 (file)
@@ -11,7 +11,7 @@ If "{target_canonical}" =~ /m68k-apple-macos/
        Set defvec m68kcoff_vec
        Set selvecs '&m68kcoff_vec'
 Else If "{target_canonical}" =~ /powerpc-apple-macos/
-       Set BFD_BACKENDS '"{o}"coff-pmac.c.o "{o}"cofflink.c.o'
+       Set BFD_BACKENDS '"{o}"coff-pmac.c.o "{o}"xcofflink.c.o'
        Set defvec pmac_xcoff_vec
        Set selvecs '&pmac_xcoff_vec'
        Set selarchs "&bfd_powerpc_arch"
index 3659494f25155e5db8991a5fbd37e2f33033539b..3ecf8ebe41d3b40d5b13b4153e247186e59296dc 100644 (file)
@@ -19,7 +19,7 @@
 /^Makefile /,/--recheck/d
 
 # Don't do any recursive subdir stuff.
-/subdir_do/s/{MAKE} subdir_do/null-command/
+/ subdir_do/s/{MAKE}/null-command/
 
 /BFD_H/s/^{BFD_H}/#{BFD_H}/
 
diff --git a/bfd/xcofflink.c b/bfd/xcofflink.c
new file mode 100644 (file)
index 0000000..7601839
--- /dev/null
@@ -0,0 +1,2817 @@
+/* POWER/PowerPC XCOFF linker support.
+   Copyright 1995 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor <ian@cygnus.com>, Cygnus Support.
+
+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 "bfdlink.h"
+#include "libbfd.h"
+#include "coff/internal.h"
+#include "libcoff.h"
+
+/* This file holds the XCOFF linker code.  A lot of it is very similar
+   to the COFF linker code.  However, it is different enough that I
+   chose to avoid trying to hack up the COFF code to support XCOFF.
+   That leads to a certain amount of duplicated code, alas.  */
+
+#define STRING_SIZE_SIZE (4)
+
+/* Get the XCOFF hash table entries for a BFD.  */
+#define obj_xcoff_sym_hashes(bfd) \
+  ((struct xcoff_link_hash_entry **) obj_coff_sym_hashes (bfd))
+
+/* An entry in the XCOFF linker hash table.  */
+
+struct xcoff_link_hash_entry
+{
+  struct bfd_link_hash_entry root;
+
+  /* Whether this symbol came from an XCOFF input file.  */
+  boolean xcoff;
+
+  /* Symbol index in output file.  Set to -1 initially.  Set to -2 if
+     there is a reloc against this symbol.  */
+  long indx;
+
+  /* Whether we have created a TOC entry for this symbol.  */
+  boolean toc;
+};
+
+/* The XCOFF linker hash table.  */
+
+struct xcoff_link_hash_table
+{
+  struct bfd_link_hash_table root;
+
+  /* The .debug string hash table.  We need to compute this while
+     reading the input files, so that we know how large the .debug
+     section will be before we assign section positions.  */
+  struct bfd_strtab_hash *debug_strtab;
+
+  /* The .debug section we will use for the final output.  */
+  asection *debug_section;
+};
+
+/* Information we keep for each section in the output file.  */
+
+struct xcoff_link_section_info
+{
+  /* The relocs to be output.  */
+  struct internal_reloc *relocs;
+  /* For each reloc against a global symbol whose index was not known
+     when the reloc was handled, the global hash table entry.  */
+  struct xcoff_link_hash_entry **rel_hashes;
+};
+
+/* Information that we pass around while doing the final link step.  */
+
+struct xcoff_final_link_info
+{
+  /* General link information.  */
+  struct bfd_link_info *info;
+  /* Output BFD.  */
+  bfd *output_bfd;
+  /* Hash table for long symbol names.  */
+  struct bfd_strtab_hash *strtab;
+  /* Array of information kept for each output section, indexed by the
+     target_index field.  */
+  struct xcoff_link_section_info *section_info;
+  /* Symbol index of last C_FILE symbol (-1 if none).  */
+  long last_file_index;
+  /* Contents of last C_FILE symbol.  */
+  struct internal_syment last_file;
+  /* Symbol index of TOC symbol.  */
+  long toc_symindx;
+  /* Buffer large enough to hold swapped symbols of any input file.  */
+  struct internal_syment *internal_syms;
+  /* Buffer large enough to hold output indices of symbols of any
+     input file.  */
+  long *sym_indices;
+  /* Buffer large enough to hold output symbols for any input file.  */
+  bfd_byte *outsyms;
+  /* Buffer large enough to hold external line numbers for any input
+     section.  */
+  bfd_byte *linenos;
+  /* Buffer large enough to hold any input section.  */
+  bfd_byte *contents;
+  /* Buffer large enough to hold external relocs of any input section.  */
+  bfd_byte *external_relocs;
+};
+
+static struct bfd_hash_entry *xcoff_link_hash_newfunc
+  PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
+static boolean xcoff_link_add_object_symbols
+  PARAMS ((bfd *, struct bfd_link_info *));
+static boolean xcoff_link_check_archive_element
+  PARAMS ((bfd *, struct bfd_link_info *, boolean *));
+static boolean xcoff_link_check_ar_symbols
+  PARAMS ((bfd *, struct bfd_link_info *, boolean *));
+static boolean xcoff_link_add_symbols PARAMS ((bfd *, struct bfd_link_info *));
+static boolean xcoff_link_input_bfd
+  PARAMS ((struct xcoff_final_link_info *, bfd *));
+static boolean xcoff_write_global_symbol
+  PARAMS ((struct xcoff_link_hash_entry *, PTR));
+static boolean xcoff_reloc_link_order
+  PARAMS ((bfd *, struct xcoff_final_link_info *, asection *,
+          struct bfd_link_order *));
+static int xcoff_sort_relocs PARAMS ((const PTR, const PTR));
+
+/* Routine to create an entry in an XCOFF link hash table.  */
+
+static struct bfd_hash_entry *
+xcoff_link_hash_newfunc (entry, table, string)
+     struct bfd_hash_entry *entry;
+     struct bfd_hash_table *table;
+     const char *string;
+{
+  struct xcoff_link_hash_entry *ret = (struct xcoff_link_hash_entry *) entry;
+
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (ret == (struct xcoff_link_hash_entry *) NULL)
+    ret = ((struct xcoff_link_hash_entry *)
+          bfd_hash_allocate (table, sizeof (struct xcoff_link_hash_entry)));
+  if (ret == (struct xcoff_link_hash_entry *) NULL)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      return (struct bfd_hash_entry *) ret;
+    }
+
+  /* Call the allocation method of the superclass.  */
+  ret = ((struct xcoff_link_hash_entry *)
+        _bfd_link_hash_newfunc ((struct bfd_hash_entry *) ret,
+                                table, string));
+  if (ret != NULL)
+    {
+      /* Set local fields.  */
+      ret->xcoff = false;
+      ret->indx = -1;
+      ret->toc = false;
+    }
+
+  return (struct bfd_hash_entry *) ret;
+}
+
+/* Create a XCOFF link hash table.  */
+
+struct bfd_link_hash_table *
+_bfd_xcoff_bfd_link_hash_table_create (abfd)
+     bfd *abfd;
+{
+  struct xcoff_link_hash_table *ret;
+
+  ret = ((struct xcoff_link_hash_table *)
+        bfd_alloc (abfd, sizeof (struct xcoff_link_hash_table)));
+  if (ret == (struct xcoff_link_hash_table *) NULL)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      return (struct bfd_link_hash_table *) NULL;
+    }
+  if (! _bfd_link_hash_table_init (&ret->root, abfd, xcoff_link_hash_newfunc))
+    {
+      bfd_release (abfd, ret);
+      return (struct bfd_link_hash_table *) NULL;
+    }
+
+  ret->debug_strtab = _bfd_xcoff_stringtab_init ();
+  ret->debug_section = NULL;
+
+  return &ret->root;
+}
+
+/* Look up an entry in an XCOFF link hash table.  */
+
+#define xcoff_link_hash_lookup(table, string, create, copy, follow) \
+  ((struct xcoff_link_hash_entry *) \
+   bfd_link_hash_lookup (&(table)->root, (string), (create), (copy),\
+                        (follow)))
+
+/* Traverse an XCOFF link hash table.  */
+
+#define xcoff_link_hash_traverse(table, func, info)                    \
+  (bfd_link_hash_traverse                                              \
+   (&(table)->root,                                                    \
+    (boolean (*) PARAMS ((struct bfd_link_hash_entry *, PTR))) (func), \
+    (info)))
+
+/* Get the XCOFF link hash table from the info structure.  This is
+   just a cast.  */
+
+#define xcoff_hash_table(p) ((struct xcoff_link_hash_table *) ((p)->hash))
+
+/* Given an XCOFF BFD, add symbols to the global hash table as
+   appropriate.  */
+
+boolean
+_bfd_xcoff_bfd_link_add_symbols (abfd, info)
+     bfd *abfd;
+     struct bfd_link_info *info;
+{
+  switch (bfd_get_format (abfd))
+    {
+    case bfd_object:
+      return xcoff_link_add_object_symbols (abfd, info);
+    case bfd_archive:
+      return (_bfd_generic_link_add_archive_symbols
+             (abfd, info, xcoff_link_check_archive_element));
+    default:
+      bfd_set_error (bfd_error_wrong_format);
+      return false;
+    }
+}
+
+/* Add symbols from an XCOFF object file.  */
+
+static boolean
+xcoff_link_add_object_symbols (abfd, info)
+     bfd *abfd;
+     struct bfd_link_info *info;
+{
+  if (! _bfd_coff_get_external_symbols (abfd))
+    return false;
+  if (! xcoff_link_add_symbols (abfd, info))
+    return false;
+  if (! info->keep_memory)
+    {
+      if (! _bfd_coff_free_symbols (abfd))
+       return false;
+    }
+  return true;
+}
+
+/* Check a single archive element to see if we need to include it in
+   the link.  *PNEEDED is set according to whether this element is
+   needed in the link or not.  This is called via
+   _bfd_generic_link_add_archive_symbols.  */
+
+static boolean
+xcoff_link_check_archive_element (abfd, info, pneeded)
+     bfd *abfd;
+     struct bfd_link_info *info;
+     boolean *pneeded;
+{
+  if (! _bfd_coff_get_external_symbols (abfd))
+    return false;
+
+  if (! xcoff_link_check_ar_symbols (abfd, info, pneeded))
+    return false;
+
+  if (*pneeded)
+    {
+      if (! xcoff_link_add_symbols (abfd, info))
+       return false;
+    }
+
+  if (! info->keep_memory || ! *pneeded)
+    {
+      if (! _bfd_coff_free_symbols (abfd))
+       return false;
+    }
+
+  return true;
+}
+
+/* Look through the symbols to see if this object file should be
+   included in the link.  */
+
+static boolean
+xcoff_link_check_ar_symbols (abfd, info, pneeded)
+     bfd *abfd;
+     struct bfd_link_info *info;
+     boolean *pneeded;
+{
+  bfd_size_type symesz;
+  bfd_byte *esym;
+  bfd_byte *esym_end;
+
+  *pneeded = false;
+
+  symesz = bfd_coff_symesz (abfd);
+  esym = (bfd_byte *) obj_coff_external_syms (abfd);
+  esym_end = esym + obj_raw_syment_count (abfd) * symesz;
+  while (esym < esym_end)
+    {
+      struct internal_syment sym;
+
+      bfd_coff_swap_sym_in (abfd, (PTR) esym, (PTR) &sym);
+
+      if (sym.n_sclass == C_EXT && sym.n_scnum != N_UNDEF)
+       {
+         const char *name;
+         char buf[SYMNMLEN + 1];
+         struct bfd_link_hash_entry *h;
+
+         /* This symbol is externally visible, and is defined by this
+             object file.  */
+
+         name = _bfd_coff_internal_syment_name (abfd, &sym, buf);
+         if (name == NULL)
+           return false;
+         h = bfd_link_hash_lookup (info->hash, name, false, false, true);
+
+         /* We are only interested in symbols that are currently
+            undefined.  If a symbol is currently known to be common,
+            XCOFF linkers do not bring in an object file which
+            defines it.  */
+         if (h != (struct bfd_link_hash_entry *) NULL
+             && h->type == bfd_link_hash_undefined)
+           {
+             if (! (*info->callbacks->add_archive_element) (info, abfd, name))
+               return false;
+             *pneeded = true;
+             return true;
+           }
+       }
+
+      esym += (sym.n_numaux + 1) * symesz;
+    }
+
+  /* We do not need this object file.  */
+  return true;
+}
+
+/* Add all the symbols from an object file to the hash table.
+
+   XCOFF is a weird format.  A normal XCOFF .o files will have three
+   COFF sections--.text, .data, and .bss--but each COFF section will
+   contain many csects.  These csects are described in the symbol
+   table.  From the linker's point of view, each csect must be
+   considered a section in its own right.  For example, a TOC entry is
+   handled as a small XMC_TC csect.  The linker must be able to merge
+   different TOC entries together, which means that it must be able to
+   extract the XMC_TC csects from the .data section of the input .o
+   file.
+
+   From the point of view of our linker, this is, of course, a hideous
+   nightmare.  We cope by actually creating sections for each csect,
+   and discarding the original sections.  We then have to handle the
+   relocation entries carefully, since the only way to tell which
+   csect they belong to is to examine the address.  */
+
+static boolean
+xcoff_link_add_symbols (abfd, info)
+     bfd *abfd;
+     struct bfd_link_info *info;
+{
+  boolean default_copy;
+  bfd_size_type symcount;
+  struct xcoff_link_hash_entry **sym_hash;
+  asection **csect_cache;
+  asection *debug_section;
+  unsigned long *debug_index = NULL;
+  bfd_byte *debug_contents = NULL;
+  asection *sub;
+  asection *csect;
+  unsigned int csect_index;
+  asection *first_csect;
+  boolean new_debug;
+  bfd_size_type symesz;
+  bfd_byte *esym;
+  bfd_byte *esym_end;
+  struct reloc_info_struct
+    {
+      struct internal_reloc *relocs;
+      boolean *used;
+    } *reloc_info = NULL;
+
+  if (info->keep_memory)
+    default_copy = false;
+  else
+    default_copy = true;
+
+  symcount = obj_raw_syment_count (abfd);
+
+  /* We keep a list of the linker hash table entries that correspond
+     to each external symbol.  */
+  sym_hash = ((struct xcoff_link_hash_entry **)
+             bfd_alloc (abfd,
+                        (symcount
+                         * sizeof (struct xcoff_link_hash_entry *))));
+  if (sym_hash == NULL && symcount != 0)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      goto error_return;
+    }
+  coff_data (abfd)->sym_hashes = (struct coff_link_hash_entry **) sym_hash;
+  memset (sym_hash, 0,
+         (size_t) symcount * sizeof (struct xcoff_link_hash_entry *));
+
+  /* Because of the weird stuff we are doing with XCOFF csects, we can
+     not easily determine which section a symbol is in, so we store
+     the information in the tdata for the input file.  */
+  csect_cache = ((asection **)
+                bfd_alloc (abfd, symcount * sizeof (asection *)));
+  if (csect_cache == NULL && symcount != 0)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      goto error_return;
+    }
+  xcoff_data (abfd)->csects = csect_cache;
+  memset (csect_cache, 0, (size_t) symcount * sizeof (asection *));
+
+  /* XCOFF stores debugging symbol names in the .debug section.  We
+     need to determine the size of the final .debug section while
+     reading the input files, before the linker starts deciding where
+     to put everything.  We build a hash table for the final .debug
+     section.  For each symbol whose name goes into it, we store the
+     index in the .debug section in the debug_indices array.  */
+  debug_section = bfd_get_section_by_name (abfd, ".debug");
+  if (debug_section != NULL && debug_section->_raw_size > 0)
+    {
+      debug_index = ((unsigned long *)
+                    bfd_zalloc (abfd, symcount * sizeof (unsigned long)));
+      if (debug_index == NULL && symcount != 0)
+       {
+         bfd_set_error (bfd_error_no_memory);
+         goto error_return;
+       }
+      xcoff_data (abfd)->debug_indices = debug_index;
+
+      /* Grab the contents of the current .debug section.  We use
+         malloc and copy the names into the string tab hash table,
+         rather than bfd_alloc, because I expect that when linking
+         many files together, many of the strings will be the same.
+         Storing the strings in the hash table should save space in
+         that case.  */
+      debug_contents = (bfd_byte *) malloc (debug_section->_raw_size);
+      if (debug_contents == NULL)
+       {
+         bfd_set_error (bfd_error_no_memory);
+         goto error_return;
+       }
+      if (! bfd_get_section_contents (abfd, debug_section,
+                                     (PTR) debug_contents, (file_ptr) 0,
+                                     debug_section->_raw_size))
+       goto error_return;
+    }
+
+  /* While splitting sections into csects, we need to assign the
+     relocs correctly.  The relocs and the csects must both be in
+     order by VMA within a given section, so we handle this by
+     scanning along the relocs as we process the csects.  We index
+     into reloc_info using the section target_index.  */
+  reloc_info = ((struct reloc_info_struct *)
+               malloc ((abfd->section_count + 1)
+                       * sizeof (struct reloc_info_struct)));
+  if (reloc_info == NULL)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      goto error_return;
+    }
+  memset ((PTR) reloc_info, 0,
+         (abfd->section_count + 1) * sizeof (struct reloc_info_struct));
+
+  /* Read in the relocs for each section which has relocs.  */
+  for (sub = abfd->sections; sub != NULL; sub = sub->next)
+    {
+      if ((sub->flags & SEC_RELOC) != 0)
+       {
+         reloc_info[sub->target_index].relocs =
+           _bfd_coff_read_internal_relocs (abfd, sub, true,
+                                           (bfd_byte *) NULL, false,
+                                           (struct internal_reloc *) NULL);
+         reloc_info[sub->target_index].used =
+           (boolean *) malloc (sub->reloc_count * sizeof (boolean));
+         if (reloc_info[sub->target_index].used == NULL)
+           {
+             bfd_set_error (bfd_error_no_memory);
+             goto error_return;
+           }
+         memset (reloc_info[sub->target_index].used, 0,
+                 sub->reloc_count * sizeof (boolean));
+       }
+    }
+
+  csect = NULL;
+  csect_index = 0;
+  first_csect = NULL;
+  new_debug = false;
+
+  symesz = bfd_coff_symesz (abfd);
+  BFD_ASSERT (symesz == bfd_coff_auxesz (abfd));
+  esym = (bfd_byte *) obj_coff_external_syms (abfd);
+  esym_end = esym + symcount * symesz;
+  while (esym < esym_end)
+    {
+      struct internal_syment sym;
+      union internal_auxent aux;
+      PTR name;
+      char buf[SYMNMLEN + 1];
+      int smtyp;
+      flagword flags;
+      asection *section;
+      bfd_vma value;
+
+      bfd_coff_swap_sym_in (abfd, (PTR) esym, (PTR) &sym);
+
+      *debug_index = (unsigned long) -1;
+
+      /* If the name of this symbol is in the .debug section, and it
+         looks like we are going to keep it, add the name to the
+         .debug hash table.  */
+      if (info->hash->creator == abfd->xvec
+         && sym._n._n_n._n_zeroes == 0
+         && info->strip != strip_all
+         && info->strip != strip_debugger
+         && info->discard != discard_all
+         && bfd_coff_symname_in_debug (abfd, &sym))
+       {
+         bfd_size_type indx;
+
+         /* Add 2 to skip the length.  */
+         name = debug_contents + sym._n._n_n._n_offset;
+
+         indx = _bfd_stringtab_add (xcoff_hash_table (info)->debug_strtab,
+                                    name, true, true);
+         if (indx == (bfd_size_type) -1)
+           goto error_return;
+         *debug_index = indx;
+         new_debug = true;
+       }
+
+      /* In this pass we are only interested in symbols with csect
+         information.  */
+      if (sym.n_sclass != C_EXT && sym.n_sclass != C_HIDEXT)
+       {
+         if (csect != NULL)
+           *csect_cache = csect;
+         else if (first_csect == NULL)
+           *csect_cache = coff_section_from_bfd_index (abfd, sym.n_scnum);
+         else
+           *csect_cache = NULL;
+         esym += (sym.n_numaux + 1) * symesz;
+         sym_hash += sym.n_numaux + 1;
+         csect_cache += sym.n_numaux + 1;
+         if (debug_index != NULL)
+           debug_index += sym.n_numaux + 1;
+         continue;
+       }
+
+      name = _bfd_coff_internal_syment_name (abfd, &sym, buf);
+      if (name == NULL)
+       goto error_return;
+
+      /* Pick up the csect auxiliary information.  */
+
+      if (sym.n_numaux == 0)
+       {
+         (*_bfd_error_handler)
+           ("%s: class %d symbol `%s' has no aux entries",
+            bfd_get_filename (abfd), sym.n_sclass, name);
+         bfd_set_error (bfd_error_bad_value);
+         goto error_return;
+       }
+
+      bfd_coff_swap_aux_in (abfd,
+                           (PTR) (esym + symesz * sym.n_numaux),
+                           sym.n_type, sym.n_sclass,
+                           sym.n_numaux - 1, sym.n_numaux,
+                           (PTR) &aux);
+
+      smtyp = SMTYP_SMTYP (aux.x_csect.x_smtyp);
+
+      flags = BSF_GLOBAL;
+      section = NULL;
+      value = 0;
+
+      switch (smtyp)
+       {
+       default:
+         (*_bfd_error_handler)
+           ("%s: symbol `%s' has unrecognized csect type %d",
+            bfd_get_filename (abfd), name, smtyp);
+         bfd_set_error (bfd_error_bad_value);
+         goto error_return;
+
+       case XTY_ER:
+         /* This is an external reference.  */
+         if (sym.n_sclass == C_HIDEXT
+             || sym.n_scnum != N_UNDEF
+             || aux.x_csect.x_scnlen.l != 0)
+           {
+             (*_bfd_error_handler)
+               ("%s: bad XTY_ER symbol `%s': class %d scnum %d scnlen %d",
+                bfd_get_filename (abfd), name, sym.n_sclass, sym.n_scnum,
+                aux.x_csect.x_scnlen.l);
+             bfd_set_error (bfd_error_bad_value);
+             goto error_return;
+           }
+         section = bfd_und_section_ptr;
+         break;
+
+       case XTY_SD:
+         /* This is a csect definition.  */
+
+         csect = NULL;
+         csect_index = -1;
+
+         /* When we see a TOC anchor, we record the TOC value.  */
+         if (aux.x_csect.x_smclas == XMC_TC0)
+           {
+             if (sym.n_sclass != C_HIDEXT
+                 || aux.x_csect.x_scnlen.l != 0)
+               {
+                 (*_bfd_error_handler)
+                   ("%s: XMC_TC0 symbol `%s' is class %d scnlen %d",
+                    bfd_get_filename (abfd), name, sym.n_sclass,
+                    aux.x_csect.x_scnlen.l);
+                 bfd_set_error (bfd_error_bad_value);
+                 goto error_return;
+               }
+             xcoff_data (abfd)->toc = sym.n_value;
+           }
+
+         /* We must merge TOC entries for the same symbol.  We can
+            merge two TOC entries if they are both C_HIDEXT, they
+            both have the same name, they are both 4 bytes long, and
+            they both have a relocation table entry for an external
+            symbol with the same name.  Unfortunately, this means
+            that we must look through the relocations.  Ick.  */
+         if (aux.x_csect.x_smclas == XMC_TC
+             && sym.n_sclass == C_HIDEXT
+             && aux.x_csect.x_scnlen.l == 4)
+           {
+             asection *enclosing;
+             bfd_size_type relindx;
+             struct internal_reloc *rel;
+             boolean *used;
+
+             enclosing = coff_section_from_bfd_index (abfd, sym.n_scnum);
+             if (enclosing == NULL)
+               return false;
+
+             /* XCOFF requires that relocs be sorted by address, so
+                 we could do a binary search here.  FIXME.  */
+             rel = reloc_info[enclosing->target_index].relocs;
+             used = reloc_info[enclosing->target_index].used;
+             for (relindx = 0;
+                  relindx < enclosing->reloc_count;
+                  relindx++, rel++, used++)
+               {
+                 if (! *used
+                     && rel->r_vaddr == (bfd_vma) sym.n_value
+                     && rel->r_size == 31
+                     && rel->r_type == 0)
+                   break;
+               }
+             if (relindx < enclosing->reloc_count)
+               {
+                 bfd_byte *erelsym;
+                 struct internal_syment relsym;
+
+                 erelsym = ((bfd_byte *) obj_coff_external_syms (abfd)
+                            + rel->r_symndx * symesz);
+                 bfd_coff_swap_sym_in (abfd, (PTR) erelsym, (PTR) &relsym);
+                 if (relsym.n_sclass == C_EXT)
+                   {
+                     const char *relname;
+                     char relbuf[SYMNMLEN + 1];
+                     boolean copy;
+                     struct xcoff_link_hash_entry *h;
+
+                     /* At this point we know that the TOC entry is
+                        for an externally visible symbol.  */
+                     relname = _bfd_coff_internal_syment_name (abfd, &relsym,
+                                                               relbuf);
+                     if (relname == NULL)
+                       return false;
+                     copy = (! info->keep_memory
+                             || relsym._n._n_n._n_zeroes != 0
+                             || relsym._n._n_n._n_offset == 0);
+                     h = xcoff_link_hash_lookup (xcoff_hash_table (info),
+                                                 relname, true, copy, false);
+                     if (h == NULL)
+                       return false;
+
+                     /* At this point h->root.type could be
+                        bfd_link_hash_new.  That should be OK, since
+                        we know for sure that we will come across
+                        this symbol as we step through the file.  */
+
+                     /* Stuff h into *sym_hash.  We normally only set
+                         *sym_hash for global variables.  Remembering
+                         it here saves us doing this work again later.  */
+                     *sym_hash = h;
+
+                     if (h->toc)
+                       {
+                         /* We already have a TOC entry for this
+                            symbol, so we can just ignore this one.
+                            We need to skip the csect, and the reloc
+                            as well.  */
+                         *used = true;
+                         break;
+                       }
+
+                     /* We are about to create a TOC entry for this
+                        symbol.  */
+                     h->toc = true;
+                   }
+               }
+           }
+
+         /* We need to create a new section.  We get the name from
+            the csect storage mapping class, so that the linker can
+            accumulate similar csects together.  */
+         {
+           static const char *csect_name_by_class[] =
+             {
+               ".pr", ".ro", ".db", ".tc", ".ua", ".rw", ".gl", ".xo",
+               ".sv", ".bs", ".ds", ".uc", ".ti", ".tb", NULL, ".tc0",
+               ".td"
+             };
+           const char *csect_name;
+           asection *enclosing;
+           struct internal_reloc *rel;
+           bfd_size_type relindx;
+           boolean *used;
+
+           if ((aux.x_csect.x_smclas >=
+                sizeof csect_name_by_class / sizeof csect_name_by_class[0])
+               || csect_name_by_class[aux.x_csect.x_smclas] == NULL)
+             {
+               (*_bfd_error_handler)
+                 ("%s: symbol `%s' has unrecognized smclas %d",
+                  bfd_get_filename (abfd), name, aux.x_csect.x_smclas);
+               bfd_set_error (bfd_error_bad_value);
+               goto error_return;
+             }
+
+           csect_name = csect_name_by_class[aux.x_csect.x_smclas];
+           csect = bfd_make_section_anyway (abfd, csect_name);
+           if (csect == NULL)
+             goto error_return;
+           enclosing = coff_section_from_bfd_index (abfd, sym.n_scnum);
+           if (enclosing == NULL)
+             goto error_return;
+           if ((bfd_vma) sym.n_value < enclosing->vma
+               || ((bfd_vma) sym.n_value + aux.x_csect.x_scnlen.l
+                   > enclosing->vma + enclosing->_raw_size))
+             {
+               (*_bfd_error_handler)
+                 ("%s: csect `%s' not in enclosing section",
+                  bfd_get_filename (abfd), name);
+               bfd_set_error (bfd_error_bad_value);
+               goto error_return;
+             }
+           csect->vma = sym.n_value;
+           csect->filepos = (enclosing->filepos
+                             + sym.n_value
+                             - enclosing->vma);
+           csect->_raw_size = aux.x_csect.x_scnlen.l;
+           csect->flags |= SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS;
+           csect->alignment_power = SMTYP_ALIGN (aux.x_csect.x_smtyp);
+
+           /* XCOFF requires that relocs be sorted by address, so we
+               could do a binary search here.  FIXME.  (XCOFF
+               unfortunately does not require that symbols be sorted
+               by address, or this would be a simple merge).  */
+           rel = reloc_info[enclosing->target_index].relocs;
+           used = reloc_info[enclosing->target_index].used;
+           for (relindx = 0;
+                relindx < enclosing->reloc_count;
+                relindx++, rel++, used++)
+             {
+               if (! *used
+                   && rel->r_vaddr >= csect->vma
+                   && rel->r_vaddr < csect->vma + csect->_raw_size)
+                 {
+                   csect->rel_filepos = (enclosing->rel_filepos
+                                         + relindx * bfd_coff_relsz (abfd));
+                   break;
+                 }
+             }
+           while (relindx < enclosing->reloc_count
+                  && ! *used
+                  && rel->r_vaddr >= csect->vma
+                  && rel->r_vaddr < csect->vma + csect->_raw_size)
+             {
+               *used = true;
+               csect->flags |= SEC_RELOC;
+               ++csect->reloc_count;
+               ++relindx;
+               ++rel;
+               ++used;
+             }
+
+           /* There are a number of other fields and section flags
+              which we do not bother to set.  */
+
+           /* Record the enclosing section in the tdata for this new
+              section.  */
+           csect->used_by_bfd =
+             ((struct coff_section_tdata *)
+              bfd_alloc (abfd, sizeof (struct coff_section_tdata)));
+           if (csect->used_by_bfd == NULL)
+             {
+               bfd_set_error (bfd_error_no_memory);
+               goto error_return;
+             }
+           coff_section_data (abfd, csect)->tdata =
+             bfd_alloc (abfd, sizeof (struct xcoff_section_tdata));
+           if (coff_section_data (abfd, csect)->tdata == NULL)
+             {
+               bfd_set_error (bfd_error_no_memory);
+               goto error_return;
+             }
+           xcoff_section_data (abfd, csect)->enclosing = enclosing;
+
+           csect_index = ((esym
+                           - (bfd_byte *) obj_coff_external_syms (abfd))
+                          / symesz);
+
+           if (first_csect == NULL)
+             first_csect = csect;
+
+           /* If this symbol is C_EXT, we treat it as starting at the
+              beginning of the newly created section.  */
+           if (sym.n_sclass == C_EXT)
+             {
+               section = csect;
+               value = 0;
+             }
+         }
+         break;
+
+       case XTY_LD:
+         /* This is a label definition.  The x_scnlen field is the
+             symbol index of the csect.  I believe that this must
+             always follow the appropriate XTY_SD symbol, so I will
+             insist on it.  */
+         {
+           boolean bad;
+
+           bad = false;
+           if (sym.n_sclass != C_EXT
+               || aux.x_csect.x_scnlen.l < 0
+               || (aux.x_csect.x_scnlen.l
+                   >= esym - (bfd_byte *) obj_coff_external_syms (abfd)))
+             bad = true;
+           if (! bad)
+             {
+               section = xcoff_data (abfd)->csects[aux.x_csect.x_scnlen.l];
+               if (section == NULL
+                   || (section->flags & SEC_HAS_CONTENTS) == 0)
+                 bad = true;
+             }
+           if (bad)
+             {
+               (*_bfd_error_handler)
+                 ("%s: misplaced XTY_LD `%s'",
+                  bfd_get_filename (abfd), name);
+               bfd_set_error (bfd_error_bad_value);
+               goto error_return;
+             }
+
+           value = sym.n_value - csect->vma;
+         }
+         break;
+
+       case XTY_CM:
+         /* This is an unitialized csect.  We could base the name on
+             the storage mapping class, but we don't bother.  If this
+             csect is externally visible, it is a common symbol.  */
+         if (sym.n_sclass == C_EXT)
+           {
+             section = bfd_com_section_ptr;
+             value = aux.x_csect.x_scnlen.l;
+           }
+         else
+           {
+             csect = bfd_make_section_anyway (abfd, ".bss");
+             if (csect == NULL)
+               goto error_return;
+             csect->vma = 0;
+             csect->_raw_size = aux.x_csect.x_scnlen.l;
+             csect->flags |= SEC_ALLOC;
+             csect->alignment_power = SMTYP_ALIGN (aux.x_csect.x_smtyp);
+             /* There are a number of other fields and section flags
+                 which we do not bother to set.  */
+
+             csect_index = ((esym
+                             - (bfd_byte *) obj_coff_external_syms (abfd))
+                            / symesz);
+
+             if (first_csect == NULL)
+               first_csect = csect;
+           }
+         break;
+       }
+
+      /* Now we have enough information to add the symbol to the
+         linker hash table.  */
+
+      if (sym.n_sclass == C_EXT)
+       {
+         boolean copy;
+
+         BFD_ASSERT (section != NULL);
+
+         /* We must copy the name into memory if we got it from the
+             syment itself, rather than the string table.  */
+         copy = default_copy;
+         if (sym._n._n_n._n_zeroes != 0
+             || sym._n._n_n._n_offset == 0)
+           copy = true;
+
+         if (! (_bfd_generic_link_add_one_symbol
+                (info, abfd, name, flags, section, value,
+                 (const char *) NULL, copy, false,
+                 (struct bfd_link_hash_entry **) sym_hash)))
+           goto error_return;
+
+         if (info->hash->creator == abfd->xvec)
+           (*sym_hash)->xcoff = true;
+       }
+
+      *csect_cache = csect;
+
+      esym += (sym.n_numaux + 1) * symesz;
+      sym_hash += sym.n_numaux + 1;
+      csect_cache += sym.n_numaux + 1;
+      if (debug_index != NULL)
+       debug_index += sym.n_numaux + 1;
+    }
+
+  if (new_debug)
+    {
+      /* Update the size of the .debug section we are building.  */
+      if (xcoff_hash_table (info)->debug_section == NULL)
+       {
+         asection *n;
+
+         n = bfd_make_section_anyway (abfd, ".debug");
+         if (n == NULL)
+           goto error_return;
+         n->flags |= SEC_HAS_CONTENTS;
+         /* Note that reading from this section will fail.  */
+         xcoff_hash_table (info)->debug_section = n;
+       }
+      xcoff_hash_table (info)->debug_section->_raw_size =
+       _bfd_stringtab_size (xcoff_hash_table (info)->debug_strtab);
+    }
+
+  /* Make sure that we have seen all the relocs.  */
+  for (sub = abfd->sections; sub != first_csect; sub = sub->next)
+    {
+      /* Reset the section size, since the data is now attached to the
+         csects.  */
+      sub->_raw_size = 0;
+
+      if ((sub->flags & SEC_RELOC) != 0)
+       {
+         bfd_size_type i;
+
+         for (i = 0; i < sub->reloc_count; i++)
+           {
+             if (! reloc_info[sub->target_index].used[i])
+               {
+                 (*_bfd_error_handler)
+                   ("%s: reloc %s:%d not in csect",
+                    bfd_get_filename (abfd), sub->name, i);
+                 bfd_set_error (bfd_error_bad_value);
+                 goto error_return;
+               }
+           }
+
+         free (reloc_info[sub->target_index].used);
+         reloc_info[sub->target_index].used = NULL;
+
+         /* Reset SEC_RELOC and the reloc_count, since the reloc
+             information is now attached to the csects.  */
+         sub->flags &=~ SEC_RELOC;
+         sub->reloc_count = 0;
+
+         /* If we are not keeping memory, free the reloc information.  */
+         if (! info->keep_memory
+             && coff_section_data (abfd, sub) != NULL
+             && coff_section_data (abfd, sub)->relocs != NULL)
+           {
+             free (coff_section_data (abfd, sub)->relocs);
+             coff_section_data (abfd, sub)->relocs = NULL;
+           }
+       }
+    }
+
+  free (reloc_info);
+
+  if (debug_contents != NULL)
+    {
+      free (debug_contents);
+      debug_contents = NULL;
+    }
+
+  return true;
+
+ error_return:
+  if (debug_contents != NULL)
+    free (debug_contents);
+  if (reloc_info != NULL)
+    {
+      for (sub = abfd->sections; sub != NULL; sub = sub->next)
+       {
+         if (reloc_info[sub->target_index].used != NULL)
+           free (reloc_info[sub->target_index].used);
+       }
+    free (reloc_info);
+    }
+  return false;
+}
+\f
+/* Do the final link step.  */
+
+boolean
+_bfd_xcoff_bfd_final_link (abfd, info)
+     bfd *abfd;
+     struct bfd_link_info *info;
+{
+  bfd_size_type symesz;
+  struct xcoff_final_link_info finfo;
+  asection *o;
+  struct bfd_link_order *p;
+  size_t max_contents_size;
+  size_t max_sym_count;
+  size_t max_lineno_count;
+  size_t max_reloc_count;
+  size_t max_output_reloc_count;
+  file_ptr rel_filepos;
+  unsigned int relsz;
+  file_ptr line_filepos;
+  unsigned int linesz;
+  bfd *sub;
+  bfd_byte *external_relocs = NULL;
+  char strbuf[STRING_SIZE_SIZE];
+
+  symesz = bfd_coff_symesz (abfd);
+
+  finfo.info = info;
+  finfo.output_bfd = abfd;
+  finfo.strtab = NULL;
+  finfo.section_info = NULL;
+  finfo.last_file_index = -1;
+  finfo.toc_symindx = -1;
+  finfo.internal_syms = NULL;
+  finfo.sym_indices = NULL;
+  finfo.outsyms = NULL;
+  finfo.linenos = NULL;
+  finfo.contents = NULL;
+  finfo.external_relocs = NULL;
+
+  xcoff_data (abfd)->coff.link_info = info;
+
+  finfo.strtab = _bfd_stringtab_init ();
+  if (finfo.strtab == NULL)
+    goto error_return;
+
+  /* Compute the file positions for all the sections.  */
+  if (! abfd->output_has_begun)
+    bfd_coff_compute_section_file_positions (abfd);
+
+  /* Count the line numbers and relocation entries required for the
+     output file.  Set the file positions for the relocs.  */
+  rel_filepos = obj_relocbase (abfd);
+  relsz = bfd_coff_relsz (abfd);
+  max_contents_size = 0;
+  max_lineno_count = 0;
+  max_reloc_count = 0;
+  for (o = abfd->sections; o != NULL; o = o->next)
+    {
+      o->reloc_count = 0;
+      o->lineno_count = 0;
+      for (p = o->link_order_head; p != NULL; p = p->next)
+       {
+         if (p->type == bfd_indirect_link_order)
+           {
+             asection *sec;
+
+             sec = p->u.indirect.section;
+
+             if (info->strip == strip_none
+                 || info->strip == strip_some)
+               o->lineno_count += sec->lineno_count;
+
+             o->reloc_count += sec->reloc_count;
+
+             if (sec->_raw_size > max_contents_size)
+               max_contents_size = sec->_raw_size;
+             if (sec->lineno_count > max_lineno_count)
+               max_lineno_count = sec->lineno_count;
+             if (sec->reloc_count > max_reloc_count)
+               max_reloc_count = sec->reloc_count;
+           }
+         else if (p->type == bfd_section_reloc_link_order
+                  || p->type == bfd_symbol_reloc_link_order)
+           ++o->reloc_count;
+       }
+      if (o->reloc_count == 0)
+       o->rel_filepos = 0;
+      else
+       {
+         o->flags |= SEC_RELOC;
+         o->rel_filepos = rel_filepos;
+         rel_filepos += o->reloc_count * relsz;
+       }
+    }
+
+  /* Allocate space for the pointers we need to keep for the relocs.  */
+  {
+    unsigned int i;
+
+    /* We use section_count + 1, rather than section_count, because
+       the target_index fields are 1 based.  */
+    finfo.section_info = ((struct xcoff_link_section_info *)
+                         malloc ((abfd->section_count + 1)
+                                 * sizeof (struct xcoff_link_section_info)));
+    if (finfo.section_info == NULL)
+      {
+       bfd_set_error (bfd_error_no_memory);
+       goto error_return;
+      }
+    for (i = 0; i <= abfd->section_count; i++)
+      {
+       finfo.section_info[i].relocs = NULL;
+       finfo.section_info[i].rel_hashes = NULL;
+      }
+  }
+
+  /* We now know the size of the relocs, so we can determine the file
+     positions of the line numbers.  */
+  line_filepos = rel_filepos;
+  linesz = bfd_coff_linesz (abfd);
+  max_output_reloc_count = 0;
+  for (o = abfd->sections; o != NULL; o = o->next)
+    {
+      if (o->lineno_count == 0)
+       o->line_filepos = 0;
+      else
+       {
+         o->line_filepos = line_filepos;
+         line_filepos += o->lineno_count * linesz;
+       }
+
+      if (o->reloc_count != 0)
+       {
+         /* We don't know the indices of global symbols until we have
+             written out all the local symbols.  For each section in
+             the output file, we keep an array of pointers to hash
+             table entries.  Each entry in the array corresponds to a
+             reloc.  When we find a reloc against a global symbol, we
+             set the corresponding entry in this array so that we can
+             fix up the symbol index after we have written out all the
+             local symbols.
+
+            Because of this problem, we also keep the relocs in
+            memory until the end of the link.  This wastes memory.
+            We could backpatch the file later, I suppose, although it
+            would be slow.  */
+         finfo.section_info[o->target_index].relocs =
+           ((struct internal_reloc *)
+            malloc (o->reloc_count * sizeof (struct internal_reloc)));
+         finfo.section_info[o->target_index].rel_hashes =
+           ((struct xcoff_link_hash_entry **)
+            malloc (o->reloc_count
+                    * sizeof (struct xcoff_link_hash_entry *)));
+         if (finfo.section_info[o->target_index].relocs == NULL
+             || finfo.section_info[o->target_index].rel_hashes == NULL)
+           {
+             bfd_set_error (bfd_error_no_memory);
+             goto error_return;
+           }
+
+         if (o->reloc_count > max_output_reloc_count)
+           max_output_reloc_count = o->reloc_count;
+       }
+
+      /* Reset the reloc and lineno counts, so that we can use them to
+        count the number of entries we have output so far.  */
+      o->reloc_count = 0;
+      o->lineno_count = 0;
+    }
+
+  obj_sym_filepos (abfd) = line_filepos;
+
+  /* Figure out the largest number of symbols in an input BFD.  Take
+     the opportunity to clear the output_has_begun fields of all the
+     input BFD's.  We want at least 4 symbols, since that is the
+     number which xcoff_write_global_symbol may need.  */
+  max_sym_count = 4;
+  for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
+    {
+      size_t sz;
+
+      sub->output_has_begun = false;
+      sz = obj_raw_syment_count (sub);
+      if (sz > max_sym_count)
+       max_sym_count = sz;
+    }
+
+  /* Allocate some buffers used while linking.  */
+  finfo.internal_syms = ((struct internal_syment *)
+                        malloc (max_sym_count
+                                * sizeof (struct internal_syment)));
+  finfo.sym_indices = (long *) malloc (max_sym_count * sizeof (long));
+  finfo.outsyms = ((bfd_byte *)
+                  malloc ((size_t) ((max_sym_count + 1) * symesz)));
+  finfo.linenos = (bfd_byte *) malloc (max_lineno_count
+                                      * bfd_coff_linesz (abfd));
+  finfo.contents = (bfd_byte *) malloc (max_contents_size);
+  finfo.external_relocs = (bfd_byte *) malloc (max_reloc_count * relsz);
+  if ((finfo.internal_syms == NULL && max_sym_count > 0)
+      || (finfo.sym_indices == NULL && max_sym_count > 0)
+      || finfo.outsyms == NULL
+      || (finfo.linenos == NULL && max_lineno_count > 0)
+      || (finfo.contents == NULL && max_contents_size > 0)
+      || (finfo.external_relocs == NULL && max_reloc_count > 0))
+    {
+      bfd_set_error (bfd_error_no_memory);
+      goto error_return;
+    }
+
+  obj_raw_syment_count (abfd) = 0;
+  xcoff_data (abfd)->toc = (bfd_vma) -1;
+
+  /* Write out all the global symbols which do not come from XCOFF
+     input files.  In COFF these must come at the end, but in XCOFF it
+     doesn't seem to matter.  */
+  xcoff_link_hash_traverse (xcoff_hash_table (info),
+                           xcoff_write_global_symbol,
+                           (PTR) &finfo);
+
+  /* We now know the position of everything in the file, except that
+     we don't know the size of the symbol table and therefore we don't
+     know where the string table starts.  We just build the string
+     table in memory as we go along.  We process all the relocations
+     for a single input file at once.  */
+  for (o = abfd->sections; o != NULL; o = o->next)
+    {
+      for (p = o->link_order_head; p != NULL; p = p->next)
+       {
+         if (p->type == bfd_indirect_link_order
+             && p->u.indirect.section->owner->xvec == abfd->xvec)
+           {
+             sub = p->u.indirect.section->owner;
+             if (! sub->output_has_begun)
+               {
+                 if (! xcoff_link_input_bfd (&finfo, sub))
+                   goto error_return;
+                 sub->output_has_begun = true;
+               }
+           }
+         else if (p->type == bfd_section_reloc_link_order
+                  || p->type == bfd_symbol_reloc_link_order)
+           {
+             if (! xcoff_reloc_link_order (abfd, &finfo, o, p))
+               goto error_return;
+           }
+         else
+           {
+             if (! _bfd_default_link_order (abfd, info, o, p))
+               goto error_return;
+           }
+       }
+    }
+
+  /* Free up the buffers used by xcoff_link_input_bfd.  */
+
+  if (finfo.internal_syms != NULL)
+    {
+      free (finfo.internal_syms);
+      finfo.internal_syms = NULL;
+    }
+  if (finfo.sym_indices != NULL)
+    {
+      free (finfo.sym_indices);
+      finfo.sym_indices = NULL;
+    }
+  if (finfo.linenos != NULL)
+    {
+      free (finfo.linenos);
+      finfo.linenos = NULL;
+    }
+  if (finfo.contents != NULL)
+    {
+      free (finfo.contents);
+      finfo.contents = NULL;
+    }
+  if (finfo.external_relocs != NULL)
+    {
+      free (finfo.external_relocs);
+      finfo.external_relocs = NULL;
+    }
+
+  /* The value of the last C_FILE symbol is supposed to be -1.  Write
+     it out again.  */
+  if (finfo.last_file_index != -1)
+    {
+      finfo.last_file.n_value = -1;
+      bfd_coff_swap_sym_out (abfd, (PTR) &finfo.last_file,
+                            (PTR) finfo.outsyms);
+      if (bfd_seek (abfd,
+                   (obj_sym_filepos (abfd)
+                    + finfo.last_file_index * symesz),
+                   SEEK_SET) != 0
+         || bfd_write (finfo.outsyms, symesz, 1, abfd) != symesz)
+       goto error_return;
+    }
+
+  if (finfo.outsyms != NULL)
+    {
+      free (finfo.outsyms);
+      finfo.outsyms = NULL;
+    }
+
+  /* Now that we have written out all the global symbols, we know the
+     symbol indices to use for relocs against them, and we can finally
+     write out the relocs.  */
+  external_relocs = (bfd_byte *) malloc (max_output_reloc_count * relsz);
+  if (external_relocs == NULL && max_output_reloc_count != 0)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      goto error_return;
+    }
+
+  for (o = abfd->sections; o != NULL; o = o->next)
+    {
+      struct internal_reloc *irel;
+      struct internal_reloc *irelend;
+      struct xcoff_link_hash_entry **rel_hash;
+      bfd_byte *erel;
+
+      if (o->reloc_count == 0)
+       continue;
+
+      /* XCOFF requires that the relocs be sorted by address.  We tend
+         to produce them in the order in which their containing csects
+         appear in the symbol table, which is not necessarily by
+         address.  So we sort them here.  There may be a better way to
+         do this.  */
+      qsort ((PTR) finfo.section_info[o->target_index].relocs,
+            o->reloc_count, sizeof (struct internal_reloc),
+            xcoff_sort_relocs);
+
+      irel = finfo.section_info[o->target_index].relocs;
+      irelend = irel + o->reloc_count;
+      rel_hash = finfo.section_info[o->target_index].rel_hashes;
+      erel = external_relocs;
+      for (; irel < irelend; irel++, rel_hash++, erel += relsz)
+       {
+         if (*rel_hash != NULL)
+           {
+             if ((*rel_hash)->indx < 0)
+               {
+                 if (! ((*info->callbacks->unattached_reloc)
+                        (info, (*rel_hash)->root.root.string, abfd, o,
+                         irel->r_vaddr)))
+                   goto error_return;
+                 (*rel_hash)->indx = 0;
+               }
+             irel->r_symndx = (*rel_hash)->indx;
+           }
+         bfd_coff_swap_reloc_out (abfd, (PTR) irel, (PTR) erel);
+       }
+
+      if (bfd_seek (abfd, o->rel_filepos, SEEK_SET) != 0
+         || bfd_write ((PTR) external_relocs, relsz, o->reloc_count,
+                       abfd) != relsz * o->reloc_count)
+       goto error_return;
+    }
+
+  if (external_relocs != NULL)
+    {
+      free (external_relocs);
+      external_relocs = NULL;
+    }
+
+  /* Free up the section information.  */
+  if (finfo.section_info != NULL)
+    {
+      unsigned int i;
+
+      for (i = 0; i < abfd->section_count; i++)
+       {
+         if (finfo.section_info[i].relocs != NULL)
+           free (finfo.section_info[i].relocs);
+         if (finfo.section_info[i].rel_hashes != NULL)
+           free (finfo.section_info[i].rel_hashes);
+       }
+      free (finfo.section_info);
+      finfo.section_info = NULL;
+    }
+
+  /* Write out the string table.  */
+  if (bfd_seek (abfd,
+               (obj_sym_filepos (abfd)
+                + obj_raw_syment_count (abfd) * symesz),
+               SEEK_SET) != 0)
+    goto error_return;
+  bfd_h_put_32 (abfd,
+               _bfd_stringtab_size (finfo.strtab) + STRING_SIZE_SIZE,
+               (bfd_byte *) strbuf);
+  if (bfd_write (strbuf, 1, STRING_SIZE_SIZE, abfd) != STRING_SIZE_SIZE)
+    goto error_return;
+  if (! _bfd_stringtab_emit (abfd, finfo.strtab))
+    goto error_return;
+
+  _bfd_stringtab_free (finfo.strtab);
+
+  /* Write out the debugging string table.  */
+  o = xcoff_hash_table (info)->debug_section;
+  if (o != NULL)
+    {
+      struct bfd_strtab_hash *debug_strtab;
+
+      debug_strtab = xcoff_hash_table (info)->debug_strtab;
+      BFD_ASSERT (o->output_section->_raw_size - o->output_offset
+                 >= _bfd_stringtab_size (debug_strtab));
+      if (bfd_seek (abfd,
+                   o->output_section->filepos + o->output_offset,
+                   SEEK_SET) != 0)
+       goto error_return;
+      if (! _bfd_stringtab_emit (abfd, debug_strtab))
+       goto error_return;
+    }
+
+  /* Setting bfd_get_symcount to 0 will cause write_object_contents to
+     not try to write out the symbols.  */
+  bfd_get_symcount (abfd) = 0;
+
+  return true;
+
+ error_return:
+  if (finfo.strtab != NULL)
+    _bfd_stringtab_free (finfo.strtab);
+  if (finfo.section_info != NULL)
+    {
+      unsigned int i;
+
+      for (i = 0; i < abfd->section_count; i++)
+       {
+         if (finfo.section_info[i].relocs != NULL)
+           free (finfo.section_info[i].relocs);
+         if (finfo.section_info[i].rel_hashes != NULL)
+           free (finfo.section_info[i].rel_hashes);
+       }
+      free (finfo.section_info);
+    }
+  if (finfo.internal_syms != NULL)
+    free (finfo.internal_syms);
+  if (finfo.sym_indices != NULL)
+    free (finfo.sym_indices);
+  if (finfo.outsyms != NULL)
+    free (finfo.outsyms);
+  if (finfo.linenos != NULL)
+    free (finfo.linenos);
+  if (finfo.contents != NULL)
+    free (finfo.contents);
+  if (finfo.external_relocs != NULL)
+    free (finfo.external_relocs);
+  if (external_relocs != NULL)
+    free (external_relocs);
+  return false;
+}
+
+/* Link an input file into the linker output file.  This function
+   handles all the sections and relocations of the input file at once.  */
+
+static boolean
+xcoff_link_input_bfd (finfo, input_bfd)
+     struct xcoff_final_link_info *finfo;
+     bfd *input_bfd;
+{
+  bfd *output_bfd;
+  const char *strings;
+  bfd_size_type syment_base;
+  unsigned int n_tmask;
+  unsigned int n_btshft;
+  boolean copy, hash;
+  bfd_size_type isymesz;
+  bfd_size_type osymesz;
+  bfd_size_type linesz;
+  bfd_byte *esym;
+  bfd_byte *esym_end;
+  struct internal_syment *isymp;
+  asection **csectpp;
+  unsigned long *debug_index;
+  long *indexp;
+  unsigned long output_index;
+  bfd_byte *outsym;
+  struct xcoff_link_hash_entry **sym_hash;
+  boolean keep_syms;
+  asection *o;
+
+  /* Move all the symbols to the output file.  */
+
+  output_bfd = finfo->output_bfd;
+  strings = NULL;
+  syment_base = obj_raw_syment_count (output_bfd);
+  isymesz = bfd_coff_symesz (input_bfd);
+  osymesz = bfd_coff_symesz (output_bfd);
+  linesz = bfd_coff_linesz (input_bfd);
+  BFD_ASSERT (linesz == bfd_coff_linesz (output_bfd));
+
+  n_tmask = coff_data (input_bfd)->local_n_tmask;
+  n_btshft = coff_data (input_bfd)->local_n_btshft;
+
+  /* Define macros so that ISFCN, et. al., macros work correctly.  */
+#define N_TMASK n_tmask
+#define N_BTSHFT n_btshft
+
+  copy = false;
+  if (! finfo->info->keep_memory)
+    copy = true;
+  hash = true;
+  if ((output_bfd->flags & BFD_TRADITIONAL_FORMAT) != 0)
+    hash = false;
+
+  if (! _bfd_coff_get_external_symbols (input_bfd))
+    return false;
+
+  esym = (bfd_byte *) obj_coff_external_syms (input_bfd);
+  esym_end = esym + obj_raw_syment_count (input_bfd) * isymesz;
+  sym_hash = obj_xcoff_sym_hashes (input_bfd);
+  csectpp = xcoff_data (input_bfd)->csects;
+  debug_index = xcoff_data (input_bfd)->debug_indices;
+  isymp = finfo->internal_syms;
+  indexp = finfo->sym_indices;
+  output_index = syment_base;
+  outsym = finfo->outsyms;
+
+  while (esym < esym_end)
+    {
+      struct internal_syment isym;
+      union internal_auxent aux;
+      int smtyp = 0;
+      boolean skip;
+      boolean require;
+      int add;
+
+      bfd_coff_swap_sym_in (input_bfd, (PTR) esym, (PTR) isymp);
+
+      /* If this is a C_EXT or C_HIDEXT symbol, we need the csect
+         information.  */
+      if (isymp->n_sclass == C_EXT || isymp->n_sclass == C_HIDEXT)
+       {
+         BFD_ASSERT (isymp->n_numaux > 0);
+         bfd_coff_swap_aux_in (input_bfd,
+                               (PTR) (esym + isymesz * isymp->n_numaux),
+                               isymp->n_type, isymp->n_sclass,
+                               isymp->n_numaux - 1, isymp->n_numaux,
+                               (PTR) &aux);
+         smtyp = SMTYP_SMTYP (aux.x_csect.x_smtyp);
+       }
+
+      /* Make a copy of *isymp so that the relocate_section function
+        always sees the original values.  This is more reliable than
+        always recomputing the symbol value even if we are stripping
+        the symbol.  */
+      isym = *isymp;
+
+      *indexp = -1;
+
+      skip = false;
+      require = false;
+      add = 1 + isym.n_numaux;
+
+      /* If we are skipping this csect, we want to skip this symbol.  */
+      if (*csectpp == NULL)
+       skip = true;
+
+      /* An XCOFF linker always skips C_STAT symbols.  */
+      if (! skip
+         && isymp->n_sclass == C_STAT)
+       skip = true;
+
+      /* We skip all but the first TOC anchor.  */
+      if (! skip
+         && isymp->n_sclass == C_HIDEXT
+         && aux.x_csect.x_smclas == XMC_TC0)
+       {
+         if (finfo->toc_symindx != -1)
+           skip = true;
+         else
+           {
+             finfo->toc_symindx = output_index;
+             xcoff_data (finfo->output_bfd)->toc =
+               ((*csectpp)->output_section->vma
+                + (*csectpp)->output_offset
+                + isym.n_value
+                - (*csectpp)->vma);
+             require = true;
+           }
+       }
+
+      /* If we are stripping all symbols, we want to skip this one.  */
+      if (! skip
+         && finfo->info->strip == strip_all)
+       skip = true;
+
+      /* We can skip resolved external references.  */
+      if (! skip
+         && isym.n_sclass == C_EXT
+         && smtyp == XTY_ER
+         && (*sym_hash)->root.type != bfd_link_hash_undefined)
+       skip = true;
+
+      /* Skip local symbols if we are discarding them.  */
+      if (! skip
+         && finfo->info->discard == discard_all
+         && isym.n_sclass != C_EXT
+         && (isym.n_sclass != C_HIDEXT
+             || smtyp != XTY_SD))
+       skip = true;
+
+      /* If we stripping debugging symbols, and this is a debugging
+         symbol, then skip it.  */
+      if (! skip
+         && finfo->info->strip == strip_debugger
+         && isym.n_scnum == N_DEBUG)
+       skip = true;
+
+      /* If some symbols are stripped based on the name, work out the
+        name and decide whether to skip this symbol.  We don't handle
+        this correctly for symbols whose names are in the .debug
+        section; to get it right we would need a new bfd_strtab_hash
+        function to return the string given the index.  */
+      if (! skip
+         && (finfo->info->strip == strip_some
+             || finfo->info->discard == discard_l)
+         && (debug_index == NULL || *debug_index == (unsigned long) -1))
+       {
+         const char *name;
+         char buf[SYMNMLEN + 1];
+
+         name = _bfd_coff_internal_syment_name (input_bfd, &isym, buf);
+         if (name == NULL)
+           return false;
+
+         if ((finfo->info->strip == strip_some
+              && (bfd_hash_lookup (finfo->info->keep_hash, name, false,
+                                   false) == NULL))
+             || (finfo->info->discard == discard_l
+                 && (isym.n_sclass != C_EXT
+                     && (isym.n_sclass != C_HIDEXT
+                         || smtyp != XTY_SD))
+                 && strncmp (name, finfo->info->lprefix,
+                             finfo->info->lprefix_len) == 0))
+           skip = true;
+       }
+
+      /* On the other hand, we can't skip global symbols which have
+         relocs against them.  FIXME: This won't always work, because
+         we're not always outputting the global symbols at the end, as
+         we do for COFF.  */
+      if (skip
+         && *sym_hash != NULL
+         && (*sym_hash)->indx == -2
+         && finfo->info->strip != strip_all)
+       skip = false;
+
+      /* We can not skip the first TOC anchor.  */
+      if (skip
+         && require
+         && finfo->info->strip != strip_all)
+       skip = false;
+
+      /* We now know whether we are to skip this symbol or not.  */
+      if (! skip)
+       {
+         /* Adjust the symbol in order to output it.  */
+
+         if (isym._n._n_n._n_zeroes == 0
+             && isym._n._n_n._n_offset != 0)
+           {
+             /* This symbol has a long name.  Enter it in the string
+                table we are building.  If *debug_index != -1, the
+                name has already been entered in the .debug section.  */
+             if (debug_index != NULL && *debug_index != (unsigned long) -1)
+               isym._n._n_n._n_offset = *debug_index;
+             else
+               {
+                 const char *name;
+                 bfd_size_type indx;
+
+                 name = _bfd_coff_internal_syment_name (input_bfd, &isym,
+                                                        (char *) NULL);
+                 if (name == NULL)
+                   return false;
+                 indx = _bfd_stringtab_add (finfo->strtab, name, hash, copy);
+                 if (indx == (bfd_size_type) -1)
+                   return false;
+                 isym._n._n_n._n_offset = STRING_SIZE_SIZE + indx;
+               }
+           }
+
+         if (isym.n_sclass == C_BSTAT)
+           {
+             unsigned long indx;
+
+             /* The value of a C_BSTAT symbol is the symbol table
+                 index of the containing csect.  */
+
+             indx = isym.n_value;
+             if (indx < obj_raw_syment_count (input_bfd))
+               {
+                 long symindx;
+
+                 symindx = finfo->sym_indices[indx];
+                 if (symindx < 0)
+                   isym.n_value = 0;
+                 else
+                   isym.n_value = symindx;
+               }
+           }
+         else if (isym.n_scnum > 0)
+           {
+             isym.n_scnum = (*csectpp)->output_section->target_index;
+             isym.n_value += ((*csectpp)->output_section->vma
+                              + (*csectpp)->output_offset
+                              - (*csectpp)->vma);
+           }
+
+         /* The value of a C_FILE symbol is the symbol index of the
+            next C_FILE symbol.  The value of the last C_FILE symbol
+            is -1.  We try to get this right, below, just before we
+            write the symbols out, but in the general case we may
+            have to write the symbol out twice.  */
+         if (isym.n_sclass == C_FILE)
+           {
+             if (finfo->last_file_index != -1
+                 && finfo->last_file.n_value != (long) output_index)
+               {
+                 /* We must correct the value of the last C_FILE entry.  */
+                 finfo->last_file.n_value = output_index;
+                 if ((bfd_size_type) finfo->last_file_index >= syment_base)
+                   {
+                     /* The last C_FILE symbol is in this input file.  */
+                     bfd_coff_swap_sym_out (output_bfd,
+                                            (PTR) &finfo->last_file,
+                                            (PTR) (finfo->outsyms
+                                                   + ((finfo->last_file_index
+                                                       - syment_base)
+                                                      * osymesz)));
+                   }
+                 else
+                   {
+                     /* We have already written out the last C_FILE
+                        symbol.  We need to write it out again.  We
+                        borrow *outsym temporarily.  */
+                     bfd_coff_swap_sym_out (output_bfd,
+                                            (PTR) &finfo->last_file,
+                                            (PTR) outsym);
+                     if (bfd_seek (output_bfd,
+                                   (obj_sym_filepos (output_bfd)
+                                    + finfo->last_file_index * osymesz),
+                                   SEEK_SET) != 0
+                         || (bfd_write (outsym, osymesz, 1, output_bfd)
+                             != osymesz))
+                       return false;
+                   }
+               }
+
+             finfo->last_file_index = output_index;
+             finfo->last_file = isym;
+           }
+
+         /* Output the symbol.  */
+
+         bfd_coff_swap_sym_out (output_bfd, (PTR) &isym, (PTR) outsym);
+
+         *indexp = output_index;
+
+         if (isym.n_sclass == C_EXT)
+           {
+             long indx;
+             struct xcoff_link_hash_entry *h;
+
+             indx = ((esym - (bfd_byte *) obj_coff_external_syms (input_bfd))
+                     / isymesz);
+             h = obj_xcoff_sym_hashes (input_bfd)[indx];
+             BFD_ASSERT (h != NULL);
+             h->indx = output_index;
+           }
+
+         output_index += add;
+         outsym += add * osymesz;
+       }
+
+      esym += add * isymesz;
+      isymp += add;
+      csectpp += add;
+      sym_hash += add;
+      if (debug_index != NULL)
+       debug_index += add;
+      ++indexp;
+      for (--add; add > 0; --add)
+       *indexp++ = -1;
+    }
+
+  /* Fix up the aux entries.  This must be done in a separate pass,
+     because we don't know the correct symbol indices until we have
+     already decided which symbols we are going to keep.  */
+
+  esym = (bfd_byte *) obj_coff_external_syms (input_bfd);
+  esym_end = esym + obj_raw_syment_count (input_bfd) * isymesz;
+  isymp = finfo->internal_syms;
+  indexp = finfo->sym_indices;
+  outsym = finfo->outsyms;
+  while (esym < esym_end)
+    {
+      int add;
+
+      add = 1 + isymp->n_numaux;
+
+      if (*indexp < 0)
+       esym += add * isymesz;
+      else
+       {
+         int i;
+
+         esym += isymesz;
+         outsym += osymesz;
+
+         for (i = 0; i < isymp->n_numaux && esym < esym_end; i++)
+           {
+             union internal_auxent aux;
+
+             bfd_coff_swap_aux_in (input_bfd, (PTR) esym, isymp->n_type,
+                                   isymp->n_sclass, i, isymp->n_numaux,
+                                   (PTR) &aux);
+
+             if (isymp->n_sclass == C_FILE)
+               {
+                 /* This is the file name (or some comment put in by
+                    the compiler).  If it is long, we must put it in
+                    the string table.  */
+                 if (aux.x_file.x_n.x_zeroes == 0
+                     && aux.x_file.x_n.x_offset != 0)
+                   {
+                     const char *filename;
+                     bfd_size_type indx;
+
+                     BFD_ASSERT (aux.x_file.x_n.x_offset
+                                 >= STRING_SIZE_SIZE);
+                     if (strings == NULL)
+                       {
+                         strings = _bfd_coff_read_string_table (input_bfd);
+                         if (strings == NULL)
+                           return false;
+                       }
+                     filename = strings + aux.x_file.x_n.x_offset;
+                     indx = _bfd_stringtab_add (finfo->strtab, filename,
+                                                hash, copy);
+                     if (indx == (bfd_size_type) -1)
+                       return false;
+                     aux.x_file.x_n.x_offset = STRING_SIZE_SIZE + indx;
+                   }
+               }
+             else if ((isymp->n_sclass == C_EXT
+                       || isymp->n_sclass == C_HIDEXT)
+                      && i + 1 == isymp->n_numaux)
+               {
+                 /* We don't support type checking.  I don't know if
+                     anybody does.  */
+                 aux.x_csect.x_parmhash = 0;
+                 /* I don't think anybody uses these fields, but we'd
+                     better clobber them just in case.  */
+                 aux.x_csect.x_stab = 0;
+                 aux.x_csect.x_snstab = 0;
+                 if (SMTYP_SMTYP (aux.x_csect.x_smtyp) == XTY_LD)
+                   {
+                     unsigned long indx;
+
+                     indx = aux.x_csect.x_scnlen.l;
+                     if (indx < obj_raw_syment_count (input_bfd))
+                       {
+                         long symindx;
+
+                         symindx = finfo->sym_indices[indx];
+                         if (symindx < 0)
+                           aux.x_sym.x_tagndx.l = 0;
+                         else
+                           aux.x_sym.x_tagndx.l = symindx;
+                       }
+                   }
+               }
+             else if (isymp->n_sclass != C_STAT || isymp->n_type != T_NULL)
+               {
+                 unsigned long indx;
+
+                 if (ISFCN (isymp->n_type)
+                     || ISTAG (isymp->n_sclass)
+                     || isymp->n_sclass == C_BLOCK)
+                   {
+                     indx = aux.x_sym.x_fcnary.x_fcn.x_endndx.l;
+                     if (indx > 0
+                         && indx < obj_raw_syment_count (input_bfd))
+                       {
+                         /* We look forward through the symbol for
+                             the index of the next symbol we are going
+                             to include.  I don't know if this is
+                             entirely right.  */
+                         while (finfo->sym_indices[indx] < 0
+                                && indx < obj_raw_syment_count (input_bfd))
+                           ++indx;
+                         if (indx >= obj_raw_syment_count (input_bfd))
+                           indx = output_index;
+                         else
+                           indx = finfo->sym_indices[indx];
+                         aux.x_sym.x_fcnary.x_fcn.x_endndx.l = indx;
+                       }
+                   }
+
+                 indx = aux.x_sym.x_tagndx.l;
+                 if (indx > 0 && indx < obj_raw_syment_count (input_bfd))
+                   {
+                     long symindx;
+
+                     symindx = finfo->sym_indices[indx];
+                     if (symindx < 0)
+                       aux.x_sym.x_tagndx.l = 0;
+                     else
+                       aux.x_sym.x_tagndx.l = symindx;
+                   }
+               }
+
+             bfd_coff_swap_aux_out (output_bfd, (PTR) &aux, isymp->n_type,
+                                    isymp->n_sclass, i, isymp->n_numaux,
+                                    (PTR) outsym);
+             outsym += osymesz;
+             esym += isymesz;
+           }
+       }
+
+      indexp += add;
+      isymp += add;
+    }
+
+  /* Relocate the line numbers, unless we are stripping them.  */
+  if (finfo->info->strip == strip_none
+      || finfo->info->strip == strip_some)
+    {
+      for (o = input_bfd->sections; o != NULL; o = o->next)
+       {
+         bfd_vma offset;
+         bfd_byte *eline;
+         bfd_byte *elineend;
+
+         /* FIXME: If SEC_HAS_CONTENTS is not set for the section,
+            then build_link_order in ldwrite.c will not have created
+            a link order, which means that we will not have seen this
+            input section in _bfd_xcoff_final_link, which means that
+            we will not have allocated space for the line numbers of
+            this section.  I don't think line numbers can be
+            meaningful for a section which does not have
+            SEC_HAS_CONTENTS set, but, if they do, this must be
+            changed.  */
+         if (o->lineno_count == 0
+             || (o->output_section->flags & SEC_HAS_CONTENTS) == 0)
+           continue;
+
+         if (bfd_seek (input_bfd, o->line_filepos, SEEK_SET) != 0
+             || bfd_read (finfo->linenos, linesz, o->lineno_count,
+                          input_bfd) != linesz * o->lineno_count)
+           return false;
+
+         offset = o->output_section->vma + o->output_offset - o->vma;
+         eline = finfo->linenos;
+         elineend = eline + linesz * o->lineno_count;
+         for (; eline < elineend; eline += linesz)
+           {
+             struct internal_lineno iline;
+
+             bfd_coff_swap_lineno_in (input_bfd, (PTR) eline, (PTR) &iline);
+
+             if (iline.l_lnno != 0)
+               iline.l_addr.l_paddr += offset;
+             else if (iline.l_addr.l_symndx >= 0
+                      && ((unsigned long) iline.l_addr.l_symndx
+                          < obj_raw_syment_count (input_bfd)))
+               {
+                 long indx;
+
+                 indx = finfo->sym_indices[iline.l_addr.l_symndx];
+
+                 if (indx < 0)
+                   {
+                     /* These line numbers are attached to a symbol
+                        which we are stripping.  We should really
+                        just discard the line numbers, but that would
+                        be a pain because we have already counted
+                        them.  */
+                     indx = 0;
+                   }
+                 else
+                   {
+                     struct internal_syment is;
+                     union internal_auxent ia;
+
+                     /* Fix up the lnnoptr field in the aux entry of
+                        the symbol.  It turns out that we can't do
+                        this when we modify the symbol aux entries,
+                        because gas sometimes screws up the lnnoptr
+                        field and makes it an offset from the start
+                        of the line numbers rather than an absolute
+                        file index.  */
+                     bfd_coff_swap_sym_in (output_bfd,
+                                           (PTR) (finfo->outsyms
+                                                  + ((indx - syment_base)
+                                                     * osymesz)),
+                                           (PTR) &is);
+                     if ((ISFCN (is.n_type)
+                          || is.n_sclass == C_BLOCK)
+                         && ((is.n_sclass == C_EXT
+                              || is.n_sclass == C_HIDEXT)
+                             ? is.n_numaux >= 2
+                             : is.n_numaux >= 1))
+                       {
+                         PTR auxptr;
+
+                         auxptr = (PTR) (finfo->outsyms
+                                         + ((indx - syment_base + 1)
+                                            * osymesz));
+                         bfd_coff_swap_aux_in (output_bfd, auxptr,
+                                               is.n_type, is.n_sclass,
+                                               0, is.n_numaux, (PTR) &ia);
+                         ia.x_sym.x_fcnary.x_fcn.x_lnnoptr =
+                           (o->output_section->line_filepos
+                            + o->output_section->lineno_count * linesz
+                            + eline - finfo->linenos);
+                         bfd_coff_swap_aux_out (output_bfd, (PTR) &ia,
+                                                is.n_type, is.n_sclass, 0,
+                                                is.n_numaux, auxptr);
+                       }
+                   }
+
+                 iline.l_addr.l_symndx = indx;
+               }
+
+             bfd_coff_swap_lineno_out (output_bfd, (PTR) &iline, (PTR) eline);
+           }
+
+         if (bfd_seek (output_bfd,
+                       (o->output_section->line_filepos
+                        + o->output_section->lineno_count * linesz),
+                       SEEK_SET) != 0
+             || bfd_write (finfo->linenos, linesz, o->lineno_count,
+                           output_bfd) != linesz * o->lineno_count)
+           return false;
+
+         o->output_section->lineno_count += o->lineno_count;
+       }
+    }
+
+  /* If we swapped out a C_FILE symbol, guess that the next C_FILE
+     symbol will be the first symbol in the next input file.  In the
+     normal case, this will save us from writing out the C_FILE symbol
+     again.  */
+  if (finfo->last_file_index != -1
+      && (bfd_size_type) finfo->last_file_index >= syment_base)
+    {
+      finfo->last_file.n_value = output_index;
+      bfd_coff_swap_sym_out (output_bfd, (PTR) &finfo->last_file,
+                            (PTR) (finfo->outsyms
+                                   + ((finfo->last_file_index - syment_base)
+                                      * osymesz)));
+    }
+
+  /* Write the modified symbols to the output file.  */
+  if (outsym > finfo->outsyms)
+    {
+      if (bfd_seek (output_bfd,
+                   obj_sym_filepos (output_bfd) + syment_base * osymesz,
+                   SEEK_SET) != 0
+         || (bfd_write (finfo->outsyms, outsym - finfo->outsyms, 1,
+                       output_bfd)
+             != (bfd_size_type) (outsym - finfo->outsyms)))
+       return false;
+
+      BFD_ASSERT ((obj_raw_syment_count (output_bfd)
+                  + (outsym - finfo->outsyms) / osymesz)
+                 == output_index);
+
+      obj_raw_syment_count (output_bfd) = output_index;
+    }
+
+  /* Don't let the linker relocation routines discard the symbols.  */
+  keep_syms = obj_coff_keep_syms (input_bfd);
+  obj_coff_keep_syms (input_bfd) = true;
+
+  /* Relocate the contents of each section.  */
+  for (o = input_bfd->sections; o != NULL; o = o->next)
+    {
+      bfd_byte *contents;
+
+      if ((o->flags & SEC_HAS_CONTENTS) == 0
+         || o->_raw_size == 0)
+       continue;
+
+      /* We have set filepos correctly for the sections we created to
+         represent csects, so bfd_get_section_contents should work.  */
+      if (coff_section_data (input_bfd, o) != NULL
+         && coff_section_data (input_bfd, o)->contents != NULL)
+       contents = coff_section_data (input_bfd, o)->contents;
+      else
+       {
+         if (! bfd_get_section_contents (input_bfd, o, finfo->contents,
+                                         (file_ptr) 0, o->_raw_size))
+           return false;
+         contents = finfo->contents;
+       }
+
+      if ((o->flags & SEC_RELOC) != 0)
+       {
+         int target_index;
+         struct internal_reloc *internal_relocs;
+         struct internal_reloc *irel;
+         bfd_vma offset;
+         struct internal_reloc *irelend;
+         struct xcoff_link_hash_entry **rel_hash;
+
+         /* Read in the relocs.  We have set rel_filepos correctly
+             for the sections we created to represent csects, so this
+             should work.  */
+         target_index = o->output_section->target_index;
+         internal_relocs = (_bfd_coff_read_internal_relocs
+                            (input_bfd, o, false, finfo->external_relocs,
+                             true,
+                             (finfo->section_info[target_index].relocs
+                              + o->output_section->reloc_count)));
+         if (internal_relocs == NULL)
+           return false;
+
+         /* Call processor specific code to relocate the section
+             contents.  */
+         if (! bfd_coff_relocate_section (output_bfd, finfo->info,
+                                          input_bfd, o,
+                                          contents,
+                                          internal_relocs,
+                                          finfo->internal_syms,
+                                          xcoff_data (input_bfd)->csects))
+           return false;
+
+         offset = o->output_section->vma + o->output_offset - o->vma;
+         irel = internal_relocs;
+         irelend = irel + o->reloc_count;
+         rel_hash = (finfo->section_info[target_index].rel_hashes
+                     + o->output_section->reloc_count);
+         for (; irel < irelend; irel++, rel_hash++)
+           {
+             struct xcoff_link_hash_entry *h;
+
+             *rel_hash = NULL;
+
+             /* Adjust the reloc address and symbol index.  */
+
+             irel->r_vaddr += offset;
+
+             if (irel->r_symndx == -1)
+               continue;
+
+             h = obj_xcoff_sym_hashes (input_bfd)[irel->r_symndx];
+             if (h != NULL)
+               {
+                 /* This is a global symbol.  */
+                 if (h->indx >= 0)
+                   irel->r_symndx = h->indx;
+                 else
+                   {
+                     /* This symbol is being written at the end of
+                        the file, and we do not yet know the symbol
+                        index.  We save the pointer to the hash table
+                        entry in the rel_hash list.  We set the indx
+                        field to -2 to indicate that this symbol must
+                        not be stripped.  */
+                     *rel_hash = h;
+                     h->indx = -2;
+                   }
+               }
+             else
+               {
+                 long indx;
+
+                 indx = finfo->sym_indices[irel->r_symndx];
+
+                 if (indx == -1)
+                   {
+                     struct internal_syment *is;
+
+                     /* Relocations against a TC0 TOC anchor are
+                         automatically transformed to be against the
+                         TOC anchor in the output file.  */
+                     is = finfo->internal_syms + irel->r_symndx;
+                     if (is->n_sclass == C_HIDEXT
+                         && is->n_numaux > 0)
+                       {
+                         PTR auxptr;
+                         union internal_auxent aux;
+
+                         auxptr = (PTR) (((bfd_byte *)
+                                          obj_coff_external_syms (input_bfd))
+                                         + ((irel->r_symndx + is->n_numaux)
+                                            * isymesz));
+                         bfd_coff_swap_aux_in (input_bfd, auxptr,
+                                               is->n_type, is->n_sclass,
+                                               is->n_numaux - 1,
+                                               is->n_numaux,
+                                               (PTR) &aux);
+                         if (SMTYP_SMTYP (aux.x_csect.x_smtyp) == XTY_SD
+                             && aux.x_csect.x_smclas == XMC_TC0)
+                           indx = finfo->toc_symindx;
+                       }
+                   }
+
+                 if (indx != -1)
+                   irel->r_symndx = indx;
+                 else
+                   {
+                     struct internal_syment *is;
+                     const char *name;
+                     char buf[SYMNMLEN + 1];
+
+                     /* This reloc is against a symbol we are
+                        stripping.  It would be possible to handle
+                        this case, but I don't think it's worth it.  */
+                     is = finfo->internal_syms + irel->r_symndx;
+
+                     name = (_bfd_coff_internal_syment_name
+                             (input_bfd, is, buf));
+                     if (name == NULL)
+                       return false;
+
+                     if (! ((*finfo->info->callbacks->unattached_reloc)
+                            (finfo->info, name, input_bfd, o,
+                             irel->r_vaddr)))
+                       return false;
+                   }
+               }
+           }
+
+         o->output_section->reloc_count += o->reloc_count;
+       }
+
+      /* Write out the modified section contents.  */
+      if (! bfd_set_section_contents (output_bfd, o->output_section,
+                                     contents, o->output_offset,
+                                     (o->_cooked_size != 0
+                                      ? o->_cooked_size
+                                      : o->_raw_size)))
+       return false;
+    }
+
+  obj_coff_keep_syms (input_bfd) = keep_syms;
+
+  if (! finfo->info->keep_memory)
+    {
+      if (! _bfd_coff_free_symbols (input_bfd))
+       return false;
+    }
+
+  return true;
+}
+
+/* Write out a non-XCOFF global symbol.  */
+
+static boolean
+xcoff_write_global_symbol (h, p)
+     struct xcoff_link_hash_entry *h;
+     PTR p;
+{
+  struct xcoff_final_link_info *finfo = (struct xcoff_final_link_info *) p;
+  bfd *output_bfd;
+  bfd_byte *outsym;
+  struct internal_syment isym;
+  union internal_auxent aux;
+
+  /* XCOFF global symbols will be written out in xcoff_link_input_bfd.
+     The only symbols we need to handle here are those from non-XCOFF
+     files and from the linker script.  */
+  if (h->xcoff)
+    return true;
+
+  output_bfd = finfo->output_bfd;
+  outsym = finfo->outsyms;
+
+  memset (&aux, 0, sizeof aux);
+
+  h->indx = obj_raw_syment_count (output_bfd);
+
+  if (strlen (h->root.root.string) <= SYMNMLEN)
+    strncpy (isym._n._n_name, h->root.root.string, SYMNMLEN);
+  else
+    {
+      boolean hash;
+      bfd_size_type indx;
+
+      hash = true;
+      if ((output_bfd->flags & BFD_TRADITIONAL_FORMAT) != 0)
+       hash = false;
+      indx = _bfd_stringtab_add (finfo->strtab, h->root.root.string, hash,
+                                false);
+      if (indx == (bfd_size_type) -1)
+       return false;
+      isym._n._n_n._n_zeroes = 0;
+      isym._n._n_n._n_offset = STRING_SIZE_SIZE + indx;
+    }
+
+  if (h->root.type == bfd_link_hash_undefined
+      || h->root.type == bfd_link_hash_undefweak)
+    {
+      isym.n_value = 0;
+      isym.n_scnum = N_UNDEF;
+      isym.n_sclass = C_EXT;
+      aux.x_csect.x_smtyp = XTY_ER;
+    }
+  else if (h->root.type == bfd_link_hash_defined
+          || h->root.type == bfd_link_hash_defweak)
+    {
+      isym.n_value = (h->root.u.def.section->output_section->vma
+                     + h->root.u.def.section->output_offset
+                     + h->root.u.def.value);
+      isym.n_scnum = h->root.u.def.section->output_section->target_index;
+      isym.n_sclass = C_HIDEXT;
+      aux.x_csect.x_smtyp = XTY_SD;
+      /* I don't know what the csect length should be in this case.  */
+    }
+  else
+    abort ();
+
+  isym.n_type = T_NULL;
+  isym.n_numaux = 1;
+
+  bfd_coff_swap_sym_out (output_bfd, (PTR) &isym, (PTR) outsym);
+  outsym += bfd_coff_symesz (output_bfd);
+
+  aux.x_csect.x_smclas = XMC_UA;
+
+  bfd_coff_swap_aux_out (output_bfd, (PTR) &aux, T_NULL, isym.n_sclass, 0, 1,
+                        (PTR) outsym);
+  outsym += bfd_coff_auxesz (output_bfd);
+
+  if ((h->root.type == bfd_link_hash_defined
+       || h->root.type == bfd_link_hash_defweak)
+      && aux.x_csect.x_smclas != XMC_TC0)
+    {
+      /* We just output an SD symbol.  Now output an LD symbol.  */
+
+      h->indx += 2;
+
+      isym.n_sclass = C_EXT;
+      bfd_coff_swap_sym_out (output_bfd, (PTR) &isym, (PTR) outsym);
+      outsym += bfd_coff_symesz (output_bfd);
+
+      aux.x_csect.x_smtyp = XTY_LD;
+      aux.x_csect.x_scnlen.l = obj_raw_syment_count (output_bfd);
+
+      bfd_coff_swap_aux_out (output_bfd, (PTR) &aux, T_NULL, C_EXT, 0, 1,
+                            (PTR) outsym);
+      outsym += bfd_coff_auxesz (output_bfd);
+    }
+
+  if (bfd_seek (output_bfd,
+               (obj_sym_filepos (output_bfd)
+                + (obj_raw_syment_count (output_bfd)
+                   * bfd_coff_symesz (output_bfd))),
+               SEEK_SET) != 0
+      || (bfd_write (finfo->outsyms, outsym - finfo->outsyms, 1, output_bfd)
+         != (bfd_size_type) (outsym - finfo->outsyms)))
+    return false;
+  obj_raw_syment_count (output_bfd) +=
+    (outsym - finfo->outsyms) / bfd_coff_symesz (output_bfd);
+
+  return true;
+}
+
+/* Handle a link order which is supposed to generate a reloc.  */
+
+static boolean
+xcoff_reloc_link_order (output_bfd, finfo, output_section, link_order)
+     bfd *output_bfd;
+     struct xcoff_final_link_info *finfo;
+     asection *output_section;
+     struct bfd_link_order *link_order;
+{
+  reloc_howto_type *howto;
+  struct internal_reloc *irel;
+  struct xcoff_link_hash_entry **rel_hash_ptr;
+
+  howto = bfd_reloc_type_lookup (output_bfd, link_order->u.reloc.p->reloc);
+  if (howto == NULL)
+    {
+      bfd_set_error (bfd_error_bad_value);
+      return false;
+    }
+
+  if (link_order->u.reloc.p->addend != 0)
+    {
+      bfd_size_type size;
+      bfd_byte *buf;
+      bfd_reloc_status_type rstat;
+      boolean ok;
+
+      size = bfd_get_reloc_size (howto);
+      buf = (bfd_byte *) bfd_zmalloc (size);
+      if (buf == NULL)
+       {
+         bfd_set_error (bfd_error_no_memory);
+         return false;
+       }
+
+      rstat = _bfd_relocate_contents (howto, output_bfd,
+                                     link_order->u.reloc.p->addend, buf);
+      switch (rstat)
+       {
+       case bfd_reloc_ok:
+         break;
+       default:
+       case bfd_reloc_outofrange:
+         abort ();
+       case bfd_reloc_overflow:
+         if (! ((*finfo->info->callbacks->reloc_overflow)
+                (finfo->info,
+                 (link_order->type == bfd_section_reloc_link_order
+                  ? bfd_section_name (output_bfd,
+                                      link_order->u.reloc.p->u.section)
+                  : link_order->u.reloc.p->u.name),
+                 howto->name, link_order->u.reloc.p->addend,
+                 (bfd *) NULL, (asection *) NULL, (bfd_vma) 0)))
+           {
+             free (buf);
+             return false;
+           }
+         break;
+       }
+      ok = bfd_set_section_contents (output_bfd, output_section, (PTR) buf,
+                                    (file_ptr) link_order->offset, size);
+      free (buf);
+      if (! ok)
+       return false;
+    }
+
+  /* Store the reloc information in the right place.  It will get
+     swapped and written out at the end of the final_link routine.  */
+
+  irel = (finfo->section_info[output_section->target_index].relocs
+         + output_section->reloc_count);
+  rel_hash_ptr = (finfo->section_info[output_section->target_index].rel_hashes
+                 + output_section->reloc_count);
+
+  memset (irel, 0, sizeof (struct internal_reloc));
+  *rel_hash_ptr = NULL;
+
+  irel->r_vaddr = output_section->vma + link_order->offset;
+
+  if (link_order->type == bfd_section_reloc_link_order)
+    {
+      /* We need to somehow locate a symbol in the right section.  The
+         symbol must either have a value of zero, or we must adjust
+         the addend by the value of the symbol.  FIXME: Write this
+         when we need it.  The old linker couldn't handle this anyhow.  */
+      abort ();
+      *rel_hash_ptr = NULL;
+      irel->r_symndx = 0;
+    }
+  else
+    {
+      struct xcoff_link_hash_entry *h;
+
+      h = xcoff_link_hash_lookup (xcoff_hash_table (finfo->info),
+                                 link_order->u.reloc.p->u.name,
+                                 false, false, true);
+      if (h != NULL)
+       {
+         if (h->indx >= 0)
+           irel->r_symndx = h->indx;
+         else
+           {
+             /* Set the index to -2 to force this symbol to get
+                written out.  */
+             h->indx = -2;
+             *rel_hash_ptr = h;
+             irel->r_symndx = 0;
+           }
+       }
+      else
+       {
+         if (! ((*finfo->info->callbacks->unattached_reloc)
+                (finfo->info, link_order->u.reloc.p->u.name, (bfd *) NULL,
+                 (asection *) NULL, (bfd_vma) 0)))
+           return false;
+         irel->r_symndx = 0;
+       }
+    }
+
+  irel->r_type = howto->type;
+  irel->r_size = howto->bitsize - 1;
+  if (howto->complain_on_overflow == complain_overflow_signed)
+    irel->r_size |= 0x80;
+
+  ++output_section->reloc_count;
+
+  return true;
+}
+
+/* Sort relocs by VMA.  This is called via qsort.  */
+
+static int
+xcoff_sort_relocs (p1, p2)
+     const PTR p1;
+     const PTR p2;
+{
+  const struct internal_reloc *r1 = (const struct internal_reloc *) p1;
+  const struct internal_reloc *r2 = (const struct internal_reloc *) p2;
+
+  if (r1->r_vaddr > r2->r_vaddr)
+    return 1;
+  else if (r1->r_vaddr < r2->r_vaddr)
+    return -1;
+  else
+    return 0;
+}
+
+/* This is the relocation function for the RS/6000/POWER/PowerPC.
+   This is currently the only processor which uses XCOFF; I hope that
+   will never change.  */
+
+boolean
+_bfd_ppc_xcoff_relocate_section (output_bfd, info, input_bfd,
+                                input_section, contents, relocs, syms,
+                                sections)
+     bfd *output_bfd;
+     struct bfd_link_info *info;
+     bfd *input_bfd;
+     asection *input_section;
+     bfd_byte *contents;
+     struct internal_reloc *relocs;
+     struct internal_syment *syms;
+     asection **sections;
+{
+  struct internal_reloc *rel;
+  struct internal_reloc *relend;
+
+  rel = relocs;
+  relend = rel + input_section->reloc_count;
+  for (; rel < relend; rel++)
+    {
+      long symndx;
+      struct xcoff_link_hash_entry *h;
+      struct internal_syment *sym;
+      bfd_vma addend;
+      bfd_vma val;
+      struct reloc_howto_struct howto;
+      bfd_reloc_status_type rstat;
+
+      /* Relocation type 0x0f is a special relocation type which is
+         merely used to prevent garbage collection from occurring for
+         the csect including the symbol which it references.  */
+      if (rel->r_type == 0x0f)
+       continue;
+
+      symndx = rel->r_symndx;
+
+      if (symndx == -1)
+       {
+         h = NULL;
+         sym = NULL;
+         addend = 0;
+       }
+      else
+       {    
+         h = obj_xcoff_sym_hashes (input_bfd)[symndx];
+         sym = syms + symndx;
+         addend = - sym->n_value;
+       }
+
+      /* We build the howto information on the fly.  */
+
+      howto.type = rel->r_type;
+      howto.rightshift = 0;
+      howto.size = 2;
+      howto.bitsize = (rel->r_size & 0x1f) + 1;
+      howto.pc_relative = false;
+      howto.bitpos = 0;
+      if ((rel->r_size & 0x80) != 0)
+       howto.complain_on_overflow = complain_overflow_signed;
+      else
+       howto.complain_on_overflow = complain_overflow_bitfield;
+      howto.special_function = NULL;
+      howto.name = "internal";
+      howto.partial_inplace = true;
+      if (howto.bitsize == 32)
+       howto.src_mask = howto.dst_mask = 0xffffffff;
+      else
+       {
+         howto.src_mask = howto.dst_mask = (1 << howto.bitsize) - 1;
+         if (howto.bitsize == 16)
+           howto.size = 1;
+       }
+      howto.pcrel_offset = false;
+
+      val = 0;
+
+      if (h == NULL)
+       {
+         asection *sec;
+
+         if (symndx == -1)
+           {
+             sec = bfd_abs_section_ptr;
+             val = 0;
+           }
+         else
+           {
+             sec = sections[symndx];
+              val = (sec->output_section->vma
+                    + sec->output_offset
+                    + sym->n_value
+                    - sec->vma);
+           }
+       }
+      else
+       {
+         if (h->root.type == bfd_link_hash_defined
+             || h->root.type == bfd_link_hash_defweak)
+           {
+             asection *sec;
+
+             sec = h->root.u.def.section;
+             val = (h->root.u.def.value
+                    + sec->output_section->vma
+                    + sec->output_offset);
+           }
+         else if (! info->relocateable)
+           {
+             if (! ((*info->callbacks->undefined_symbol)
+                    (info, h->root.root.string, input_bfd, input_section,
+                     rel->r_vaddr - input_section->vma)))
+               return false;
+           }
+       }
+
+      /* I took the relocation type definitions from two documents:
+        the PowerPC AIX Version 4 Application Binary Interface, First
+        Edition (April 1992), and the PowerOpen ABI, Big-Endian
+        32-Bit Hardware Implementation (June 30, 1994).  Differences
+        between the documents are noted below.  */
+
+      switch (rel->r_type)
+       {
+       case 0x04:
+       case 0x14:
+       case 0x15:
+         /* These relocs are defined by the PowerPC ABI to be
+             relative branches which use half of the difference
+             between the symbol and the program counter.  I can't
+             quite figure out when this is useful.  These relocs are
+             not defined by the PowerOpen ABI.  */
+       default:
+         (*_bfd_error_handler)
+           ("%s: unsupported relocation type 0x%02x",
+            bfd_get_filename (input_bfd), (unsigned int) rel->r_type);
+         bfd_set_error (bfd_error_bad_value);
+         return false;
+       case 0x00:
+         /* Simple positive relocation.  */
+         break;
+       case 0x01:
+         /* Simple negative relocation.  */
+         val = - val;
+         break;
+       case 0x02:
+         /* Simple PC relative relocation.  */
+         howto.pc_relative = true;
+         break;
+       case 0x03:
+         /* TOC relative relocation.  The value in the instruction in
+             the input file is the offset from the input file TOC to
+             the desired location.  We want the offset from the final
+             TOC to the desired location.  We have:
+                isym = iTOC + in
+                iinsn = in + o
+                osym = oTOC + on
+                oinsn = on + o
+            so we must change insn by on - in.
+            */
+       case 0x05:
+         /* Global linkage relocation.  The value of this relocation
+             is the address of the entry in the TOC section.  */
+       case 0x06:
+         /* Local object TOC address.  I can't figure out the
+             difference between this and case 0x05.  */
+       case 0x12:
+         /* TOC relative relocation.  A TOC relative load instruction
+             which may be changed to a load address instruction.
+             FIXME: We don't currently implement this optimization.  */
+       case 0x13:
+         /* TOC relative relocation.  This is a TOC relative load
+             address instruction which may be changed to a load
+             instruction.  FIXME: I don't know if this is the correct
+             implementation.  */
+         val = ((val - xcoff_data (output_bfd)->toc)
+                - (sym->n_value - xcoff_data (input_bfd)->toc));
+         addend = 0;
+         break;
+       case 0x08:
+         /* Absolute branch.  We don't want to mess with the lower
+             two bits of the instruction.  */
+       case 0x16:
+         /* The PowerPC ABI defines this as an absolute call which
+             may be modified to become a relative call.  The PowerOpen
+             ABI does not define this relocation type.  */
+       case 0x18:
+         /* Absolute branch which may be modified to become a
+             relative branch.  */
+       case 0x19:
+         /* The PowerPC ABI defines this as an absolute branch to a
+             fixed address which may be modified to an absolute branch
+             to a symbol.  The PowerOpen ABI does not define this
+             relocation type.  */
+       case 0x1b:
+         /* The PowerPC ABI defines this as an absolute branch to a
+             fixed address which may be modified to a relative branch.
+             The PowerOpen ABI does not define this relocation type.  */
+         howto.src_mask &= ~3;
+         howto.dst_mask = howto.src_mask;
+         break;
+       case 0x0a:
+         /* Relative branch.  We don't want to mess with the lower
+             two bits of the instruction.  */
+       case 0x17:
+         /* The PowerPC ABI defines this as a relative call which may
+             be modified to become an absolute call.  The PowerOpen
+             ABI does not define this relocation type.  */
+       case 0x1a:
+         /* A relative branch which may be modified to become an
+             absolute branch.  FIXME: We don't implement this,
+             although we should for symbols of storage mapping class
+             XMC_XO.  */
+         howto.pc_relative = true;
+         howto.src_mask &= ~3;
+         howto.dst_mask = howto.src_mask;
+         break;
+       case 0x0c:
+         /* The PowerPC AIX ABI describes this as a load which may be
+             changed to a load address.  The PowerOpen ABI says this
+             is the same as case 0x00.  */
+         break;
+       case 0x0d:
+         /* The PowerPC AIX ABI describes this as a load address
+             which may be changed to a load.  The PowerOpen ABI says
+             this is the same as case 0x00.  */
+         break;
+       }
+
+      rstat = _bfd_final_link_relocate (&howto, input_bfd, input_section,
+                                       contents,
+                                       rel->r_vaddr - input_section->vma,
+                                       val, addend);
+
+      switch (rstat)
+       {
+       default:
+         abort ();
+       case bfd_reloc_ok:
+         break;
+       case bfd_reloc_overflow:
+         {
+           const char *name;
+           char buf[SYMNMLEN + 1];
+           char howto_name[10];
+
+           if (symndx == -1)
+             name = "*ABS*";
+           else if (h != NULL)
+             name = h->root.root.string;
+           else
+             {
+               name = _bfd_coff_internal_syment_name (input_bfd, sym, buf);
+               if (name == NULL)
+                 return false;
+             }
+           sprintf (howto_name, "0x%02x", rel->r_type);
+
+           if (! ((*info->callbacks->reloc_overflow)
+                  (info, name, howto_name, (bfd_vma) 0, input_bfd,
+                   input_section, rel->r_vaddr - input_section->vma)))
+             return false;
+         }
+       }
+    }
+
+  return true;
+}