]> Git Repo - binutils.git/blobdiff - bfd/aoutx.h
* aoutx.h (translate_from_native_sym_flags): Treat N_SETV symbols
[binutils.git] / bfd / aoutx.h
index ca56a50c763a801d848cdd97a9c215c36915aebb..97d3f617b5a8e7715bde36ba6c9c82adfaad8060 100644 (file)
@@ -121,7 +121,6 @@ DESCRIPTION
 #define KEEPIT flags
 #define KEEPITTYPE int
 
-#include <assert.h>
 #include <string.h>            /* For strchr and friends */
 #include "bfd.h"
 #include <sysdep.h>
@@ -134,6 +133,10 @@ DESCRIPTION
 #include "aout/ar.h"
 
 static boolean aout_get_external_symbols PARAMS ((bfd *));
+static boolean translate_from_native_sym_flags
+  PARAMS ((bfd *, aout_symbol_type *));
+static boolean translate_to_native_sym_flags
+  PARAMS ((bfd *, asymbol *, struct external_nlist *));
 
 /*
 SUBSECTION
@@ -198,6 +201,29 @@ HOWTO( 7,         0,  4,   64, true,  0, complain_overflow_signed,  0,"DISP64",    tr
 { -1 },
 HOWTO( 9,             0,  1,   16, false, 0, complain_overflow_bitfield,0,"BASE16",    false,0xffffffff,0xffffffff, false),
 HOWTO(10,             0,  2,   32, false, 0, complain_overflow_bitfield,0,"BASE32",    false,0xffffffff,0xffffffff, false),
+{ -1 },
+{ -1 },
+{ -1 },
+{ -1 },
+{ -1 },
+  HOWTO(16,           0,  2,    0, false, 0, complain_overflow_bitfield,0,"JMP_TABLE", false,         0,0x00000000, false),
+{ -1 },
+{ -1 },
+{ -1 },
+{ -1 },
+{ -1 },
+{ -1 },
+{ -1 },
+{ -1 }, { -1 }, { -1 }, { -1 }, { -1 }, { -1 }, { -1 }, { -1 },
+  HOWTO(32,           0,  2,    0, false, 0, complain_overflow_bitfield,0,"RELATIVE",  false,         0,0x00000000, false),
+{ -1 },
+{ -1 },
+{ -1 },
+{ -1 },
+{ -1 },
+{ -1 },
+{ -1 },
+  HOWTO(40,           0,  2,    0, false, 0, complain_overflow_bitfield,0,"BASEREL",   false,         0,0x00000000, false),
 };
 
 #define TABLE_SIZE(TABLE)      (sizeof(TABLE)/sizeof(TABLE[0]))
@@ -334,7 +360,23 @@ NAME(aout,swap_exec_header_out) (abfd, execp, raw_bytes)
   PUT_WORD (abfd, execp->a_drsize, bytes->e_drsize);
 }
 
+/* Make all the section for an a.out file.  */
 
+boolean
+NAME(aout,make_sections) (abfd)
+     bfd *abfd;
+{
+  if (obj_textsec (abfd) == (asection *) NULL
+      && bfd_make_section (abfd, ".text") == (asection *) NULL)
+    return false;
+  if (obj_datasec (abfd) == (asection *) NULL
+      && bfd_make_section (abfd, ".data") == (asection *) NULL)
+    return false;
+  if (obj_bsssec (abfd) == (asection *) NULL
+      && bfd_make_section (abfd, ".bss") == (asection *) NULL)
+    return false;
+  return true;
+}
 
 /*
 FUNCTION
@@ -394,16 +436,28 @@ NAME(aout,some_aout_object_p) (abfd, execp, callback_to_real_object_p)
 
   if (N_MAGIC (*execp) == ZMAGIC)
     {
-      abfd->flags |= D_PAGED|WP_TEXT;
-      adata(abfd).magic = z_magic;
+      abfd->flags |= D_PAGED | WP_TEXT;
+      adata (abfd).magic = z_magic;
+    }
+  else if (N_MAGIC (*execp) == QMAGIC)
+    {
+      abfd->flags |= D_PAGED | WP_TEXT;
+      adata (abfd).magic = z_magic;
+      adata (abfd).subformat = q_magic_format;
     }
   else if (N_MAGIC (*execp) == NMAGIC)
     {
       abfd->flags |= WP_TEXT;
-      adata(abfd).magic = n_magic;
+      adata (abfd).magic = n_magic;
     }
+  else if (N_MAGIC (*execp) == OMAGIC)
+    adata (abfd).magic = o_magic;
   else
-    adata(abfd).magic = o_magic;
+    {
+      /* Should have been checked with N_BADMAG before this routine
+        was called.  */
+      abort ();
+    }
 
   bfd_get_start_address (abfd) = execp->a_entry;
 
@@ -420,18 +474,8 @@ NAME(aout,some_aout_object_p) (abfd, execp, callback_to_real_object_p)
   obj_aout_external_strings (abfd) = NULL;
   obj_aout_sym_hashes (abfd) = NULL;
 
-  /* Create the sections.  This is raunchy, but bfd_close wants to reclaim
-     them.  */
-
-  obj_textsec (abfd) = bfd_make_section_old_way (abfd, ".text");
-  obj_datasec (abfd) = bfd_make_section_old_way (abfd, ".data");
-  obj_bsssec (abfd) = bfd_make_section_old_way (abfd, ".bss");
-
-#if 0
-  (void)bfd_make_section (abfd, ".text");
-  (void)bfd_make_section (abfd, ".data");
-  (void)bfd_make_section (abfd, ".bss");
-#endif
+  if (! NAME(aout,make_sections) (abfd))
+    return NULL;
 
   obj_datasec (abfd)->_raw_size = execp->a_data;
   obj_bsssec (abfd)->_raw_size = execp->a_bss;
@@ -565,17 +609,9 @@ NAME(aout,mkobject) (abfd)
   abfd->tdata.aout_data = rawptr;
   exec_hdr (abfd) = &(rawptr->e);
 
-  /* For simplicity's sake we just make all the sections right here. */
-
   obj_textsec (abfd) = (asection *)NULL;
   obj_datasec (abfd) = (asection *)NULL;
   obj_bsssec (abfd) = (asection *)NULL;
-  bfd_make_section (abfd, ".text");
-  bfd_make_section (abfd, ".data");
-  bfd_make_section (abfd, ".bss");
-  bfd_make_section (abfd, BFD_ABS_SECTION_NAME);
-  bfd_make_section (abfd, BFD_UND_SECTION_NAME);
-  bfd_make_section (abfd, BFD_COM_SECTION_NAME);
 
   return true;
 }
@@ -601,13 +637,15 @@ DESCRIPTION
 */
 
 enum machine_type
-NAME(aout,machine_type) (arch, machine)
+NAME(aout,machine_type) (arch, machine, unknown)
      enum bfd_architecture arch;
      unsigned long machine;
