Mon Sep 25 22:49:32 1995 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
[deliverable/binutils-gdb.git] / bfd / sunos.c
index 7f62a33ad860ae91f72901e4d89c3af79c3a3bc5..f97a0a789c1531c3f5894dcebe9f66cd2af142fe 100644 (file)
@@ -1,5 +1,5 @@
 /* BFD backend for SunOS binaries.
-   Copyright (C) 1990-1991 Free Software Foundation, Inc.
+   Copyright (C) 1990, 91, 92, 93, 94, 1995 Free Software Foundation, Inc.
    Written by Cygnus Support.
 
 This file is part of BFD, the Binary File Descriptor library.
@@ -16,25 +16,67 @@ 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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
-#define ARCH 32
 #define TARGETNAME "a.out-sunos-big"
 #define MY(OP) CAT(sunos_big_,OP)
 
 #include "bfd.h"
+#include "bfdlink.h"
+#include "libaout.h"
 
 /* Static routines defined in this file.  */
 
-struct external_nlist;
-
 static boolean sunos_read_dynamic_info PARAMS ((bfd *));
-static bfd_size_type MY(read_dynamic_symbols)
-     PARAMS ((bfd *, struct external_nlist **, char **, bfd_size_type *));
-static bfd_size_type MY(read_dynamic_relocs) PARAMS ((bfd *, PTR *));
+static long sunos_get_dynamic_symtab_upper_bound PARAMS ((bfd *));
+static boolean sunos_slurp_dynamic_symtab PARAMS ((bfd *));
+static long sunos_canonicalize_dynamic_symtab PARAMS ((bfd *, asymbol **));
+static long sunos_get_dynamic_reloc_upper_bound PARAMS ((bfd *));
+static long sunos_canonicalize_dynamic_reloc
+  PARAMS ((bfd *, arelent **, asymbol **));
+static struct bfd_hash_entry *sunos_link_hash_newfunc
+  PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
+static struct bfd_link_hash_table *sunos_link_hash_table_create
+  PARAMS ((bfd *));
+static boolean sunos_create_dynamic_sections
+  PARAMS ((bfd *, struct bfd_link_info *, boolean));
+static boolean sunos_add_dynamic_symbols
+  PARAMS ((bfd *, struct bfd_link_info *, struct external_nlist **,
+          bfd_size_type *, char **));
+static boolean sunos_add_one_symbol
+  PARAMS ((struct bfd_link_info *, bfd *, const char *, flagword, asection *,
+          bfd_vma, const char *, boolean, boolean,
+          struct bfd_link_hash_entry **));
+static boolean sunos_scan_relocs
+  PARAMS ((struct bfd_link_info *, bfd *, asection *, bfd_size_type));
+static boolean sunos_scan_std_relocs
+  PARAMS ((struct bfd_link_info *, bfd *, asection *,
+          const struct reloc_std_external *, bfd_size_type));
+static boolean sunos_scan_ext_relocs
+  PARAMS ((struct bfd_link_info *, bfd *, asection *,
+          const struct reloc_ext_external *, bfd_size_type));
+static boolean sunos_link_dynamic_object
+  PARAMS ((struct bfd_link_info *, bfd *));
+static boolean sunos_write_dynamic_symbol
+  PARAMS ((bfd *, struct bfd_link_info *, struct aout_link_hash_entry *));
+static boolean sunos_check_dynamic_reloc
+  PARAMS ((struct bfd_link_info *, bfd *, asection *,
+          struct aout_link_hash_entry *, PTR, bfd_byte *, boolean *,
+          bfd_vma *));
+static boolean sunos_finish_dynamic_link
+  PARAMS ((bfd *, struct bfd_link_info *));
 
-#define MY_read_dynamic_symbols MY(read_dynamic_symbols)
-#define MY_read_dynamic_relocs MY(read_dynamic_relocs)
+#define MY_get_dynamic_symtab_upper_bound sunos_get_dynamic_symtab_upper_bound
+#define MY_canonicalize_dynamic_symtab sunos_canonicalize_dynamic_symtab
+#define MY_get_dynamic_reloc_upper_bound sunos_get_dynamic_reloc_upper_bound
+#define MY_canonicalize_dynamic_reloc sunos_canonicalize_dynamic_reloc
+#define MY_bfd_link_hash_table_create sunos_link_hash_table_create
+#define MY_add_dynamic_symbols sunos_add_dynamic_symbols
+#define MY_add_one_symbol sunos_add_one_symbol
+#define MY_link_dynamic_object sunos_link_dynamic_object
+#define MY_write_dynamic_symbol sunos_write_dynamic_symbol
+#define MY_check_dynamic_reloc sunos_check_dynamic_reloc
+#define MY_finish_dynamic_link sunos_finish_dynamic_link
 
 /* Include the usual a.out support.  */
 #include "aoutf1.h"
@@ -49,18 +91,27 @@ struct sunos_dynamic_info
   /* Dynamic information.  */
   struct internal_sun4_dynamic_link dyninfo;
   /* Number of dynamic symbols.  */
-  bfd_size_type dynsym_count;
+  unsigned long dynsym_count;
   /* Read in nlists for dynamic symbols.  */
   struct external_nlist *dynsym;
+  /* asymbol structures for dynamic symbols.  */
+  aout_symbol_type *canonical_dynsym;
   /* Read in dynamic string table.  */
   char *dynstr;
   /* Number of dynamic relocs.  */
-  bfd_size_type dynrel_count;
+  unsigned long dynrel_count;
   /* Read in dynamic relocs.  This may be reloc_std_external or
      reloc_ext_external.  */
   PTR dynrel;
+  /* arelent structures for dynamic relocs.  */
+  arelent *canonical_dynrel;
 };
 
+/* The hash table of dynamic symbols is composed of two word entries.
+   See include/aout/sun4.h for details.  */
+
+#define HASH_ENTRY_SIZE (2 * BYTES_IN_WORD)
+
 /* Read in the basic dynamic information.  This locates the __DYNAMIC
    structure and uses it to find the dynamic_link structure.  It
    creates and saves a sunos_dynamic_info structure.  If it can't find
@@ -72,10 +123,8 @@ sunos_read_dynamic_info (abfd)
      bfd *abfd;
 {
   struct sunos_dynamic_info *info;
-  struct external_nlist dynsym;
-  char buf[sizeof "__DYNAMIC"];
   asection *dynsec;
-  file_ptr dynoff;
+  bfd_vma dynoff;
   struct external_sun4_dynamic dyninfo;
   unsigned long dynver;
   struct external_sun4_dynamic_link linkinfo;
@@ -83,40 +132,38 @@ sunos_read_dynamic_info (abfd)
   if (obj_aout_dynamic_info (abfd) != (PTR) NULL)
     return true;
 
+  if ((abfd->flags & DYNAMIC) == 0)
+    {
+      bfd_set_error (bfd_error_invalid_operation);
+      return false;
+    }
+
   info = ((struct sunos_dynamic_info *)
          bfd_zalloc (abfd, sizeof (struct sunos_dynamic_info)));
+  if (!info)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      return false;
+    }
   info->valid = false;
   info->dynsym = NULL;
   info->dynstr = NULL;
+  info->canonical_dynsym = NULL;
   info->dynrel = NULL;
+  info->canonical_dynrel = NULL;
   obj_aout_dynamic_info (abfd) = (PTR) info;
 
-  /* We look for the __DYNAMIC symbol to locate the dynamic linking
-     information.  It should be the first symbol if it is defined.  If
-     we can't find it, don't sweat it.  */
-  if ((abfd->flags & DYNAMIC) == 0
-      || bfd_get_symcount (abfd) <= 0
-      || bfd_seek (abfd, obj_sym_filepos (abfd), SEEK_SET) != 0
-      || (bfd_read ((PTR) &dynsym, 1, EXTERNAL_NLIST_SIZE, abfd)
-         != EXTERNAL_NLIST_SIZE)
-      || ((dynsym.e_type[0] & N_TYPE) != N_DATA
-         && (dynsym.e_type[0] & N_TYPE) != N_TEXT)
-      || bfd_seek (abfd,
-                  obj_str_filepos (abfd) + GET_WORD (abfd, dynsym.e_strx),
-                  SEEK_SET) != 0
-      || bfd_read ((PTR) buf, 1, sizeof buf, abfd) != sizeof buf
-      || buf[sizeof buf - 1] != '\0'
-      || strcmp (buf, "__DYNAMIC") != 0)
+  /* This code used to look for the __DYNAMIC symbol to locate the dynamic
+     linking information.
+     However this inhibits recovering the dynamic symbols from a
+     stripped object file, so blindly assume that the dynamic linking
+     information is located at the start of the data section.
+     We could verify this assumption later by looking through the dynamic
+     symbols for the __DYNAMIC symbol.  */
+  if ((abfd->flags & DYNAMIC) == 0)
     return true;
-
-  if ((dynsym.e_type[0] & N_TYPE) == N_DATA)
-    dynsec = obj_datasec (abfd);
-  else
-    dynsec = obj_textsec (abfd);
-  if (! bfd_get_section_contents (abfd, dynsec, (PTR) &dyninfo,
-                                 (GET_WORD (abfd, dynsym.e_value)
-                                  - bfd_get_section_vma (abfd, dynsec)),
-                                 sizeof dyninfo))
+  if (! bfd_get_section_contents (abfd, obj_datasec (abfd), (PTR) &dyninfo,
+                                 (file_ptr) 0, sizeof dyninfo))
     return true;
 
   dynver = GET_WORD (abfd, dyninfo.ld_version);
@@ -132,7 +179,7 @@ sunos_read_dynamic_info (abfd)
   else
     dynsec = obj_datasec (abfd);
   dynoff -= bfd_get_section_vma (abfd, dynsec);
-  if (dynoff < 0 || dynoff > bfd_section_size (abfd, dynsec))
+  if (dynoff > bfd_section_size (abfd, dynsec))
     return true;
 
   /* This executable appears to be dynamically linked in a way that we
@@ -157,66 +204,149 @@ sunos_read_dynamic_info (abfd)
   info->dyninfo.ld_text = GET_WORD (abfd, linkinfo.ld_text);
   info->dyninfo.ld_plt_sz = GET_WORD (abfd, linkinfo.ld_plt_sz);
 
+  /* Reportedly the addresses need to be offset by the size of the
+     exec header in an NMAGIC file.  */
+  if (adata (abfd).magic == n_magic)
+    {
+      unsigned long exec_bytes_size = adata (abfd).exec_bytes_size;
+
+      info->dyninfo.ld_need += exec_bytes_size;
+      info->dyninfo.ld_rules += exec_bytes_size;
+      info->dyninfo.ld_rel += exec_bytes_size;
+      info->dyninfo.ld_hash += exec_bytes_size;
+      info->dyninfo.ld_stab += exec_bytes_size;
+      info->dyninfo.ld_symbols += exec_bytes_size;
+    }
+
   /* The only way to get the size of the symbol information appears to
      be to determine the distance between it and the string table.  */
   info->dynsym_count = ((info->dyninfo.ld_symbols - info->dyninfo.ld_stab)
                        / EXTERNAL_NLIST_SIZE);
   BFD_ASSERT (info->dynsym_count * EXTERNAL_NLIST_SIZE
-             == info->dyninfo.ld_symbols - info->dyninfo.ld_stab);
+             == (unsigned long) (info->dyninfo.ld_symbols
+                                 - info->dyninfo.ld_stab));
 
   /* Similarly, the relocs end at the hash table.  */
   info->dynrel_count = ((info->dyninfo.ld_hash - info->dyninfo.ld_rel)
                        / obj_reloc_entry_size (abfd));
   BFD_ASSERT (info->dynrel_count * obj_reloc_entry_size (abfd)
-             == info->dyninfo.ld_hash - info->dyninfo.ld_rel);
+             == (unsigned long) (info->dyninfo.ld_hash
+                                 - info->dyninfo.ld_rel));
 
   info->valid = true;
 
   return true;
 }
 
-/* Read in the dynamic symbols.  */
+/* Return the amount of memory required for the dynamic symbols.  */
 
-static bfd_size_type
-MY(read_dynamic_symbols) (abfd, syms, strs, strsize)
+static long
+sunos_get_dynamic_symtab_upper_bound (abfd)
      bfd *abfd;