+     boolean *unknown;
 {
   enum machine_type arch_flags;
 
   arch_flags = M_UNKNOWN;
+  *unknown = true;
 
   switch (arch) {
   case bfd_arch_sparc:
@@ -617,7 +655,7 @@ NAME(aout,machine_type) (arch, machine)
   case bfd_arch_m68k:
     switch (machine) {
     case 0:            arch_flags = M_68010; break;
-    case 68000:                arch_flags = M_UNKNOWN; break;
+    case 68000:                arch_flags = M_UNKNOWN; *unknown = false; break;
     case 68010:                arch_flags = M_68010; break;
     case 68020:                arch_flags = M_68020; break;
     default:           arch_flags = M_UNKNOWN; break;
@@ -647,6 +685,10 @@ NAME(aout,machine_type) (arch, machine)
   default:
     arch_flags = M_UNKNOWN;
   }
+
+  if (arch_flags != M_UNKNOWN)
+    *unknown = false;
+
   return arch_flags;
 }
 
@@ -676,9 +718,14 @@ NAME(aout,set_arch_mach) (abfd, arch, machine)
   if (! bfd_default_set_arch_mach (abfd, arch, machine))
     return false;
 
-  if (arch != bfd_arch_unknown &&
-      NAME(aout,machine_type) (arch, machine) == M_UNKNOWN)
-    return false;              /* We can't represent this type */
+  if (arch != bfd_arch_unknown)
+    {
+      boolean unknown;
+
+      NAME(aout,machine_type) (arch, machine, &unknown);
+      if (unknown)
+       return false;
+    }
 
   /* Determine the size of a relocation entry */
   switch (arch) {
@@ -798,7 +845,10 @@ adjust_z_magic (abfd, execp)
   execp->a_text = obj_textsec(abfd)->_raw_size;
   if (ztih && (!abdp || (abdp && !abdp->exec_header_not_counted)))
     execp->a_text += adata(abfd).exec_bytes_size;
-  N_SET_MAGIC (*execp, ZMAGIC);
+  if (obj_aout_subformat (abfd) == q_magic_format)
+    N_SET_MAGIC (*execp, QMAGIC);
+  else
+    N_SET_MAGIC (*execp, ZMAGIC);
 
   /* Spec says data section should be rounded up to page boundary.  */
   obj_datasec(abfd)->_raw_size
@@ -878,12 +928,11 @@ NAME(aout,adjust_sizes_and_vmas) (abfd, text_size, text_end)
 {
   struct internal_exec *execp = exec_hdr (abfd);
 
-  if ((obj_textsec (abfd) == NULL) || (obj_datasec (abfd) == NULL))
-    {
-      bfd_set_error (bfd_error_invalid_operation);
-      return false;
-    }
-  if (adata(abfd).magic != undecided_magic) return true;
+  if (! NAME(aout,make_sections) (abfd))
+    return false;
+
+  if (adata(abfd).magic != undecided_magic)
+    return true;
 
   obj_textsec(abfd)->_raw_size =
     align_power(obj_textsec(abfd)->_raw_size,
@@ -908,7 +957,6 @@ NAME(aout,adjust_sizes_and_vmas) (abfd, text_size, text_end)
 
   if (abfd->flags & D_PAGED)
     /* Whether or not WP_TEXT is set -- let D_PAGED override.  */
-    /* @@ What about QMAGIC?  */
     adata(abfd).magic = z_magic;
   else if (abfd->flags & WP_TEXT)
     adata(abfd).magic = n_magic;
@@ -1045,44 +1093,6 @@ NAME(aout,set_section_contents) (abfd, section, location, offset, count)
   return true;
 }
 \f
-/* Classify stabs symbols */
-
-#define sym_in_text_section(sym) \
-  (((sym)->type  & (N_ABS | N_TEXT | N_DATA | N_BSS))== N_TEXT)
-
-#define sym_in_data_section(sym) \
-  (((sym)->type  & (N_ABS | N_TEXT | N_DATA | N_BSS))== N_DATA)
-
-#define sym_in_bss_section(sym) \
-  (((sym)->type  & (N_ABS | N_TEXT | N_DATA | N_BSS))== N_BSS)
-
-/* Symbol is undefined if type is N_UNDF|N_EXT and if it has
-  zero in the "value" field.  Nonzeroes there are fortrancommon
-  symbols.  */
-#define sym_is_undefined(sym) \
-  ((sym)->type == (N_UNDF | N_EXT) && (sym)->symbol.value == 0)
-
-/* Symbol is a global definition if N_EXT is on and if it has
-  a nonzero type field.  */
-#define sym_is_global_defn(sym) \
-  (((sym)->type & N_EXT) && (sym)->type & N_TYPE)
-
-/* Symbol is debugger info if any bits outside N_TYPE or N_EXT
-  are on.  */
-#define sym_is_debugger_info(sym) \
-  (((sym)->type & ~(N_EXT | N_TYPE)) || (sym)->type == N_FN)
-
-#define sym_is_fortrancommon(sym)       \
-  (((sym)->type == (N_EXT)) && (sym)->symbol.value != 0)
-
-/* Symbol is absolute if it has N_ABS set */
-#define sym_is_absolute(sym) \
-  (((sym)->type  & N_TYPE)== N_ABS)
-
-
-#define sym_is_indirect(sym) \
-  (((sym)->type & N_ABS)== N_ABS)
-
 /* Read the external symbols from an a.out file.  */
 
 static boolean
@@ -1119,7 +1129,8 @@ aout_get_external_symbols (abfd)
       obj_aout_external_sym_count (abfd) = count;
     }
       
-  if (obj_aout_external_strings (abfd) == NULL)
+  if (obj_aout_external_strings (abfd) == NULL
+      && exec_hdr (abfd)->a_syms != 0)
     {
       unsigned char string_chars[BYTES_IN_WORD];
       bfd_size_type stringsize;
@@ -1159,302 +1170,353 @@ aout_get_external_symbols (abfd)
   return true;
 }
 
-/* Only in their own functions for ease of debugging; when sym flags have
-  stabilised these should be inlined into their (single) caller */
+/* Translate an a.out symbol into a BFD symbol.  The desc, other, type
+   and symbol->value fields of CACHE_PTR will be set from the a.out
+   nlist structure.  This function is responsible for setting
+   symbol->flags and symbol->section, and adjusting symbol->value.  */
 
 static boolean
-translate_from_native_sym_flags (sym_pointer, cache_ptr, abfd)
-     struct external_nlist *sym_pointer;
-     aout_symbol_type * cache_ptr;
-     bfd * abfd;
+translate_from_native_sym_flags (abfd, cache_ptr)
+     bfd *abfd;
+     aout_symbol_type *cache_ptr;
 {
-  cache_ptr->symbol.section = 0;
-  switch (cache_ptr->type & N_TYPE)
+  flagword visible;
+
+  if ((cache_ptr->type & N_STAB) != 0
+      || cache_ptr->type == N_FN)
+    {
+      asection *sec;
+
+      /* This is a debugging symbol.  */
+
+      cache_ptr->symbol.flags = BSF_DEBUGGING;
+
+      /* Work out the symbol section.  */
+      switch (cache_ptr->type & N_TYPE)
+       {
+       case N_TEXT:
+       case N_FN:
+         sec = obj_textsec (abfd);
+         break;
+       case N_DATA:
+         sec = obj_datasec (abfd);
+         break;
+       case N_BSS:
+         sec = obj_bsssec (abfd);
+         break;
+       default:
+       case N_ABS:
+         sec = &bfd_abs_section;
+         break;
+       }
+
+      cache_ptr->symbol.section = sec;
+      cache_ptr->symbol.value -= sec->vma;
+
+      return true;
+    }
+
+  /* Get the default visibility.  This does not apply to all types, so
+     we just hold it in a local variable to use if wanted.  */
+  if ((cache_ptr->type & N_EXT) == 0)
+    visible = BSF_LOCAL;
+  else
+    visible = BSF_GLOBAL;
+
+  switch (cache_ptr->type)
     {
+    default:
+    case N_ABS: case N_ABS | N_EXT:
+      cache_ptr->symbol.section = &bfd_abs_section;
+      cache_ptr->symbol.flags = visible;
+      break;
+
+    case N_UNDF | N_EXT:
+      if (cache_ptr->symbol.value != 0)
+       {
+         /* This is a common symbol.  */
+         cache_ptr->symbol.flags = BSF_GLOBAL;
+         cache_ptr->symbol.section = &bfd_com_section;
+       }
+      else
+       {
+         cache_ptr->symbol.flags = 0;
+         cache_ptr->symbol.section = &bfd_und_section;
+       }
+      break;
+
+    case N_TEXT: case N_TEXT | N_EXT:
+      cache_ptr->symbol.section = obj_textsec (abfd);
+      cache_ptr->symbol.value -= cache_ptr->symbol.section->vma;
+      cache_ptr->symbol.flags = visible;
+      break;
+
+      /* N_SETV symbols used to represent set vectors placed in the
+        data section.  They are no longer generated.  Theoretically,
+        it was possible to extract the entries and combine them with
+        new ones, although I don't know if that was ever actually
+        done.  Unless that feature is restored, treat them as data
+        symbols.  */
+    case N_SETV: case N_SETV | N_EXT:
+    case N_DATA: case N_DATA | N_EXT:
+      cache_ptr->symbol.section = obj_datasec (abfd);
+      cache_ptr->symbol.value -= cache_ptr->symbol.section->vma;
+      cache_ptr->symbol.flags = visible;
+      break;
+
+    case N_BSS: case N_BSS | N_EXT:
+      cache_ptr->symbol.section = obj_bsssec (abfd);
+      cache_ptr->symbol.value -= cache_ptr->symbol.section->vma;
+      cache_ptr->symbol.flags = visible;
+      break;
+
     case N_SETA: case N_SETA | N_EXT:
     case N_SETT: case N_SETT | N_EXT:
     case N_SETD: case N_SETD | N_EXT:
     case N_SETB: case N_SETB | N_EXT:
       {
-       char *copy = bfd_alloc (abfd, strlen (cache_ptr->symbol.name) + 1);
        asection *section;
+       arelent_chain *reloc;
        asection *into_section;
-       arelent_chain *reloc = (arelent_chain *) bfd_alloc (abfd, sizeof (arelent_chain));
 
-       if (!copy || !reloc)
+       /* This is a set symbol.  The name of the symbol is the name
+          of the set (e.g., __CTOR_LIST__).  The value of the symbol
+          is the value to add to the set.  We create a section with
+          the same name as the symbol, and add a reloc to insert the
+          appropriate value into the section.
+
+          This action is actually obsolete; it used to make the
+          linker do the right thing, but the linker no longer uses
+          this function.  */
+
+       section = bfd_get_section_by_name (abfd, cache_ptr->symbol.name);
+       if (section == NULL)
+         {
+           char *copy;
+
+           copy = bfd_alloc (abfd, strlen (cache_ptr->symbol.name) + 1);
+           if (copy == NULL)
+             {
+               bfd_set_error (bfd_error_no_memory);
+               return false;
+             }
+
+           strcpy (copy, cache_ptr->symbol.name);
+           section = bfd_make_section (abfd, copy);
+           if (section == NULL)
+             return false;
+         }
+
+       reloc = (arelent_chain *) bfd_alloc (abfd, sizeof (arelent_chain));
+       if (reloc == NULL)
          {
            bfd_set_error (bfd_error_no_memory);
            return false;
          }
 
-       strcpy (copy, cache_ptr->symbol.name);
-
-       /* Make sure that this bfd has a section with the right contructor
-          name */
-       section = bfd_get_section_by_name (abfd, copy);
-       if (!section)
-         section = bfd_make_section (abfd, copy);
-
-       /* Build a relocation entry for the constructor */
-       switch ((cache_ptr->type & N_TYPE))
+       /* Build a relocation entry for the constructor.  */
+       switch (cache_ptr->type & N_TYPE)
          {
-         case N_SETA: case N_SETA | N_EXT:
+         case N_SETA:
            into_section = &bfd_abs_section;
            cache_ptr->type = N_ABS;
            break;
-         case N_SETT: case N_SETT | N_EXT:
-           into_section = (asection *) obj_textsec (abfd);
+         case N_SETT:
+           into_section = obj_textsec (abfd);
            cache_ptr->type = N_TEXT;
            break;
-         case N_SETD: case N_SETD | N_EXT:
-           into_section = (asection *) obj_datasec (abfd);
+         case N_SETD:
+           into_section = obj_datasec (abfd);
            cache_ptr->type = N_DATA;
            break;
-         case N_SETB: case N_SETB | N_EXT:
-           into_section = (asection *) obj_bsssec (abfd);
+         case N_SETB:
+           into_section = obj_bsssec (abfd);
            cache_ptr->type = N_BSS;
            break;
-         default:
-           bfd_set_error (bfd_error_bad_value);
-           return false;
          }
 
-       /* Build a relocation pointing into the constuctor section
-          pointing at the symbol in the set vector specified */
-
+       /* Build a relocation pointing into the constructor section
+          pointing at the symbol in the set vector specified.  */
        reloc->relent.addend = cache_ptr->symbol.value;
-       cache_ptr->symbol.section = into_section->symbol->section;
+       cache_ptr->symbol.section = into_section;
        reloc->relent.sym_ptr_ptr = into_section->symbol_ptr_ptr;
 
-
-       /* We modify the symbol to belong to a section depending upon the
-          name of the symbol - probably __CTOR__ or __DTOR__ but we don't
-          really care, and add to the size of the section to contain a
-          pointer to the symbol. Build a reloc entry to relocate to this
-          symbol attached to this section.  */
-
+       /* We modify the symbol to belong to a section depending upon
+          the name of the symbol, and add to the size of the section
+          to contain a pointer to the symbol. Build a reloc entry to
+          relocate to this symbol attached to this section.  */
        section->flags = SEC_CONSTRUCTOR | SEC_RELOC;
 
-
        section->reloc_count++;
        section->alignment_power = 2;
 
        reloc->next = section->constructor_chain;
        section->constructor_chain = reloc;
        reloc->relent.address = section->_raw_size;
-       section->_raw_size += sizeof (int *);
+       section->_raw_size += BYTES_IN_WORD;
+
+       if (obj_reloc_entry_size (abfd) == RELOC_EXT_SIZE)
+         reloc->relent.howto = howto_table_ext + CTOR_TABLE_RELOC_IDX;
+       else
+         reloc->relent.howto = howto_table_std + CTOR_TABLE_RELOC_IDX;
 
-       reloc->relent.howto
-         = (obj_reloc_entry_size(abfd) == RELOC_EXT_SIZE
-            ? howto_table_ext : howto_table_std)
-           + CTOR_TABLE_RELOC_IDX;
        cache_ptr->symbol.flags |= BSF_CONSTRUCTOR;
       }
       break;
-    default:
-      if (cache_ptr->type == N_WARNING)
-       {
-         /* This symbol is the text of a warning message, the next symbol
-            is the symbol to associate the warning with */
-         cache_ptr->symbol.flags = BSF_DEBUGGING | BSF_WARNING;
-
-         /* @@ Stuffing pointers into integers is a no-no.
-            We can usually get away with it if the integer is
-            large enough though.  */
-         if (sizeof (cache_ptr + 1) > sizeof (bfd_vma))
-           abort ();
-         cache_ptr->symbol.value = (bfd_vma) ((cache_ptr + 1));
-
-          /* We don't use a warning symbol's section, but we need
-            it to be nonzero for the sanity check below, so
-            pick one arbitrarily.  */
-         cache_ptr->symbol.section = &bfd_abs_section;
-
-         /* We furgle with the next symbol in place.
-            We don't want it to be undefined, we'll trample the type */
-         (sym_pointer + 1)->e_type[0] = 0xff;
-         break;
-       }
-      if ((cache_ptr->type | N_EXT) == (N_INDR | N_EXT))
-       {
-         /* Two symbols in a row for an INDR message. The first symbol
-            contains the name we will match, the second symbol contains
-            the name the first name is translated into. It is supplied to
-            us undefined. This is good, since we want to pull in any files
-            which define it */
-         cache_ptr->symbol.flags = BSF_DEBUGGING | BSF_INDIRECT;
-
-         /* @@ Stuffing pointers into integers is a no-no.
-            We can usually get away with it if the integer is
-            large enough though.  */
-         if (sizeof (cache_ptr + 1) > sizeof (bfd_vma))
-           abort ();
-
-         cache_ptr->symbol.value = (bfd_vma) ((cache_ptr + 1));
-         cache_ptr->symbol.section = &bfd_ind_section;
-       }
 
-      else if (sym_is_debugger_info (cache_ptr))
-       {
-         cache_ptr->symbol.flags = BSF_DEBUGGING;
-         /* Work out the section correct for this symbol */
-         switch (cache_ptr->type & N_TYPE)
-           {
-           case N_TEXT:
-           case N_FN:
-             cache_ptr->symbol.section = obj_textsec (abfd);
-             cache_ptr->symbol.value -= obj_textsec (abfd)->vma;
-             break;
-           case N_DATA:
-             cache_ptr->symbol.value -= obj_datasec (abfd)->vma;
-             cache_ptr->symbol.section = obj_datasec (abfd);
-             break;
-           case N_BSS:
-             cache_ptr->symbol.section = obj_bsssec (abfd);
-             cache_ptr->symbol.value -= obj_bsssec (abfd)->vma;
-             break;
-           default:
-           case N_ABS:
-             cache_ptr->symbol.section = &bfd_abs_section;
-             break;
-           }
-       }
-      else
-       {
+    case N_WARNING:
+      /* This symbol is the text of a warning message.  The next
+        symbol is the symbol to associate the warning with.  If a
+        reference is made to that symbol, a warning is issued.  */
+      cache_ptr->symbol.flags = BSF_DEBUGGING | BSF_WARNING;
 
-         if (sym_is_fortrancommon (cache_ptr))
-           {
-             cache_ptr->symbol.flags = 0;
-             cache_ptr->symbol.section = &bfd_com_section;
-           }
-         else
-           {
+      /* @@ Stuffing pointers into integers is a no-no.  We can
+        usually get away with it if the integer is large enough
+        though.  */
+      if (sizeof (cache_ptr + 1) > sizeof (bfd_vma))
+       abort ();
+      cache_ptr->symbol.value = (bfd_vma) (cache_ptr + 1);
 
+      cache_ptr->symbol.section = &bfd_abs_section;
 
-           }
+      break;
 
-         /* In a.out, the value of a symbol is always relative to the
-          * start of the file, if this is a data symbol we'll subtract
-          * the size of the text section to get the section relative
-          * value. If this is a bss symbol (which would be strange)
-          * we'll subtract the size of the previous two sections
-          * to find the section relative address.
-          */
+    case N_INDR: case N_INDR | N_EXT:
+      /* An indirect symbol.  This consists of two symbols in a row.
+        The first symbol is the name of the indirection.  The second
+        symbol is the name of the target.  A reference to the first
+        symbol becomes a reference to the second.  */
+      cache_ptr->symbol.flags = BSF_DEBUGGING | BSF_INDIRECT | visible;
 
-         if (sym_in_text_section (cache_ptr))
-           {
-             cache_ptr->symbol.value -= obj_textsec (abfd)->vma;
-             cache_ptr->symbol.section = obj_textsec (abfd);
-           }
-         else if (sym_in_data_section (cache_ptr))
-           {
-             cache_ptr->symbol.value -= obj_datasec (abfd)->vma;
-             cache_ptr->symbol.section = obj_datasec (abfd);
-           }
-         else if (sym_in_bss_section (cache_ptr))
-           {
-             cache_ptr->symbol.section = obj_bsssec (abfd);
-             cache_ptr->symbol.value -= obj_bsssec (abfd)->vma;
-           }
-         else if (sym_is_undefined (cache_ptr))
-           {
-             cache_ptr->symbol.flags = 0;
-             cache_ptr->symbol.section = &bfd_und_section;
-           }
-         else if (sym_is_absolute (cache_ptr))
-           {
-             cache_ptr->symbol.section = &bfd_abs_section;
-           }
+      /* @@ Stuffing pointers into integers is a no-no.  We can
+        usually get away with it if the integer is large enough
+        though.  */
+      if (sizeof (cache_ptr + 1) > sizeof (bfd_vma))
+       abort ();
+      cache_ptr->symbol.value = (bfd_vma) (cache_ptr + 1);
 
-         if (sym_is_global_defn (cache_ptr))
-           {
-             cache_ptr->symbol.flags = BSF_GLOBAL | BSF_EXPORT;
-           }
-         else if (! sym_is_undefined (cache_ptr))
-           {
-             cache_ptr->symbol.flags = BSF_LOCAL;
-           }
-       }
+      cache_ptr->symbol.section = &bfd_ind_section;
+
+      break;
+
+    case N_WEAKU:
+      cache_ptr->symbol.section = &bfd_und_section;
+      cache_ptr->symbol.flags = BSF_WEAK;
+      break;
+
+    case N_WEAKA:
+      cache_ptr->symbol.section = &bfd_abs_section;
+      cache_ptr->symbol.flags = BSF_WEAK;
+      break;
+
+    case N_WEAKT:
+      cache_ptr->symbol.section = obj_textsec (abfd);
+      cache_ptr->symbol.value -= cache_ptr->symbol.section->vma;
+      cache_ptr->symbol.flags = BSF_WEAK;
+      break;
+
+    case N_WEAKD:
+      cache_ptr->symbol.section = obj_datasec (abfd);
+      cache_ptr->symbol.value -= cache_ptr->symbol.section->vma;
+      cache_ptr->symbol.flags = BSF_WEAK;
+      break;
+
+    case N_WEAKB:
+      cache_ptr->symbol.section = obj_bsssec (abfd);
+      cache_ptr->symbol.value -= cache_ptr->symbol.section->vma;
+      cache_ptr->symbol.flags = BSF_WEAK;
+      break;
     }
-  if (cache_ptr->symbol.section == 0)
-    abort ();
+
   return true;
 }
 
+/* Set the fields of SYM_POINTER according to CACHE_PTR.  */
 
 static boolean
-translate_to_native_sym_flags (sym_pointer, cache_ptr, abfd)
-     struct external_nlist *sym_pointer;
-     asymbol *cache_ptr;
+translate_to_native_sym_flags (abfd, cache_ptr, sym_pointer)
      bfd *abfd;
+     asymbol *cache_ptr;
+     struct external_nlist *sym_pointer;
 {
   bfd_vma value = cache_ptr->value;
 
-  /* mask out any existing type bits in case copying from one section
-     to another */
+  /* Mask out any existing type bits in case copying from one section
+     to another */
   sym_pointer->e_type[0] &= ~N_TYPE;
 
-  /* We attempt to order these tests by decreasing frequency of success,
-     according to tcov when linking the linker.  */
-  if (bfd_get_output_section(cache_ptr) == &bfd_abs_section) {
+  if (bfd_get_section (cache_ptr) == &bfd_abs_section)
     sym_pointer->e_type[0] |= N_ABS;
-  }
-  else if (bfd_get_output_section(cache_ptr) == obj_textsec (abfd)) {
+  else if (bfd_get_section (cache_ptr) == obj_textsec (abfd)
+          || (bfd_get_section (cache_ptr)->output_section
+              == obj_textsec (abfd)))
     sym_pointer->e_type[0] |= N_TEXT;
-  }
-  else if (bfd_get_output_section(cache_ptr) == obj_datasec (abfd)) {
+  else if (bfd_get_section (cache_ptr) == obj_datasec (abfd)
+          || (bfd_get_section (cache_ptr)->output_section
+              == obj_datasec (abfd)))
     sym_pointer->e_type[0] |= N_DATA;
-  }
-  else if (bfd_get_output_section(cache_ptr) == obj_bsssec (abfd)) {
+  else if (bfd_get_section (cache_ptr) == obj_bsssec (abfd)
+          || (bfd_get_section (cache_ptr)->output_section
+              == obj_bsssec (abfd)))
     sym_pointer->e_type[0] |= N_BSS;
-  }
-  else if (bfd_get_output_section(cache_ptr) == &bfd_und_section) {
-    sym_pointer->e_type[0] = (N_UNDF | N_EXT);
-  }
-  else if (bfd_get_output_section(cache_ptr) == &bfd_ind_section) {
+  else if (bfd_get_section (cache_ptr) == &bfd_und_section)
+    sym_pointer->e_type[0] = N_UNDF | N_EXT;
+  else if (bfd_get_section (cache_ptr) == &bfd_ind_section)
     sym_pointer->e_type[0] = N_INDR;
-  }
-  else if (bfd_get_output_section(cache_ptr) == NULL) {
-    /* Protect the bfd_is_com_section call.
-       This case occurs, e.g., for the *DEBUG* section of a COFF file.  */
-    bfd_set_error (bfd_error_nonrepresentable_section);
-    return false;
-  }
-  else if (bfd_is_com_section (bfd_get_output_section (cache_ptr))) {
-    sym_pointer->e_type[0] = (N_UNDF | N_EXT);
-  }
-  else {
-    bfd_set_error (bfd_error_nonrepresentable_section);
-    return false;
-  }
+  else if (bfd_get_section (cache_ptr) == NULL)
+    {
+      /* Protect the bfd_is_com_section call.  This case occurs, e.g.,
+        for the *DEBUG* section of a COFF file.  */
+      bfd_set_error (bfd_error_nonrepresentable_section);
+      return false;
+    }
+  else if (bfd_is_com_section (bfd_get_section (cache_ptr)))
+    sym_pointer->e_type[0] = N_UNDF | N_EXT;
+  else
+    {
+      bfd_set_error (bfd_error_nonrepresentable_section);
+      return false;
+    }
 
   /* Turn the symbol from section relative to absolute again */
+  value += cache_ptr->section->vma;
 
-  value +=  cache_ptr->section->output_section->vma  + cache_ptr->section->output_offset ;
-
-
-  if (cache_ptr->flags & (BSF_WARNING)) {
+  if ((cache_ptr->flags & BSF_WARNING) != 0)
     sym_pointer->e_type[0] = N_WARNING;
-    (sym_pointer+1)->e_type[0] = 1;
-  }
 
-  if (cache_ptr->flags & BSF_DEBUGGING) {
-    sym_pointer->e_type[0] = ((aout_symbol_type *)cache_ptr)->type;
-  }
-  else if (cache_ptr->flags & (BSF_GLOBAL | BSF_EXPORT)) {
+  if ((cache_ptr->flags & BSF_DEBUGGING) != 0)
+    sym_pointer->e_type[0] = ((aout_symbol_type *) cache_ptr)->type;
+  else if ((cache_ptr->flags & BSF_GLOBAL) != 0)
     sym_pointer->e_type[0] |= N_EXT;
-  }
-  if (cache_ptr->flags & BSF_CONSTRUCTOR) {
-    int type = ((aout_symbol_type *)cache_ptr)->type;
-    switch (type)
-      {
-      case N_ABS:      type = N_SETA; break;
-      case N_TEXT:     type = N_SETT; break;
-      case N_DATA:     type = N_SETD; break;
-      case N_BSS:      type = N_SETB; break;
-      }
-    sym_pointer->e_type[0] = type;
-  }
+
+  if ((cache_ptr->flags & BSF_CONSTRUCTOR) != 0)
+    {
+      int type = ((aout_symbol_type *) cache_ptr)->type;
+      switch (type)
+       {
+       case N_ABS:     type = N_SETA; break;
+       case N_TEXT:    type = N_SETT; break;
+       case N_DATA:    type = N_SETD; break;
+       case N_BSS:     type = N_SETB; break;
+       }
+      sym_pointer->e_type[0] = type;
+    }
+
+  if ((cache_ptr->flags & BSF_WEAK) != 0)
+    {
+      int type;
+
+      switch (sym_pointer->e_type[0] & N_TYPE)
+       {
+       default:
+       case N_ABS:     type = N_WEAKA; break;
+       case N_TEXT:    type = N_WEAKT; break;
+       case N_DATA:    type = N_WEAKD; break;
+       case N_BSS:     type = N_WEAKB; break;
+       case N_UNDF:    type = N_WEAKU; break;
+       }
+      sym_pointer->e_type[0] = type;
+    }
 
   PUT_WORD(abfd, value, sym_pointer->e_value);
 
@@ -1463,7 +1525,6 @@ translate_to_native_sym_flags (sym_pointer, cache_ptr, abfd)
 \f
 /* Native-level interface to symbols. */
 
-
 asymbol *
 NAME(aout,make_empty_symbol) (abfd)
      bfd *abfd;
@@ -1520,7 +1581,7 @@ NAME(aout,translate_symbol_table) (abfd, in, ext, count, str, strsize, dynamic)
       in->type = bfd_h_get_8 (abfd,  ext->e_type);
       in->symbol.udata = 0;
 
-      if (!translate_from_native_sym_flags (ext, in, abfd))
+      if (! translate_from_native_sym_flags (abfd, in))
        return false;
 
       if (dynamic)
@@ -1560,13 +1621,12 @@ NAME(aout,slurp_symbol_table) (abfd)
   cached_size = (obj_aout_external_sym_count (abfd)
                 * sizeof (aout_symbol_type));
   cached = (aout_symbol_type *) malloc (cached_size);
-  memset (cached, 0, cached_size);
-
   if (cached == NULL)
     {
       bfd_set_error (bfd_error_no_memory);
       return false;
     }
+  memset (cached, 0, cached_size);
 
   /* Convert from external symbol information to internal.  */
   if (! (NAME(aout,translate_symbol_table)
@@ -1599,424 +1659,226 @@ NAME(aout,slurp_symbol_table) (abfd)
   return true;
 }
 \f
-/* Possible improvements:
+/* We use a hash table when writing out symbols so that we only write
+   out a particular string once.  This helps particularly when the
+   linker writes out stabs debugging entries, because each different
+   contributing object file tends to have many duplicate stabs
+   strings.
+
+   Possible improvements:
    + look for strings matching trailing substrings of other strings
    + better data structures?  balanced trees?
-   + smaller per-string or per-symbol data?  re-use some of the symbol's
-     data fields?
-   + also look at reducing memory use elsewhere -- maybe if we didn't have to
-     construct the entire symbol table at once, we could get by with smaller
-     amounts of VM?  (What effect does that have on the string table
-     reductions?)
-   + rip this out of here, put it into its own file in bfd or libiberty, so
-     coff and elf can use it too.  I'll work on this soon, but have more
-     pressing tasks right now.
-
-   A hash table might(?) be more efficient for handling exactly the cases that
-   are handled now, but for trailing substring matches, I think we want to
-   examine the `nearest' values (reverse-)lexically, not merely impose a strict
-   order, nor look only for exact-match or not-match.  I don't think a hash
-   table would be very useful for that, and I don't feel like fleshing out two
-   completely different implementations.  [raeburn:930419.0331EDT] */
-
-struct stringtab_entry {
-  /* Hash value for this string.  Only useful so long as we aren't doing
-     substring matches.  */
-  unsigned int hash;
-
-  /* Next node to look at, depending on whether the hash value of the string
-     being searched for is less than or greater than the hash value of the
-     current node.  For now, `equal to' is lumped in with `greater than', for
-     space efficiency.  It's not a common enough case to warrant another field
-     to be used for all nodes.  */
-  struct stringtab_entry *less;
-  struct stringtab_entry *greater;
-
-  /* The string itself.  */
-  CONST char *string;
-
-  /* The index allocated for this string.  */
-  bfd_size_type index;
-
-#ifdef GATHER_STATISTICS
-  /* How many references have there been to this string?  (Not currently used;
-     could be dumped out for anaylsis, if anyone's interested.)  */
-  unsigned long count;
-#endif
-
-  /* Next node in linked list, in suggested output order.  */
-  struct stringtab_entry *next_to_output;
-};
+   + look at reducing memory use elsewhere -- maybe if we didn't have
+     to construct the entire symbol table at once, we could get by
+     with smaller amounts of VM?  (What effect does that have on the
+     string table reductions?)
 
-struct stringtab_data {
-  /* Tree of string table entries.  */
-  struct stringtab_entry *strings;
+   This hash table code breaks dbx on SunOS 4.1.3, so we don't do it
+   if BFD_TRADITIONAL_FORMAT is set.  */
 
-  /* Fudge factor used to center top node of tree.  */
-  int hash_zero;
+/* An entry in the strtab hash table.  */
 
-  /* Next index value to issue.  */
+struct strtab_hash_entry
+{
+  struct bfd_hash_entry root;
+  /* Index in string table.  */
   bfd_size_type index;
-
-  /* Index used for empty strings.  Cached here because checking for them
-     is really easy, and we can avoid searching the tree.  */
-  bfd_size_type empty_string_index;
-
-  /* These fields indicate the two ends of a singly-linked list that indicates
-     the order strings should be written out in.  Use this order, and no
-     seeking will need to be done, so output efficiency should be maximized. */
-  struct stringtab_entry **end;
-  struct stringtab_entry *output_order;
-
-#ifdef GATHER_STATISTICS
-  /* Number of strings which duplicate strings already in the table.  */
-  unsigned long duplicates;
-
-  /* Number of bytes saved by not having to write all the duplicate strings. */
-  unsigned long bytes_saved;
-
-  /* Number of zero-length strings.  Currently, these all turn into
-     references to the null byte at the end of the first string.  In some
-     cases (possibly not all?  explore this...), it should be possible to
-     simply write out a zero index value.  */
-  unsigned long empty_strings;
-
-  /* Number of times the hash values matched but the strings were different.
-     Note that this includes the number of times the other string(s) occurs, so
-     there may only be two strings hashing to the same value, even if this
-     number is very large.  */
-  unsigned long bad_hash_matches;
-
-  /* Null strings aren't counted in this one.
-     This will probably only be nonzero if we've got an input file
-     which was produced by `ld -r' (i.e., it's already been processed
-     through this code).  Under some operating systems, native tools
-     may make all empty strings have the same index; but the pointer
-     check won't catch those, because to get to that stage we'd already
-     have to compute the checksum, which requires reading the string,
-     so we short-circuit that case with empty_string_index above.  */
-  unsigned long pointer_matches;
-
-  /* Number of comparisons done.  I figure with the algorithms in use below,
-     the average number of comparisons done (per symbol) should be roughly
-     log-base-2 of the number of unique strings.  */
-  unsigned long n_compares;
-#endif
+  /* Next string in strtab.  */
+  struct strtab_hash_entry *next;
 };
 
-/* Some utility functions for the string table code.  */
+/* The strtab hash table.  */
 
-/* For speed, only hash on the first this many bytes of strings.
-   This number was chosen by profiling ld linking itself, with -g.  */
-#define HASHMAXLEN 25
+struct strtab_hash
+{
+  struct bfd_hash_table table;
+  /* Size of strtab--also next available index.  */
+  bfd_size_type size;
+  /* First string in strtab.  */
+  struct strtab_hash_entry *first;
+  /* Last string in strtab.  */
+  struct strtab_hash_entry *last;
+};
 
-#define HASH_CHAR(c) (sum ^= sum >> 20, sum ^= sum << 7, sum += (c))
+static struct bfd_hash_entry *strtab_hash_newfunc
+  PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
+static boolean stringtab_init PARAMS ((struct strtab_hash *));
+static bfd_size_type add_to_stringtab
+  PARAMS ((bfd *, struct strtab_hash *, const char *, boolean));
+static boolean emit_stringtab PARAMS ((bfd *, struct strtab_hash *));
 
-static INLINE unsigned int
-hash (string, len)
-     unsigned char *string;
-     register unsigned int len;
+/* Routine to create an entry in a strtab.  */
+
+static struct bfd_hash_entry *
+strtab_hash_newfunc (entry, table, string)
+     struct bfd_hash_entry *entry;
+     struct bfd_hash_table *table;
+     const char *string;
 {
-  register unsigned int sum = 0;
+  struct strtab_hash_entry *ret = (struct strtab_hash_entry *) entry;
 
-  if (len > HASHMAXLEN)
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (ret == (struct strtab_hash_entry *) NULL)
+    ret = ((struct strtab_hash_entry *)
+          bfd_hash_allocate (table, sizeof (struct strtab_hash_entry)));
+  if (ret == (struct strtab_hash_entry *) NULL)
     {
-      HASH_CHAR (len);
-      len = HASHMAXLEN;
+      bfd_set_error (bfd_error_no_memory);
+      return NULL;
     }
 
-  while (len--)
+  /* Call the allocation method of the superclass.  */
+  ret = ((struct strtab_hash_entry *)
+        bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string));
+
+  if (ret)
     {
-      HASH_CHAR (*string++);
+      /* Initialize the local fields.  */
+      ret->index = (bfd_size_type) -1;
+      ret->next = NULL;
     }
-  return sum;
-}
 
-static INLINE void
-stringtab_init (tab)
-     struct stringtab_data *tab;
-{
-  tab->strings = 0;
-  tab->output_order = 0;
-  tab->hash_zero = 0;
-  tab->end = &tab->output_order;
-
-  /* Initial string table length includes size of length field.  */
-  tab->index = BYTES_IN_WORD;
-  tab->empty_string_index = -1;
-#ifdef GATHER_STATISTICS
-  tab->duplicates = 0;
-  tab->empty_strings = 0;
-  tab->bad_hash_matches = 0;
-  tab->pointer_matches = 0;
-  tab->bytes_saved = 0;
-  tab->n_compares = 0;
-#endif
+  return (struct bfd_hash_entry *) ret;
 }
 
-static INLINE int
-compare (entry, str, hash)
-     struct stringtab_entry *entry;
-     CONST char *str;
-     unsigned int hash;
-{
-  return hash - entry->hash;
-}
+/* Look up an entry in an strtab.  */
+
+#define strtab_hash_lookup(t, string, create, copy) \
+  ((struct strtab_hash_entry *) \
+   bfd_hash_lookup (&(t)->table, (string), (create), (copy)))
+
+/* Create a new strtab.  */
 
-#ifdef GATHER_STATISTICS
-/* Don't want to have to link in math library with all bfd applications...  */
-static INLINE double
-log2 (num)
-     int num;
+static boolean
+stringtab_init (table)
+     struct strtab_hash *table;
 {
-  double d = num;
-  int n = 0;
-  while (d >= 2.0)
-    n++, d /= 2.0;
-  return ((d > 1.41) ? 0.5 : 0) + n;
+  if (! bfd_hash_table_init (&table->table, strtab_hash_newfunc))
+    return false;
+
+  /* Leave space for the size of the string table.  */
+  table->size = BYTES_IN_WORD;
+
+  table->first = NULL;
+  table->last = NULL;
+
+  return true;
 }
-#endif
 
-/* Main string table routines.  */
-/* Returns index in string table.  Whether or not this actually adds an
-   entry into the string table should be irrelevant -- it just has to
-   return a valid index.  */
-static bfd_size_type
-add_to_stringtab (abfd, str, tab)
+/* Free a strtab.  */
+
+#define stringtab_free(tab) bfd_hash_table_free (&(tab)->table)
+
+/* Get the index of a string in a strtab, adding it if it is not
+   already present.  If HASH is false, we don't really use the hash
+   table, and we don't eliminate duplicate strings.  */
+
+static INLINE bfd_size_type
+add_to_stringtab (abfd, tab, str, copy)
      bfd *abfd;
-     CONST char *str;
-     struct stringtab_data *tab;
+     struct strtab_hash *tab;
+     const char *str;
+     boolean copy;
 {
-  struct stringtab_entry **ep;
-  register struct stringtab_entry *entry;
-  unsigned int hashval, len;
+  register struct strtab_hash_entry *entry;
 
-  if (str[0] == 0)
-    {
-      bfd_size_type index;
-      CONST bfd_size_type minus_one = -1;
-
-#ifdef GATHER_STATISTICS
-      tab->empty_strings++;
-#endif
-      index = tab->empty_string_index;
-      if (index != minus_one)
-       {
-       got_empty:
-#ifdef GATHER_STATISTICS
-         tab->bytes_saved++;
-         tab->duplicates++;
-#endif
-         return index;
-       }
+  /* An index of 0 always means the empty string.  */
+  if (*str == '\0')
+    return 0;
 
-      /* Need to find it.  */
-      entry = tab->strings;
-      if (entry)
-       {
-         index = entry->index + strlen (entry->string);
-         tab->empty_string_index = index;
-         goto got_empty;
-       }
-      len = 0;
-    }
-  else
-    len = strlen (str);
-
-  /* The hash_zero value is chosen such that the first symbol gets a value of
-     zero.  With a balanced tree, this wouldn't be very useful, but without it,
-     we might get a more even split at the top level, instead of skewing it
-     badly should hash("/usr/lib/crt0.o") (or whatever) be far from zero. */
-  hashval = hash (str, len) ^ tab->hash_zero;
-  ep = &tab->strings;
-  if (!*ep)
+  if ((abfd->flags & BFD_TRADITIONAL_FORMAT) == 0)
     {
-      tab->hash_zero = hashval;
-      hashval = 0;
-      goto add_it;
+      entry = strtab_hash_lookup (tab, str, true, copy);
+      if (entry == NULL)
+       return (bfd_size_type) -1;
     }
-
-  while (*ep)
+  else
     {
-      register int cmp;
-
-      entry = *ep;
-#ifdef GATHER_STATISTICS
-      tab->n_compares++;
-#endif
-      cmp = compare (entry, str, hashval);
-      /* The not-equal cases are more frequent, so check them first.  */
-      if (cmp > 0)
-       ep = &entry->greater;
-      else if (cmp < 0)
-       ep = &entry->less;
+      entry = ((struct strtab_hash_entry *)
+              bfd_hash_allocate (&tab->table,
+                                 sizeof (struct strtab_hash_entry)));
+      if (entry == NULL)
+       return (bfd_size_type) -1;
+      if (! copy)
+       entry->root.string = str;
       else
        {
-         if (entry->string == str)
-           {
-#ifdef GATHER_STATISTICS
-             tab->pointer_matches++;
-#endif
-             goto match;
-           }
-         /* Compare the first bytes to save a function call if they
-            don't match.  */
-         if (entry->string[0] == str[0] && !strcmp (entry->string, str))
-           {
-           match:
-#ifdef GATHER_STATISTICS
-             entry->count++;
-             tab->bytes_saved += len + 1;
-             tab->duplicates++;
-#endif
-             /* If we're in the linker, and the new string is from a new
-                input file which might have already had these reductions
-                run over it, we want to keep the new string pointer.  I
-                don't think we're likely to see any (or nearly as many,
-                at least) cases where a later string is in the same location
-                as an earlier one rather than this one.  */
-             entry->string = str;
-             return entry->index;
-           }
-#ifdef GATHER_STATISTICS
-         tab->bad_hash_matches++;
-#endif
-         ep = &entry->greater;
+         char *n;
+
+         n = (char *) bfd_hash_allocate (&tab->table, strlen (str) + 1);
+         if (n == NULL)
+           return (bfd_size_type) -1;
+         entry->root.string = n;
        }
+      entry->index = (bfd_size_type) -1;
+      entry->next = NULL;
     }
 
-  /* If we get here, nothing that's in the table already matched.
-     EP points to the `next' field at the end of the chain; stick a
-     new entry on here.  */
- add_it:
-  entry = (struct stringtab_entry *)
-    bfd_alloc_by_size_t (abfd, sizeof (struct stringtab_entry));
-  if (!entry)
+  if (entry->index == (bfd_size_type) -1)
     {
-      bfd_set_error (bfd_error_no_memory);
-      abort();                 /* FIXME */
+      entry->index = tab->size;
+      tab->size += strlen (str) + 1;
+      if (tab->first == NULL)
+       tab->first = entry;
+      else
+       tab->last->next = entry;
+      tab->last = entry;
     }
 
-  entry->less = entry->greater = 0;
-  entry->hash = hashval;
-  entry->index = tab->index;
-  entry->string = str;
-  entry->next_to_output = 0;
-#ifdef GATHER_STATISTICS
-  entry->count = 1;
-#endif
-
-  assert (*tab->end == 0);
-  *(tab->end) = entry;
-  tab->end = &entry->next_to_output;
-  assert (*tab->end == 0);
-
-  {
-    tab->index += len + 1;
-    if (len == 0)
-      tab->empty_string_index = entry->index;
-  }
-  assert (*ep == 0);
-  *ep = entry;
   return entry->index;
 }
 
+/* Write out a strtab.  ABFD is already at the right location in the
+   file.  */
+
 static boolean
-emit_strtab (abfd, tab)
-     bfd *abfd;
-     struct stringtab_data *tab;
+emit_stringtab (abfd, tab)
+     register bfd *abfd;
+     struct strtab_hash *tab;
 {
-  struct stringtab_entry *entry;
-#ifdef GATHER_STATISTICS
-  int count = 0;
-#endif
-
-  /* Be sure to put string length into correct byte ordering before writing
-     it out.  */
-  char buffer[BYTES_IN_WORD];
+  bfd_byte buffer[BYTES_IN_WORD];
+  register struct strtab_hash_entry *entry;
 
-  PUT_WORD (abfd, tab->index, (unsigned char *) buffer);
+  PUT_WORD (abfd, tab->size, buffer);
   if (bfd_write ((PTR) buffer, 1, BYTES_IN_WORD, abfd) != BYTES_IN_WORD)
     return false;
 
-  for (entry = tab->output_order; entry; entry = entry->next_to_output)
+  for (entry = tab->first; entry != NULL; entry = entry->next)
     {
-      size_t len = strlen (entry->string) + 1;
+      register const char *str;
+      register size_t len;
 
-      if (bfd_write ((PTR) entry->string, 1, len, abfd) != len)
+      str = entry->root.string;
+      len = strlen (str) + 1;
+      if (bfd_write ((PTR) str, 1, len, abfd) != len)
        return false;
-
-#ifdef GATHER_STATISTICS
-      count++;
-#endif
     }
 
-#ifdef GATHER_STATISTICS
-  /* Short form only, for now.
-     To do:  Specify output file.  Conditionalize on environment?  Detailed
-     analysis if desired.  */
-  {
-    int n_syms = bfd_get_symcount (abfd);
-
-    fprintf (stderr, "String table data for output file:\n");
-    fprintf (stderr, "  %8d symbols output\n", n_syms);
-    fprintf (stderr, "  %8d duplicate strings\n", tab->duplicates);
-    fprintf (stderr, "  %8d empty strings\n", tab->empty_strings);
-    fprintf (stderr, "  %8d unique strings output\n", count);
-    fprintf (stderr, "  %8d pointer matches\n", tab->pointer_matches);
-    fprintf (stderr, "  %8d bytes saved\n", tab->bytes_saved);
-    fprintf (stderr, "  %8d bad hash matches\n", tab->bad_hash_matches);
-    fprintf (stderr, "  %8d hash-val comparisons\n", tab->n_compares);
-    if (n_syms)
-      {
-       double n_compares = tab->n_compares;
-       double avg_compares = n_compares / n_syms;
-       /* The second value here should usually be near one.  */
-       fprintf (stderr,
-                "\t    average %f comparisons per symbol (%f * log2 nstrings)\n",
-                avg_compares, avg_compares / log2 (count));
-      }
-  }
-#endif
-
-/* Old code:
-  unsigned int count;
-  generic = bfd_get_outsymbols(abfd);
-  for (count = 0; count < bfd_get_symcount(abfd); count++)
-    {
-      asymbol *g = *(generic++);
-
-      if (g->name)
-       {
-         size_t length = strlen(g->name)+1;
-         bfd_write((PTR)g->name, 1, length, abfd);
-       }
-      g->KEEPIT = (KEEPITTYPE) count;
-    } */
-
   return true;
 }
-
+\f
 boolean
 NAME(aout,write_syms) (abfd)
      bfd *abfd;
 {
   unsigned int count ;
   asymbol **generic = bfd_get_outsymbols (abfd);
-  struct stringtab_data strtab;
+  struct strtab_hash strtab;
 
-  stringtab_init (&strtab);
+  if (! stringtab_init (&strtab))
+    return false;
 
   for (count = 0; count < bfd_get_symcount (abfd); count++)
     {
       asymbol *g = generic[count];
+      bfd_size_type indx;
       struct external_nlist nsp;
 
-      if (g->name)
-       PUT_WORD (abfd, add_to_stringtab (abfd, g->name, &strtab),
-                 (unsigned char *) nsp.e_strx);
-      else
-       PUT_WORD (abfd, 0, (unsigned char *)nsp.e_strx);
+      indx = add_to_stringtab (abfd, &strtab, g->name, false);
+      if (indx == (bfd_size_type) -1)
+       goto error_return;
+      PUT_WORD (abfd, indx, (bfd_byte *) nsp.e_strx);
 
       if (bfd_asymbol_flavour(g) == abfd->xvec->flavour)
        {
@@ -2031,19 +1893,28 @@ NAME(aout,write_syms) (abfd)
          bfd_h_put_8(abfd, 0, nsp.e_type);
        }
 
-      if (! translate_to_native_sym_flags (&nsp, g, abfd))
-       return false;
+      if (! translate_to_native_sym_flags (abfd, g, &nsp))
+       goto error_return;
 
       if (bfd_write((PTR)&nsp,1,EXTERNAL_NLIST_SIZE, abfd)
          != EXTERNAL_NLIST_SIZE)
-       return false;
+       goto error_return;
 
       /* NB: `KEEPIT' currently overlays `flags', so set this only
         here, at the end.  */
       g->KEEPIT = count;
     }
 
-  return emit_strtab (abfd, &strtab);
+  if (! emit_stringtab (abfd, &strtab))
+    goto error_return;
+
+  stringtab_free (&strtab);
+
+  return true;
+
+error_return:
+  stringtab_free (&strtab);
+  return false;
 }
 
 \f
@@ -2088,9 +1959,8 @@ NAME(aout,swap_std_reloc_out) (abfd, g, natptr)
   r_pcrel  = (int) g->howto->pc_relative; /* Relative to PC? */
   /* XXX This relies on relocs coming from a.out files.  */
   r_baserel = (g->howto->type & 8) != 0;
-  /* r_jmptable, r_relative???  FIXME-soon */
-  r_jmptable = 0;
-  r_relative = 0;
+  r_jmptable = (g->howto->type & 16) != 0;
+  r_relative = (g->howto->type & 32) != 0;
 
 #if 0
   /* For a standard reloc, the addend is in the object file.  */
@@ -2353,13 +2223,11 @@ NAME(aout,swap_std_reloc_in) (abfd, bytes, cache_ptr, symbols)
                        >> RELOC_STD_BITS_LENGTH_SH_LITTLE;
   }
 
-  howto_idx = r_length + 4 * r_pcrel + 8 * r_baserel;
+  howto_idx = r_length + 4 * r_pcrel + 8 * r_baserel
+             + 16 * r_jmptable + 32 * r_relative;
   BFD_ASSERT (howto_idx < TABLE_SIZE (howto_table_std));
   cache_ptr->howto =  howto_table_std + howto_idx;
   BFD_ASSERT (cache_ptr->howto->type != -1);
-  BFD_ASSERT (r_jmptable == 0);
-  BFD_ASSERT (r_relative == 0);
-  /* FIXME-soon:  Roll jmptable, relative bits into howto setting */
 
   MOVE_ADDRESS(0);
 }
@@ -2513,6 +2381,12 @@ NAME(aout,canonicalize_reloc) (abfd, section, relptr, symbols)
   arelent *tblptr = section->relocation;
   unsigned int count;
 
+  if (section == obj_bsssec (abfd))
+    {
+      *relptr = NULL;
+      return 0;
+    }
+
   if (!(tblptr || NAME(aout,slurp_reloc_table)(abfd, section, symbols)))
     return -1;
 
@@ -2559,6 +2433,9 @@ NAME(aout,get_reloc_upper_bound) (abfd, asect)
            * ((exec_hdr(abfd)->a_trsize / obj_reloc_entry_size (abfd))
               + 1));
 
+  if (asect == obj_bsssec (abfd))
+    return sizeof (arelent *);
+
   bfd_set_error (bfd_error_invalid_operation);
   return -1;
 }
@@ -2804,26 +2681,6 @@ NAME(aout,bfd_free_cached_info) (abfd)
 \f
 /* a.out link code.  */
 
-/* a.out linker hash table entries.  */
-
-struct aout_link_hash_entry
-{
-  struct bfd_link_hash_entry root;
-  /* Symbol index in output file.  */
-  int indx;
-};
-
-/* a.out linker hash table.  */
-
-struct aout_link_hash_table
-{
-  struct bfd_link_hash_table root;
-};
-
-static struct bfd_hash_entry *aout_link_hash_newfunc
-  PARAMS ((struct bfd_hash_entry *entry,
-          struct bfd_hash_table *table,
-          const char *string));
 static boolean aout_link_add_object_symbols
   PARAMS ((bfd *, struct bfd_link_info *));
 static boolean aout_link_check_archive_element
@@ -2836,8 +2693,8 @@ static boolean aout_link_add_symbols
 
 /* Routine to create an entry in an a.out link hash table.  */
 
-static struct bfd_hash_entry *
-aout_link_hash_newfunc (entry, table, string)
+struct bfd_hash_entry *
+NAME(aout,link_hash_newfunc) (entry, table, string)
      struct bfd_hash_entry *entry;
      struct bfd_hash_table *table;
      const char *string;
@@ -2860,12 +2717,28 @@ aout_link_hash_newfunc (entry, table, string)
         _bfd_link_hash_newfunc ((struct bfd_hash_entry *) ret,
                                 table, string));
   if (ret)