-     struct external_nlist **syms;
-     char **strs;
-     bfd_size_type *strsize;
 {
   struct sunos_dynamic_info *info;
 
-  if (obj_aout_dynamic_info (abfd) == (PTR) NULL)
+  if (! sunos_read_dynamic_info (abfd))
+    return -1;
+
+  info = (struct sunos_dynamic_info *) obj_aout_dynamic_info (abfd);
+  if (! info->valid)
+    {
+      bfd_set_error (bfd_error_no_symbols);
+      return -1;
+    }
+
+  return (info->dynsym_count + 1) * sizeof (asymbol *);
+}
+
+/* Read the external dynamic symbols.  */
+
+static boolean
+sunos_slurp_dynamic_symtab (abfd)
+     bfd *abfd;
+{
+  struct sunos_dynamic_info *info;
+
+  /* Get the general dynamic information.  */
+  if (obj_aout_dynamic_info (abfd) == NULL)
     {
       if (! sunos_read_dynamic_info (abfd))
-         return (bfd_size_type) -1;
+         return false;
     }
 
   info = (struct sunos_dynamic_info *) obj_aout_dynamic_info (abfd);
-  if (! info->valid || info->dynsym_count == 0)
-    return 0;
+  if (! info->valid)
+    {
+      bfd_set_error (bfd_error_no_symbols);
+      return false;
+    }
 
+  /* Get the dynamic nlist structures.  */
   if (info->dynsym == (struct external_nlist *) NULL)
     {
       info->dynsym = ((struct external_nlist *)
                      bfd_alloc (abfd,
                                 (info->dynsym_count
                                  * EXTERNAL_NLIST_SIZE)));
-      info->dynstr = (char *) bfd_alloc (abfd, info->dyninfo.ld_symb_size);
+      if (info->dynsym == NULL && info->dynsym_count != 0)
+       {
+         bfd_set_error (bfd_error_no_memory);
+         return false;
+       }
       if (bfd_seek (abfd, info->dyninfo.ld_stab, SEEK_SET) != 0
          || (bfd_read ((PTR) info->dynsym, info->dynsym_count,
                        EXTERNAL_NLIST_SIZE, abfd)
-             != info->dynsym_count * EXTERNAL_NLIST_SIZE)
-         || bfd_seek (abfd, info->dyninfo.ld_symbols, SEEK_SET) != 0
+             != info->dynsym_count * EXTERNAL_NLIST_SIZE))
+       {
+         if (info->dynsym != NULL)
+           {
+             bfd_release (abfd, info->dynsym);
+             info->dynsym = NULL;
+           }
+         return false;
+       }
+    }
+
+  /* Get the dynamic strings.  */
+  if (info->dynstr == (char *) NULL)
+    {
+      info->dynstr = (char *) bfd_alloc (abfd, info->dyninfo.ld_symb_size);
+      if (info->dynstr == NULL && info->dyninfo.ld_symb_size != 0)
+       {
+         bfd_set_error (bfd_error_no_memory);
+         return false;
+       }
+      if (bfd_seek (abfd, info->dyninfo.ld_symbols, SEEK_SET) != 0
          || (bfd_read ((PTR) info->dynstr, 1, info->dyninfo.ld_symb_size,
                        abfd)
              != info->dyninfo.ld_symb_size))
-       return (bfd_size_type) -1;
+       {
+         if (info->dynstr != NULL)
+           {
+             bfd_release (abfd, info->dynstr);
+             info->dynstr = NULL;
+           }
+         return false;
+       }
     }
 
-  *syms = info->dynsym;
-  *strs = info->dynstr;
-  *strsize = info->dyninfo.ld_symb_size;
+  return true;
+}
+
+/* Read in the dynamic symbols.  */
+
+static long
+sunos_canonicalize_dynamic_symtab (abfd, storage)
+     bfd *abfd;
+     asymbol **storage;
+{
+  struct sunos_dynamic_info *info;
+  unsigned long i;
+
+  if (! sunos_slurp_dynamic_symtab (abfd))
+    return -1;
+
+  info = (struct sunos_dynamic_info *) obj_aout_dynamic_info (abfd);
 
 #ifdef CHECK_DYNAMIC_HASH
   /* Check my understanding of the dynamic hash table by making sure
@@ -229,7 +359,9 @@ MY(read_dynamic_symbols) (abfd, syms, strs, strsize)
     if (info->dyninfo.ld_buckets > info->dynsym_count)
       abort ();
     table_size = info->dyninfo.ld_stab - info->dyninfo.ld_hash;
-    table = (bfd_byte *) alloca (table_size);
+    table = (bfd_byte *) malloc (table_size);
+    if (table == NULL && table_size != 0)
+      abort ();
     if (bfd_seek (abfd, info->dyninfo.ld_hash, SEEK_SET) != 0
        || bfd_read ((PTR) table, 1, table_size, abfd) != table_size)
       abort ();
@@ -245,51 +377,2314 @@ MY(read_dynamic_symbols) (abfd, syms, strs, strsize)
          hash = (hash << 1) + *name++;
        hash &= 0x7fffffff;
        hash %= info->dyninfo.ld_buckets;
-       while (GET_WORD (abfd, table + 8 * hash) != i)
+       while (GET_WORD (abfd, table + hash * HASH_ENTRY_SIZE) != i)
          {
-           hash = GET_WORD (abfd, table + 8 * hash + 4);
-           if (hash == 0 || hash >= table_size / 8)
+           hash = GET_WORD (abfd,
+                            table + hash * HASH_ENTRY_SIZE + BYTES_IN_WORD);
+           if (hash == 0 || hash >= table_size / HASH_ENTRY_SIZE)
              abort ();
          }
       }
+    free (table);
   }
 #endif /* CHECK_DYNAMIC_HASH */
 
+  /* Get the asymbol structures corresponding to the dynamic nlist
+     structures.  */
+  if (info->canonical_dynsym == (aout_symbol_type *) NULL)
+    {
+      info->canonical_dynsym = ((aout_symbol_type *)
+                               bfd_alloc (abfd,
+                                          (info->dynsym_count
+                                           * sizeof (aout_symbol_type))));
+      if (info->canonical_dynsym == NULL && info->dynsym_count != 0)
+       {
+         bfd_set_error (bfd_error_no_memory);
+         return -1;
+       }
+
+      if (! aout_32_translate_symbol_table (abfd, info->canonical_dynsym,
+                                           info->dynsym, info->dynsym_count,
+                                           info->dynstr,
+                                           info->dyninfo.ld_symb_size,
+                                           true))
+       {
+         if (info->canonical_dynsym != NULL)
+           {
+             bfd_release (abfd, info->canonical_dynsym);
+             info->canonical_dynsym = NULL;
+           }
+         return -1;
+       }
+    }
+
+  /* Return pointers to the dynamic asymbol structures.  */
+  for (i = 0; i < info->dynsym_count; i++)
+    *storage++ = (asymbol *) (info->canonical_dynsym + i);
+  *storage = NULL;
+
   return info->dynsym_count;
 }
 
-/* Read in the dynamic relocs for a section.  */
+/* Return the amount of memory required for the dynamic relocs.  */
+
+static long
+sunos_get_dynamic_reloc_upper_bound (abfd)
+     bfd *abfd;
+{
+  struct sunos_dynamic_info *info;
+
+  if (! sunos_read_dynamic_info (abfd))
+    return -1;
+
+  info = (struct sunos_dynamic_info *) obj_aout_dynamic_info (abfd);
+  if (! info->valid)
+    {
+      bfd_set_error (bfd_error_no_symbols);
+      return -1;
+    }
+
+  return (info->dynrel_count + 1) * sizeof (arelent *);
+}
+
+/* Read in the dynamic relocs.  */
 
-static bfd_size_type
-MY(read_dynamic_relocs) (abfd, relocs)
+static long
+sunos_canonicalize_dynamic_reloc (abfd, storage, syms)
      bfd *abfd;
-     PTR *relocs;
+     arelent **storage;
+     asymbol **syms;
 {
   struct sunos_dynamic_info *info;
+  unsigned long i;
 
+  /* Get the general dynamic information.  */
   if (obj_aout_dynamic_info (abfd) == (PTR) NULL)
     {
       if (! sunos_read_dynamic_info (abfd))
-         return (bfd_size_type) -1;
+       return -1;
     }
 
   info = (struct sunos_dynamic_info *) obj_aout_dynamic_info (abfd);
-  if (! info->valid || info->dynrel_count == 0)
-    return 0;
+  if (! info->valid)
+    {
+      bfd_set_error (bfd_error_no_symbols);
+      return -1;
+    }
 
-  if (info->dynrel == (struct external_nlist *) NULL)
+  /* Get the dynamic reloc information.  */
+  if (info->dynrel == NULL)
     {
       info->dynrel = (PTR) bfd_alloc (abfd,
                                      (info->dynrel_count
                                       * obj_reloc_entry_size (abfd)));
+      if (info->dynrel == NULL && info->dynrel_count != 0)
+       {
+         bfd_set_error (bfd_error_no_memory);
+         return -1;
+       }
       if (bfd_seek (abfd, info->dyninfo.ld_rel, SEEK_SET) != 0
          || (bfd_read ((PTR) info->dynrel, info->dynrel_count,
                        obj_reloc_entry_size (abfd), abfd)
              != info->dynrel_count * obj_reloc_entry_size (abfd)))
-       return (bfd_size_type) -1;
+       {
+         if (info->dynrel != NULL)
+           {
+             bfd_release (abfd, info->dynrel);
+             info->dynrel = NULL;
+           }
+         return -1;
+       }
+    }
+
+  /* Get the arelent structures corresponding to the dynamic reloc
+     information.  */
+  if (info->canonical_dynrel == (arelent *) NULL)
+    {
+      arelent *to;
+
+      info->canonical_dynrel = ((arelent *)
+                               bfd_alloc (abfd,
+                                          (info->dynrel_count
+                                           * sizeof (arelent))));
+      if (info->canonical_dynrel == NULL && info->dynrel_count != 0)
+       {
+         bfd_set_error (bfd_error_no_memory);
+         return -1;
+       }
+      
+      to = info->canonical_dynrel;
+
+      if (obj_reloc_entry_size (abfd) == RELOC_EXT_SIZE)
+       {
+         register struct reloc_ext_external *p;
+         struct reloc_ext_external *pend;
+
+         p = (struct reloc_ext_external *) info->dynrel;
+         pend = p + info->dynrel_count;
+         for (; p < pend; p++, to++)
+           NAME(aout,swap_ext_reloc_in) (abfd, p, to, syms,
+                                         info->dynsym_count);
+       }
+      else
+       {
+         register struct reloc_std_external *p;
+         struct reloc_std_external *pend;
+
+         p = (struct reloc_std_external *) info->dynrel;
+         pend = p + info->dynrel_count;
+         for (; p < pend; p++, to++)
+           NAME(aout,swap_std_reloc_in) (abfd, p, to, syms,
+                                         info->dynsym_count);
+       }
     }
 
-  *relocs = info->dynrel;
+  /* Return pointers to the dynamic arelent structures.  */
+  for (i = 0; i < info->dynrel_count; i++)
+    *storage++ = info->canonical_dynrel + i;
+  *storage = NULL;
 
   return info->dynrel_count;
 }