-    /* Set local fields.  */
-    ret->indx = -1;
+    {
+      /* Set local fields.  */
+      ret->written = false;
+      ret->indx = -1;
+    }
 
   return (struct bfd_hash_entry *) ret;
 }
 
+/* Initialize an a.out link hash table.  */
+
+boolean
+NAME(aout,link_hash_table_init) (table, abfd, newfunc)
+     struct aout_link_hash_table *table;
+     bfd *abfd;
+     struct bfd_hash_entry *(*newfunc) PARAMS ((struct bfd_hash_entry *,
+                                               struct bfd_hash_table *,
+                                               const char *));
+{
+  return _bfd_link_hash_table_init (&table->root, abfd, newfunc);
+}
+
 /* Create an a.out link hash table.  */
 
 struct bfd_link_hash_table *
@@ -2881,8 +2754,8 @@ NAME(aout,link_hash_table_create) (abfd)
        bfd_set_error (bfd_error_no_memory);
        return (struct bfd_link_hash_table *) NULL;
       }
-  if (! _bfd_link_hash_table_init (&ret->root, abfd,
-                                  aout_link_hash_newfunc))
+  if (! NAME(aout,link_hash_table_init) (ret, abfd,
+                                        NAME(aout,link_hash_newfunc)))
     {
       free (ret);
       return (struct bfd_link_hash_table *) NULL;
@@ -2890,25 +2763,6 @@ NAME(aout,link_hash_table_create) (abfd)
   return &ret->root;
 }
 
-/* Look up an entry in an a.out link hash table.  */
-
-#define aout_link_hash_lookup(table, string, create, copy, follow) \
-  ((struct aout_link_hash_entry *) \
-   bfd_link_hash_lookup (&(table)->root, (string), (create), (copy), (follow)))
-
-/* Traverse an a.out link hash table.  */
-
-#define aout_link_hash_traverse(table, func, info)                     \
-  (bfd_link_hash_traverse                                              \
-   (&(table)->root,                                                    \
-    (boolean (*) PARAMS ((struct bfd_link_hash_entry *, PTR))) (func), \
-    (info)))
-
-/* Get the a.out link hash table from the info structure.  This is
-   just a cast.  */
-
-#define aout_hash_table(p) ((struct aout_link_hash_table *) ((p)->hash))
-
 /* Given an a.out BFD, add symbols to the global hash table as
    appropriate.  */
 
@@ -3033,8 +2887,14 @@ aout_link_check_ar_symbols (abfd, info, pneeded)
       const char *name;
       struct bfd_link_hash_entry *h;
 
-      /* Ignore symbols that are not externally visible.  */
-      if ((type & N_EXT) == 0)
+      /* Ignore symbols that are not externally visible.  This is an
+        optimization only, as we check the type more thoroughly
+        below.  */
+      if ((type & N_EXT) == 0
+         && type != N_WEAKA
+         && type != N_WEAKT
+         && type != N_WEAKD
+         && type != N_WEAKB)
        {
          if (type == N_WARNING
              || type == N_INDR)
@@ -3070,7 +2930,13 @@ aout_link_check_ar_symbols (abfd, info, pneeded)
                 int a;
             and this object file from the archive includes
                 int a = 5;
-            In such a case we must include this object file.  */
+            In such a case we must include this object file.
+
+            FIXME: The SunOS 4.1.3 linker will pull in the archive
+            element if the symbol is defined in the .data section,
+            but not if it is defined in the .text section.  That
+            seems a bit crazy to me, and I haven't implemented it.
+            However, it might be correct.  */
          if (! (*info->callbacks->add_archive_element) (info, abfd, name))
            return false;
          *pneeded = true;
@@ -3120,6 +2986,23 @@ aout_link_check_ar_symbols (abfd, info, pneeded)
                }
            }
        }