+\f
+/* Code to handle linking of SunOS shared libraries.  */
+
+/* A SPARC procedure linkage table entry is 12 bytes.  The first entry
+   in the table is a jump which is filled in by the runtime linker.
+   The remaining entries are branches back to the first entry,
+   followed by an index into the relocation table encoded to look like
+   a sethi of %g0.  */
+
+#define SPARC_PLT_ENTRY_SIZE (12)
+
+static const bfd_byte sparc_plt_first_entry[SPARC_PLT_ENTRY_SIZE] =
+{
+  /* sethi %hi(0),%g1; address filled in by runtime linker.  */
+  0x3, 0, 0, 0,
+  /* jmp %g1; offset filled in by runtime linker.  */
+  0x81, 0xc0, 0x60, 0,
+  /* nop */
+  0x1, 0, 0, 0
+};
+
+/* save %sp, -96, %sp */
+#define SPARC_PLT_ENTRY_WORD0 0x9de3bfa0
+/* call; address filled in later.  */
+#define SPARC_PLT_ENTRY_WORD1 0x40000000
+/* sethi; reloc index filled in later.  */
+#define SPARC_PLT_ENTRY_WORD2 0x01000000
+
+/* This sequence is used when for the jump table entry to a defined
+   symbol in a complete executable.  It is used when linking PIC
+   compiled code which is not being put into a shared library.  */
+/* sethi <address to be filled in later>, %g1 */
+#define SPARC_PLT_PIC_WORD0 0x03000000
+/* jmp %g1 + <address to be filled in later> */
+#define SPARC_PLT_PIC_WORD1 0x81c06000
+/* nop */
+#define SPARC_PLT_PIC_WORD2 0x01000000
+
+/* An m68k procedure linkage table entry is 8 bytes.  The first entry
+   in the table is a jump which is filled in the by the runtime
+   linker.  The remaining entries are branches back to the first
+   entry, followed by a two byte index into the relocation table.  */
+
+#define M68K_PLT_ENTRY_SIZE (8)
+
+static const bfd_byte m68k_plt_first_entry[M68K_PLT_ENTRY_SIZE] =
+{
+  /* jmps @# */
+  0x4e, 0xf9,
+  /* Filled in by runtime linker with a magic address.  */
+  0, 0, 0, 0,
+  /* Not used?  */
+  0, 0
+};
+
+/* bsrl */
+#define M68K_PLT_ENTRY_WORD0 (0x61ff)
+/* Remaining words filled in later.  */
+
+/* An entry in the SunOS linker hash table.  */
+
+struct sunos_link_hash_entry
+{
+  struct aout_link_hash_entry root;
+
+  /* If this is a dynamic symbol, this is its index into the dynamic
+     symbol table.  This is initialized to -1.  As the linker looks at
+     the input files, it changes this to -2 if it will be added to the
+     dynamic symbol table.  After all the input files have been seen,
+     the linker will know whether to build a dynamic symbol table; if
+     it does build one, this becomes the index into the table.  */
+  long dynindx;
+
+  /* If this is a dynamic symbol, this is the index of the name in the
+     dynamic symbol string table.  */
+  long dynstr_index;
+
+  /* The offset into the global offset table used for this symbol.  If
+     the symbol does not require a GOT entry, this is 0.  */
+  bfd_vma got_offset;
+
+  /* The offset into the procedure linkage table used for this symbol.
+     If the symbol does not require a PLT entry, this is 0.  */
+  bfd_vma plt_offset;
+
+  /* Some linker flags.  */
+  unsigned char flags;
+  /* Symbol is referenced by a regular object.  */
+#define SUNOS_REF_REGULAR 01
+  /* Symbol is defined by a regular object.  */
+#define SUNOS_DEF_REGULAR 02
+  /* Symbol is referenced by a dynamic object.  */
+#define SUNOS_REF_DYNAMIC 010
+  /* Symbol is defined by a dynamic object.  */
+#define SUNOS_DEF_DYNAMIC 020
+};
+
+/* The SunOS linker hash table.  */
+
+struct sunos_link_hash_table
+{
+  struct aout_link_hash_table root;
+
+  /* The object which holds the dynamic sections.  */
+  bfd *dynobj;
+
+  /* Whether we have created the dynamic sections.  */
+  boolean dynamic_sections_created;
+
+  /* Whether we need the dynamic sections.  */
+  boolean dynamic_sections_needed;
+
+  /* The number of dynamic symbols.  */
+  size_t dynsymcount;
+
+  /* The number of buckets in the hash table.  */
+  size_t bucketcount;
+};
+
+/* Routine to create an entry in an SunOS link hash table.  */
+
+static struct bfd_hash_entry *
+sunos_link_hash_newfunc (entry, table, string)
+     struct bfd_hash_entry *entry;
+     struct bfd_hash_table *table;
+     const char *string;
+{
+  struct sunos_link_hash_entry *ret = (struct sunos_link_hash_entry *) entry;
+
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (ret == (struct sunos_link_hash_entry *) NULL)
+    ret = ((struct sunos_link_hash_entry *)
+          bfd_hash_allocate (table, sizeof (struct sunos_link_hash_entry)));
+  if (ret == (struct sunos_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 sunos_link_hash_entry *)
+        NAME(aout,link_hash_newfunc) ((struct bfd_hash_entry *) ret,
+                                      table, string));
+  if (ret != NULL)
+    {
+      /* Set local fields.  */
+      ret->dynindx = -1;
+      ret->dynstr_index = -1;
+      ret->got_offset = 0;
+      ret->plt_offset = 0;
+      ret->flags = 0;
+    }
+
+  return (struct bfd_hash_entry *) ret;
+}
+
+/* Create a SunOS link hash table.  */
+
+static struct bfd_link_hash_table *
+sunos_link_hash_table_create (abfd)
+     bfd *abfd;
+{
+  struct sunos_link_hash_table *ret;
+
+  ret = ((struct sunos_link_hash_table *)
+        bfd_alloc (abfd, sizeof (struct sunos_link_hash_table)));
+  if (ret == (struct sunos_link_hash_table *) NULL)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      return (struct bfd_link_hash_table *) NULL;
+    }
+  if (! NAME(aout,link_hash_table_init) (&ret->root, abfd,
+                                        sunos_link_hash_newfunc))
+    {
+      free (ret);
+      return (struct bfd_link_hash_table *) NULL;
+    }
+
+  ret->dynobj = NULL;
+  ret->dynamic_sections_created = false;
+  ret->dynamic_sections_needed = false;
+  ret->dynsymcount = 0;
+  ret->bucketcount = 0;
+
+  return &ret->root.root;
+}
+
+/* Look up an entry in an SunOS link hash table.  */
+
+#define sunos_link_hash_lookup(table, string, create, copy, follow) \
+  ((struct sunos_link_hash_entry *) \
+   aout_link_hash_lookup (&(table)->root, (string), (create), (copy),\
+                         (follow)))
+
+/* Traverse a SunOS link hash table.  */
+
+#define sunos_link_hash_traverse(table, func, info)                    \
+  (aout_link_hash_traverse                                             \
+   (&(table)->root,                                                    \
+    (boolean (*) PARAMS ((struct aout_link_hash_entry *, PTR))) (func),        \
+    (info)))
+
+/* Get the SunOS link hash table from the info structure.  This is
+   just a cast.  */
+
+#define sunos_hash_table(p) ((struct sunos_link_hash_table *) ((p)->hash))
+
+static boolean sunos_scan_dynamic_symbol
+  PARAMS ((struct sunos_link_hash_entry *, PTR));
+
+/* Create the dynamic sections needed if we are linking against a
+   dynamic object, or if we are linking PIC compiled code.  ABFD is a
+   bfd we can attach the dynamic sections to.  The linker script will
+   look for these special sections names and put them in the right
+   place in the output file.  See include/aout/sun4.h for more details
+   of the dynamic linking information.  */
+
+static boolean
+sunos_create_dynamic_sections (abfd, info, needed)
+     bfd *abfd;
+     struct bfd_link_info *info;
+     boolean needed;
+{
+  asection *s;
+
+  if (! sunos_hash_table (info)->dynamic_sections_created)
+    {
+      flagword flags;
+
+      sunos_hash_table (info)->dynobj = abfd;
+
+      flags = SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY;
+
+      /* The .dynamic section holds the basic dynamic information: the
+        sun4_dynamic structure, the dynamic debugger information, and
+        the sun4_dynamic_link structure.  */
+      s = bfd_make_section (abfd, ".dynamic");
+      if (s == NULL
+         || ! bfd_set_section_flags (abfd, s, flags)
+         || ! bfd_set_section_alignment (abfd, s, 2))
+       return false;
+
+      /* The .got section holds the global offset table.  The address
+        is put in the ld_got field.  */
+      s = bfd_make_section (abfd, ".got");
+      if (s == NULL
+         || ! bfd_set_section_flags (abfd, s, flags)
+         || ! bfd_set_section_alignment (abfd, s, 2))
+       return false;
+
+      /* The .plt section holds the procedure linkage table.  The
+        address is put in the ld_plt field.  */
+      s = bfd_make_section (abfd, ".plt");
+      if (s == NULL
+         || ! bfd_set_section_flags (abfd, s, flags | SEC_CODE)
+         || ! bfd_set_section_alignment (abfd, s, 2))
+       return false;
+
+      /* The .dynrel section holds the dynamic relocs.  The address is
+        put in the ld_rel field.  */
+      s = bfd_make_section (abfd, ".dynrel");
+      if (s == NULL
+         || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
+         || ! bfd_set_section_alignment (abfd, s, 2))
+       return false;
+
+      /* The .hash section holds the dynamic hash table.  The address
+        is put in the ld_hash field.  */
+      s = bfd_make_section (abfd, ".hash");
+      if (s == NULL
+         || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
+         || ! bfd_set_section_alignment (abfd, s, 2))
+       return false;
+
+      /* The .dynsym section holds the dynamic symbols.  The address
+        is put in the ld_stab field.  */
+      s = bfd_make_section (abfd, ".dynsym");
+      if (s == NULL
+         || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
+         || ! bfd_set_section_alignment (abfd, s, 2))
+       return false;
+
+      /* The .dynstr section holds the dynamic symbol string table.
+        The address is put in the ld_symbols field.  */
+      s = bfd_make_section (abfd, ".dynstr");
+      if (s == NULL
+         || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
+         || ! bfd_set_section_alignment (abfd, s, 2))
+       return false;
+
+      sunos_hash_table (info)->dynamic_sections_created = true;
+    }
+
+  if (needed && ! sunos_hash_table (info)->dynamic_sections_needed)
+    {
+      bfd *dynobj;
+
+      dynobj = sunos_hash_table (info)->dynobj;
+
+      s = bfd_get_section_by_name (dynobj, ".got");
+      s->_raw_size = BYTES_IN_WORD;
+
+      sunos_hash_table (info)->dynamic_sections_needed = true;
+    }
+
+  return true;
+}
+
+/* Add dynamic symbols during a link.  This is called by the a.out
+   backend linker when it encounters an object with the DYNAMIC flag
+   set.  */
+
+static boolean
+sunos_add_dynamic_symbols (abfd, info, symsp, sym_countp, stringsp)
+     bfd *abfd;
+     struct bfd_link_info *info;
+     struct external_nlist **symsp;
+     bfd_size_type *sym_countp;
+     char **stringsp;
+{
+  asection *s;
+  bfd *dynobj;
+  struct sunos_dynamic_info *dinfo;
+
+  /* We do not want to include the sections in a dynamic object in the
+     output file.  We hack by simply clobbering the list of sections
+     in the BFD.  This could be handled more cleanly by, say, a new
+     section flag; the existing SEC_NEVER_LOAD flag is not the one we
+     want, because that one still implies that the section takes up
+     space in the output file.  */
+  abfd->sections = NULL;
+
+  /* The native linker seems to just ignore dynamic objects when -r is
+     used.  */
+  if (info->relocateable)
+    return true;
+
+  /* There's no hope of using a dynamic object which does not exactly
+     match the format of the output file.  */
+  if (info->hash->creator != abfd->xvec)
+    {
+      bfd_set_error (bfd_error_invalid_operation);
+      return false;
+    }
+
+  /* Make sure we have all the required information.  */
+  if (! sunos_create_dynamic_sections (abfd, info, true))
+    return false;
+
+  /* Make sure we have a .need and a .rules sections.  These are only
+     needed if there really is a dynamic object in the link, so they
+     are not added by sunos_create_dynamic_sections.  */
+  dynobj = sunos_hash_table (info)->dynobj;
+  if (bfd_get_section_by_name (dynobj, ".need") == NULL)
+    {
+      /* The .need section holds the list of names of shared objets
+        which must be included at runtime.  The address of this
+        section is put in the ld_need field.  */
+      s = bfd_make_section (dynobj, ".need");
+      if (s == NULL
+         || ! bfd_set_section_flags (dynobj, s,
+                                     (SEC_ALLOC
+                                      | SEC_LOAD
+                                      | SEC_HAS_CONTENTS
+                                      | SEC_IN_MEMORY
+                                      | SEC_READONLY))
+         || ! bfd_set_section_alignment (dynobj, s, 2))
+       return false;
+    }
+
+  if (bfd_get_section_by_name (dynobj, ".rules") == NULL)
+    {
+      /* The .rules section holds the path to search for shared
+        objects.  The address of this section is put in the ld_rules
+        field.  */
+      s = bfd_make_section (dynobj, ".rules");
+      if (s == NULL
+         || ! bfd_set_section_flags (dynobj, s,
+                                     (SEC_ALLOC
+                                      | SEC_LOAD
+                                      | SEC_HAS_CONTENTS
+                                      | SEC_IN_MEMORY
+                                      | SEC_READONLY))
+         || ! bfd_set_section_alignment (dynobj, s, 2))
+       return false;
+    }
+
+  /* Pick up the dynamic symbols and return them to the caller.  */
+  if (! sunos_slurp_dynamic_symtab (abfd))
+    return false;
+
+  dinfo = (struct sunos_dynamic_info *) obj_aout_dynamic_info (abfd);
+  *symsp = dinfo->dynsym;
+  *sym_countp = dinfo->dynsym_count;
+  *stringsp = dinfo->dynstr;
+
+  return true;
+}
+
+/* Function to add a single symbol to the linker hash table.  This is
+   a wrapper around _bfd_generic_link_add_one_symbol which handles the
+   tweaking needed for dynamic linking support.  */
+
+static boolean
+sunos_add_one_symbol (info, abfd, name, flags, section, value, string,
+                     copy, collect, hashp)
+     struct bfd_link_info *info;
+     bfd *abfd;
+     const char *name;
+     flagword flags;
+     asection *section;
+     bfd_vma value;
+     const char *string;
+     boolean copy;
+     boolean collect;
+     struct bfd_link_hash_entry **hashp;
+{
+  struct sunos_link_hash_entry *h;
+  int new_flag;
+
+  if (! sunos_hash_table (info)->dynamic_sections_created)
+    {
+      /* We must create the dynamic sections while reading the input
+         files, even though at this point we don't know if any of the
+         sections will be needed.  This will ensure that the dynamic
+         sections are mapped to the right output section.  It does no
+         harm to create these sections if they are not needed.  */
+      if (! sunos_create_dynamic_sections (abfd, info, info->shared))
+       return false;
+    }
+
+  h = sunos_link_hash_lookup (sunos_hash_table (info), name, true, copy,
+                             false);
+  if (h == NULL)
+    return false;
+
+  if (hashp != NULL)
+    *hashp = (struct bfd_link_hash_entry *) h;
+
+  /* Treat a common symbol in a dynamic object as defined in the .bss
+     section of the dynamic object.  We don't want to allocate space
+     for it in our process image.  */
+  if ((abfd->flags & DYNAMIC) != 0
+      && bfd_is_com_section (section))
+    section = obj_bsssec (abfd);
+
+  if (! bfd_is_und_section (section)
+      && h->root.root.type != bfd_link_hash_new
+      && h->root.root.type != bfd_link_hash_undefined
+      && h->root.root.type != bfd_link_hash_defweak)
+    {
+      /* We are defining the symbol, and it is already defined.  This
+        is a potential multiple definition error.  */
+      if ((abfd->flags & DYNAMIC) != 0)
+       {
+         /* The definition we are adding is from a dynamic object.
+            We do not want this new definition to override the
+            existing definition, so we pretend it is just a
+            reference.  */
+         section = bfd_und_section_ptr;
+       }
+      else if (h->root.root.type == bfd_link_hash_defined
+              && h->root.root.u.def.section->owner != NULL
+              && (h->root.root.u.def.section->owner->flags & DYNAMIC) != 0)
+       {
+         /* The existing definition is from a dynamic object.  We
+            want to override it with the definition we just found.
+            Clobber the existing definition.  */
+         h->root.root.type = bfd_link_hash_new;
+       }
+      else if (h->root.root.type == bfd_link_hash_common
+              && (h->root.root.u.c.p->section->owner->flags & DYNAMIC) != 0)
+       {
+         /* The existing definition is from a dynamic object.  We
+            want to override it with the definition we just found.
+            Clobber the existing definition.  We can't set it to new,
+            because it is on the undefined list.  */
+         h->root.root.type = bfd_link_hash_undefined;
+         h->root.root.u.undef.abfd = h->root.root.u.c.p->section->owner;
+       }
+    }
+
+  /* Do the usual procedure for adding a symbol.  */
+  if (! _bfd_generic_link_add_one_symbol (info, abfd, name, flags, section,
+                                         value, string, copy, collect,
+                                         hashp))
+    return false;
+
+  if (abfd->xvec == info->hash->creator)
+    {
+      /* Set a flag in the hash table entry indicating the type of
+        reference or definition we just found.  Keep a count of the
+        number of dynamic symbols we find.  A dynamic symbol is one
+        which is referenced or defined by both a regular object and a
+        shared object.  */
+      if ((abfd->flags & DYNAMIC) == 0)
+       {
+         if (bfd_is_und_section (section))
+           new_flag = SUNOS_REF_REGULAR;
+         else
+           new_flag = SUNOS_DEF_REGULAR;
+       }
+      else
+       {
+         if (bfd_is_und_section (section))
+           new_flag = SUNOS_REF_DYNAMIC;
+         else
+           new_flag = SUNOS_DEF_DYNAMIC;
+       }
+      h->flags |= new_flag;
+
+      if (h->dynindx == -1
+         && (h->flags & (SUNOS_DEF_REGULAR | SUNOS_REF_REGULAR)) != 0)
+       {
+         ++sunos_hash_table (info)->dynsymcount;
+         h->dynindx = -2;
+       }
+    }
+
+  return true;
+}
+
+/* Record an assignment made to a symbol by a linker script.  We need
+   this in case some dynamic object refers to this symbol.  */
+
+boolean
+bfd_sunos_record_link_assignment (output_bfd, info, name)
+     bfd *output_bfd;
+     struct bfd_link_info *info;
+     const char *name;
+{
+  struct sunos_link_hash_entry *h;
+
+  /* This is called after we have examined all the input objects.  If
+     the symbol does not exist, it merely means that no object refers
+     to it, and we can just ignore it at this point.  */
+  h = sunos_link_hash_lookup (sunos_hash_table (info), name,
+                             false, false, false);
+  if (h == NULL)
+    return true;
+
+  /* In a shared library, the __DYNAMIC symbol does not appear in the
+     dynamic symbol table.  */
+  if (! info->shared || strcmp (name, "__DYNAMIC") != 0)
+    {
+      h->flags |= SUNOS_DEF_REGULAR;
+
+      if (h->dynindx == -1)
+       {
+         ++sunos_hash_table (info)->dynsymcount;
+         h->dynindx = -2;
+       }
+    }
+
+  return true;
+}
+
+/* Set up the sizes and contents of the dynamic sections created in
+   sunos_add_dynamic_symbols.  This is called by the SunOS linker
+   emulation before_allocation routine.  We must set the sizes of the
+   sections before the linker sets the addresses of the various
+   sections.  This unfortunately requires reading all the relocs so
+   that we can work out which ones need to become dynamic relocs.  If
+   info->keep_memory is true, we keep the relocs in memory; otherwise,
+   we discard them, and will read them again later.  */
+
+boolean
+bfd_sunos_size_dynamic_sections (output_bfd, info, sdynptr, sneedptr,
+                                srulesptr)
+     bfd *output_bfd;
+     struct bfd_link_info *info;
+     asection **sdynptr;
+     asection **sneedptr;
+     asection **srulesptr;
+{
+  bfd *dynobj;
+  size_t dynsymcount;
+  struct sunos_link_hash_entry *h;
+  asection *s;
+  size_t bucketcount;
+  size_t hashalloc;
+  size_t i;
+  bfd *sub;
+
+  *sdynptr = NULL;
+  *sneedptr = NULL;
+  *srulesptr = NULL;
+
+  /* Look through all the input BFD's and read their relocs.  It would
+     be better if we didn't have to do this, but there is no other way
+     to determine the number of dynamic relocs we need, and, more
+     importantly, there is no other way to know which symbols should
+     get an entry in the procedure linkage table.  */
+  for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
+    {
+      if ((sub->flags & DYNAMIC) == 0)
+       {
+         if (! sunos_scan_relocs (info, sub, obj_textsec (sub),
+                                  exec_hdr (sub)->a_trsize)
+             || ! sunos_scan_relocs (info, sub, obj_datasec (sub),
+                                     exec_hdr (sub)->a_drsize))
+           return false;
+       }
+    }
+
+  dynobj = sunos_hash_table (info)->dynobj;
+  dynsymcount = sunos_hash_table (info)->dynsymcount;
+
+  /* If there were no dynamic objects in the link, and we don't need
+     to build a global offset table, there is nothing to do here.  */
+  if (! sunos_hash_table (info)->dynamic_sections_needed)
+    return true;
+
+  /* If __GLOBAL_OFFSET_TABLE_ was mentioned, define it.  */
+  h = sunos_link_hash_lookup (sunos_hash_table (info),
+                             "__GLOBAL_OFFSET_TABLE_", false, false, false);
+  if (h != NULL && (h->flags & SUNOS_REF_REGULAR) != 0)
+    {
+      h->flags |= SUNOS_DEF_REGULAR;
+      if (h->dynindx == -1)
+       {
+         ++sunos_hash_table (info)->dynsymcount;
+         h->dynindx = -2;
+       }
+      h->root.root.type = bfd_link_hash_defined;
+      h->root.root.u.def.section = bfd_get_section_by_name (dynobj, ".got");
+      h->root.root.u.def.value = 0;
+    }
+
+  /* The .dynamic section is always the same size.  */
+  s = bfd_get_section_by_name (dynobj, ".dynamic");
+  BFD_ASSERT (s != NULL);
+  s->_raw_size = (sizeof (struct external_sun4_dynamic)
+                 + EXTERNAL_SUN4_DYNAMIC_DEBUGGER_SIZE
+                 + sizeof (struct external_sun4_dynamic_link));
+
+  /* Set the size of the .dynsym and .hash sections.  We counted the
+     number of dynamic symbols as we read the input files.  We will
+     build the dynamic symbol table (.dynsym) and the hash table
+     (.hash) when we build the final symbol table, because until then
+     we do not know the correct value to give the symbols.  We build
+     the dynamic symbol string table (.dynstr) in a traversal of the
+     symbol table using sunos_scan_dynamic_symbol.  */
+  s = bfd_get_section_by_name (dynobj, ".dynsym");
+  BFD_ASSERT (s != NULL);
+  s->_raw_size = dynsymcount * sizeof (struct external_nlist);
+  s->contents = (bfd_byte *) bfd_alloc (output_bfd, s->_raw_size);
+  if (s->contents == NULL && s->_raw_size != 0)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      return false;
+    }
+      
+  /* The number of buckets is just the number of symbols divided by
+     four.  To compute the final size of the hash table, we must
+     actually compute the hash table.  Normally we need exactly as
+     many entries in the hash table as there are dynamic symbols, but
+     if some of the buckets are not used we will need additional
+     entries.  In the worst case, every symbol will hash to the same
+     bucket, and we will need BUCKETCOUNT - 1 extra entries.  */
+  if (dynsymcount >= 4)
+    bucketcount = dynsymcount / 4;
+  else if (dynsymcount > 0)
+    bucketcount = dynsymcount;
+  else
+    bucketcount = 1;
+  s = bfd_get_section_by_name (dynobj, ".hash");
+  BFD_ASSERT (s != NULL);
+  hashalloc = (dynsymcount + bucketcount - 1) * HASH_ENTRY_SIZE;
+  s->contents = (bfd_byte *) bfd_alloc (dynobj, hashalloc);
+  if (s->contents == NULL && dynsymcount > 0)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      return false;
+    }
+  memset (s->contents, 0, hashalloc);
+  for (i = 0; i < bucketcount; i++)
+    PUT_WORD (output_bfd, (bfd_vma) -1, s->contents + i * HASH_ENTRY_SIZE);
+  s->_raw_size = bucketcount * HASH_ENTRY_SIZE;
+
+  sunos_hash_table (info)->bucketcount = bucketcount;
+
+  /* Scan all the symbols, place them in the dynamic symbol table, and
+     build the dynamic hash table.  We reuse dynsymcount as a counter
+     for the number of symbols we have added so far.  */
+  sunos_hash_table (info)->dynsymcount = 0;
+  sunos_link_hash_traverse (sunos_hash_table (info),
+                           sunos_scan_dynamic_symbol,
+                           (PTR) info);
+  BFD_ASSERT (sunos_hash_table (info)->dynsymcount == dynsymcount);
+
+  /* The SunOS native linker seems to align the total size of the
+     symbol strings to a multiple of 8.  I don't know if this is
+     important, but it can't hurt much.  */
+  s = bfd_get_section_by_name (dynobj, ".dynstr");
+  BFD_ASSERT (s != NULL);
+  if ((s->_raw_size & 7) != 0)
+    {
+      bfd_size_type add;
+      bfd_byte *contents;
+
+      add = 8 - (s->_raw_size & 7);
+      contents = (bfd_byte *) realloc (s->contents,
+                                      (size_t) (s->_raw_size + add));
+      if (contents == NULL)
+       {
+         bfd_set_error (bfd_error_no_memory);
+         return false;
+       }
+      memset (contents + s->_raw_size, 0, (size_t) add);
+      s->contents = contents;
+      s->_raw_size += add;
+    }
+
+  /* Now that we have worked out the sizes of the procedure linkage
+     table and the dynamic relocs, allocate storage for them.  */
+  s = bfd_get_section_by_name (dynobj, ".plt");
+  BFD_ASSERT (s != NULL);
+  if (s->_raw_size != 0)
+    {
+      s->contents = (bfd_byte *) bfd_alloc (dynobj, s->_raw_size);
+      if (s->contents == NULL)
+       {
+         bfd_set_error (bfd_error_no_memory);
+         return false;
+       }
+
+      /* Fill in the first entry in the table.  */
+      switch (bfd_get_arch (dynobj))
+       {
+       case bfd_arch_sparc:
+         memcpy (s->contents, sparc_plt_first_entry, SPARC_PLT_ENTRY_SIZE);
+         break;
+
+       case bfd_arch_m68k:
+         memcpy (s->contents, m68k_plt_first_entry, M68K_PLT_ENTRY_SIZE);
+         break;
+
+       default:
+         abort ();
+       }
+    }
+
+  s = bfd_get_section_by_name (dynobj, ".dynrel");
+  if (s->_raw_size != 0)
+    {
+      s->contents = (bfd_byte *) bfd_alloc (dynobj, s->_raw_size);
+      if (s->contents == NULL)
+       {
+         bfd_set_error (bfd_error_no_memory);
+         return false;
+       }
+    }
+  /* We use the reloc_count field to keep track of how many of the
+     relocs we have output so far.  */
+  s->reloc_count = 0;
+
+  /* Make space for the global offset table.  */
+  s = bfd_get_section_by_name (dynobj, ".got");
+  s->contents = (bfd_byte *) bfd_alloc (dynobj, s->_raw_size);
+  if (s->contents == NULL)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      return false;
+    }
+
+  *sdynptr = bfd_get_section_by_name (dynobj, ".dynamic");
+  *sneedptr = bfd_get_section_by_name (dynobj, ".need");
+  *srulesptr = bfd_get_section_by_name (dynobj, ".rules");
+
+  return true;
+}
+
+/* Scan the relocs for an input section.  */
+
+static boolean
+sunos_scan_relocs (info, abfd, sec, rel_size)
+     struct bfd_link_info *info;
+     bfd *abfd;
+     asection *sec;
+     bfd_size_type rel_size;
+{
+  PTR relocs;
+  PTR free_relocs = NULL;
+
+  if (rel_size == 0)
+    return true;
+
+  if (! info->keep_memory)
+    relocs = free_relocs = malloc ((size_t) rel_size);
+  else
+    {
+      struct aout_section_data_struct *n;
+
+      n = ((struct aout_section_data_struct *)
+          bfd_alloc (abfd, sizeof (struct aout_section_data_struct)));
+      if (n == NULL)
+       relocs = NULL;
+      else
+       {
+         set_aout_section_data (sec, n);
+         relocs = malloc ((size_t) rel_size);
+         aout_section_data (sec)->relocs = relocs;
+       }
+    }
+  if (relocs == NULL)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      return false;
+    }
+
+  if (bfd_seek (abfd, sec->rel_filepos, SEEK_SET) != 0
+      || bfd_read (relocs, 1, rel_size, abfd) != rel_size)
+    goto error_return;
+
+  if (obj_reloc_entry_size (abfd) == RELOC_STD_SIZE)
+    {
+      if (! sunos_scan_std_relocs (info, abfd, sec,
+                                  (struct reloc_std_external *) relocs,
+                                  rel_size))
+       goto error_return;
+    }
+  else
+    {
+      if (! sunos_scan_ext_relocs (info, abfd, sec,
+                                  (struct reloc_ext_external *) relocs,
+                                  rel_size))
+       goto error_return;
+    }
+
+  if (free_relocs != NULL)
+    free (free_relocs);
+
+  return true;
+
+ error_return:
+  if (free_relocs != NULL)
+    free (free_relocs);
+  return false;
+}
+
+/* Scan the relocs for an input section using standard relocs.  We
+   need to figure out what to do for each reloc against a dynamic
+   symbol.  If the symbol is in the .text section, an entry is made in
+   the procedure linkage table.  Note that this will do the wrong
+   thing if the symbol is actually data; I don't think the Sun 3
+   native linker handles this case correctly either.  If the symbol is
+   not in the .text section, we must preserve the reloc as a dynamic
+   reloc.  FIXME: We should also handle the PIC relocs here by
+   building global offset table entries.  */
+
+static boolean
+sunos_scan_std_relocs (info, abfd, sec, relocs, rel_size)
+     struct bfd_link_info *info;
+     bfd *abfd;
+     asection *sec;
+     const struct reloc_std_external *relocs;
+     bfd_size_type rel_size;
+{
+  bfd *dynobj;
+  asection *splt = NULL;
+  asection *srel = NULL;
+  struct sunos_link_hash_entry **sym_hashes;
+  const struct reloc_std_external *rel, *relend;
+
+  /* We only know how to handle m68k plt entries.  */
+  if (bfd_get_arch (abfd) != bfd_arch_m68k)
+    {
+      bfd_set_error (bfd_error_invalid_target);
+      return false;
+    }
+
+  dynobj = NULL;
+
+  sym_hashes = (struct sunos_link_hash_entry **) obj_aout_sym_hashes (abfd);
+
+  relend = relocs + rel_size / RELOC_STD_SIZE;
+  for (rel = relocs; rel < relend; rel++)
+    {
+      int r_index;
+      struct sunos_link_hash_entry *h;
+
+      /* We only want relocs against external symbols.  */
+      if (abfd->xvec->header_byteorder_big_p)
+       {
+         if ((rel->r_type[0] & RELOC_STD_BITS_EXTERN_BIG) == 0)
+           continue;
+       }
+      else
+       {
+         if ((rel->r_type[0] & RELOC_STD_BITS_EXTERN_LITTLE) == 0)
+           continue;
+       }
+
+      /* Get the symbol index.  */
+      if (abfd->xvec->header_byteorder_big_p)
+       r_index = ((rel->r_index[0] << 16)
+                  | (rel->r_index[1] << 8)
+                  | rel->r_index[2]);
+      else
+       r_index = ((rel->r_index[2] << 16)
+                  | (rel->r_index[1] << 8)
+                  | rel->r_index[0]);
+
+      /* Get the hash table entry.  */
+      h = sym_hashes[r_index];
+      if (h == NULL)
+       {
+         /* This should not normally happen, but it will in any case
+            be caught in the relocation phase.  */
+         continue;
+       }
+
+      /* At this point common symbols have already been allocated, so
+        we don't have to worry about them.  We need to consider that
+        we may have already seen this symbol and marked it undefined;
+        if the symbol is really undefined, then SUNOS_DEF_DYNAMIC
+        will be zero.  */
+      if (h->root.root.type != bfd_link_hash_defined
+         && h->root.root.type != bfd_link_hash_defweak
+         && h->root.root.type != bfd_link_hash_undefined)
+       continue;
+
+      if ((h->flags & SUNOS_DEF_DYNAMIC) == 0
+         || (h->flags & SUNOS_DEF_REGULAR) != 0)
+       continue;
+
+      if (dynobj == NULL)
+       {
+         if (! sunos_create_dynamic_sections (abfd, info, true))
+           return false;
+         dynobj = sunos_hash_table (info)->dynobj;
+         splt = bfd_get_section_by_name (dynobj, ".plt");
+         srel = bfd_get_section_by_name (dynobj, ".dynrel");
+         BFD_ASSERT (splt != NULL && srel != NULL);
+       }
+
+      BFD_ASSERT ((h->flags & SUNOS_REF_REGULAR) != 0);
+      BFD_ASSERT (h->plt_offset != 0
+                 || ((h->root.root.type == bfd_link_hash_defined
+                      || h->root.root.type == bfd_link_hash_defweak)
+                     ? (h->root.root.u.def.section->owner->flags
+                        & DYNAMIC) != 0
+                     : (h->root.root.u.undef.abfd->flags & DYNAMIC) != 0));
+
+      /* This reloc is against a symbol defined only by a dynamic
+        object.  */
+
+      if (h->root.root.type == bfd_link_hash_undefined)
+       {
+         /* Presumably this symbol was marked as being undefined by
+            an earlier reloc.  */
+         srel->_raw_size += RELOC_STD_SIZE;
+       }
+      else if ((h->root.root.u.def.section->flags & SEC_CODE) == 0)
+       {
+         bfd *sub;
+
+         /* This reloc is not in the .text section.  It must be
+            copied into the dynamic relocs.  We mark the symbol as
+            being undefined.  */
+         srel->_raw_size += RELOC_STD_SIZE;
+         sub = h->root.root.u.def.section->owner;
+         h->root.root.type = bfd_link_hash_undefined;
+         h->root.root.u.undef.abfd = sub;
+       }
+      else
+       {
+         /* This symbol is in the .text section.  We must give it an
+            entry in the procedure linkage table, if we have not
+            already done so.  We change the definition of the symbol
+            to the .plt section; this will cause relocs against it to
+            be handled correctly.  */
+         if (h->plt_offset == 0)
+           {
+             if (splt->_raw_size == 0)
+               splt->_raw_size = M68K_PLT_ENTRY_SIZE;
+             h->plt_offset = splt->_raw_size;
+
+             if ((h->flags & SUNOS_DEF_REGULAR) == 0)
+               {
+                 h->root.root.u.def.section = splt;
+                 h->root.root.u.def.value = splt->_raw_size;
+               }
+
+             splt->_raw_size += M68K_PLT_ENTRY_SIZE;
+
+             /* We may also need a dynamic reloc entry.  */
+             if ((h->flags & SUNOS_DEF_REGULAR) == 0)
+               srel->_raw_size += RELOC_STD_SIZE;
+           }
+       }
+    }
+
+  return true;
+}
+
+/* Scan the relocs for an input section using extended relocs.  We
+   need to figure out what to do for each reloc against a dynamic
+   symbol.  If the reloc is a WDISP30, and the symbol is in the .text
+   section, an entry is made in the procedure linkage table.
+   Otherwise, we must preserve the reloc as a dynamic reloc.  */
+
+static boolean
+sunos_scan_ext_relocs (info, abfd, sec, relocs, rel_size)
+     struct bfd_link_info *info;
+     bfd *abfd;
+     asection *sec;
+     const struct reloc_ext_external *relocs;
+     bfd_size_type rel_size;
+{
+  bfd *dynobj;
+  struct sunos_link_hash_entry **sym_hashes;
+  const struct reloc_ext_external *rel, *relend;
+  asection *splt = NULL;
+  asection *sgot = NULL;
+  asection *srel = NULL;
+
+  /* We only know how to handle SPARC plt entries.  */
+  if (bfd_get_arch (abfd) != bfd_arch_sparc)
+    {
+      bfd_set_error (bfd_error_invalid_target);
+      return false;
+    }
+
+  dynobj = NULL;
+
+  sym_hashes = (struct sunos_link_hash_entry **) obj_aout_sym_hashes (abfd);
+
+  relend = relocs + rel_size / RELOC_EXT_SIZE;
+  for (rel = relocs; rel < relend; rel++)
+    {
+      unsigned int r_index;
+      int r_extern;
+      int r_type;
+      struct sunos_link_hash_entry *h = NULL;
+
+      /* Swap in the reloc information.  */
+      if (abfd->xvec->header_byteorder_big_p)
+       {
+         r_index = ((rel->r_index[0] << 16)
+                    | (rel->r_index[1] << 8)
+                    | rel->r_index[2]);
+         r_extern = (0 != (rel->r_type[0] & RELOC_EXT_BITS_EXTERN_BIG));
+         r_type = ((rel->r_type[0] & RELOC_EXT_BITS_TYPE_BIG)
+                   >> RELOC_EXT_BITS_TYPE_SH_BIG);
+       }
+      else
+       {
+         r_index = ((rel->r_index[2] << 16)
+                    | (rel->r_index[1] << 8)
+                    | rel->r_index[0]);
+         r_extern = (0 != (rel->r_type[0] & RELOC_EXT_BITS_EXTERN_LITTLE));
+         r_type = ((rel->r_type[0] & RELOC_EXT_BITS_TYPE_LITTLE)
+                   >> RELOC_EXT_BITS_TYPE_SH_LITTLE);
+       }
+
+      if (r_extern)
+       {
+         h = sym_hashes[r_index];
+         if (h == NULL)
+           {
+             /* This should not normally happen, but it will in any
+                case be caught in the relocation phase.  */
+             continue;
+           }
+       }
+      else
+       {
+         if (r_index >= bfd_get_symcount (abfd))
+           {
+             /* This is abnormal, but should be caught in the
+                 relocation phase.  */
+             continue;
+           }
+       }
+
+      /* If this is a base relative reloc, we need to make an entry in
+         the .got section.  */
+      if (r_type == RELOC_BASE10
+         || r_type == RELOC_BASE13
+         || r_type == RELOC_BASE22)
+       {
+         if (dynobj == NULL)
+           {
+             if (! sunos_create_dynamic_sections (abfd, info, true))
+               return false;
+             dynobj = sunos_hash_table (info)->dynobj;
+             splt = bfd_get_section_by_name (dynobj, ".plt");
+             sgot = bfd_get_section_by_name (dynobj, ".got");
+             srel = bfd_get_section_by_name (dynobj, ".dynrel");
+             BFD_ASSERT (splt != NULL && sgot != NULL && srel != NULL);
+           }
+
+         if (r_extern)
+           {
+             if (h->got_offset != 0)
+               continue;
+
+             h->got_offset = sgot->_raw_size;
+           }
+         else
+           {
+             if (adata (abfd).local_got_offsets == NULL)
+               {
+                 adata (abfd).local_got_offsets =
+                   (bfd_vma *) bfd_zalloc (abfd,
+                                           (bfd_get_symcount (abfd)
+                                            * sizeof (bfd_vma)));
+                 if (adata (abfd).local_got_offsets == NULL)
+                   {
+                     bfd_set_error (bfd_error_no_memory);
+                     return false;
+                   }
+               }
+
+             if (adata (abfd).local_got_offsets[r_index] != 0)
+               continue;
+
+             adata (abfd).local_got_offsets[r_index] = sgot->_raw_size;
+           }
+
+         sgot->_raw_size += BYTES_IN_WORD;
+
+         /* If we are making a shared library, or if the symbol is
+            defined by a dynamic object, we will need a dynamic reloc
+            entry.  */
+         if (info->shared
+             || (h != NULL
+                 && (h->flags & SUNOS_DEF_DYNAMIC) != 0
+                 && (h->flags & SUNOS_DEF_REGULAR) == 0))
+           srel->_raw_size += RELOC_EXT_SIZE;
+
+         continue;
+       }
+
+      /* Otherwise, we are only interested in relocs against symbols
+         defined in dynamic objects but not in regular objects.  We
+         only need to consider relocs against external symbols.  */
+      if (! r_extern)
+       {
+         /* But, if we are creating a shared library, we need to
+             generate an absolute reloc.  */
+         if (info->shared)
+           {
+             if (sec == obj_textsec (abfd))
+               {
+                 (*_bfd_error_handler)
+                   ("%s: may not have .text section relocs in shared library",
+                    bfd_get_filename (abfd));
+                 bfd_set_error (bfd_error_nonrepresentable_section);
+                 return false;
+               }
+
+             if (dynobj == NULL)
+               {
+                 if (! sunos_create_dynamic_sections (abfd, info, true))
+                   return false;
+                 dynobj = sunos_hash_table (info)->dynobj;
+                 splt = bfd_get_section_by_name (dynobj, ".plt");
+                 sgot = bfd_get_section_by_name (dynobj, ".got");
+                 srel = bfd_get_section_by_name (dynobj, ".dynrel");
+                 BFD_ASSERT (splt != NULL && sgot != NULL && srel != NULL);
+               }
+
+             srel->_raw_size += RELOC_EXT_SIZE;
+           }
+
+         continue;
+       }
+
+      /* At this point common symbols have already been allocated, so
+        we don't have to worry about them.  We need to consider that
+        we may have already seen this symbol and marked it undefined;
+        if the symbol is really undefined, then SUNOS_DEF_DYNAMIC
+        will be zero.  */
+      if (h->root.root.type != bfd_link_hash_defined
+         && h->root.root.type != bfd_link_hash_defweak
+         && h->root.root.type != bfd_link_hash_undefined)
+       continue;
+
+      if (r_type != RELOC_JMP_TBL
+         && ! info->shared
+         && ((h->flags & SUNOS_DEF_DYNAMIC) == 0
+             || (h->flags & SUNOS_DEF_REGULAR) != 0))
+       continue;
+
+      if (strcmp (h->root.root.root.string, "__GLOBAL_OFFSET_TABLE_") == 0)
+       continue;
+
+      if (dynobj == NULL)
+       {
+         if (! sunos_create_dynamic_sections (abfd, info, true))
+           return false;
+         dynobj = sunos_hash_table (info)->dynobj;
+         splt = bfd_get_section_by_name (dynobj, ".plt");
+         sgot = bfd_get_section_by_name (dynobj, ".got");
+         srel = bfd_get_section_by_name (dynobj, ".dynrel");
+         BFD_ASSERT (splt != NULL && sgot != NULL && srel != NULL);
+       }
+
+      BFD_ASSERT (r_type == RELOC_JMP_TBL
+                 || (h->flags & SUNOS_REF_REGULAR) != 0);
+      BFD_ASSERT (r_type == RELOC_JMP_TBL
+                 || info->shared
+                 || h->plt_offset != 0
+                 || ((h->root.root.type == bfd_link_hash_defined
+                      || h->root.root.type == bfd_link_hash_defweak)
+                     ? (h->root.root.u.def.section->owner->flags
+                        & DYNAMIC) != 0
+                     : (h->root.root.u.undef.abfd->flags & DYNAMIC) != 0));
+
+      /* This reloc is against a symbol defined only by a dynamic
+        object, or it is a jump table reloc from PIC compiled code.  */
+
+      if (r_type != RELOC_JMP_TBL
+         && h->root.root.type == bfd_link_hash_undefined)
+       {
+         /* Presumably this symbol was marked as being undefined by
+            an earlier reloc.  */
+         srel->_raw_size += RELOC_EXT_SIZE;
+       }
+      else if (r_type != RELOC_JMP_TBL
+              && (h->root.root.u.def.section->flags & SEC_CODE) == 0)
+       {
+         bfd *sub;
+
+         /* This reloc is not in the .text section.  It must be
+            copied into the dynamic relocs.  We mark the symbol as
+            being undefined.  */
+         srel->_raw_size += RELOC_EXT_SIZE;
+         if ((h->flags & SUNOS_DEF_REGULAR) == 0)
+           {
+             sub = h->root.root.u.def.section->owner;
+             h->root.root.type = bfd_link_hash_undefined;
+             h->root.root.u.undef.abfd = sub;
+           }
+       }
+      else
+       {
+         /* This symbol is in the .text section.  We must give it an
+            entry in the procedure linkage table, if we have not
+            already done so.  We change the definition of the symbol
+            to the .plt section; this will cause relocs against it to
+            be handled correctly.  */
+         if (h->plt_offset == 0)
+           {
+             if (splt->_raw_size == 0)
+               splt->_raw_size = SPARC_PLT_ENTRY_SIZE;
+             h->plt_offset = splt->_raw_size;
+
+             if ((h->flags & SUNOS_DEF_REGULAR) == 0)
+               {
+                 if (h->root.root.type == bfd_link_hash_undefined)
+                   h->root.root.type = bfd_link_hash_defined;
+                 h->root.root.u.def.section = splt;
+                 h->root.root.u.def.value = splt->_raw_size;
+               }
+
+             splt->_raw_size += SPARC_PLT_ENTRY_SIZE;
+
+             /* We will also need a dynamic reloc entry, unless this
+                 is a JMP_TBL reloc produced by linking PIC compiled
+                 code, and we are not making a shared library.  */
+             if (info->shared || (h->flags & SUNOS_DEF_REGULAR) == 0)
+               srel->_raw_size += RELOC_EXT_SIZE;
+           }
+
+         /* If we are creating a shared library, we need to copy over
+             any reloc other than a jump table reloc.  */
+         if (info->shared && r_type != RELOC_JMP_TBL)
+           srel->_raw_size += RELOC_EXT_SIZE;
+       }
+    }
+
+  return true;
+}
+
+/* Build the hash table of dynamic symbols, and to mark as written all
+   symbols from dynamic objects which we do not plan to write out.  */
+
+static boolean
+sunos_scan_dynamic_symbol (h, data)
+     struct sunos_link_hash_entry *h;
+     PTR data;
+{
+  struct bfd_link_info *info = (struct bfd_link_info *) data;
+
+  /* Set the written flag for symbols we do not want to write out as
+     part of the regular symbol table.  This is all symbols which are
+     not defined in a regular object file.  For some reason symbols
+     which are referenced by a regular object and defined by a dynamic
+     object do not seem to show up in the regular symbol table.  */
+  if ((h->flags & SUNOS_DEF_REGULAR) == 0
+      && strcmp (h->root.root.root.string, "__DYNAMIC") != 0)
+    h->root.written = true;
+
+  /* If this symbol is defined by a dynamic object and referenced by a
+     regular object, see whether we gave it a reasonable value while
+     scanning the relocs.  */
+
+  if ((h->flags & SUNOS_DEF_REGULAR) == 0
+      && (h->flags & SUNOS_DEF_DYNAMIC) != 0
+      && (h->flags & SUNOS_REF_REGULAR) != 0)
+    {
+      if ((h->root.root.type == bfd_link_hash_defined
+          || h->root.root.type == bfd_link_hash_defweak)
+         && ((h->root.root.u.def.section->owner->flags & DYNAMIC) != 0)
+         && h->root.root.u.def.section->output_section == NULL)
+       {
+         bfd *sub;
+
+         /* This symbol is currently defined in a dynamic section
+            which is not being put into the output file.  This
+            implies that there is no reloc against the symbol.  I'm
+            not sure why this case would ever occur.  In any case, we
+            change the symbol to be undefined.  */
+         sub = h->root.root.u.def.section->owner;
+         h->root.root.type = bfd_link_hash_undefined;
+         h->root.root.u.undef.abfd = sub;
+       }
+    }
+
+  /* If this symbol is defined or referenced by a regular file, add it
+     to the dynamic symbols.  */
+  if ((h->flags & (SUNOS_DEF_REGULAR | SUNOS_REF_REGULAR)) != 0)
+    {
+      asection *s;
+      size_t len;
+      bfd_byte *contents;
+      unsigned char *name;
+      unsigned long hash;
+      bfd *dynobj;
+
+      BFD_ASSERT (h->dynindx == -2);
+
+      dynobj = sunos_hash_table (info)->dynobj;
+
+      h->dynindx = sunos_hash_table (info)->dynsymcount;
+      ++sunos_hash_table (info)->dynsymcount;
+
+      len = strlen (h->root.root.root.string);
+
+      /* We don't bother to construct a BFD hash table for the strings
+        which are the names of the dynamic symbols.  Using a hash
+        table for the regular symbols is beneficial, because the
+        regular symbols includes the debugging symbols, which have
+        long names and are often duplicated in several object files.
+        There are no debugging symbols in the dynamic symbols.  */
+      s = bfd_get_section_by_name (dynobj, ".dynstr");
+      BFD_ASSERT (s != NULL);
+      if (s->contents == NULL)
+       contents = (bfd_byte *) malloc (len + 1);
+      else
+       contents = (bfd_byte *) realloc (s->contents,
+                                        (size_t) (s->_raw_size + len + 1));
+      if (contents == NULL)
+       {
+         bfd_set_error (bfd_error_no_memory);
+         return false;
+       }
+      s->contents = contents;
+
+      h->dynstr_index = s->_raw_size;
+      strcpy (contents + s->_raw_size, h->root.root.root.string);
+      s->_raw_size += len + 1;
+
+      /* Add it to the dynamic hash table.  */
+      name = (unsigned char *) h->root.root.root.string;
+      hash = 0;
+      while (*name != '\0')
+       hash = (hash << 1) + *name++;
+      hash &= 0x7fffffff;
+      hash %= sunos_hash_table (info)->bucketcount;
+
+      s = bfd_get_section_by_name (dynobj, ".hash");
+      BFD_ASSERT (s != NULL);
+
+      if (GET_SWORD (dynobj, s->contents + hash * HASH_ENTRY_SIZE) == -1)
+       PUT_WORD (dynobj, h->dynindx, s->contents + hash * HASH_ENTRY_SIZE);
+      else
+       {
+         bfd_vma next;
+
+         next = GET_WORD (dynobj,
+                          (s->contents
+                           + hash * HASH_ENTRY_SIZE
+                           + BYTES_IN_WORD));
+         PUT_WORD (dynobj, s->_raw_size / HASH_ENTRY_SIZE,
+                   s->contents + hash * HASH_ENTRY_SIZE + BYTES_IN_WORD);
+         PUT_WORD (dynobj, h->dynindx, s->contents + s->_raw_size);
+         PUT_WORD (dynobj, next, s->contents + s->_raw_size + BYTES_IN_WORD);
+         s->_raw_size += HASH_ENTRY_SIZE;
+       }
+    }
+
+  return true;
+}
+
+/* Link a dynamic object.  We actually don't have anything to do at
+   this point.  This entry point exists to prevent the regular linker
+   code from doing anything with the object.  */
+
+/*ARGSUSED*/
+static boolean
+sunos_link_dynamic_object (info, abfd)
+     struct bfd_link_info *info;
+     bfd *abfd;
+{
+  return true;
+}
+
+/* Write out a dynamic symbol.  This is called by the final traversal
+   over the symbol table.  */
+
+static boolean
+sunos_write_dynamic_symbol (output_bfd, info, harg)
+     bfd *output_bfd;
+     struct bfd_link_info *info;
+     struct aout_link_hash_entry *harg;
+{
+  struct sunos_link_hash_entry *h = (struct sunos_link_hash_entry *) harg;
+  int type;
+  bfd_vma val;
+  asection *s;
+  struct external_nlist *outsym;
+
+  if (h->dynindx < 0)
+    return true;
+
+  switch (h->root.root.type)
+    {
+    default:
+    case bfd_link_hash_new:
+      abort ();
+      /* Avoid variable not initialized warnings.  */
+      return true;
+    case bfd_link_hash_undefined:
+      type = N_UNDF | N_EXT;
+      val = 0;
+      break;
+    case bfd_link_hash_defined:
+    case bfd_link_hash_defweak:
+      {
+       asection *sec;
+       asection *output_section;
+
+       sec = h->root.root.u.def.section;
+       output_section = sec->output_section;
+       BFD_ASSERT (bfd_is_abs_section (output_section)
+                   || output_section->owner == output_bfd);
+       if (h->plt_offset != 0
+           && (h->flags & SUNOS_DEF_REGULAR) == 0)
+         {
+           type = N_UNDF | N_EXT;
+           val = 0;
+         }
+       else
+         {
+           if (output_section == obj_textsec (output_bfd))
+             type = (h->root.root.type == bfd_link_hash_defined
+                     ? N_TEXT
+                     : N_WEAKT);
+           else if (output_section == obj_datasec (output_bfd))
+             type = (h->root.root.type == bfd_link_hash_defined
+                     ? N_DATA
+                     : N_WEAKD);
+           else if (output_section == obj_bsssec (output_bfd))
+             type = (h->root.root.type == bfd_link_hash_defined
+                     ? N_BSS
+                     : N_WEAKB);
+           else
+             type = (h->root.root.type == bfd_link_hash_defined
+                     ? N_ABS
+                     : N_WEAKA);
+           type |= N_EXT;
+           val = (h->root.root.u.def.value
+                  + output_section->vma
+                  + sec->output_offset);
+         }
+      }
+      break;
+    case bfd_link_hash_common:
+      type = N_UNDF | N_EXT;
+      val = h->root.root.u.c.size;
+      break;
+    case bfd_link_hash_undefweak:
+      type = N_WEAKU;
+      val = 0;
+      break;
+    case bfd_link_hash_indirect:
+    case bfd_link_hash_warning:
+      /* FIXME: Ignore these for now.  The circumstances under which
+        they should be written out are not clear to me.  */
+      return true;
+    }
+
+  s = bfd_get_section_by_name (sunos_hash_table (info)->dynobj, ".dynsym");
+  BFD_ASSERT (s != NULL);
+  outsym = ((struct external_nlist *)
+           (s->contents + h->dynindx * EXTERNAL_NLIST_SIZE));
+
+  bfd_h_put_8 (output_bfd, type, outsym->e_type);
+  bfd_h_put_8 (output_bfd, 0, outsym->e_other);
+
+  /* FIXME: The native linker doesn't use 0 for desc.  It seems to use
+     one less than the desc value in the shared library, although that
+     seems unlikely.  */
+  bfd_h_put_16 (output_bfd, 0, outsym->e_desc);
+
+  PUT_WORD (output_bfd, h->dynstr_index, outsym->e_strx);
+  PUT_WORD (output_bfd, val, outsym->e_value);
+
+  /* If this symbol is in the procedure linkage table, fill in the
+     table entry.  */
+  if (h->plt_offset != 0)
+    {
+      bfd *dynobj;
+      asection *splt;
+      bfd_byte *p;
+      asection *s;
+      bfd_vma r_address;
+
+      dynobj = sunos_hash_table (info)->dynobj;
+      splt = bfd_get_section_by_name (dynobj, ".plt");
+      p = splt->contents + h->plt_offset;
+
+      s = bfd_get_section_by_name (dynobj, ".dynrel");
+
+      r_address = (splt->output_section->vma
+                  + splt->output_offset
+                  + h->plt_offset);
+
+      switch (bfd_get_arch (output_bfd))
+       {
+       case bfd_arch_sparc:
+         if (info->shared || (h->flags & SUNOS_DEF_REGULAR) == 0)
+           {
+             bfd_put_32 (output_bfd, SPARC_PLT_ENTRY_WORD0, p);
+             bfd_put_32 (output_bfd,
+                         (SPARC_PLT_ENTRY_WORD1
+                          + (((- (h->plt_offset + 4) >> 2)
+                              & 0x3fffffff))),
+                         p + 4);
+             bfd_put_32 (output_bfd, SPARC_PLT_ENTRY_WORD2 + s->reloc_count,
+                         p + 8);
+           }
+         else
+           {
+             bfd_vma val;
+
+             val = (h->root.root.u.def.section->output_section->vma
+                    + h->root.root.u.def.section->output_offset
+                    + h->root.root.u.def.value);
+             bfd_put_32 (output_bfd,
+                         SPARC_PLT_PIC_WORD0 + ((val >> 10) & 0x3fffff),
+                         p);
+             bfd_put_32 (output_bfd,
+                         SPARC_PLT_PIC_WORD1 + (val & 0x3ff),
+                         p + 4);
+             bfd_put_32 (output_bfd, SPARC_PLT_PIC_WORD2, p + 8);
+           }
+         break;
+
+       case bfd_arch_m68k:
+         if (! info->shared && (h->flags & SUNOS_DEF_REGULAR) != 0)
+           abort ();
+         bfd_put_16 (output_bfd, M68K_PLT_ENTRY_WORD0, p);
+         bfd_put_32 (output_bfd, (- (h->plt_offset + 2)), p + 2);
+         bfd_put_16 (output_bfd, s->reloc_count, p + 6);
+         r_address += 2;
+         break;
+
+       default:
+         abort ();
+       }
+
+      /* We also need to add a jump table reloc, unless this is the
+         result of a JMP_TBL reloc from PIC compiled code.  */
+      if (info->shared || (h->flags & SUNOS_DEF_REGULAR) == 0)
+       {
+         BFD_ASSERT (s->reloc_count * obj_reloc_entry_size (dynobj)
+                     < s->_raw_size);
+         p = s->contents + s->reloc_count * obj_reloc_entry_size (output_bfd);
+         if (obj_reloc_entry_size (output_bfd) == RELOC_STD_SIZE)
+           {
+             struct reloc_std_external *srel;
+
+             srel = (struct reloc_std_external *) p;
+             PUT_WORD (output_bfd, r_address, srel->r_address);
+             if (output_bfd->xvec->header_byteorder_big_p)
+               {
+                 srel->r_index[0] = h->dynindx >> 16;
+                 srel->r_index[1] = h->dynindx >> 8;
+                 srel->r_index[2] = h->dynindx;
+                 srel->r_type[0] = (RELOC_STD_BITS_EXTERN_BIG
+                                    | RELOC_STD_BITS_JMPTABLE_BIG);
+               }
+             else
+               {
+                 srel->r_index[2] = h->dynindx >> 16;
+                 srel->r_index[1] = h->dynindx >> 8;
+                 srel->r_index[0] = h->dynindx;
+                 srel->r_type[0] = (RELOC_STD_BITS_EXTERN_LITTLE
+                                    | RELOC_STD_BITS_JMPTABLE_LITTLE);
+               }
+           }
+         else
+           {
+             struct reloc_ext_external *erel;
+
+             erel = (struct reloc_ext_external *) p;
+             PUT_WORD (output_bfd, r_address, erel->r_address);
+             if (output_bfd->xvec->header_byteorder_big_p)
+               {
+                 erel->r_index[0] = h->dynindx >> 16;
+                 erel->r_index[1] = h->dynindx >> 8;
+                 erel->r_index[2] = h->dynindx;
+                 erel->r_type[0] =
+                   (RELOC_EXT_BITS_EXTERN_BIG
+                    | (RELOC_JMP_SLOT << RELOC_EXT_BITS_TYPE_SH_BIG));
+               }
+             else
+               {
+                 erel->r_index[2] = h->dynindx >> 16;
+                 erel->r_index[1] = h->dynindx >> 8;
+                 erel->r_index[0] = h->dynindx;
+                 erel->r_type[0] =
+                   (RELOC_EXT_BITS_EXTERN_LITTLE
+                    | (RELOC_JMP_SLOT << RELOC_EXT_BITS_TYPE_SH_LITTLE));
+               }
+             PUT_WORD (output_bfd, (bfd_vma) 0, erel->r_addend);
+           }
+
+         ++s->reloc_count;
+       }
+    }
+
+  return true;
+}
+
+/* This is called for each reloc against an external symbol.  If this
+   is a reloc which are are going to copy as a dynamic reloc, then
+   copy it over, and tell the caller to not bother processing this
+   reloc.  */
+
+/*ARGSUSED*/
+static boolean
+sunos_check_dynamic_reloc (info, input_bfd, input_section, harg, reloc,
+                          contents, skip, relocationp)
+     struct bfd_link_info *info;
+     bfd *input_bfd;
+     asection *input_section;
+     struct aout_link_hash_entry *harg;
+     PTR reloc;
+     bfd_byte *contents;
+     boolean *skip;
+     bfd_vma *relocationp;
+{
+  struct sunos_link_hash_entry *h = (struct sunos_link_hash_entry *) harg;
+  bfd *dynobj;
+  boolean baserel;
+  boolean jmptbl;
+  asection *s;
+  bfd_byte *p;
+  long indx;
+
+  *skip = false;
+
+  dynobj = sunos_hash_table (info)->dynobj;
+
+  if (h != NULL && h->plt_offset != 0)
+    {
+      asection *splt;
+
+      /* Redirect the relocation to the PLT entry.  */
+      splt = bfd_get_section_by_name (dynobj, ".plt");
+      *relocationp = (splt->output_section->vma
+                     + splt->output_offset
+                     + h->plt_offset);
+    }
+
+  if (obj_reloc_entry_size (input_bfd) == RELOC_STD_SIZE)
+    {
+      struct reloc_std_external *srel;
+
+      srel = (struct reloc_std_external *) reloc;
+      if (input_bfd->xvec->header_byteorder_big_p)
+       {
+         baserel = (0 != (srel->r_type[0] & RELOC_STD_BITS_BASEREL_BIG));
+         jmptbl = (0 != (srel->r_type[0] & RELOC_STD_BITS_JMPTABLE_BIG));
+       }
+      else
+       {
+         baserel = (0 != (srel->r_type[0] & RELOC_STD_BITS_BASEREL_LITTLE));
+         jmptbl = (0 != (srel->r_type[0] & RELOC_STD_BITS_JMPTABLE_LITTLE));
+       }
+    }
+  else
+    {
+      struct reloc_ext_external *erel;
+      int r_type;
+
+      erel = (struct reloc_ext_external *) reloc;
+      if (input_bfd->xvec->header_byteorder_big_p)
+       r_type = ((erel->r_type[0] & RELOC_EXT_BITS_TYPE_BIG)
+                 >> RELOC_EXT_BITS_TYPE_SH_BIG);
+      else
+       r_type = ((erel->r_type[0] & RELOC_EXT_BITS_TYPE_LITTLE)
+                 >> RELOC_EXT_BITS_TYPE_SH_LITTLE);
+      baserel = (r_type == RELOC_BASE10
+                || r_type == RELOC_BASE13
+                || r_type == RELOC_BASE22);
+      jmptbl = r_type == RELOC_JMP_TBL;
+    }
+
+  if (baserel)
+    {
+      bfd_vma *got_offsetp;
+      asection *sgot;
+
+      if (h != NULL)
+       got_offsetp = &h->got_offset;
+      else if (adata (input_bfd).local_got_offsets == NULL)
+       got_offsetp = NULL;
+      else
+       {
+         struct reloc_std_external *srel;
+         int r_index;
+
+         srel = (struct reloc_std_external *) reloc;
+         if (obj_reloc_entry_size (input_bfd) == RELOC_STD_SIZE)
+           {
+             if (input_bfd->xvec->header_byteorder_big_p)
+               r_index = ((srel->r_index[0] << 16)
+                          | (srel->r_index[1] << 8)
+                          | srel->r_index[2]);
+             else
+               r_index = ((srel->r_index[2] << 16)
+                          | (srel->r_index[1] << 8)
+                          | srel->r_index[0]);
+           }
+         else
+           {
+             struct reloc_ext_external *erel;
+
+             erel = (struct reloc_ext_external *) reloc;
+             if (input_bfd->xvec->header_byteorder_big_p)
+               r_index = ((erel->r_index[0] << 16)
+                          | (erel->r_index[1] << 8)
+                          | erel->r_index[2]);
+             else
+               r_index = ((erel->r_index[2] << 16)
+                          | (erel->r_index[1] << 8)
+                          | erel->r_index[0]);
+           }
+
+         got_offsetp = adata (input_bfd).local_got_offsets + r_index;
+       }
+
+      BFD_ASSERT (got_offsetp != NULL && *got_offsetp != 0);
+
+      sgot = bfd_get_section_by_name (dynobj, ".got");
+
+      /* We set the least significant bit to indicate whether we have
+        already initialized the GOT entry.  */
+      if ((*got_offsetp & 1) == 0)
+       {
+         if (h == NULL
+             || (! info->shared
+                 && ((h->flags & SUNOS_DEF_DYNAMIC) == 0
+                     || (h->flags & SUNOS_DEF_REGULAR) != 0)))
+           PUT_WORD (dynobj, *relocationp, sgot->contents + *got_offsetp);
+         else
+           PUT_WORD (dynobj, 0, sgot->contents + *got_offsetp);
+
+         if (info->shared
+             || (h != NULL
+                 && (h->flags & SUNOS_DEF_DYNAMIC) != 0
+                 && (h->flags & SUNOS_DEF_REGULAR) == 0))
+           {
+             /* We need to create a GLOB_DAT or 32 reloc to tell the
+                 dynamic linker to fill in this entry in the table.  */
+
+             s = bfd_get_section_by_name (dynobj, ".dynrel");
+             BFD_ASSERT (s != NULL);
+             BFD_ASSERT (s->reloc_count * obj_reloc_entry_size (dynobj)
+                         < s->_raw_size);
+
+             p = (s->contents
+                  + s->reloc_count * obj_reloc_entry_size (dynobj));
+
+             if (h != NULL)
+               indx = h->dynindx;
+             else
+               indx = 0;
+
+             if (obj_reloc_entry_size (dynobj) == RELOC_STD_SIZE)
+               {
+                 struct reloc_std_external *srel;
+
+                 srel = (struct reloc_std_external *) p;
+                 PUT_WORD (dynobj,
+                           (*got_offsetp
+                            + sgot->output_section->vma
+                            + sgot->output_offset),
+                           srel->r_address);
+                 if (dynobj->xvec->header_byteorder_big_p)
+                   {
+                     srel->r_index[0] = indx >> 16;
+                     srel->r_index[1] = indx >> 8;
+                     srel->r_index[2] = indx;
+                     if (h == NULL)
+                       srel->r_type[0] = 2 << RELOC_STD_BITS_LENGTH_SH_BIG;
+                     else
+                       srel->r_type[0] =
+                         (RELOC_STD_BITS_EXTERN_BIG
+                          | RELOC_STD_BITS_BASEREL_BIG
+                          | RELOC_STD_BITS_RELATIVE_BIG
+                          | (2 << RELOC_STD_BITS_LENGTH_SH_BIG));
+                   }
+                 else
+                   {
+                     srel->r_index[2] = indx >> 16;
+                     srel->r_index[1] = indx >> 8;
+                     srel->r_index[0] = indx;
+                     if (h == NULL)
+                       srel->r_type[0] = 2 << RELOC_STD_BITS_LENGTH_SH_LITTLE;
+                     else
+                       srel->r_type[0] =
+                         (RELOC_STD_BITS_EXTERN_LITTLE
+                          | RELOC_STD_BITS_BASEREL_LITTLE
+                          | RELOC_STD_BITS_RELATIVE_LITTLE
+                          | (2 << RELOC_STD_BITS_LENGTH_SH_LITTLE));
+                   }
+               }
+             else
+               {
+                 struct reloc_ext_external *erel;
+
+                 erel = (struct reloc_ext_external *) p;
+                 PUT_WORD (dynobj,
+                           (*got_offsetp
+                            + sgot->output_section->vma
+                            + sgot->output_offset),
+                           erel->r_address);
+                 if (dynobj->xvec->header_byteorder_big_p)
+                   {
+                     erel->r_index[0] = indx >> 16;
+                     erel->r_index[1] = indx >> 8;
+                     erel->r_index[2] = indx;
+                     if (h == NULL)
+                       erel->r_type[0] =
+                         RELOC_32 << RELOC_EXT_BITS_TYPE_SH_BIG;
+                     else
+                       erel->r_type[0] =
+                         (RELOC_EXT_BITS_EXTERN_BIG
+                          | (RELOC_GLOB_DAT << RELOC_EXT_BITS_TYPE_SH_BIG));
+                   }
+                 else
+                   {
+                     erel->r_index[2] = indx >> 16;
+                     erel->r_index[1] = indx >> 8;
+                     erel->r_index[0] = indx;
+                     if (h == NULL)
+                       erel->r_type[0] =
+                         RELOC_32 << RELOC_EXT_BITS_TYPE_SH_LITTLE;
+                     else
+                       erel->r_type[0] =
+                         (RELOC_EXT_BITS_EXTERN_LITTLE
+                          | (RELOC_GLOB_DAT
+                             << RELOC_EXT_BITS_TYPE_SH_LITTLE));
+                   }
+                 PUT_WORD (dynobj, 0, erel->r_addend);
+               }
+
+             ++s->reloc_count;
+           }
+
+         *got_offsetp |= 1;
+       }
+
+      *relocationp = sgot->vma + (*got_offsetp &~ 1);
+
+      /* There is nothing else to do for a base relative reloc.  */
+      return true;
+    }
+
+  if (! sunos_hash_table (info)->dynamic_sections_needed)
+    return true;
+  if (! info->shared)
+    {
+      if (h == NULL
+         || h->dynindx == -1
+         || h->root.root.type != bfd_link_hash_undefined
+         || (h->flags & SUNOS_DEF_REGULAR) != 0
+         || (h->flags & SUNOS_DEF_DYNAMIC) == 0
+         || (h->root.root.u.undef.abfd->flags & DYNAMIC) == 0)
+       return true;
+    }
+  else
+    {
+      if (h != NULL
+         && (h->dynindx == -1
+             || jmptbl
+             || strcmp (h->root.root.root.string,
+                        "__GLOBAL_OFFSET_TABLE_") == 0))
+       return true;
+      BFD_ASSERT (input_section != obj_textsec (input_bfd));
+    }
+
+  /* It looks like this is a reloc we are supposed to copy.  */
+
+  s = bfd_get_section_by_name (dynobj, ".dynrel");
+  BFD_ASSERT (s != NULL);
+  BFD_ASSERT (s->reloc_count * obj_reloc_entry_size (dynobj) < s->_raw_size);
+
+  p = s->contents + s->reloc_count * obj_reloc_entry_size (dynobj);
+
+  /* Copy the reloc over.  */
+  memcpy (p, reloc, obj_reloc_entry_size (dynobj));
+
+  if (h != NULL)
+    indx = h->dynindx;
+  else
+    indx = 0;
+
+  /* Adjust the address and symbol index.  */
+  if (obj_reloc_entry_size (dynobj) == RELOC_STD_SIZE)
+    {
+      struct reloc_std_external *srel;
+
+      srel = (struct reloc_std_external *) p;
+      PUT_WORD (dynobj,
+               (GET_WORD (dynobj, srel->r_address)
+                + input_section->output_section->vma
+                + input_section->output_offset),
+               srel->r_address);
+      if (dynobj->xvec->header_byteorder_big_p)
+       {
+         srel->r_index[0] = indx >> 16;
+         srel->r_index[1] = indx >> 8;
+         srel->r_index[2] = indx;
+       }
+      else
+       {
+         srel->r_index[2] = indx >> 16;
+         srel->r_index[1] = indx >> 8;
+         srel->r_index[0] = indx;
+       }
+    }
+  else
+    {
+      struct reloc_ext_external *erel;
+
+      erel = (struct reloc_ext_external *) p;
+      PUT_WORD (dynobj,
+               (GET_WORD (dynobj, erel->r_address)
+                + input_section->output_section->vma
+                + input_section->output_offset),
+               erel->r_address);
+      if (dynobj->xvec->header_byteorder_big_p)
+       {
+         erel->r_index[0] = indx >> 16;
+         erel->r_index[1] = indx >> 8;
+         erel->r_index[2] = indx;
+       }
+      else
+       {
+         erel->r_index[2] = indx >> 16;
+         erel->r_index[1] = indx >> 8;
+         erel->r_index[0] = indx;
+       }
+    }
+
+  ++s->reloc_count;
+
+  if (h != NULL)
+    *skip = true;
+
+  return true;
+}
+
+/* Finish up the dynamic linking information.  */
+
+static boolean
+sunos_finish_dynamic_link (abfd, info)
+     bfd *abfd;
+     struct bfd_link_info *info;
+{
+  bfd *dynobj;
+  asection *o;
+  asection *s;
+  asection *sdyn;
+  struct external_sun4_dynamic esd;
+  struct external_sun4_dynamic_link esdl;
+
+  if (! sunos_hash_table (info)->dynamic_sections_needed)
+    return true;
+
+  dynobj = sunos_hash_table (info)->dynobj;
+
+  sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
+  BFD_ASSERT (sdyn != NULL);
+
+  /* Finish up the .need section.  The linker emulation code filled it
+     in, but with offsets from the start of the section instead of
+     real addresses.  Now that we know the section location, we can
+     fill in the final values.  */
+  s = bfd_get_section_by_name (dynobj, ".need");
+  if (s != NULL && s->_raw_size != 0)
+    {
+      file_ptr filepos;
+      bfd_byte *p;
+
+      filepos = s->output_section->filepos + s->output_offset;
+      p = s->contents;
+      while (1)
+       {
+         bfd_vma val;
+
+         PUT_WORD (dynobj, GET_WORD (dynobj, p) + filepos, p);
+         val = GET_WORD (dynobj, p + 12);
+         if (val == 0)
+           break;
+         PUT_WORD (dynobj, val + filepos, p + 12);
+         p += 16;
+       }
+    }
+
+  /* The first entry in the .got section is the address of the
+     dynamic information, unless this is a shared library.  */
+  s = bfd_get_section_by_name (dynobj, ".got");
+  BFD_ASSERT (s != NULL);
+  if (info->shared)
+    PUT_WORD (dynobj, 0, s->contents);
+  else
+    PUT_WORD (dynobj, sdyn->output_section->vma + sdyn->output_offset,
+             s->contents);
+
+  for (o = dynobj->sections; o != NULL; o = o->next)
+    {
+      if ((o->flags & SEC_HAS_CONTENTS) != 0
+         && o->contents != NULL)
+       {
+         BFD_ASSERT (o->output_section != NULL
+                     && o->output_section->owner == abfd);
+         if (! bfd_set_section_contents (abfd, o->output_section,
+                                         o->contents, o->output_offset,
+                                         o->_raw_size))
+           return false;
+       }
+    }
+
+  /* Finish up the dynamic link information.  */
+  PUT_WORD (dynobj, (bfd_vma) 3, esd.ld_version);
+  PUT_WORD (dynobj,
+           sdyn->output_section->vma + sdyn->output_offset + sizeof esd,
+           esd.ldd);
+  PUT_WORD (dynobj,
+           (sdyn->output_section->vma
+            + sdyn->output_offset
+            + sizeof esd
+            + EXTERNAL_SUN4_DYNAMIC_DEBUGGER_SIZE),
+           esd.ld);
+
+  if (! bfd_set_section_contents (abfd, sdyn->output_section, &esd,
+                                 sdyn->output_offset, sizeof esd))
+    return false;
+
+
+  PUT_WORD (dynobj, (bfd_vma) 0, esdl.ld_loaded);
+
+  s = bfd_get_section_by_name (dynobj, ".need");
+  if (s == NULL || s->_raw_size == 0)
+    PUT_WORD (dynobj, (bfd_vma) 0, esdl.ld_need);
+  else
+    PUT_WORD (dynobj, s->output_section->filepos + s->output_offset,
+             esdl.ld_need);
+
+  s = bfd_get_section_by_name (dynobj, ".rules");
+  if (s == NULL || s->_raw_size == 0)
+    PUT_WORD (dynobj, (bfd_vma) 0, esdl.ld_rules);
+  else
+    PUT_WORD (dynobj, s->output_section->filepos + s->output_offset,
+             esdl.ld_rules);
+
+  s = bfd_get_section_by_name (dynobj, ".got");
+  BFD_ASSERT (s != NULL);
+  PUT_WORD (dynobj, s->output_section->vma + s->output_offset, esdl.ld_got);
+
+  s = bfd_get_section_by_name (dynobj, ".plt");
+  BFD_ASSERT (s != NULL);
+  PUT_WORD (dynobj, s->output_section->vma + s->output_offset, esdl.ld_plt);
+  PUT_WORD (dynobj, s->_raw_size, esdl.ld_plt_sz);
+
+  s = bfd_get_section_by_name (dynobj, ".dynrel");
+  BFD_ASSERT (s != NULL);
+  BFD_ASSERT (s->reloc_count * obj_reloc_entry_size (dynobj) == s->_raw_size);
+  PUT_WORD (dynobj, s->output_section->filepos + s->output_offset,
+           esdl.ld_rel);
+
+  s = bfd_get_section_by_name (dynobj, ".hash");
+  BFD_ASSERT (s != NULL);
+  PUT_WORD (dynobj, s->output_section->filepos + s->output_offset,
+           esdl.ld_hash);
+
+  s = bfd_get_section_by_name (dynobj, ".dynsym");
+  BFD_ASSERT (s != NULL);
+  PUT_WORD (dynobj, s->output_section->filepos + s->output_offset,
+           esdl.ld_stab);
+
+  PUT_WORD (dynobj, (bfd_vma) 0, esdl.ld_stab_hash);
+
+  PUT_WORD (dynobj, (bfd_vma) sunos_hash_table (info)->bucketcount,
+           esdl.ld_buckets);
+
+  s = bfd_get_section_by_name (dynobj, ".dynstr");
+  BFD_ASSERT (s != NULL);
+  PUT_WORD (dynobj, s->output_section->filepos + s->output_offset,
+           esdl.ld_symbols);
+  PUT_WORD (dynobj, s->_raw_size, esdl.ld_symb_size);
+
+  /* The size of the text area is the size of the .text section
+     rounded up to a page boundary.  FIXME: Should the page size be
+     conditional on something?  */
+  PUT_WORD (dynobj,
+           BFD_ALIGN (obj_textsec (abfd)->_raw_size, 0x2000),
+           esdl.ld_text);
+  
+  if (! bfd_set_section_contents (abfd, sdyn->output_section, &esdl,
+                                 (sdyn->output_offset
+                                  + sizeof esd
+                                  + EXTERNAL_SUN4_DYNAMIC_DEBUGGER_SIZE),
+                                 sizeof esdl))
+    return false;
+
+  abfd->flags |= DYNAMIC;
+
+  return true;
+}
This page took 0.069075 seconds and 4 git commands to generate.