+
+      if (type == N_WEAKA
+         || type == N_WEAKT
+         || type == N_WEAKD
+         || type == N_WEAKB)
+       {
+         /* This symbol is weak but defined.  We must pull it in if
+            the current link symbol is undefined, but we don't want
+            it if the current link symbol is common.  */
+         if (h->type == bfd_link_hash_undefined)
+           {
+             if (! (*info->callbacks->add_archive_element) (info, abfd, name))
+               return false;
+             *pneeded = true;
+             return true;
+           }
+       }
     }
 
   /* We do not need this object file.  */
@@ -3133,6 +3016,11 @@ aout_link_add_symbols (abfd, info)
      bfd *abfd;
      struct bfd_link_info *info;
 {
+  boolean (*add_one_symbol) PARAMS ((struct bfd_link_info *, bfd *,
+                                    const char *, flagword, asection *,
+                                    bfd_vma, const char *, boolean,
+                                    boolean,
+                                    struct bfd_link_hash_entry **));
   bfd_size_type sym_count;
   char *strings;
   boolean copy;
@@ -3155,13 +3043,24 @@ aout_link_add_symbols (abfd, info)
              bfd_alloc (abfd,
                         ((size_t) sym_count
                          * sizeof (struct aout_link_hash_entry *))));
-  if (!sym_hash)
+  if (sym_hash == NULL && sym_count != 0)
     {
       bfd_set_error (bfd_error_no_memory);
       return false;
     }
   obj_aout_sym_hashes (abfd) = sym_hash;
 
+  if ((abfd->flags & DYNAMIC) != 0
+      && aout_backend_info (abfd)->add_dynamic_symbols != NULL)
+    {
+      if (! (*aout_backend_info (abfd)->add_dynamic_symbols) (abfd, info))
+       return false;
+    }
+
+  add_one_symbol = aout_backend_info (abfd)->add_one_symbol;
+  if (add_one_symbol == NULL)
+    add_one_symbol = _bfd_generic_link_add_one_symbol;
+
   p = obj_aout_external_syms (abfd);
   pend = p + sym_count;
   for (; p < pend; p++, sym_hash++)
@@ -3181,29 +3080,6 @@ aout_link_add_symbols (abfd, info)
       if ((type & N_STAB) != 0)
        continue;
 
-      /* Ignore symbols that are not external.  */
-      if ((type & N_EXT) == 0
-         && type != N_WARNING
-         && type != N_SETA
-         && type != N_SETT
-         && type != N_SETD
-         && type != N_SETB)
-       {
-         /* If this is an N_INDR symbol we must skip the next entry,
-            which is the symbol to indirect to (actually, an N_INDR
-            symbol without N_EXT set is pretty useless).  */
-         if (type == N_INDR)
-           {
-             ++p;
-             ++sym_hash;
-           }
-         continue;
-       }
-
-      /* Ignore N_FN symbols (these appear to have N_EXT set).  */
-      if (type == N_FN)
-       continue;
-
       name = strings + GET_WORD (abfd, p->e_strx);
       value = GET_WORD (abfd, p->e_value);
       flags = BSF_GLOBAL;
@@ -3212,11 +3088,32 @@ aout_link_add_symbols (abfd, info)
        {
        default:
          abort ();
+
+       case N_UNDF:
+       case N_ABS:
+       case N_TEXT:
+       case N_DATA:
+       case N_BSS:
+       case N_FN_SEQ:
+       case N_COMM:
+       case N_SETV:
+       case N_FN:
+         /* Ignore symbols that are not externally visible.  */
+         continue;
+       case N_INDR:
+         /* Ignore local indirect symbol.  */
+         ++p;
+         ++sym_hash;
+         continue;
+
        case N_UNDF | N_EXT:
-         if (value != 0)
-           section = &bfd_com_section;
+         if (value == 0)
+           {
+             section = &bfd_und_section;
+             flags = 0;
+           }
          else
-           section = &bfd_und_section;
+           section = &bfd_com_section;
          break;
        case N_ABS | N_EXT:
          section = &bfd_abs_section;
@@ -3226,6 +3123,9 @@ aout_link_add_symbols (abfd, info)
          value -= bfd_get_section_vma (abfd, section);
          break;
        case N_DATA | N_EXT:
+       case N_SETV | N_EXT:
+         /* Treat N_SETV symbols as N_DATA symbol; see comment in
+            translate_from_native_sym_flags.  */
          section = obj_datasec (abfd);
          value -= bfd_get_section_vma (abfd, section);
          break;
@@ -3274,9 +3174,32 @@ aout_link_add_symbols (abfd, info)
          section = &bfd_und_section;
          flags |= BSF_WARNING;
          break;
+       case N_WEAKU:
+         section = &bfd_und_section;
+         flags = BSF_WEAK;
+         break;
+       case N_WEAKA:
+         section = &bfd_abs_section;
+         flags = BSF_WEAK;
+         break;
+       case N_WEAKT:
+         section = obj_textsec (abfd);
+         value -= bfd_get_section_vma (abfd, section);
+         flags = BSF_WEAK;
+         break;
+       case N_WEAKD:
+         section = obj_datasec (abfd);
+         value -= bfd_get_section_vma (abfd, section);
+         flags = BSF_WEAK;
+         break;
+       case N_WEAKB:
+         section = obj_bsssec (abfd);
+         value -= bfd_get_section_vma (abfd, section);
+         flags = BSF_WEAK;
+         break;
        }
 
-      if (! (_bfd_generic_link_add_one_symbol
+      if (! ((*add_one_symbol)
             (info, abfd, name, flags, section, value, string, copy, false,
              (struct bfd_link_hash_entry **) sym_hash)))
        return false;
@@ -3302,7 +3225,7 @@ struct aout_final_link_info
   /* File position of symbols.  */
   file_ptr symoff;
   /* String table.  */
-  struct stringtab_data strtab;
+  struct strtab_hash strtab;
 };
 
 static boolean aout_link_input_bfd
@@ -3381,13 +3304,15 @@ NAME(aout,final_link) (abfd, info, callback)
              abort ();
            }
        }
-      trsize += (_bfd_count_link_order_relocs (obj_textsec (abfd)
-                                              ->link_order_head)
-                * obj_reloc_entry_size (abfd));
+      if (obj_textsec (abfd) != (asection *) NULL)
+       trsize += (_bfd_count_link_order_relocs (obj_textsec (abfd)
+                                                ->link_order_head)
+                  * obj_reloc_entry_size (abfd));
       exec_hdr (abfd)->a_trsize = trsize;
-      drsize += (_bfd_count_link_order_relocs (obj_datasec (abfd)
-                                              ->link_order_head)
-                * obj_reloc_entry_size (abfd));
+      if (obj_datasec (abfd) != (asection *) NULL)
+       drsize += (_bfd_count_link_order_relocs (obj_datasec (abfd)
+                                                ->link_order_head)
+                  * obj_reloc_entry_size (abfd));
       exec_hdr (abfd)->a_drsize = drsize;
     }
 
@@ -3417,7 +3342,8 @@ NAME(aout,final_link) (abfd, info, callback)
   obj_aout_external_sym_count (abfd) = 0;
 
   /* We accumulate the string table as we write out the symbols.  */
-  stringtab_init (&aout_info.strtab);
+  if (! stringtab_init (&aout_info.strtab))
+    return false;
 
   /* The most time efficient way to do the link would be to read all
      the input object files into memory and then sort out the
@@ -3505,6 +3431,13 @@ NAME(aout,final_link) (abfd, info, callback)
        }
     }
 
+  /* Finish up any dynamic linking we may be doing.  */
+  if (aout_backend_info (abfd)->finish_dynamic_link != NULL)
+    {
+      if (! (*aout_backend_info (abfd)->finish_dynamic_link) (abfd, info))
+       return false;
+    }
+
   /* Update the header information.  */
   abfd->symcount = obj_aout_external_sym_count (abfd);
   exec_hdr (abfd)->a_syms = abfd->symcount * EXTERNAL_NLIST_SIZE;
@@ -3517,7 +3450,7 @@ NAME(aout,final_link) (abfd, info, callback)
   /* Write out the string table.  */
   if (bfd_seek (abfd, obj_str_filepos (abfd), SEEK_SET) != 0)
     return false;
-  return emit_strtab (abfd, &aout_info.strtab);
+  return emit_stringtab (abfd, &aout_info.strtab);
 }
 
 /* Link an a.out input BFD into the output file.  */
@@ -3532,6 +3465,14 @@ aout_link_input_bfd (finfo, input_bfd)
 
   BFD_ASSERT (bfd_get_format (input_bfd) == bfd_object);
 
+  /* If this is a dynamic object, it may need special handling.  */
+  if ((input_bfd->flags & DYNAMIC) != 0
+      && aout_backend_info (input_bfd)->link_dynamic_object != NULL)
+    {
+      return ((*aout_backend_info (input_bfd)->link_dynamic_object)
+             (finfo->info, input_bfd));
+    }
+
   /* Get the symbols.  We probably have them already, unless
      finfo->info->keep_memory is false.  */
   if (! aout_get_external_symbols (input_bfd))
@@ -3596,6 +3537,7 @@ aout_link_write_symbols (finfo, input_bfd, symbol_map)
   enum bfd_link_discard discard;
   struct external_nlist *output_syms = NULL;
   struct external_nlist *outsym;
+  bfd_size_type strtab_index;
   register struct external_nlist *sym;
   struct external_nlist *sym_end;
   struct aout_link_hash_entry **sym_hash;
@@ -3627,10 +3569,11 @@ aout_link_write_symbols (finfo, input_bfd, symbol_map)
       bfd_h_put_8 (output_bfd, N_TEXT, outsym->e_type);
       bfd_h_put_8 (output_bfd, 0, outsym->e_other);
       bfd_h_put_16 (output_bfd, (bfd_vma) 0, outsym->e_desc);
-      PUT_WORD (output_bfd,
-               add_to_stringtab (output_bfd, input_bfd->filename,
-                                 &finfo->strtab),
-               outsym->e_strx);
+      strtab_index = add_to_stringtab (output_bfd, &finfo->strtab,
+                                      input_bfd->filename, false);
+      if (strtab_index == (bfd_size_type) -1)
+       goto error_return;
+      PUT_WORD (output_bfd, strtab_index, outsym->e_strx);
       PUT_WORD (output_bfd,
                (bfd_get_section_vma (output_bfd,
                                      obj_textsec (input_bfd)->output_section)
@@ -3653,6 +3596,7 @@ aout_link_write_symbols (finfo, input_bfd, symbol_map)
       boolean skip;
       asection *symsec;
       bfd_vma val = 0;
+      boolean copy;
 
       *symbol_map = -1;
 
@@ -3696,7 +3640,8 @@ aout_link_write_symbols (finfo, input_bfd, symbol_map)
                  || h->root.type == bfd_link_hash_warning))
            {
              hresolve = (struct aout_link_hash_entry *) h->root.u.i.link;
-             while (hresolve->root.type == bfd_link_hash_indirect)
+             while (hresolve->root.type == bfd_link_hash_indirect
+                    || hresolve->root.type == bfd_link_hash_warning)
                hresolve = ((struct aout_link_hash_entry *)
                            hresolve->root.u.i.link);
              *sym_hash = hresolve;
@@ -3704,8 +3649,11 @@ aout_link_write_symbols (finfo, input_bfd, symbol_map)
 
          /* If the symbol has already been written out, skip it.  */
          if (h != (struct aout_link_hash_entry *) NULL
-             && h->root.written)
+             && h->root.type != bfd_link_hash_warning
+             && h->written)
            {
+             if ((type & N_TYPE) == N_INDR)
+               skip_indirect = true;
              *symbol_map = h->indx;
              continue;
            }
@@ -3732,18 +3680,22 @@ aout_link_write_symbols (finfo, input_bfd, symbol_map)
          if (skip)
            {
              if (h != (struct aout_link_hash_entry *) NULL)
-               h->root.written = true;
+               h->written = true;
              continue;
            }
 
          /* Get the value of the symbol.  */
-         if ((type & N_TYPE) == N_TEXT)
+         if ((type & N_TYPE) == N_TEXT
+             || type == N_WEAKT)
            symsec = obj_textsec (input_bfd);
-         else if ((type & N_TYPE) == N_DATA)
+         else if ((type & N_TYPE) == N_DATA
+                  || type == N_WEAKD)
            symsec = obj_datasec (input_bfd);
-         else if ((type & N_TYPE) == N_BSS)
+         else if ((type & N_TYPE) == N_BSS
+                  || type == N_WEAKB)
            symsec = obj_bsssec (input_bfd);
-         else if ((type & N_TYPE) == N_ABS)
+         else if ((type & N_TYPE) == N_ABS
+                  || type == N_WEAKA)
            symsec = &bfd_abs_section;
          else if (((type & N_TYPE) == N_INDR
                    && (hresolve == (struct aout_link_hash_entry *) NULL
@@ -3816,6 +3768,11 @@ aout_link_write_symbols (finfo, input_bfd, symbol_map)
                }
              else if (hresolve->root.type == bfd_link_hash_common)
                val = hresolve->root.u.c.size;
+             else if (hresolve->root.type == bfd_link_hash_weak)
+               {
+                 val = 0;
+                 type = N_WEAKU;
+               }
              else
                val = 0;
 
@@ -3831,7 +3788,7 @@ aout_link_write_symbols (finfo, input_bfd, symbol_map)
             it is a local symbol see if we should discard it.  */
          if (h != (struct aout_link_hash_entry *) NULL)
            {
-             h->root.written = true;
+             h->written = true;
              h->indx = obj_aout_external_sym_count (output_bfd);
            }
          else
@@ -3866,6 +3823,7 @@ aout_link_write_symbols (finfo, input_bfd, symbol_map)
                   outsym->e_other);
       bfd_h_put_16 (output_bfd, bfd_h_get_16 (input_bfd, sym->e_desc),
                    outsym->e_desc);
+      copy = false;
       if (! finfo->info->keep_memory)
        {
          /* name points into a string table which we are going to
@@ -3874,17 +3832,13 @@ aout_link_write_symbols (finfo, input_bfd, symbol_map)
          if (h != (struct aout_link_hash_entry *) NULL)
            name = (*sym_hash)->root.root.string;
          else
-           {
-             char *n;
-
-             n = bfd_alloc (output_bfd, strlen (name) + 1);
-             strcpy (n, name);
-             name = n;
-           }
+           copy = true;
        }
-      PUT_WORD (output_bfd,
-               add_to_stringtab (output_bfd, name, &finfo->strtab),
-               outsym->e_strx);
+      strtab_index = add_to_stringtab (output_bfd, &finfo->strtab,
+                                      name, copy);
+      if (strtab_index == (bfd_size_type) -1)
+       goto error_return;
+      PUT_WORD (output_bfd, strtab_index, outsym->e_strx);
       PUT_WORD (output_bfd, val, outsym->e_value);
       *symbol_map = obj_aout_external_sym_count (output_bfd);
       ++obj_aout_external_sym_count (output_bfd);
@@ -3928,11 +3882,24 @@ aout_link_write_other_symbol (h, data)
   int type;
   bfd_vma val;
   struct external_nlist outsym;
+  bfd_size_type indx;
+
+  output_bfd = finfo->output_bfd;
+
+  if (aout_backend_info (output_bfd)->write_dynamic_symbol != NULL)
+    {
+      if (! ((*aout_backend_info (output_bfd)->write_dynamic_symbol)
+            (output_bfd, finfo->info, h)))
+       {
+         /* FIXME: No way to handle errors.  */
+         abort ();
+       }
+    }
 
-  if (h->root.written)
+  if (h->written)
     return true;
 
-  h->root.written = true;
+  h->written = true;
 
   if (finfo->info->strip == strip_all
       || (finfo->info->strip == strip_some
@@ -3940,8 +3907,6 @@ aout_link_write_other_symbol (h, data)
                              false, false) == NULL))
     return true;
 
-  output_bfd = finfo->output_bfd;
-
   switch (h->root.type)
     {
     default:
@@ -3957,7 +3922,7 @@ aout_link_write_other_symbol (h, data)
       {
        asection *sec;
 
-       sec = h->root.u.def.section;
+       sec = h->root.u.def.section->output_section;
        BFD_ASSERT (sec == &bfd_abs_section
                    || sec->owner == output_bfd);
        if (sec == obj_textsec (output_bfd))
@@ -3969,14 +3934,17 @@ aout_link_write_other_symbol (h, data)
        else
          type = N_ABS | N_EXT;
        val = (h->root.u.def.value
-              + sec->output_section->vma
-              + sec->output_offset);
+              + sec->vma
+              + h->root.u.def.section->output_offset);
       }
       break;
     case bfd_link_hash_common:
       type = N_UNDF | N_EXT;
       val = h->root.u.c.size;
       break;
+    case bfd_link_hash_weak:
+      type = N_WEAKU;
+      val = 0;
     case bfd_link_hash_indirect:
     case bfd_link_hash_warning:
       /* FIXME: Ignore these for now.  The circumstances under which
@@ -3987,9 +3955,14 @@ aout_link_write_other_symbol (h, data)
   bfd_h_put_8 (output_bfd, type, outsym.e_type);
   bfd_h_put_8 (output_bfd, 0, outsym.e_other);
   bfd_h_put_16 (output_bfd, 0, outsym.e_desc);
-  PUT_WORD (output_bfd,
-           add_to_stringtab (output_bfd, h->root.root.string, &finfo->strtab),
-           outsym.e_strx);
+  indx = add_to_stringtab (output_bfd, &finfo->strtab, h->root.root.string,
+                          false);
+  if (indx == (bfd_size_type) -1)
+    {
+      /* FIXME: No way to handle errors.  */
+      abort ();
+    }
+  PUT_WORD (output_bfd, indx, outsym.e_strx);
   PUT_WORD (output_bfd, val, outsym.e_value);
 
   if (bfd_seek (output_bfd, finfo->symoff, SEEK_SET) != 0
@@ -4021,7 +3994,8 @@ aout_link_input_section (finfo, input_bfd, input_section, reloff_ptr,
 {
   bfd_size_type input_size;
   bfd_byte *contents = NULL;
-  PTR relocs = NULL;
+  PTR relocs;
+  PTR free_relocs = NULL;
 
   /* Get the section contents.  */
   input_size = bfd_section_size (input_bfd, input_section);
@@ -4035,16 +4009,22 @@ aout_link_input_section (finfo, input_bfd, input_section, reloff_ptr,
                                  (file_ptr) 0, input_size))
     goto error_return;
 
-  /* Read in the relocs.  */
-  relocs = (PTR) malloc (rel_size);
-  if (relocs == NULL && rel_size != 0)
+  /* Read in the relocs if we haven't already done it.  */
+  if (aout_section_data (input_section) != NULL
+      && aout_section_data (input_section)->relocs != NULL)
+    relocs = aout_section_data (input_section)->relocs;
+  else
     {
-      bfd_set_error (bfd_error_no_memory);
-      goto error_return;
+      relocs = free_relocs = (PTR) malloc (rel_size);
+      if (relocs == NULL && rel_size != 0)
+       {
+         bfd_set_error (bfd_error_no_memory);
+         goto error_return;
+       }
+      if (bfd_seek (input_bfd, input_section->rel_filepos, SEEK_SET) != 0
+         || bfd_read (relocs, 1, rel_size, input_bfd) != rel_size)
+       goto error_return;
     }
-  if (bfd_seek (input_bfd, input_section->rel_filepos, SEEK_SET) != 0
-      || bfd_read (relocs, 1, rel_size, input_bfd) != rel_size)
-    goto error_return;
 
   /* Relocate the section contents.  */
   if (obj_reloc_entry_size (input_bfd) == RELOC_STD_SIZE)
@@ -4090,14 +4070,14 @@ aout_link_input_section (finfo, input_bfd, input_section, reloff_ptr,
                          <= obj_datasec (finfo->output_bfd)->rel_filepos)));
     }
 
-  if (relocs != NULL)
-    free (relocs);
+  if (free_relocs != NULL)
+    free (free_relocs);
   if (contents != NULL)
     free (contents);
   return true;
  error_return:
-  if (relocs != NULL)
-    free (relocs);
+  if (free_relocs != NULL)
+    free (free_relocs);
   if (contents != NULL)
     free (contents);
   return false;
@@ -4139,6 +4119,10 @@ aout_link_input_section_std (finfo, input_bfd, input_section, relocs,
      bfd_byte *contents;
      int *symbol_map;
 {
+  boolean (*check_dynamic_reloc) PARAMS ((struct bfd_link_info *,
+                                         bfd *, asection *,
+                                         struct aout_link_hash_entry *,
+                                         PTR, boolean *));
   bfd *output_bfd;
   boolean relocateable;
   struct external_nlist *syms;
@@ -4149,6 +4133,7 @@ aout_link_input_section_std (finfo, input_bfd, input_section, relocs,
   struct reloc_std_external *rel_end;
 
   output_bfd = finfo->output_bfd;
+  check_dynamic_reloc = aout_backend_info (output_bfd)->check_dynamic_reloc;
 
   BFD_ASSERT (obj_reloc_entry_size (input_bfd) == RELOC_STD_SIZE);
   BFD_ASSERT (input_bfd->xvec->header_byteorder_big_p
@@ -4205,10 +4190,9 @@ aout_link_input_section_std (finfo, input_bfd, input_section, relocs,
                       >> RELOC_STD_BITS_LENGTH_SH_LITTLE);
        }
 
-      howto_idx = r_length + 4 * r_pcrel + 8 * r_baserel;
+      howto_idx = r_length + 4 * r_pcrel + 8 * r_baserel
+                 + 16 * r_jmptable + 32 * r_relative;
       BFD_ASSERT (howto_idx < TABLE_SIZE (howto_table_std));
-      BFD_ASSERT (r_jmptable == 0);
-      BFD_ASSERT (r_relative == 0);
 
       if (relocateable)
        {
@@ -4327,6 +4311,19 @@ aout_link_input_section_std (finfo, input_bfd, input_section, relocs,
              struct aout_link_hash_entry *h;
 
              h = sym_hashes[r_index];
+
+             if (check_dynamic_reloc != NULL)
+               {
+                 boolean skip;
+
+                 if (! ((*check_dynamic_reloc)
+                        (finfo->info, input_bfd, input_section, h,
+                         (PTR) rel, &skip)))
+                   return false;
+                 if (skip)
+                   continue;
+               }
+
              if (h != (struct aout_link_hash_entry *) NULL
                  && h->root.type == bfd_link_hash_defined)
                {
@@ -4334,6 +4331,9 @@ aout_link_input_section_std (finfo, input_bfd, input_section, relocs,
                                + h->root.u.def.section->output_section->vma
                                + h->root.u.def.section->output_offset);
                }
+             else if (h != (struct aout_link_hash_entry *) NULL
+                      && h->root.type == bfd_link_hash_weak)
+               relocation = 0;
              else
                {
                  const char *name;
@@ -4411,6 +4411,10 @@ aout_link_input_section_ext (finfo, input_bfd, input_section, relocs,
      bfd_byte *contents;
      int *symbol_map;
 {
+  boolean (*check_dynamic_reloc) PARAMS ((struct bfd_link_info *,
+                                         bfd *, asection *,
+                                         struct aout_link_hash_entry *,
+                                         PTR, boolean *));
   bfd *output_bfd;
   boolean relocateable;
   struct external_nlist *syms;
@@ -4421,6 +4425,7 @@ aout_link_input_section_ext (finfo, input_bfd, input_section, relocs,
   struct reloc_ext_external *rel_end;
 
   output_bfd = finfo->output_bfd;
+  check_dynamic_reloc = aout_backend_info (output_bfd)->check_dynamic_reloc;
 
   BFD_ASSERT (obj_reloc_entry_size (input_bfd) == RELOC_EXT_SIZE);
   BFD_ASSERT (input_bfd->xvec->header_byteorder_big_p
@@ -4603,6 +4608,19 @@ aout_link_input_section_ext (finfo, input_bfd, input_section, relocs,
              struct aout_link_hash_entry *h;
 
              h = sym_hashes[r_index];
+
+             if (check_dynamic_reloc != NULL)
+               {
+                 boolean skip;
+
+                 if (! ((*check_dynamic_reloc)
+                        (finfo->info, input_bfd, input_section, h,
+                         (PTR) rel, &skip)))
+                   return false;
+                 if (skip)
+                   continue;
+               }
+
              if (h != (struct aout_link_hash_entry *) NULL
                  && h->root.type == bfd_link_hash_defined)
                {
@@ -4610,6 +4628,9 @@ aout_link_input_section_ext (finfo, input_bfd, input_section, relocs,
                                + h->root.u.def.section->output_section->vma
                                + h->root.u.def.section->output_offset);
                }
+             else if (h != (struct aout_link_hash_entry *) NULL
+                      && h->root.type == bfd_link_hash_weak)
+               relocation = 0;
              else
                {
                  const char *name;
@@ -4775,8 +4796,8 @@ aout_link_reloc_link_order (finfo, o, p)
 
       r_pcrel = howto->pc_relative;
       r_baserel = (howto->type & 8) != 0;
-      r_jmptable = 0;
-      r_relative = 0;
+      r_jmptable = (howto->type & 16) != 0;
+      r_relative = (howto->type & 32) != 0;
       r_length = howto->size;
 
       PUT_WORD (finfo->output_bfd, p->offset, srel.r_address);
@@ -4822,7 +4843,7 @@ aout_link_reloc_link_order (finfo, o, p)
          boolean ok;
 
          size = bfd_get_reloc_size (howto);
-         buf = (bfd_byte*) bfd_zmalloc (size);
+         buf = (bfd_byte *) bfd_zmalloc (size);
          if (buf == (bfd_byte *) NULL)
            {
              bfd_set_error (bfd_error_no_memory);
This page took 0.089056 seconds and 4 git commands to generate.