bfd/
[deliverable/binutils-gdb.git] / bfd / xcofflink.c
index b1cac27b2dc53357c386e00acc1563a919c9a5a1..f02f66862f0ce0755f56b9a98fad0c590e39cd6b 100644 (file)
@@ -1,13 +1,13 @@
 /* POWER/PowerPC XCOFF linker support.
-   Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
-   Free Software Foundation, Inc.
+   Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+   2005, 2006, 2007, 2008 Free Software Foundation, Inc.
    Written by Ian Lance Taylor <ian@cygnus.com>, Cygnus Support.
 
    This file is part of BFD, the Binary File Descriptor library.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
 
-#include "bfd.h"
 #include "sysdep.h"
+#include "bfd.h"
 #include "bfdlink.h"
 #include "libbfd.h"
 #include "coff/internal.h"
@@ -257,7 +258,12 @@ _bfd_xcoff_canonicalize_dynamic_symtab (bfd *abfd, asymbol **psyms)
 
       symbuf->symbol.flags = BSF_NO_FLAGS;
       if ((ldsym.l_smtype & L_EXPORT) != 0)
-       symbuf->symbol.flags |= BSF_GLOBAL;
+       {
+         if ((ldsym.l_smtype & L_WEAK) != 0)
+           symbuf->symbol.flags |= BSF_WEAK;
+         else
+           symbuf->symbol.flags |= BSF_GLOBAL;
+       }
 
       /* FIXME: We have no way to record the other information stored
         with the loader symbol.  */
@@ -444,7 +450,8 @@ _bfd_xcoff_bfd_link_hash_table_create (bfd *abfd)
   ret = bfd_malloc (amt);
   if (ret == NULL)
     return NULL;
-  if (! _bfd_link_hash_table_init (&ret->root, abfd, xcoff_link_hash_newfunc))
+  if (!_bfd_link_hash_table_init (&ret->root, abfd, xcoff_link_hash_newfunc,
+                                 sizeof (struct xcoff_link_hash_entry)))
     {
       free (ret);
       return NULL;
@@ -538,6 +545,36 @@ xcoff_read_internal_relocs (bfd *abfd,
                                         require_internal, internal_relocs);
 }
 \f
+/* H is the bfd symbol associated with exported .loader symbol LDSYM.
+   Return true if LDSYM defines H.  */
+
+static bfd_boolean
+xcoff_dynamic_definition_p (struct xcoff_link_hash_entry *h,
+                           struct internal_ldsym *ldsym)
+{
+  /* If we didn't know about H before processing LDSYM, LDSYM
+     definitely defines H.  */
+  if (h->root.type == bfd_link_hash_new)
+    return TRUE;
+
+  /* If H is currently a weak dynamic symbol, and if LDSYM is a strong
+     dynamic symbol, LDSYM trumps the current definition of H.  */
+  if ((ldsym->l_smtype & L_WEAK) == 0
+      && (h->flags & XCOFF_DEF_DYNAMIC) != 0
+      && (h->flags & XCOFF_DEF_REGULAR) == 0
+      && (h->root.type == bfd_link_hash_defweak
+         || h->root.type == bfd_link_hash_undefweak))
+    return TRUE;
+
+  /* If H is currently undefined, LDSYM defines it.  */
+  if ((h->flags & XCOFF_DEF_DYNAMIC) == 0
+      && (h->root.type == bfd_link_hash_undefined
+         || h->root.type == bfd_link_hash_undefweak))
+    return TRUE;
+
+  return FALSE;
+}
+
 /* This function is used to add symbols from a dynamic object to the
    global symbol table.  */
 
@@ -558,7 +595,7 @@ xcoff_link_add_dynamic_symbols (bfd *abfd, struct bfd_link_info *info)
 
   /* We can only handle a dynamic object if we are generating an XCOFF
      output file.  */
-   if (info->hash->creator != abfd->xvec)
+   if (info->output_bfd->xvec != abfd->xvec)
     {
       (*_bfd_error_handler)
        (_("%s: XCOFF shared object when not producing XCOFF output"),
@@ -636,43 +673,33 @@ xcoff_link_add_dynamic_symbols (bfd *abfd, struct bfd_link_info *info)
       if (h == NULL)
        return FALSE;
 
-      h->flags |= XCOFF_DEF_DYNAMIC;
-
-      /* If the symbol is undefined, and the BFD it was found in is
-        not a dynamic object, change the BFD to this dynamic object,
-        so that we can get the correct import file ID.  */
-      if ((h->root.type == bfd_link_hash_undefined
-          || h->root.type == bfd_link_hash_undefweak)
-         && (h->root.u.undef.abfd == NULL
-             || (h->root.u.undef.abfd->flags & DYNAMIC) == 0))
-       h->root.u.undef.abfd = abfd;
-
-      if (h->root.type == bfd_link_hash_new)
-       {
-         h->root.type = bfd_link_hash_undefined;
-         h->root.u.undef.abfd = abfd;
-         /* We do not want to add this to the undefined symbol list.  */
-       }
-
-      if (h->smclas == XMC_UA
-         || h->root.type == bfd_link_hash_undefined
-         || h->root.type == bfd_link_hash_undefweak)
-       h->smclas = ldsym.l_smclas;
-
-      /* Unless this is an XMC_XO symbol, we don't bother to actually
-        define it, since we don't have a section to put it in anyhow.
-        Instead, the relocation routines handle the DEF_DYNAMIC flag
-        correctly.  */
+      if (!xcoff_dynamic_definition_p (h, &ldsym))
+       continue;
 
-      if (h->smclas == XMC_XO
-         && (h->root.type == bfd_link_hash_undefined
-             || h->root.type == bfd_link_hash_undefweak))
+      h->flags |= XCOFF_DEF_DYNAMIC;
+      h->smclas = ldsym.l_smclas;
+      if (h->smclas == XMC_XO)
        {
          /* This symbol has an absolute value.  */
-         h->root.type = bfd_link_hash_defined;
+         if ((ldsym.l_smtype & L_WEAK) != 0)
+           h->root.type = bfd_link_hash_defweak;
+         else
+           h->root.type = bfd_link_hash_defined;
          h->root.u.def.section = bfd_abs_section_ptr;
          h->root.u.def.value = ldsym.l_value;
        }
+      else
+       {
+         /* Otherwise, we don't bother to actually define the symbol,
+            since we don't have a section to put it in anyhow.
+            We assume instead that an undefined XCOFF_DEF_DYNAMIC symbol
+            should be imported from the symbol's undef.abfd.  */
+         if ((ldsym.l_smtype & L_WEAK) != 0)
+           h->root.type = bfd_link_hash_undefweak;
+         else
+           h->root.type = bfd_link_hash_undefined;
+         h->root.u.undef.abfd = abfd;
+       }
 
       /* If this symbol defines a function descriptor, then it
         implicitly defines the function code as well.  */
@@ -699,33 +726,30 @@ xcoff_link_add_dynamic_symbols (bfd *abfd, struct bfd_link_info *info)
              if (hds == NULL)
                return FALSE;
 
-             if (hds->root.type == bfd_link_hash_new)
-               {
-                 hds->root.type = bfd_link_hash_undefined;
-                 hds->root.u.undef.abfd = abfd;
-                 /* We do not want to add this to the undefined
-                    symbol list.  */
-               }
-
              hds->descriptor = h;
              h->descriptor = hds;
            }
 
-         hds->flags |= XCOFF_DEF_DYNAMIC;
-         if (hds->smclas == XMC_UA)
-           hds->smclas = XMC_PR;
-
-         /* An absolute symbol appears to actually define code, not a
-            function descriptor.  This is how some math functions are
-            implemented on AIX 4.1.  */
-         if (h->smclas == XMC_XO
-             && (hds->root.type == bfd_link_hash_undefined
-                 || hds->root.type == bfd_link_hash_undefweak))
+         if (xcoff_dynamic_definition_p (hds, &ldsym))
            {
-             hds->smclas = XMC_XO;
-             hds->root.type = bfd_link_hash_defined;
-             hds->root.u.def.section = bfd_abs_section_ptr;
-             hds->root.u.def.value = ldsym.l_value;
+             hds->root.type = h->root.type;
+             hds->flags |= XCOFF_DEF_DYNAMIC;
+             if (h->smclas == XMC_XO)
+               {
+                 /* An absolute symbol appears to actually define code, not a
+                    function descriptor.  This is how some math functions are
+                    implemented on AIX 4.1.  */
+                 hds->smclas = XMC_XO;
+                 hds->root.u.def.section = bfd_abs_section_ptr;
+                 hds->root.u.def.value = ldsym.l_value;
+               }
+             else
+               {
+                 hds->smclas = XMC_PR;
+                 hds->root.u.undef.abfd = abfd;
+                 /* We do not want to add this to the undefined
+                    symbol list.  */
+               }
            }
        }
     }
@@ -784,7 +808,7 @@ xcoff_link_create_extra_sections (bfd * abfd, struct bfd_link_info *info)
 {
   bfd_boolean return_value = FALSE;
 
-  if (info->hash->creator == abfd->xvec)
+  if (info->output_bfd->xvec == abfd->xvec)
     {
       /* We need to build a .loader section, so we do it here.  This
         won't work if we're producing an XCOFF output file with no
@@ -793,27 +817,27 @@ xcoff_link_create_extra_sections (bfd * abfd, struct bfd_link_info *info)
       if (xcoff_hash_table (info)->loader_section == NULL)
        {
          asection *lsec;
+         flagword flags = SEC_HAS_CONTENTS | SEC_IN_MEMORY;
 
-         lsec = bfd_make_section_anyway (abfd, ".loader");
+         lsec = bfd_make_section_anyway_with_flags (abfd, ".loader", flags);
          if (lsec == NULL)
            goto end_return;
 
          xcoff_hash_table (info)->loader_section = lsec;
-         lsec->flags |= SEC_HAS_CONTENTS | SEC_IN_MEMORY;
        }
 
       /* Likewise for the linkage section.  */
       if (xcoff_hash_table (info)->linkage_section == NULL)
        {
          asection *lsec;
+         flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
+                           | SEC_IN_MEMORY);
 
-         lsec = bfd_make_section_anyway (abfd, ".gl");
+         lsec = bfd_make_section_anyway_with_flags (abfd, ".gl", flags);
          if (lsec == NULL)
            goto end_return;
 
          xcoff_hash_table (info)->linkage_section = lsec;
-         lsec->flags |= (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
-                         | SEC_IN_MEMORY);
          lsec->alignment_power = 2;
        }
 
@@ -821,14 +845,14 @@ xcoff_link_create_extra_sections (bfd * abfd, struct bfd_link_info *info)
       if (xcoff_hash_table (info)->toc_section == NULL)
        {
          asection *tsec;
+         flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
+                           | SEC_IN_MEMORY);
 
-         tsec = bfd_make_section_anyway (abfd, ".tc");
+         tsec = bfd_make_section_anyway_with_flags (abfd, ".tc", flags);
          if (tsec == NULL)
            goto end_return;
 
          xcoff_hash_table (info)->toc_section = tsec;
-         tsec->flags |= (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
-                         | SEC_IN_MEMORY);
          tsec->alignment_power = 2;
        }
 
@@ -836,14 +860,14 @@ xcoff_link_create_extra_sections (bfd * abfd, struct bfd_link_info *info)
       if (xcoff_hash_table (info)->descriptor_section == NULL)
        {
          asection *dsec;
+         flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
+                           | SEC_IN_MEMORY);
 
-         dsec = bfd_make_section_anyway (abfd, ".ds");
+         dsec = bfd_make_section_anyway_with_flags (abfd, ".ds", flags);
          if (dsec == NULL)
            goto end_return;
 
          xcoff_hash_table (info)->descriptor_section = dsec;
-         dsec->flags |= (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
-                         | SEC_IN_MEMORY);
          dsec->alignment_power = 2;
        }
 
@@ -852,13 +876,13 @@ xcoff_link_create_extra_sections (bfd * abfd, struct bfd_link_info *info)
          && info->strip != strip_all)
        {
          asection *dsec;
+         flagword flags = SEC_HAS_CONTENTS | SEC_IN_MEMORY;
 
-         dsec = bfd_make_section_anyway (abfd, ".debug");
+         dsec = bfd_make_section_anyway_with_flags (abfd, ".debug", flags);
          if (dsec == NULL)
            goto end_return;
 
          xcoff_hash_table (info)->debug_section = dsec;
-         dsec->flags |= SEC_HAS_CONTENTS | SEC_IN_MEMORY;
        }
     }
 
@@ -945,6 +969,7 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
   bfd_size_type symcount;
   struct xcoff_link_hash_entry **sym_hash;
   asection **csect_cache;
+  unsigned int *lineno_counts;
   bfd_size_type linesz;
   asection *o;
   asection *last_real;
@@ -1011,6 +1036,15 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
     goto error_return;
   xcoff_data (abfd)->csects = csect_cache;
 
+  /* We garbage-collect line-number information on a symbol-by-symbol
+     basis, so we need to have quick access to the number of entries
+     per symbol.  */
+  amt = symcount * sizeof (unsigned int);
+  lineno_counts = bfd_zalloc (abfd, amt);
+  if (lineno_counts == NULL && symcount != 0)
+    goto error_return;
+  xcoff_data (abfd)->lineno_counts = lineno_counts;
+
   /* While splitting sections into csects, we need to assign the
      relocs correctly.  The relocs and the csects must both be in
      order by VMA within a given section, so we handle this by
@@ -1075,7 +1109,6 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
       const char *name;
       char buf[SYMNMLEN + 1];
       int smtyp;
-      flagword flags;
       asection *section;
       bfd_vma value;
       struct xcoff_link_hash_entry *set_toc;
@@ -1084,23 +1117,15 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
 
       /* In this pass we are only interested in symbols with csect
         information.  */
-      if (sym.n_sclass != C_EXT && sym.n_sclass != C_HIDEXT)
+      if (!CSECT_SYM_P (sym.n_sclass))
        {
          /* Set csect_cache,
             Normally csect is a .pr, .rw  etc. created in the loop
             If C_FILE or first time, handle special
 
-            Advance esym, sym_hash, csect_hash ptr's
-            Keep track of the last_symndx for the current file.  */
-         if (sym.n_sclass == C_FILE && csect != NULL)
-           {
-             xcoff_section_data (abfd, csect)->last_symndx =
-               ((esym
-                 - (bfd_byte *) obj_coff_external_syms (abfd))
-                / symesz);
-             csect = NULL;
-           }
-
+            Advance esym, sym_hash, csect_hash ptrs.  */
+         if (sym.n_sclass == C_FILE)
+           csect = NULL;
          if (csect != NULL)
            *csect_cache = csect;
          else if (first_csect == NULL || sym.n_sclass == C_FILE)
@@ -1110,6 +1135,7 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
          esym += (sym.n_numaux + 1) * symesz;
          sym_hash += sym.n_numaux + 1;
          csect_cache += sym.n_numaux + 1;
+         lineno_counts += sym.n_numaux + 1;
 
          continue;
        }
@@ -1180,7 +1206,7 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
                          if (lin.l_lnno == 0)
                            break;
                        }
-                     csect->lineno_count += (linp - linpstart) / linesz;
+                     *lineno_counts = (linp - linpstart) / linesz;
                      /* The setting of line_filepos will only be
                         useful if all the line number entries for a
                         csect are contiguous; this only matters for
@@ -1211,7 +1237,6 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
 
       smtyp = SMTYP_SMTYP (aux.x_csect.x_smtyp);
 
-      flags = BSF_GLOBAL;
       section = NULL;
       value = 0;
       set_toc = NULL;
@@ -1251,13 +1276,6 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
          break;
 
        case XTY_SD:
-         /* This is a csect definition.  */
-         if (csect != NULL)
-           {
-             xcoff_section_data (abfd, csect)->last_symndx =
-               ((esym - (bfd_byte *) obj_coff_external_syms (abfd)) / symesz);
-           }
-
          csect = NULL;
          csect_index = -(unsigned) 1;
 
@@ -1292,7 +1310,7 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
             place.  */
          if (aux.x_csect.x_smclas == XMC_TC
              && sym.n_sclass == C_HIDEXT
-             && info->hash->creator == abfd->xvec
+             && info->output_bfd->xvec == abfd->xvec
              && ((bfd_xcoff_is_xcoff32 (abfd)
                   && aux.x_csect.x_scnlen.l == 4)
                  || (bfd_xcoff_is_xcoff64 (abfd)
@@ -1329,7 +1347,7 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
                  erelsym = ((bfd_byte *) obj_coff_external_syms (abfd)
                             + rel->r_symndx * symesz);
                  bfd_coff_swap_sym_in (abfd, (void *) erelsym, (void *) &relsym);
-                 if (relsym.n_sclass == C_EXT)
+                 if (EXTERN_SYM_P (relsym.n_sclass))
                    {
                      const char *relname;
                      char relbuf[SYMNMLEN + 1];
@@ -1484,9 +1502,9 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
            if (first_csect == NULL)
              first_csect = csect;
 
-           /* If this symbol is C_EXT, we treat it as starting at the
+           /* If this symbol is external, we treat it as starting at the
               beginning of the newly created section.  */
-           if (sym.n_sclass == C_EXT)
+           if (EXTERN_SYM_P (sym.n_sclass))
              {
                section = csect;
                value = 0;
@@ -1539,28 +1557,21 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
             named .tocbss, and rely on the linker script to put that
             in the TOC area.  */
 
-         if (csect != NULL)
-           {
-             xcoff_section_data (abfd, csect)->last_symndx =
-               ((esym
-                 - (bfd_byte *) obj_coff_external_syms (abfd))
-                / symesz);
-           }
-
          if (aux.x_csect.x_smclas == XMC_TD)
            {
              /* The linker script puts the .td section in the data
                 section after the .tc section.  */
-             csect = bfd_make_section_anyway (abfd, ".td");
+             csect = bfd_make_section_anyway_with_flags (abfd, ".td",
+                                                         SEC_ALLOC);
            }
          else
-           csect = bfd_make_section_anyway (abfd, ".bss");
+           csect = bfd_make_section_anyway_with_flags (abfd, ".bss",
+                                                       SEC_ALLOC);
 
          if (csect == NULL)
            goto error_return;
          csect->vma = sym.n_value;
          csect->size = aux.x_csect.x_scnlen.l;
-         csect->flags |= SEC_ALLOC;
          csect->alignment_power = SMTYP_ALIGN (aux.x_csect.x_smtyp);
          /* There are a number of other fields and section flags
             which we do not bother to set.  */
@@ -1582,7 +1593,7 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
          if (first_csect == NULL)
            first_csect = csect;
 
-         if (sym.n_sclass == C_EXT)
+         if (EXTERN_SYM_P (sym.n_sclass))
            {
              csect->flags |= SEC_IS_COMMON;
              csect->size = 0;
@@ -1623,9 +1634,10 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
       /* Now we have enough information to add the symbol to the
         linker hash table.  */
 
-      if (sym.n_sclass == C_EXT)
+      if (EXTERN_SYM_P (sym.n_sclass))
        {
          bfd_boolean copy;
+         flagword flags;
 
          BFD_ASSERT (section != NULL);
 
@@ -1661,7 +1673,7 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
             shared object, which will cause symbol redefinitions,
             although this is an easier case to detect.  */
 
-         if (info->hash->creator == abfd->xvec)
+         if (info->output_bfd->xvec == abfd->xvec)
            {
              if (! bfd_is_und_section (section))
                *sym_hash = xcoff_link_hash_lookup (xcoff_hash_table (info),
@@ -1696,8 +1708,8 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
                      section = bfd_und_section_ptr;
                      value = 0;
                    }
-                 else if (((*sym_hash)->root.u.def.section->owner->flags
-                           & DYNAMIC) != 0)
+                 else if (((*sym_hash)->flags & XCOFF_DEF_REGULAR) == 0
+                          && ((*sym_hash)->flags & XCOFF_DEF_DYNAMIC) != 0)
                    {
                      /* The existing symbol is from a shared library.
                         Replace it.  */
@@ -1713,6 +1725,12 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
                      section = bfd_und_section_ptr;
                      value = 0;
                    }
+                 else if (sym.n_sclass == C_AIX_WEAKEXT
+                          || (*sym_hash)->root.type == bfd_link_hash_defweak)
+                   {
+                     /* At least one of the definitions is weak.
+                        Allow the normal rules to take effect.  */
+                   }
                  else if ((*sym_hash)->root.u.undef.next != NULL
                           || info->hash->undefs_tail == &(*sym_hash)->root)
                    {
@@ -1732,8 +1750,7 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
                    }
                }
              else if (((*sym_hash)->flags & XCOFF_MULTIPLY_DEFINED) != 0
-                      && ((*sym_hash)->root.type == bfd_link_hash_defined
-                          || (*sym_hash)->root.type == bfd_link_hash_defweak)
+                      && (*sym_hash)->root.type == bfd_link_hash_defined
                       && (bfd_is_und_section (section)
                           || bfd_is_com_section (section)))
                {
@@ -1768,6 +1785,7 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
             a second time from the csects.  */
          BFD_ASSERT (last_real->next == first_csect);
          last_real->next = NULL;
+         flags = (sym.n_sclass == C_EXT ? BSF_GLOBAL : BSF_WEAK);
          if (! (_bfd_generic_link_add_one_symbol
                 (info, abfd, name, flags, section, value,
                  NULL, copy, TRUE,
@@ -1786,7 +1804,7 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
                  = csect->alignment_power;
            }
 
-         if (info->hash->creator == abfd->xvec)
+         if (info->output_bfd->xvec == abfd->xvec)
            {
              int flag;
 
@@ -1802,11 +1820,20 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
            }
        }
 
-      *csect_cache = csect;
+      if (smtyp == XTY_ER)
+       *csect_cache = section;
+      else
+       {
+         *csect_cache = csect;
+         if (csect != NULL)
+           xcoff_section_data (abfd, csect)->last_symndx
+             = (esym - (bfd_byte *) obj_coff_external_syms (abfd)) / symesz;
+       }
 
       esym += (sym.n_numaux + 1) * symesz;
       sym_hash += sym.n_numaux + 1;
       csect_cache += sym.n_numaux + 1;
+      lineno_counts += sym.n_numaux + 1;
     }
 
   BFD_ASSERT (last_real == NULL || last_real->next == first_csect);
@@ -1842,19 +1869,16 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
                  goto error_return;
                }
 
-             /* We identify all symbols which are called, so that we
-                can create glue code for calls to functions imported
-                from dynamic objects.  */
-             if (info->hash->creator == abfd->xvec
+             /* We identify all function symbols that are the target
+                of a relocation, so that we can create glue code for
+                functions imported from dynamic objects.  */
+             if (info->output_bfd->xvec == abfd->xvec
                  && *rel_csect != bfd_und_section_ptr
-                 && (rel->r_type == R_BR
-                     || rel->r_type == R_RBR)
                  && obj_xcoff_sym_hashes (abfd)[rel->r_symndx] != NULL)
                {
                  struct xcoff_link_hash_entry *h;
 
                  h = obj_xcoff_sym_hashes (abfd)[rel->r_symndx];
-                 h->flags |= XCOFF_CALLED;
                  /* If the symbol name starts with a period, it is
                     the code of a function.  If the symbol is
                     currently undefined, then add an undefined symbol
@@ -1887,11 +1911,12 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
                          hds = (struct xcoff_link_hash_entry *) bh;
                        }
                      hds->flags |= XCOFF_DESCRIPTOR;
-                     BFD_ASSERT ((hds->flags & XCOFF_CALLED) == 0
-                                 && (h->flags & XCOFF_DESCRIPTOR) == 0);
+                     BFD_ASSERT ((h->flags & XCOFF_DESCRIPTOR) == 0);
                      hds->descriptor = h;
                      h->descriptor = hds;
                    }
+                 if (h->root.root.string[0] == '.')
+                   h->flags |= XCOFF_CALLED;
                }
            }
 
@@ -2063,7 +2088,7 @@ xcoff_link_check_ar_symbols (bfd *abfd,
 
   if ((abfd->flags & DYNAMIC) != 0
       && ! info->static_link
-      && info->hash->creator == abfd->xvec)
+      && info->output_bfd->xvec == abfd->xvec)
     return xcoff_link_check_dynamic_ar_symbols (abfd, info, pneeded);
 
   symesz = bfd_coff_symesz (abfd);
@@ -2075,7 +2100,7 @@ xcoff_link_check_ar_symbols (bfd *abfd,
 
       bfd_coff_swap_sym_in (abfd, (void *) esym, (void *) &sym);
 
-      if (sym.n_sclass == C_EXT && sym.n_scnum != N_UNDEF)
+      if (EXTERN_SYM_P (sym.n_sclass) && sym.n_scnum != N_UNDEF)
        {
          const char *name;
          char buf[SYMNMLEN + 1];
@@ -2096,7 +2121,7 @@ xcoff_link_check_ar_symbols (bfd *abfd,
             undefined references in shared objects.  */
          if (h != NULL
              && h->type == bfd_link_hash_undefined
-             && (info->hash->creator != abfd->xvec
+             && (info->output_bfd->xvec != abfd->xvec
                  || (((struct xcoff_link_hash_entry *) h)->flags
                      & XCOFF_DEF_DYNAMIC) == 0))
            {
@@ -2177,7 +2202,7 @@ _bfd_xcoff_bfd_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
        while (member != NULL)
          {
            if (bfd_check_format (member, bfd_object)
-               && (info->hash->creator == member->xvec)
+               && (info->output_bfd->xvec == member->xvec)
                && (! bfd_has_map (abfd) || (member->flags & DYNAMIC) != 0))
              {
                bfd_boolean needed;
@@ -2200,6 +2225,185 @@ _bfd_xcoff_bfd_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
     }
 }
 \f
+/* If symbol H has not been interpreted as a function descriptor,
+   see whether it should be.  Set up its descriptor information if so.  */
+
+static bfd_boolean
+xcoff_find_function (struct bfd_link_info *info,
+                    struct xcoff_link_hash_entry *h)
+{
+  if ((h->flags & XCOFF_DESCRIPTOR) == 0
+      && h->root.root.string[0] != '.')
+    {
+      char *fnname;
+      struct xcoff_link_hash_entry *hfn;
+      bfd_size_type amt;
+
+      amt = strlen (h->root.root.string) + 2;
+      fnname = bfd_malloc (amt);
+      if (fnname == NULL)
+       return FALSE;
+      fnname[0] = '.';
+      strcpy (fnname + 1, h->root.root.string);
+      hfn = xcoff_link_hash_lookup (xcoff_hash_table (info),
+                                   fnname, FALSE, FALSE, TRUE);
+      free (fnname);
+      if (hfn != NULL
+         && hfn->smclas == XMC_PR
+         && (hfn->root.type == bfd_link_hash_defined
+             || hfn->root.type == bfd_link_hash_defweak))
+       {
+         h->flags |= XCOFF_DESCRIPTOR;
+         h->descriptor = hfn;
+         hfn->descriptor = h;
+       }
+    }
+  return TRUE;
+}
+
+/* H is an imported symbol.  Set the import module's path, file and member
+   to IMPATH, IMPFILE and IMPMEMBER respectively.  All three are null if
+   no specific import module is specified.  */
+
+static bfd_boolean
+xcoff_set_import_path (struct bfd_link_info *info,
+                      struct xcoff_link_hash_entry *h,
+                      const char *imppath, const char *impfile,
+                      const char *impmember)
+{
+  unsigned int c;
+  struct xcoff_import_file **pp;
+
+  /* We overload the ldindx field to hold the l_ifile value for this
+     symbol.  */
+  BFD_ASSERT (h->ldsym == NULL);
+  BFD_ASSERT ((h->flags & XCOFF_BUILT_LDSYM) == 0);
+  if (imppath == NULL)
+    h->ldindx = -1;
+  else
+    {
+      /* We start c at 1 because the first entry in the import list is
+        reserved for the library search path.  */
+      for (pp = &xcoff_hash_table (info)->imports, c = 1;
+          *pp != NULL;
+          pp = &(*pp)->next, ++c)
+       {
+         if (strcmp ((*pp)->path, imppath) == 0
+             && strcmp ((*pp)->file, impfile) == 0
+             && strcmp ((*pp)->member, impmember) == 0)
+           break;
+       }
+
+      if (*pp == NULL)
+       {
+         struct xcoff_import_file *n;
+         bfd_size_type amt = sizeof (* n);
+
+         n = bfd_alloc (info->output_bfd, amt);
+         if (n == NULL)
+           return FALSE;
+         n->next = NULL;
+         n->path = imppath;
+         n->file = impfile;
+         n->member = impmember;
+         *pp = n;
+       }
+      h->ldindx = c;
+    }
+  return TRUE;
+}
+\f
+/* Return true if the given bfd contains at least one shared object.  */
+
+static bfd_boolean
+xcoff_archive_contains_shared_object_p (bfd *archive)
+{
+  bfd *member;
+
+  member = bfd_openr_next_archived_file (archive, NULL);
+  while (member != NULL && (member->flags & DYNAMIC) == 0)
+    member = bfd_openr_next_archived_file (archive, member);
+  return member != NULL;
+}
+
+/* Symbol H qualifies for export by -bexpfull.  Return true if it also
+   qualifies for export by -bexpall.  */
+
+static bfd_boolean
+xcoff_covered_by_expall_p (struct xcoff_link_hash_entry *h)
+{
+  /* Exclude symbols beginning with '_'.  */
+  if (h->root.root.string[0] == '_')
+    return FALSE;
+
+  /* Exclude archive members that would otherwise be unreferenced.  */
+  if ((h->flags & XCOFF_MARK) == 0
+      && (h->root.type == bfd_link_hash_defined
+         || h->root.type == bfd_link_hash_defweak)
+      && h->root.u.def.section->owner != NULL
+      && h->root.u.def.section->owner->my_archive != NULL)
+    return FALSE;
+
+  return TRUE;
+}
+
+/* Return true if symbol H qualifies for the forms of automatic export
+   specified by AUTO_EXPORT_FLAGS.  */
+
+static bfd_boolean
+xcoff_auto_export_p (struct xcoff_link_hash_entry *h,
+                    unsigned int auto_export_flags)
+{
+  /* Don't automatically export things that were explicitly exported.  */
+  if ((h->flags & XCOFF_EXPORT) != 0)
+    return FALSE;
+
+  /* Don't export things that we don't define.  */
+  if ((h->flags & XCOFF_DEF_REGULAR) == 0)
+    return FALSE;
+
+  /* Don't export functions; export their descriptors instead.  */
+  if (h->root.root.string[0] == '.')
+    return FALSE;
+
+  /* We don't export a symbol which is being defined by an object
+     included from an archive which contains a shared object.  The
+     rationale is that if an archive contains both an unshared and
+     a shared object, then there must be some reason that the
+     unshared object is unshared, and we don't want to start
+     providing a shared version of it.  In particular, this solves
+     a bug involving the _savefNN set of functions.  gcc will call
+     those functions without providing a slot to restore the TOC,
+     so it is essential that these functions be linked in directly
+     and not from a shared object, which means that a shared
+     object which also happens to link them in must not export
+     them.  This is confusing, but I haven't been able to think of
+     a different approach.  Note that the symbols can, of course,
+     be exported explicitly.  */
+  if (h->root.type == bfd_link_hash_defined
+      || h->root.type == bfd_link_hash_defweak)
+    {
+      bfd *owner;
+
+      owner = h->root.u.def.section->owner;
+      if (owner != NULL
+         && owner->my_archive != NULL
+         && xcoff_archive_contains_shared_object_p (owner->my_archive))
+       return FALSE;
+    }
+
+  /* Otherwise, all symbols are exported by -bexpfull.  */
+  if ((auto_export_flags & XCOFF_EXPFULL) != 0)
+    return TRUE;
+
+  /* Despite its name, -bexpall exports most but not all symbols.  */
+  if ((auto_export_flags & XCOFF_EXPALL) != 0
+      && xcoff_covered_by_expall_p (h))
+    return TRUE;
+
+  return FALSE;
+}
+\f
 /* Mark a symbol as not being garbage, including the section in which
    it is defined.  */
 
@@ -2210,6 +2414,141 @@ xcoff_mark_symbol (struct bfd_link_info *info, struct xcoff_link_hash_entry *h)
     return TRUE;
 
   h->flags |= XCOFF_MARK;
+
+  /* If we're marking an undefined symbol, try find some way of
+     defining it.  */
+  if (!info->relocatable
+      && (h->flags & XCOFF_IMPORT) == 0
+      && (h->flags & XCOFF_DEF_REGULAR) == 0
+      && (h->root.type == bfd_link_hash_undefined
+         || h->root.type == bfd_link_hash_undefweak))
+    {
+      /* First check whether this symbol can be interpreted as an
+        undefined function descriptor for a defined function symbol.  */
+      if (!xcoff_find_function (info, h))
+       return FALSE;
+
+      if ((h->flags & XCOFF_DESCRIPTOR) != 0
+         && (h->descriptor->root.type == bfd_link_hash_defined
+             || h->descriptor->root.type == bfd_link_hash_defweak))
+       {
+         /* This is a descriptor for a defined symbol, but the input
+            objects have not defined the descriptor itself.  Fill in
+            the definition automatically.
+
+            Note that we do this even if we found a dynamic definition
+            of H.  The local function definition logically overrides
+            the dynamic one.  */
+         asection *sec;
+
+         sec = xcoff_hash_table (info)->descriptor_section;
+         h->root.type = bfd_link_hash_defined;
+         h->root.u.def.section = sec;
+         h->root.u.def.value = sec->size;
+         h->smclas = XMC_DS;
+         h->flags |= XCOFF_DEF_REGULAR;
+
+         /* The size of the function descriptor depends on whether this
+            is xcoff32 (12) or xcoff64 (24).  */
+         sec->size += bfd_xcoff_function_descriptor_size (sec->owner);
+
+         /* A function descriptor uses two relocs: one for the
+            associated code, and one for the TOC address.  */
+         xcoff_hash_table (info)->ldrel_count += 2;
+         sec->reloc_count += 2;
+
+         /* Mark the function itself.  */
+         if (!xcoff_mark_symbol (info, h->descriptor))
+           return FALSE;
+
+         /* Mark the TOC section, so that we get an anchor
+            to relocate against.  */
+         if (!xcoff_mark (info, xcoff_hash_table (info)->toc_section))
+           return FALSE;
+
+         /* We handle writing out the contents of the descriptor in
+            xcoff_write_global_symbol.  */
+       }
+      else if ((h->flags & XCOFF_CALLED) != 0)
+       {
+         /* This is a function symbol for which we need to create
+            linkage code.  */
+         asection *sec;
+         struct xcoff_link_hash_entry *hds;
+
+         /* Mark the descriptor (and its TOC section).  */
+         hds = h->descriptor;
+         BFD_ASSERT ((hds->root.type == bfd_link_hash_undefined
+                      || hds->root.type == bfd_link_hash_undefweak)
+                     && (hds->flags & XCOFF_DEF_REGULAR) == 0);
+         if (!xcoff_mark_symbol (info, hds))
+           return FALSE;
+
+         /* Treat this symbol as undefined if the descriptor was.  */
+         if ((hds->flags & XCOFF_WAS_UNDEFINED) != 0)
+           h->flags |= XCOFF_WAS_UNDEFINED;
+
+         /* Allocate room for the global linkage code itself.  */
+         sec = xcoff_hash_table (info)->linkage_section;
+         h->root.type = bfd_link_hash_defined;
+         h->root.u.def.section = sec;
+         h->root.u.def.value = sec->size;
+         h->smclas = XMC_GL;
+         h->flags |= XCOFF_DEF_REGULAR;
+         sec->size += bfd_xcoff_glink_code_size (info->output_bfd);
+
+         /* The global linkage code requires a TOC entry for the
+            descriptor.  */
+         if (hds->toc_section == NULL)
+           {
+             int byte_size;
+
+             /* 32 vs 64
+                xcoff32 uses 4 bytes in the toc.
+                xcoff64 uses 8 bytes in the toc.  */
+             if (bfd_xcoff_is_xcoff64 (info->output_bfd))
+               byte_size = 8;
+             else if (bfd_xcoff_is_xcoff32 (info->output_bfd))
+               byte_size = 4;
+             else
+               return FALSE;
+
+             /* Allocate room in the fallback TOC section.  */
+             hds->toc_section = xcoff_hash_table (info)->toc_section;
+             hds->u.toc_offset = hds->toc_section->size;
+             hds->toc_section->size += byte_size;
+             if (!xcoff_mark (info, hds->toc_section))
+               return FALSE;
+
+             /* Allocate room for a static and dynamic R_TOC
+                relocation.  */
+             ++xcoff_hash_table (info)->ldrel_count;
+             ++hds->toc_section->reloc_count;
+
+             /* Set the index to -2 to force this symbol to
+                get written out.  */
+             hds->indx = -2;
+             hds->flags |= XCOFF_SET_TOC | XCOFF_LDREL;
+           }
+       }
+      else if ((h->flags & XCOFF_DEF_DYNAMIC) == 0)
+       {
+         /* Record that the symbol was undefined, then import it.
+            -brtl links use a special fake import file.  */
+         h->flags |= XCOFF_WAS_UNDEFINED | XCOFF_IMPORT;
+         if (xcoff_hash_table (info)->rtld)
+           {
+             if (!xcoff_set_import_path (info, h, "", "..", ""))
+               return FALSE;
+           }
+         else
+           {
+             if (!xcoff_set_import_path (info, h, NULL, NULL, NULL))
+               return FALSE;
+           }
+       }
+    }
+
   if (h->root.type == bfd_link_hash_defined
       || h->root.type == bfd_link_hash_defweak)
     {
@@ -2234,6 +2573,30 @@ xcoff_mark_symbol (struct bfd_link_info *info, struct xcoff_link_hash_entry *h)
   return TRUE;
 }
 
+/* Look for a symbol called NAME.  If the symbol is defined, mark it.
+   If the symbol exists, set FLAGS.  */
+
+static bfd_boolean
+xcoff_mark_symbol_by_name (struct bfd_link_info *info,
+                          const char *name, unsigned int flags)
+{
+  struct xcoff_link_hash_entry *h;
+
+  h = xcoff_link_hash_lookup (xcoff_hash_table (info), name,
+                             FALSE, FALSE, TRUE);
+  if (h != NULL)
+    {
+      h->flags |= flags;
+      if (h->root.type == bfd_link_hash_defined
+         || h->root.type == bfd_link_hash_defweak)
+       {
+         if (!xcoff_mark (info, h->root.u.def.section))
+           return FALSE;
+       }
+    }
+  return TRUE;
+}
+
 /* The mark phase of garbage collection.  For a given section, mark
    it, and all the sections which define symbols to which it refers.
    Because this function needs to look at the relocs, we also count
@@ -2249,30 +2612,28 @@ xcoff_mark (struct bfd_link_info *info, asection *sec)
 
   sec->flags |= SEC_MARK;
 
-  if (sec->owner->xvec == info->hash->creator
+  if (sec->owner->xvec == info->output_bfd->xvec
       && coff_section_data (sec->owner, sec) != NULL
       && xcoff_section_data (sec->owner, sec) != NULL)
     {
-      struct xcoff_link_hash_entry **hp, **hpend;
+      struct xcoff_link_hash_entry **syms;
       struct internal_reloc *rel, *relend;
+      asection **csects;
+      unsigned long i, first, last;
 
       /* Mark all the symbols in this section.  */
-      hp = (obj_xcoff_sym_hashes (sec->owner)
-           + xcoff_section_data (sec->owner, sec)->first_symndx);
-      hpend = (obj_xcoff_sym_hashes (sec->owner)
-              + xcoff_section_data (sec->owner, sec)->last_symndx);
-      for (; hp < hpend; hp++)
-       {
-         struct xcoff_link_hash_entry *h;
-
-         h = *hp;
-         if (h != NULL
-             && (h->flags & XCOFF_MARK) == 0)
-           {
-             if (! xcoff_mark_symbol (info, h))
-               return FALSE;
-           }
-       }
+      syms = obj_xcoff_sym_hashes (sec->owner);
+      csects = xcoff_data (sec->owner)->csects;
+      first = xcoff_section_data (sec->owner, sec)->first_symndx;
+      last = xcoff_section_data (sec->owner, sec)->last_symndx;
+      for (i = first; i <= last; i++)
+       if (csects[i] == sec
+           && syms[i] != NULL
+           && (syms[i]->flags & XCOFF_MARK) == 0)
+         {
+           if (!xcoff_mark_symbol (info, syms[i]))
+             return FALSE;
+         }
 
       /* Look through the section relocs.  */
       if ((sec->flags & SEC_RELOC) != 0
@@ -2285,7 +2646,6 @@ xcoff_mark (struct bfd_link_info *info, asection *sec)
          relend = rel + sec->reloc_count;
          for (; rel < relend; rel++)
            {
-             asection *rsec;
              struct xcoff_link_hash_entry *h;
 
              if ((unsigned int) rel->r_symndx
@@ -2293,19 +2653,25 @@ xcoff_mark (struct bfd_link_info *info, asection *sec)
                continue;
 
              h = obj_xcoff_sym_hashes (sec->owner)[rel->r_symndx];
-             if (h != NULL
-                 && (h->flags & XCOFF_MARK) == 0)
+             if (h != NULL)
                {
-                 if (! xcoff_mark_symbol (info, h))
-                   return FALSE;
+                 if ((h->flags & XCOFF_MARK) == 0)
+                   {
+                     if (!xcoff_mark_symbol (info, h))
+                       return FALSE;
+                   }
                }
-
-             rsec = xcoff_data (sec->owner)->csects[rel->r_symndx];
-             if (rsec != NULL
-                 && (rsec->flags & SEC_MARK) == 0)
+             else
                {
-                 if (! xcoff_mark (info, rsec))
-                   return FALSE;
+                 asection *rsec;
+
+                 rsec = xcoff_data (sec->owner)->csects[rel->r_symndx];
+                 if (rsec != NULL
+                     && (rsec->flags & SEC_MARK) == 0)
+                   {
+                     if (!xcoff_mark (info, rsec))
+                       return FALSE;
+                   }
                }
 
              /* See if this reloc needs to be copied into the .loader
@@ -2317,21 +2683,20 @@ xcoff_mark (struct bfd_link_info *info, asection *sec)
                      || h->root.type == bfd_link_hash_defined
                      || h->root.type == bfd_link_hash_defweak
                      || h->root.type == bfd_link_hash_common
-                     || ((h->flags & XCOFF_CALLED) != 0
-                         && (h->root.type == bfd_link_hash_undefined
-                             || h->root.type == bfd_link_hash_undefweak)
-                         && h->root.root.string[0] == '.'
-                         && h->descriptor != NULL
-                         && ((h->descriptor->flags & XCOFF_DEF_DYNAMIC) != 0
-                             || ((h->descriptor->flags & XCOFF_IMPORT) != 0
-                                 && (h->descriptor->flags
-                                     & XCOFF_DEF_REGULAR) == 0))))
+                     /* We will always provide a local definition of
+                        function symbols.  */
+                     || (h->flags & XCOFF_CALLED) != 0)
                    break;
                  /* Fall through.  */
                case R_POS:
                case R_NEG:
                case R_RL:
                case R_RLA:
+                 if (h != NULL
+                     && (h->root.type == bfd_link_hash_defined
+                         || h->root.type == bfd_link_hash_defweak)
+                     && bfd_is_abs_section (h->root.u.def.section))
+                   break;
                  ++xcoff_hash_table (info)->ldrel_count;
                  if (h != NULL)
                    h->flags |= XCOFF_LDREL;
@@ -2383,11 +2748,10 @@ xcoff_sweep (struct bfd_link_info *info)
              /* Keep all sections from non-XCOFF input files.  Keep
                 special sections.  Keep .debug sections for the
                 moment.  */
-             if (sub->xvec != info->hash->creator
+             if (sub->xvec != info->output_bfd->xvec
                  || o == xcoff_hash_table (info)->debug_section
                  || o == xcoff_hash_table (info)->loader_section
                  || o == xcoff_hash_table (info)->linkage_section
-                 || o == xcoff_hash_table (info)->toc_section
                  || o == xcoff_hash_table (info)->descriptor_section
                  || strcmp (o->name, ".debug") == 0)
                o->flags |= SEC_MARK;
@@ -2395,7 +2759,6 @@ xcoff_sweep (struct bfd_link_info *info)
                {
                  o->size = 0;
                  o->reloc_count = 0;
-                 o->lineno_count = 0;
                }
            }
        }
@@ -2475,8 +2838,7 @@ bfd_xcoff_import_symbol (bfd *output_bfd,
              hds->root.u.undef.abfd = h->root.u.undef.abfd;
            }
          hds->flags |= XCOFF_DESCRIPTOR;
-         BFD_ASSERT ((hds->flags & XCOFF_CALLED) == 0
-                     && (h->flags & XCOFF_DESCRIPTOR) == 0);
+         BFD_ASSERT ((h->flags & XCOFF_DESCRIPTOR) == 0);
          hds->descriptor = h;
          h->descriptor = hds;
        }
@@ -2506,48 +2868,11 @@ bfd_xcoff_import_symbol (bfd *output_bfd,
       h->root.type = bfd_link_hash_defined;
       h->root.u.def.section = bfd_abs_section_ptr;
       h->root.u.def.value = val;
+      h->smclas = XMC_XO;
     }
 
-  /* We overload the ldindx field to hold the l_ifile value for this
-     symbol.  */
-  BFD_ASSERT (h->ldsym == NULL);
-  BFD_ASSERT ((h->flags & XCOFF_BUILT_LDSYM) == 0);
-  if (imppath == NULL)
-    h->ldindx = -1;
-  else
-    {
-      unsigned int c;
-      struct xcoff_import_file **pp;
-
-      /* We start c at 1 because the first entry in the import list is
-        reserved for the library search path.  */
-      for (pp = &xcoff_hash_table (info)->imports, c = 1;
-          *pp != NULL;
-          pp = &(*pp)->next, ++c)
-       {
-         if (strcmp ((*pp)->path, imppath) == 0
-             && strcmp ((*pp)->file, impfile) == 0
-             && strcmp ((*pp)->member, impmember) == 0)
-           break;
-       }
-
-      if (*pp == NULL)
-       {
-         struct xcoff_import_file *n;
-         bfd_size_type amt = sizeof (* n);
-
-         n = bfd_alloc (output_bfd, amt);
-         if (n == NULL)
-           return FALSE;
-         n->next = NULL;
-         n->path = imppath;
-         n->file = impfile;
-         n->member = impmember;
-         *pp = n;
-       }
-
-      h->ldindx = c;
-    }
+  if (!xcoff_set_import_path (info, h, imppath, impfile, impmember))
+    return FALSE;
 
   return TRUE;
 }
@@ -2569,34 +2894,6 @@ bfd_xcoff_export_symbol (bfd *output_bfd,
   /* FIXME: I'm not at all sure what syscall is supposed to mean, so
      I'm just going to ignore it until somebody explains it.  */
 
-  /* See if this is a function descriptor.  It may be one even though
-     it is not so marked.  */
-  if ((h->flags & XCOFF_DESCRIPTOR) == 0
-      && h->root.root.string[0] != '.')
-    {
-      char *fnname;
-      struct xcoff_link_hash_entry *hfn;
-      bfd_size_type amt = strlen (h->root.root.string) + 2;
-
-      fnname = bfd_malloc (amt);
-      if (fnname == NULL)
-       return FALSE;
-      fnname[0] = '.';
-      strcpy (fnname + 1, h->root.root.string);
-      hfn = xcoff_link_hash_lookup (xcoff_hash_table (info),
-                                   fnname, FALSE, FALSE, TRUE);
-      free (fnname);
-      if (hfn != NULL
-         && hfn->smclas == XMC_PR
-         && (hfn->root.type == bfd_link_hash_defined
-             || hfn->root.type == bfd_link_hash_defweak))
-       {
-         h->flags |= XCOFF_DESCRIPTOR;
-         h->descriptor = hfn;
-         hfn->descriptor = h;
-       }
-    }
-
   /* Make sure we don't garbage collect this symbol.  */
   if (! xcoff_mark_symbol (info, h))
     return FALSE;
@@ -2672,8 +2969,56 @@ bfd_xcoff_record_link_assignment (bfd *output_bfd,
   return TRUE;
 }
 
+/* An xcoff_link_hash_traverse callback for which DATA points to an
+   xcoff_loader_info.  Mark all symbols that should be automatically
+   exported.  */
+
+static bfd_boolean
+xcoff_mark_auto_exports (struct xcoff_link_hash_entry *h, void *data)
+{
+  struct xcoff_loader_info *ldinfo;
+
+  ldinfo = (struct xcoff_loader_info *) data;
+  if (xcoff_auto_export_p (h, ldinfo->auto_export_flags))
+    {
+      if (!xcoff_mark_symbol (ldinfo->info, h))
+       ldinfo->failed = TRUE;
+    }
+  return TRUE;
+}
+
 /* Add a symbol to the .loader symbols, if necessary.  */
 
+/* INPUT_BFD has an external symbol associated with hash table entry H
+   and csect CSECT.   Return true if INPUT_BFD defines H.  */
+
+static bfd_boolean
+xcoff_final_definition_p (bfd *input_bfd, struct xcoff_link_hash_entry *h,
+                         asection *csect)
+{
+  switch (h->root.type)
+    {
+    case bfd_link_hash_defined:
+    case bfd_link_hash_defweak:
+      /* No input bfd owns absolute symbols.  They are written by
+        xcoff_write_global_symbol instead.  */
+      return (!bfd_is_abs_section (csect)
+             && h->root.u.def.section == csect);
+
+    case bfd_link_hash_common:
+      return h->root.u.c.p->section->owner == input_bfd;
+
+    case bfd_link_hash_undefined:
+    case bfd_link_hash_undefweak:
+      /* We can't treat undef.abfd as the owner because that bfd
+        might be a dynamic object.  Allow any bfd to claim it.  */
+      return TRUE;
+
+    default:
+      abort ();
+    }
+}
+
 static bfd_boolean
 xcoff_build_ldsyms (struct xcoff_link_hash_entry *h, void * p)
 {
@@ -2703,50 +3048,8 @@ xcoff_build_ldsyms (struct xcoff_link_hash_entry *h, void * p)
   /* If all defined symbols should be exported, mark them now.  We
      don't want to export the actual functions, just the function
      descriptors.  */
-  if (ldinfo->export_defineds
-      && (h->flags & XCOFF_DEF_REGULAR) != 0
-      && h->root.root.string[0] != '.')
-    {
-      bfd_boolean export;
-
-      /* We don't export a symbol which is being defined by an object
-        included from an archive which contains a shared object.  The
-        rationale is that if an archive contains both an unshared and
-        a shared object, then there must be some reason that the
-        unshared object is unshared, and we don't want to start
-        providing a shared version of it.  In particular, this solves
-        a bug involving the _savefNN set of functions.  gcc will call
-        those functions without providing a slot to restore the TOC,
-        so it is essential that these functions be linked in directly
-        and not from a shared object, which means that a shared
-        object which also happens to link them in must not export
-        them.  This is confusing, but I haven't been able to think of
-        a different approach.  Note that the symbols can, of course,
-        be exported explicitly.  */
-      export = TRUE;
-      if ((h->root.type == bfd_link_hash_defined
-          || h->root.type == bfd_link_hash_defweak)
-         && h->root.u.def.section->owner != NULL
-         && h->root.u.def.section->owner->my_archive != NULL)
-       {
-         bfd *arbfd, *member;
-
-         arbfd = h->root.u.def.section->owner->my_archive;
-         member = bfd_openr_next_archived_file (arbfd, NULL);
-         while (member != NULL)
-           {
-             if ((member->flags & DYNAMIC) != 0)
-               {
-                 export = FALSE;
-                 break;
-               }
-             member = bfd_openr_next_archived_file (arbfd, member);
-           }
-       }
-
-      if (export)
-       h->flags |= XCOFF_EXPORT;
-    }
+  if (xcoff_auto_export_p (h, ldinfo->auto_export_flags))
+    h->flags |= XCOFF_EXPORT;
 
   /* We don't want to garbage collect symbols which are not defined in
      XCOFF files.  This is a convenient place to mark them.  */
@@ -2756,118 +3059,19 @@ xcoff_build_ldsyms (struct xcoff_link_hash_entry *h, void * p)
          || h->root.type == bfd_link_hash_defweak)
       && (h->root.u.def.section->owner == NULL
          || (h->root.u.def.section->owner->xvec
-             != ldinfo->info->hash->creator)))
+             != ldinfo->info->output_bfd->xvec)))
     h->flags |= XCOFF_MARK;
 
-  /* If this symbol is called and defined in a dynamic object, or it
-     is imported, then we need to set up global linkage code for it.
-     (Unless we did garbage collection and we didn't need this
-     symbol.)  */
-  if ((h->flags & XCOFF_CALLED) != 0
-      && (h->root.type == bfd_link_hash_undefined
-         || h->root.type == bfd_link_hash_undefweak)
-      && h->root.root.string[0] == '.'
-      && h->descriptor != NULL
-      && ((h->descriptor->flags & XCOFF_DEF_DYNAMIC) != 0
-         || ((h->descriptor->flags & XCOFF_IMPORT) != 0
-             && (h->descriptor->flags & XCOFF_DEF_REGULAR) == 0))
-      && (! xcoff_hash_table (ldinfo->info)->gc
-         || (h->flags & XCOFF_MARK) != 0))
-    {
-      asection *sec;
-      struct xcoff_link_hash_entry *hds;
-
-      sec = xcoff_hash_table (ldinfo->info)->linkage_section;
-      h->root.type = bfd_link_hash_defined;
-      h->root.u.def.section = sec;
-      h->root.u.def.value = sec->size;
-      h->smclas = XMC_GL;
-      h->flags |= XCOFF_DEF_REGULAR;
-      sec->size += bfd_xcoff_glink_code_size(ldinfo->output_bfd);
-
-      /* The global linkage code requires a TOC entry for the
-        descriptor.  */
-      hds = h->descriptor;
-      BFD_ASSERT ((hds->root.type == bfd_link_hash_undefined
-                  || hds->root.type == bfd_link_hash_undefweak)
-                 && (hds->flags & XCOFF_DEF_REGULAR) == 0);
-      hds->flags |= XCOFF_MARK;
-      if (hds->toc_section == NULL)
-       {
-         int byte_size;
-
-         /* 32 vs 64
-            xcoff32 uses 4 bytes in the toc.
-            xcoff64 uses 8 bytes in the toc.  */
-         if (bfd_xcoff_is_xcoff64 (ldinfo->output_bfd))
-           byte_size = 8;
-         else if (bfd_xcoff_is_xcoff32 (ldinfo->output_bfd))
-           byte_size = 4;
-         else
-           return FALSE;
-
-         hds->toc_section = xcoff_hash_table (ldinfo->info)->toc_section;
-         hds->u.toc_offset = hds->toc_section->size;
-         hds->toc_section->size += byte_size;
-         ++xcoff_hash_table (ldinfo->info)->ldrel_count;
-         ++hds->toc_section->reloc_count;
-         hds->indx = -2;
-         hds->flags |= XCOFF_SET_TOC | XCOFF_LDREL;
-
-         /* We need to call xcoff_build_ldsyms recursively here,
-            because we may already have passed hds on the traversal.  */
-         xcoff_build_ldsyms (hds, p);
-       }
-    }
-
   /* If this symbol is exported, but not defined, we need to try to
      define it.  */
   if ((h->flags & XCOFF_EXPORT) != 0
-      && (h->flags & XCOFF_IMPORT) == 0
-      && (h->flags & XCOFF_DEF_REGULAR) == 0
-      && (h->flags & XCOFF_DEF_DYNAMIC) == 0
-      && (h->root.type == bfd_link_hash_undefined
-         || h->root.type == bfd_link_hash_undefweak))
+      && (h->flags & XCOFF_WAS_UNDEFINED) != 0)
     {
-      if ((h->flags & XCOFF_DESCRIPTOR) != 0
-         && (h->descriptor->root.type == bfd_link_hash_defined
-             || h->descriptor->root.type == bfd_link_hash_defweak))
-       {
-         asection *sec;
-
-         /* This is an undefined function descriptor associated with
-            a defined entry point.  We can build up a function
-            descriptor ourselves.  Believe it or not, the AIX linker
-            actually does this, and there are cases where we need to
-            do it as well.  */
-         sec = xcoff_hash_table (ldinfo->info)->descriptor_section;
-         h->root.type = bfd_link_hash_defined;
-         h->root.u.def.section = sec;
-         h->root.u.def.value = sec->size;
-         h->smclas = XMC_DS;
-         h->flags |= XCOFF_DEF_REGULAR;
-
-         /* The size of the function descriptor depends if this is an
-            xcoff32 (12) or xcoff64 (24).  */
-         sec->size +=
-           bfd_xcoff_function_descriptor_size(ldinfo->output_bfd);
-
-         /* A function descriptor uses two relocs: one for the
-            associated code, and one for the TOC address.  */
-         xcoff_hash_table (ldinfo->info)->ldrel_count += 2;
-         sec->reloc_count += 2;
-
-         /* We handle writing out the contents of the descriptor in
-            xcoff_write_global_symbol.  */
-       }
-      else
-       {
-         (*_bfd_error_handler)
-           (_("warning: attempt to export undefined symbol `%s'"),
-            h->root.root.string);
-         h->ldsym = NULL;
-         return TRUE;
-       }
+      (*_bfd_error_handler)
+       (_("warning: attempt to export undefined symbol `%s'"),
+        h->root.root.string);
+      h->ldsym = NULL;
+      return TRUE;
     }
 
   /* If this is still a common symbol, and it wasn't garbage
@@ -2924,7 +3128,12 @@ xcoff_build_ldsyms (struct xcoff_link_hash_entry *h, void * p)
     }
 
   if ((h->flags & XCOFF_IMPORT) != 0)
-    h->ldsym->l_ifile = h->ldindx;
+    {
+      /* Give imported descriptors class XMC_DS rather than XMC_UA.  */
+      if ((h->flags & XCOFF_DESCRIPTOR) != 0)
+       h->smclas = XMC_DS;
+      h->ldsym->l_ifile = h->ldindx;
+    }
 
   /* The first 3 symbol table indices are reserved to indicate the
      data, text and bss sections.  */
@@ -2940,6 +3149,98 @@ xcoff_build_ldsyms (struct xcoff_link_hash_entry *h, void * p)
 
   return TRUE;
 }
+
+/* INPUT_BFD includes XCOFF symbol ISYM, which is associated with linker
+   hash table entry H and csect CSECT.  AUX contains ISYM's auxillary
+   csect information, if any.  NAME is the function's name if the name
+   is stored in the .debug section, otherwise it is null.
+
+   Return 1 if we should include an appropriately-adjusted ISYM
+   in the output file, 0 if we should discard ISYM, or -1 if an
+   error occured.  */
+
+static int
+xcoff_keep_symbol_p (struct bfd_link_info *info, bfd *input_bfd,
+                    struct internal_syment *isym,
+                    union internal_auxent *aux,
+                    struct xcoff_link_hash_entry *h,
+                    asection *csect, const char *name)
+{
+  int smtyp;
+
+  /* If we are skipping this csect, we want to strip the symbol too.  */
+  if (csect == NULL)
+    return 0;
+
+  /* Likewise if we garbage-collected the csect.  */
+  if (xcoff_hash_table (info)->gc
+      && !bfd_is_abs_section (csect)
+      && !bfd_is_und_section (csect)
+      && (csect->flags & SEC_MARK) == 0)
+    return 0;
+
+  /* An XCOFF linker always removes C_STAT symbols.  */
+  if (isym->n_sclass == C_STAT)
+    return 0;
+
+  /* We generate the TOC anchor separately.  */
+  if (isym->n_sclass == C_HIDEXT
+      && aux->x_csect.x_smclas == XMC_TC0)
+    return 0;
+
+  /* If we are stripping all symbols, we want to discard this one.  */
+  if (info->strip == strip_all)
+    return 0;
+
+  /* Discard symbols that are defined elsewhere.  */
+  if (EXTERN_SYM_P (isym->n_sclass))
+    {
+      if ((h->flags & XCOFF_ALLOCATED) != 0)
+       return 0;
+      if (!xcoff_final_definition_p (input_bfd, h, csect))
+       return 0;
+    }
+
+  /* If we're discarding local symbols, check whether ISYM is local.  */
+  smtyp = SMTYP_SMTYP (aux->x_csect.x_smtyp);
+  if (info->discard == discard_all
+      && !EXTERN_SYM_P (isym->n_sclass)
+      && (isym->n_sclass != C_HIDEXT || smtyp != XTY_SD))
+    return 0;
+
+  /* If we're stripping debugging symbols, check whether ISYM is one.  */
+  if (info->strip == strip_debugger
+      && isym->n_scnum == N_DEBUG)
+    return 0;
+
+  /* If we are stripping symbols based on name, check how ISYM's
+     name should be handled.  */
+  if (info->strip == strip_some
+      || info->discard == discard_l)
+    {
+      char buf[SYMNMLEN + 1];
+
+      if (name == NULL)
+       {
+         name = _bfd_coff_internal_syment_name (input_bfd, isym, buf);
+         if (name == NULL)
+           return -1;
+       }
+
+      if (info->strip == strip_some
+         && bfd_hash_lookup (info->keep_hash, name, FALSE, FALSE) == NULL)
+       return 0;
+
+      if (info->discard == discard_l
+         && !EXTERN_SYM_P (isym->n_sclass)
+         && (isym->n_sclass != C_HIDEXT || smtyp != XTY_SD)
+         && bfd_is_local_label_name (input_bfd, name))
+       return 0;
+    }
+
+  return 1;
+}
+
 /* Build the .loader section.  This is called by the XCOFF linker
    emulation before_allocation routine.  We must set the size of the
    .loader section before the linker lays out the output file.
@@ -2952,10 +3253,9 @@ xcoff_build_ldsyms (struct xcoff_link_hash_entry *h, void * p)
    -bmaxdata linker option).  GC is whether to do garbage collection
    (the -bgc linker option).  MODTYPE is the module type (the
    -bmodtype linker option).  TEXTRO is whether the text section must
-   be read only (the -btextro linker option).  EXPORT_DEFINEDS is
-   whether all defined symbols should be exported (the -unix linker
-   option).  SPECIAL_SECTIONS is set by this routine to csects with
-   magic names like _end.  */
+   be read only (the -btextro linker option).  AUTO_EXPORT_FLAGS
+   is a mask of XCOFF_EXPALL and XCOFF_EXPFULL.  SPECIAL_SECTIONS
+   is set by this routine to csects with magic names like _end.  */
 
 bfd_boolean
 bfd_xcoff_size_dynamic_sections (bfd *output_bfd,
@@ -2968,11 +3268,10 @@ bfd_xcoff_size_dynamic_sections (bfd *output_bfd,
                                 bfd_boolean gc,
                                 int modtype,
                                 bfd_boolean textro,
-                                bfd_boolean export_defineds,
+                                unsigned int auto_export_flags,
                                 asection **special_sections,
                                 bfd_boolean rtld)
 {
-  struct xcoff_link_hash_entry *hentry;
   asection *lsec;
   struct xcoff_loader_info ldinfo;
   int i;
@@ -2997,7 +3296,7 @@ bfd_xcoff_size_dynamic_sections (bfd *output_bfd,
   ldinfo.failed = FALSE;
   ldinfo.output_bfd = output_bfd;
   ldinfo.info = info;
-  ldinfo.export_defineds = export_defineds;
+  ldinfo.auto_export_flags = auto_export_flags;
   ldinfo.ldsym_count = 0;
   ldinfo.string_size = 0;
   ldinfo.strings = NULL;
@@ -3009,15 +3308,7 @@ bfd_xcoff_size_dynamic_sections (bfd *output_bfd,
 
   xcoff_hash_table (info)->file_align = file_align;
   xcoff_hash_table (info)->textro = textro;
-
-  hentry = NULL;
-  if (entry != NULL)
-    {
-      hentry = xcoff_link_hash_lookup (xcoff_hash_table (info), entry,
-                                      FALSE, FALSE, TRUE);
-      if (hentry != NULL)
-       hentry->flags |= XCOFF_ENTRY;
-    }
+  xcoff_hash_table (info)->rtld = rtld;
 
   /* __rtinit */
   if (info->init_function || info->fini_function || rtld)
@@ -3071,11 +3362,7 @@ bfd_xcoff_size_dynamic_sections (bfd *output_bfd,
     }
 
   /* Garbage collect unused sections.  */
-  if (info->relocatable
-      || ! gc
-      || hentry == NULL
-      || (hentry->root.type != bfd_link_hash_defined
-         && hentry->root.type != bfd_link_hash_defweak))
+  if (info->relocatable || !gc)
     {
       gc = FALSE;
       xcoff_hash_table (info)->gc = FALSE;
@@ -3088,7 +3375,12 @@ bfd_xcoff_size_dynamic_sections (bfd *output_bfd,
 
          for (o = sub->sections; o != NULL; o = o->next)
            {
-             if ((o->flags & SEC_MARK) == 0)
+             /* We shouldn't unconditionaly mark the TOC section.
+                The output file should only have a TOC if either
+                (a) one of the input files did or (b) we end up
+                creating TOC references as part of the link process.  */
+             if (o != xcoff_hash_table (info)->toc_section
+                 && (o->flags & SEC_MARK) == 0)
                {
                  if (! xcoff_mark (info, o))
                    goto error_return;
@@ -3098,8 +3390,22 @@ bfd_xcoff_size_dynamic_sections (bfd *output_bfd,
     }
   else
     {
-      if (! xcoff_mark (info, hentry->root.u.def.section))
+      if (entry != NULL
+         && !xcoff_mark_symbol_by_name (info, entry, XCOFF_ENTRY))
        goto error_return;
+      if (info->init_function != NULL
+         && !xcoff_mark_symbol_by_name (info, info->init_function, 0))
+       goto error_return;
+      if (info->fini_function != NULL
+         && !xcoff_mark_symbol_by_name (info, info->fini_function, 0))
+       goto error_return;
+      if (auto_export_flags != 0)
+       {
+         xcoff_link_hash_traverse (xcoff_hash_table (info),
+                                   xcoff_mark_auto_exports, &ldinfo);
+         if (ldinfo.failed)
+           goto error_return;
+       }
       xcoff_sweep (info);
       xcoff_hash_table (info)->gc = TRUE;
     }
@@ -3241,96 +3547,142 @@ bfd_xcoff_size_dynamic_sections (bfd *output_bfd,
        goto error_return;
     }
 
-  /* Now that we've done garbage collection, figure out the contents
-     of the .debug section.  */
+  /* Now that we've done garbage collection, decide which symbols to keep,
+     and figure out the contents of the .debug section.  */
   debug_strtab = xcoff_hash_table (info)->debug_strtab;
 
   for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
     {
       asection *subdeb;
       bfd_size_type symcount;
-      unsigned long *debug_index;
+      long *debug_index;
       asection **csectpp;
+      unsigned int *lineno_counts;
+      struct xcoff_link_hash_entry **sym_hash;
       bfd_byte *esym, *esymend;
       bfd_size_type symesz;
 
-      if (sub->xvec != info->hash->creator)
-       continue;
-      subdeb = bfd_get_section_by_name (sub, ".debug");
-      if (subdeb == NULL || subdeb->size == 0)
+      if (sub->xvec != info->output_bfd->xvec)
        continue;
 
-      if (info->strip == strip_all
-         || info->strip == strip_debugger
-         || info->discard == discard_all)
-       {
-         subdeb->size = 0;
-         continue;
-       }
+      if ((sub->flags & DYNAMIC) != 0
+         && !info->static_link)
+       continue;
 
       if (! _bfd_coff_get_external_symbols (sub))
        goto error_return;
 
       symcount = obj_raw_syment_count (sub);
-      debug_index = bfd_zalloc (sub, symcount * sizeof (unsigned long));
+      debug_index = bfd_zalloc (sub, symcount * sizeof (long));
       if (debug_index == NULL)
        goto error_return;
       xcoff_data (sub)->debug_indices = debug_index;
 
-      /* Grab the contents of the .debug section.  We use malloc and
-        copy the names into the debug stringtab, rather than
-        bfd_alloc, because I expect that, when linking many files
-        together, many of the strings will be the same.  Storing the
-        strings in the hash table should save space in this case.  */
-      if (! bfd_malloc_and_get_section (sub, subdeb, &debug_contents))
-       goto error_return;
+      if (info->strip == strip_all
+         || info->strip == strip_debugger
+         || info->discard == discard_all)
+       /* We're stripping all debugging information, so there's no need
+          to read SUB's .debug section.  */
+       subdeb = NULL;
+      else
+       {
+         /* Grab the contents of SUB's .debug section, if any.  */
+         subdeb = bfd_get_section_by_name (sub, ".debug");
+         if (subdeb != NULL && subdeb->size > 0)
+           {
+             /* We use malloc and copy the names into the debug
+                stringtab, rather than bfd_alloc, because I expect
+                that, when linking many files together, many of the
+                strings will be the same.  Storing the strings in the
+                hash table should save space in this case.  */
+             if (!bfd_malloc_and_get_section (sub, subdeb, &debug_contents))
+               goto error_return;
+           }
+       }
 
       csectpp = xcoff_data (sub)->csects;
+      lineno_counts = xcoff_data (sub)->lineno_counts;
+      sym_hash = obj_xcoff_sym_hashes (sub);
+      symesz = bfd_coff_symesz (sub);
+      esym = (bfd_byte *) obj_coff_external_syms (sub);
+      esymend = esym + symcount * symesz;
 
-      /* Dynamic object do not have csectpp's.  */
-      if (NULL != csectpp)
+      while (esym < esymend)
        {
-         symesz = bfd_coff_symesz (sub);
-         esym = (bfd_byte *) obj_coff_external_syms (sub);
-         esymend = esym + symcount * symesz;
+         struct internal_syment sym;
+         union internal_auxent aux;
+         asection *csect;
+         const char *name;
+         int keep_p;
 
-         while (esym < esymend)
+         bfd_coff_swap_sym_in (sub, esym, &sym);
+
+         /* Read in the csect information, if any.  */
+         if (CSECT_SYM_P (sym.n_sclass))
            {
-             struct internal_syment sym;
+             BFD_ASSERT (sym.n_numaux > 0);
+             bfd_coff_swap_aux_in (sub, esym + symesz * sym.n_numaux,
+                                   sym.n_type, sym.n_sclass,
+                                   sym.n_numaux - 1, sym.n_numaux, &aux);
+           }
 
-             bfd_coff_swap_sym_in (sub, (void *) esym, (void *) &sym);
+         /* If this symbol's name is stored in the debug section,
+            get a pointer to it.  */
+         if (debug_contents != NULL
+             && sym._n._n_n._n_zeroes == 0
+             && bfd_coff_symname_in_debug (sub, &sym))
+           name = (const char *) debug_contents + sym._n._n_n._n_offset;
+         else
+           name = NULL;
 
-             *debug_index = (unsigned long) -1;
+         /* Decide whether to copy this symbol to the output file.  */
+         csect = *csectpp;
+         keep_p = xcoff_keep_symbol_p (info, sub, &sym, &aux,
+                                       *sym_hash, csect, name);
+         if (keep_p < 0)
+           return FALSE;
 
-             if (sym._n._n_n._n_zeroes == 0
-                 && *csectpp != NULL
-                 && (! gc
-                     || ((*csectpp)->flags & SEC_MARK) != 0
-                     || *csectpp == bfd_abs_section_ptr)
-                 && bfd_coff_symname_in_debug (sub, &sym))
+         if (!keep_p)
+           /* Use a debug_index of -2 to record that a symbol should
+              be stripped.  */
+           *debug_index = -2;
+         else
+           {
+             /* See whether we should store the symbol name in the
+                output .debug section.  */
+             if (name != NULL)
                {
-                 char *name;
                  bfd_size_type indx;
 
-                 name = (char *) debug_contents + sym._n._n_n._n_offset;
                  indx = _bfd_stringtab_add (debug_strtab, name, TRUE, TRUE);
                  if (indx == (bfd_size_type) -1)
                    goto error_return;
                  *debug_index = indx;
                }
-
-             esym += (sym.n_numaux + 1) * symesz;
-             csectpp += sym.n_numaux + 1;
-             debug_index += sym.n_numaux + 1;
+             else
+               *debug_index = -1;
+             if (*sym_hash != 0)
+               (*sym_hash)->flags |= XCOFF_ALLOCATED;
+             if (*lineno_counts > 0)
+               csect->output_section->lineno_count += *lineno_counts;
            }
+
+         esym += (sym.n_numaux + 1) * symesz;
+         csectpp += sym.n_numaux + 1;
+         sym_hash += sym.n_numaux + 1;
+         lineno_counts += sym.n_numaux + 1;
+         debug_index += sym.n_numaux + 1;
        }
 
-      free (debug_contents);
-      debug_contents = NULL;
+      if (debug_contents)
+       {
+         free (debug_contents);
+         debug_contents = NULL;
 
-      /* Clear the size of subdeb, so that it is not included directly
-        in the output file.  */
-      subdeb->size = 0;
+         /* Clear the size of subdeb, so that it is not included directly
+            in the output file.  */
+         subdeb->size = 0;
+       }
 
       if (! info->keep_memory)
        {
@@ -3407,7 +3759,8 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
   struct xcoff_link_hash_entry **sym_hash;
   struct internal_syment *isymp;
   asection **csectpp;
-  unsigned long *debug_index;
+  unsigned int *lineno_counts;
+  long *debug_index;
   long *indexp;
   unsigned long output_index;
   bfd_byte *outsym;
@@ -3447,6 +3800,9 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
   if (! _bfd_coff_get_external_symbols (input_bfd))
     return FALSE;
 
+  /* Make one pass over the symbols and assign indices to symbols that
+     we have decided to keep.  Also use create .loader symbol information
+     and update information in hash table entries.  */
   esym = (bfd_byte *) obj_coff_external_syms (input_bfd);
   esym_end = esym + obj_raw_syment_count (input_bfd) * isymesz;
   sym_hash = obj_xcoff_sym_hashes (input_bfd);
@@ -3455,24 +3811,16 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
   isymp = finfo->internal_syms;
   indexp = finfo->sym_indices;
   output_index = syment_base;
-  outsym = finfo->outsyms;
-  incls = 0;
-  oline = NULL;
-
   while (esym < esym_end)
     {
-      struct internal_syment isym;
       union internal_auxent aux;
       int smtyp = 0;
-      bfd_boolean skip;
-      bfd_boolean require;
       int add;
 
       bfd_coff_swap_sym_in (input_bfd, (void *) esym, (void *) isymp);
 
-      /* If this is a C_EXT or C_HIDEXT symbol, we need the csect
-        information.  */
-      if (isymp->n_sclass == C_EXT || isymp->n_sclass == C_HIDEXT)
+      /* Read in the csect information, if any.  */
+      if (CSECT_SYM_P (isymp->n_sclass))
        {
          BFD_ASSERT (isymp->n_numaux > 0);
          bfd_coff_swap_aux_in (input_bfd,
@@ -3484,39 +3832,32 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
          smtyp = SMTYP_SMTYP (aux.x_csect.x_smtyp);
        }
 
-      /* Make a copy of *isymp so that the relocate_section function
-        always sees the original values.  This is more reliable than
-        always recomputing the symbol value even if we are stripping
-        the symbol.  */
-      isym = *isymp;
-
       /* If this symbol is in the .loader section, swap out the
         .loader symbol information.  If this is an external symbol
         reference to a defined symbol, though, then wait until we get
         to the definition.  */
-      if (isym.n_sclass == C_EXT
+      if (EXTERN_SYM_P (isymp->n_sclass)
          && *sym_hash != NULL
          && (*sym_hash)->ldsym != NULL
-         && (smtyp != XTY_ER
-             || (*sym_hash)->root.type == bfd_link_hash_undefined))
+         && xcoff_final_definition_p (input_bfd, *sym_hash, *csectpp))
        {
          struct xcoff_link_hash_entry *h;
          struct internal_ldsym *ldsym;
 
          h = *sym_hash;
          ldsym = h->ldsym;
-         if (isym.n_scnum > 0)
+         if (isymp->n_scnum > 0)
            {
              ldsym->l_scnum = (*csectpp)->output_section->target_index;
-             ldsym->l_value = (isym.n_value
+             ldsym->l_value = (isymp->n_value
                                + (*csectpp)->output_section->vma
                                + (*csectpp)->output_offset
                                - (*csectpp)->vma);
            }
          else
            {
-             ldsym->l_scnum = isym.n_scnum;
-             ldsym->l_value = isym.n_value;
+             ldsym->l_scnum = isymp->n_scnum;
+             ldsym->l_value = isymp->n_value;
            }
 
          ldsym->l_smtype = smtyp;
@@ -3530,6 +3871,8 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
            ldsym->l_smtype |= L_EXPORT;
          if ((h->flags & XCOFF_ENTRY) != 0)
            ldsym->l_smtype |= L_ENTRY;
+         if (isymp->n_sclass == C_AIX_WEAKEXT)
+           ldsym->l_smtype |= L_WEAK;
 
          ldsym->l_smclas = aux.x_csect.x_smclas;
 
@@ -3581,184 +3924,81 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
            }
        }
 
-      *indexp = -1;
-
-      skip = FALSE;
-      require = FALSE;
-      add = 1 + isym.n_numaux;
-
-      /* If we are skipping this csect, we want to skip this symbol.  */
-      if (*csectpp == NULL)
-       skip = TRUE;
-
-      /* If we garbage collected this csect, we want to skip this
-        symbol.  */
-      if (! skip
-         && xcoff_hash_table (finfo->info)->gc
-         && ((*csectpp)->flags & SEC_MARK) == 0
-         && *csectpp != bfd_abs_section_ptr)
-       skip = TRUE;
-
-      /* An XCOFF linker always skips C_STAT symbols.  */
-      if (! skip
-         && isymp->n_sclass == C_STAT)
-       skip = TRUE;
-
-      /* We skip all but the first TOC anchor.  */
-      if (! skip
-         && isymp->n_sclass == C_HIDEXT
-         && aux.x_csect.x_smclas == XMC_TC0)
-       {
-         if (finfo->toc_symindx != -1)
-           skip = TRUE;
-         else
-           {
-             bfd_vma tocval, tocend;
-             bfd *inp;
-
-             tocval = ((*csectpp)->output_section->vma
-                       + (*csectpp)->output_offset
-                       + isym.n_value
-                       - (*csectpp)->vma);
-
-             /* We want to find out if tocval is a good value to use
-                as the TOC anchor--that is, whether we can access all
-                of the TOC using a 16 bit offset from tocval.  This
-                test assumes that the TOC comes at the end of the
-                output section, as it does in the default linker
-                script.  */
-             tocend = ((*csectpp)->output_section->vma
-                       + (*csectpp)->output_section->size);
-             for (inp = finfo->info->input_bfds;
-                  inp != NULL;
-                  inp = inp->link_next)
-               {
-
-                 for (o = inp->sections; o != NULL; o = o->next)
-                   if (strcmp (o->name, ".tocbss") == 0)
-                     {
-                       bfd_vma new_toc_end;
-                       new_toc_end = (o->output_section->vma
-                                      + o->output_offset
-                                      + o->size);
-                       if (new_toc_end > tocend)
-                         tocend = new_toc_end;
-                     }
-
-               }
-
-             if (tocval + 0x10000 < tocend)
-               {
-                 (*_bfd_error_handler)
-                   (_("TOC overflow: 0x%lx > 0x10000; try -mminimal-toc when compiling"),
-                    (unsigned long) (tocend - tocval));
-                 bfd_set_error (bfd_error_file_too_big);
-                 return FALSE;
-               }
-
-             if (tocval + 0x8000 < tocend)
-               {
-                 bfd_vma tocadd;
+      add = 1 + isymp->n_numaux;
 
-                 tocadd = tocend - (tocval + 0x8000);
-                 tocval += tocadd;
-                 isym.n_value += tocadd;
-               }
+      if (*debug_index == -2)
+       /* We've decided to strip this symbol.  */
+       *indexp = -1;
+      else
+       {
+         /* Assign the next unused index to this symbol.  */
+         *indexp = output_index;
 
-             finfo->toc_symindx = output_index;
-             xcoff_data (finfo->output_bfd)->toc = tocval;
-             xcoff_data (finfo->output_bfd)->sntoc =
-               (*csectpp)->output_section->target_index;
-             require = TRUE;
+         if (EXTERN_SYM_P (isymp->n_sclass))
+           {
+             BFD_ASSERT (*sym_hash != NULL);
+             (*sym_hash)->indx = output_index;
+           }
 
+         /* If this is a symbol in the TOC which we may have merged
+            (class XMC_TC), remember the symbol index of the TOC
+            symbol.  */
+         if (isymp->n_sclass == C_HIDEXT
+             && aux.x_csect.x_smclas == XMC_TC
+             && *sym_hash != NULL)
+           {
+             BFD_ASSERT (((*sym_hash)->flags & XCOFF_SET_TOC) == 0);
+             BFD_ASSERT ((*sym_hash)->toc_section != NULL);
+             (*sym_hash)->u.toc_indx = output_index;
            }
-       }
 
-      /* If we are stripping all symbols, we want to skip this one.  */
-      if (! skip
-         && finfo->info->strip == strip_all)
-       skip = TRUE;
-
-      /* We can skip resolved external references.  */
-      if (! skip
-         && isym.n_sclass == C_EXT
-         && smtyp == XTY_ER
-         && (*sym_hash)->root.type != bfd_link_hash_undefined)
-       skip = TRUE;
-
-      /* We can skip common symbols if they got defined somewhere
-        else.  */
-      if (! skip
-         && isym.n_sclass == C_EXT
-         && smtyp == XTY_CM
-         && ((*sym_hash)->root.type != bfd_link_hash_common
-             || (*sym_hash)->root.u.c.p->section != *csectpp)
-         && ((*sym_hash)->root.type != bfd_link_hash_defined
-             || (*sym_hash)->root.u.def.section != *csectpp))
-       skip = TRUE;
-
-      /* Skip local symbols if we are discarding them.  */
-      if (! skip
-         && finfo->info->discard == discard_all
-         && isym.n_sclass != C_EXT
-         && (isym.n_sclass != C_HIDEXT
-             || smtyp != XTY_SD))
-       skip = TRUE;
-
-      /* If we stripping debugging symbols, and this is a debugging
-        symbol, then skip it.  */
-      if (! skip
-         && finfo->info->strip == strip_debugger
-         && isym.n_scnum == N_DEBUG)
-       skip = TRUE;
-
-      /* If some symbols are stripped based on the name, work out the
-        name and decide whether to skip this symbol.  We don't handle
-        this correctly for symbols whose names are in the .debug
-        section; to get it right we would need a new bfd_strtab_hash
-        function to return the string given the index.  */
-      if (! skip
-         && (finfo->info->strip == strip_some
-             || finfo->info->discard == discard_l)
-         && (debug_index == NULL || *debug_index == (unsigned long) -1))
-       {
-         const char *name;
-         char buf[SYMNMLEN + 1];
+         output_index += add;
+       }
 
-         name = _bfd_coff_internal_syment_name (input_bfd, &isym, buf);
+      esym += add * isymesz;
+      isymp += add;
+      csectpp += add;
+      sym_hash += add;
+      debug_index += add;
+      ++indexp;
+      for (--add; add > 0; --add)
+       *indexp++ = -1;
+    }
 
-         if (name == NULL)
-           return FALSE;
+  /* Now write out the symbols that we decided to keep.  */
 
-         if ((finfo->info->strip == strip_some
-              && (bfd_hash_lookup (finfo->info->keep_hash, name, FALSE,
-                                   FALSE) == NULL))
-             || (finfo->info->discard == discard_l
-                 && (isym.n_sclass != C_EXT
-                     && (isym.n_sclass != C_HIDEXT
-                         || smtyp != XTY_SD))
-                 && bfd_is_local_label_name (input_bfd, name)))
-           skip = TRUE;
-       }
+  esym = (bfd_byte *) obj_coff_external_syms (input_bfd);
+  esym_end = esym + obj_raw_syment_count (input_bfd) * isymesz;
+  isymp = finfo->internal_syms;
+  indexp = finfo->sym_indices;
+  csectpp = xcoff_data (input_bfd)->csects;
+  lineno_counts = xcoff_data (input_bfd)->lineno_counts;
+  debug_index = xcoff_data (input_bfd)->debug_indices;
+  outsym = finfo->outsyms;
+  incls = 0;
+  oline = NULL;
+  while (esym < esym_end)
+    {
+      int add;
 
-      /* We can not skip the first TOC anchor.  */
-      if (skip
-         && require
-         && finfo->info->strip != strip_all)
-       skip = FALSE;
+      add = 1 + isymp->n_numaux;
 
-      /* We now know whether we are to skip this symbol or not.  */
-      if (! skip)
+      if (*indexp < 0)
+       esym += add * isymesz;
+      else
        {
-         /* Adjust the symbol in order to output it.  */
+         struct internal_syment isym;
+         int i;
 
+         /* Adjust the symbol in order to output it.  */
+         isym = *isymp;
          if (isym._n._n_n._n_zeroes == 0
              && isym._n._n_n._n_offset != 0)
            {
              /* This symbol has a long name.  Enter it in the string
                 table we are building.  If *debug_index != -1, the
                 name has already been entered in the .debug section.  */
-             if (debug_index != NULL && *debug_index != (unsigned long) -1)
+             if (*debug_index >= 0)
                isym._n._n_n._n_offset = *debug_index;
              else
                {
@@ -3776,17 +4016,6 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
                }
            }
 
-         if (isym.n_sclass != C_BSTAT
-             && isym.n_sclass != C_ESTAT
-             && isym.n_sclass != C_DECL
-             && isym.n_scnum > 0)
-           {
-             isym.n_scnum = (*csectpp)->output_section->target_index;
-             isym.n_value += ((*csectpp)->output_section->vma
-                              + (*csectpp)->output_offset
-                              - (*csectpp)->vma);
-           }
-
          /* The value of a C_FILE symbol is the symbol index of the
             next C_FILE symbol.  The value of the last C_FILE symbol
             is -1.  We try to get this right, below, just before we
@@ -3795,10 +4024,10 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
          if (isym.n_sclass == C_FILE)
            {
              if (finfo->last_file_index != -1
-                 && finfo->last_file.n_value != (bfd_vma) output_index)
+                 && finfo->last_file.n_value != (bfd_vma) *indexp)
                {
                  /* We must correct the value of the last C_FILE entry.  */
-                 finfo->last_file.n_value = output_index;
+                 finfo->last_file.n_value = *indexp;
                  if ((bfd_size_type) finfo->last_file_index >= syment_base)
                    {
                      /* The last C_FILE symbol is in this input file.  */
@@ -3829,7 +4058,7 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
                    }
                }
 
-             finfo->last_file_index = output_index;
+             finfo->last_file_index = *indexp;
              finfo->last_file = isym;
            }
 
@@ -3842,84 +4071,12 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
              isym.n_value = finfo->line_filepos;
              ++incls;
            }
-
-         /* Output the symbol.  */
-
-         bfd_coff_swap_sym_out (output_bfd, (void *) &isym, (void *) outsym);
-
-         *indexp = output_index;
-
-         if (isym.n_sclass == C_EXT)
-           {
-             long indx;
-             struct xcoff_link_hash_entry *h;
-
-             indx = ((esym - (bfd_byte *) obj_coff_external_syms (input_bfd))
-                     / isymesz);
-             h = obj_xcoff_sym_hashes (input_bfd)[indx];
-             BFD_ASSERT (h != NULL);
-             h->indx = output_index;
-           }
-
-         /* If this is a symbol in the TOC which we may have merged
-            (class XMC_TC), remember the symbol index of the TOC
-            symbol.  */
-         if (isym.n_sclass == C_HIDEXT
-             && aux.x_csect.x_smclas == XMC_TC
-             && *sym_hash != NULL)
-           {
-             BFD_ASSERT (((*sym_hash)->flags & XCOFF_SET_TOC) == 0);
-             BFD_ASSERT ((*sym_hash)->toc_section != NULL);
-             (*sym_hash)->u.toc_indx = output_index;
-           }
-
-         output_index += add;
-         outsym += add * osymesz;
-       }
-
-      esym += add * isymesz;
-      isymp += add;
-      csectpp += add;
-      sym_hash += add;
-      if (debug_index != NULL)
-       debug_index += add;
-      ++indexp;
-      for (--add; add > 0; --add)
-       *indexp++ = -1;
-    }
-
-  /* Fix up the aux entries and the C_BSTAT symbols.  This must be
-     done in a separate pass, because we don't know the correct symbol
-     indices until we have already decided which symbols we are going
-     to keep.  */
-
-  esym = (bfd_byte *) obj_coff_external_syms (input_bfd);
-  esym_end = esym + obj_raw_syment_count (input_bfd) * isymesz;
-  isymp = finfo->internal_syms;
-  indexp = finfo->sym_indices;
-  csectpp = xcoff_data (input_bfd)->csects;
-  outsym = finfo->outsyms;
-  while (esym < esym_end)
-    {
-      int add;
-
-      add = 1 + isymp->n_numaux;
-
-      if (*indexp < 0)
-       esym += add * isymesz;
-      else
-       {
-         int i;
-
-         if (isymp->n_sclass == C_BSTAT)
+         /* The value of a C_BSTAT symbol is the symbol table
+            index of the containing csect.  */
+         else if (isym.n_sclass == C_BSTAT)
            {
-             struct internal_syment isym;
-
              bfd_vma indx;
 
-             /* The value of a C_BSTAT symbol is the symbol table
-                index of the containing csect.  */
-             bfd_coff_swap_sym_in (output_bfd, (void *) outsym, (void *) &isym);
              indx = isym.n_value;
              if (indx < obj_raw_syment_count (input_bfd))
                {
@@ -3930,10 +4087,20 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
                    isym.n_value = 0;
                  else
                    isym.n_value = symindx;
-                 bfd_coff_swap_sym_out (output_bfd, (void *) &isym,
-                                        (void *) outsym);
                }
            }
+         else if (isym.n_sclass != C_ESTAT
+                  && isym.n_sclass != C_DECL
+                  && isym.n_scnum > 0)
+           {
+             isym.n_scnum = (*csectpp)->output_section->target_index;
+             isym.n_value += ((*csectpp)->output_section->vma
+                              + (*csectpp)->output_offset
+                              - (*csectpp)->vma);
+           }
+
+         /* Output the symbol.  */
+         bfd_coff_swap_sym_out (output_bfd, (void *) &isym, (void *) outsym);
 
          esym += isymesz;
          outsym += osymesz;
@@ -3973,8 +4140,7 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
                      aux.x_file.x_n.x_offset = STRING_SIZE_SIZE + indx;
                    }
                }
-             else if ((isymp->n_sclass == C_EXT
-                       || isymp->n_sclass == C_HIDEXT)
+             else if (CSECT_SYM_P (isymp->n_sclass)
                       && i + 1 == isymp->n_numaux)
                {
 
@@ -4053,15 +4219,13 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
              /* Copy over the line numbers, unless we are stripping
                 them.  We do this on a symbol by symbol basis in
                 order to more easily handle garbage collection.  */
-             if ((isymp->n_sclass == C_EXT
-                  || isymp->n_sclass == C_HIDEXT)
+             if (CSECT_SYM_P (isymp->n_sclass)
                  && i == 0
                  && isymp->n_numaux > 1
                  && ISFCN (isymp->n_type)
                  && aux.x_sym.x_fcnary.x_fcn.x_lnnoptr != 0)
                {
-                 if (finfo->info->strip != strip_none
-                     && finfo->info->strip != strip_some)
+                 if (*lineno_counts == 0)
                    aux.x_sym.x_fcnary.x_fcn.x_lnnoptr = 0;
                  else
                    {
@@ -4069,14 +4233,21 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
                      unsigned int enc_count;
                      bfd_signed_vma linoff;
                      struct internal_lineno lin;
+                     bfd_byte *linp;
+                     bfd_byte *linpend;
+                     bfd_vma offset;
+                     file_ptr pos;
+                     bfd_size_type amt;
 
+                     /* Read in the enclosing section's line-number
+                        information, if we haven't already.  */
                      o = *csectpp;
                      enclosing = xcoff_section_data (abfd, o)->enclosing;
                      enc_count = xcoff_section_data (abfd, o)->lineno_count;
                      if (oline != enclosing)
                        {
-                         file_ptr pos = enclosing->line_filepos;
-                         bfd_size_type amt = linesz * enc_count;
+                         pos = enclosing->line_filepos;
+                         amt = linesz * enc_count;
                          if (bfd_seek (input_bfd, pos, SEEK_SET) != 0
                              || (bfd_bread (finfo->linenos, amt, input_bfd)
                                  != amt))
@@ -4084,114 +4255,83 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
                          oline = enclosing;
                        }
 
+                     /* Copy across the first entry, adjusting its
+                        symbol index.  */
                      linoff = (aux.x_sym.x_fcnary.x_fcn.x_lnnoptr
                                - enclosing->line_filepos);
-
-                     bfd_coff_swap_lineno_in (input_bfd,
-                                              (void *) (finfo->linenos + linoff),
-                                              (void *) &lin);
-                     if (lin.l_lnno != 0
-                         || ((bfd_size_type) lin.l_addr.l_symndx
-                             != ((esym
-                                  - isymesz
-                                  - ((bfd_byte *)
-                                     obj_coff_external_syms (input_bfd)))
-                                 / isymesz)))
-                       aux.x_sym.x_fcnary.x_fcn.x_lnnoptr = 0;
-                     else
+                     linp = finfo->linenos + linoff;
+                     bfd_coff_swap_lineno_in (input_bfd, linp, &lin);
+                     lin.l_addr.l_symndx = *indexp;
+                     bfd_coff_swap_lineno_out (output_bfd, &lin, linp);
+                     linp += linesz;
+
+                     /* Copy the other entries, adjusting their addresses.  */
+                     linpend = linp + *lineno_counts * linesz;
+                     offset = (o->output_section->vma
+                               + o->output_offset
+                               - o->vma);
+                     for (; linp < linpend; linp += linesz)
                        {
-                         bfd_byte *linpend, *linp;
-                         bfd_vma offset;
-                         bfd_size_type count;
-
-                         lin.l_addr.l_symndx = *indexp;
-                         bfd_coff_swap_lineno_out (output_bfd, (void *) &lin,
-                                                   (void *) (finfo->linenos
-                                                          + linoff));
-
-                         linpend = (finfo->linenos
-                                    + enc_count * linesz);
-                         offset = (o->output_section->vma
-                                   + o->output_offset
-                                   - o->vma);
-                         for (linp = finfo->linenos + linoff + linesz;
-                              linp < linpend;
-                              linp += linesz)
-                           {
-                             bfd_coff_swap_lineno_in (input_bfd, (void *) linp,
-                                                      (void *) &lin);
-                             if (lin.l_lnno == 0)
-                               break;
-                             lin.l_addr.l_paddr += offset;
-                             bfd_coff_swap_lineno_out (output_bfd,
-                                                       (void *) &lin,
-                                                       (void *) linp);
-                           }
-
-                         count = (linp - (finfo->linenos + linoff)) / linesz;
+                         bfd_coff_swap_lineno_in (input_bfd, linp, &lin);
+                         lin.l_addr.l_paddr += offset;
+                         bfd_coff_swap_lineno_out (output_bfd, &lin, linp);
+                       }
 
-                         aux.x_sym.x_fcnary.x_fcn.x_lnnoptr =
-                           (o->output_section->line_filepos
+                     /* Write out the entries we've just processed.  */
+                     pos = (o->output_section->line_filepos
                             + o->output_section->lineno_count * linesz);
+                     amt = linesz * *lineno_counts;
+                     if (bfd_seek (output_bfd, pos, SEEK_SET) != 0
+                         || bfd_bwrite (finfo->linenos + linoff,
+                                        amt, output_bfd) != amt)
+                       return FALSE;
+                     o->output_section->lineno_count += *lineno_counts;
 
-                         if (bfd_seek (output_bfd,
-                                       aux.x_sym.x_fcnary.x_fcn.x_lnnoptr,
-                                       SEEK_SET) != 0
-                             || (bfd_bwrite (finfo->linenos + linoff,
-                                            linesz * count, output_bfd)
-                                 != linesz * count))
-                           return FALSE;
-
-                         o->output_section->lineno_count += count;
+                     /* Record the offset of the symbol's line numbers
+                        in the output file.  */
+                     aux.x_sym.x_fcnary.x_fcn.x_lnnoptr = pos;
 
-                         if (incls > 0)
+                     if (incls > 0)
+                       {
+                         struct internal_syment *iisp, *iispend;
+                         long *iindp;
+                         bfd_byte *oos;
+                         bfd_vma range_start, range_end;
+                         int iiadd;
+
+                         /* Update any C_BINCL or C_EINCL symbols
+                            that refer to a line number in the
+                            range we just output.  */
+                         iisp = finfo->internal_syms;
+                         iispend = iisp + obj_raw_syment_count (input_bfd);
+                         iindp = finfo->sym_indices;
+                         oos = finfo->outsyms;
+                         range_start = enclosing->line_filepos + linoff;
+                         range_end = range_start + *lineno_counts * linesz;
+                         while (iisp < iispend)
                            {
-                             struct internal_syment *iisp, *iispend;
-                             long *iindp;
-                             bfd_byte *oos;
-                             int iiadd;
-
-                             /* Update any C_BINCL or C_EINCL symbols
-                                that refer to a line number in the
-                                range we just output.  */
-                             iisp = finfo->internal_syms;
-                             iispend = (iisp
-                                        + obj_raw_syment_count (input_bfd));
-                             iindp = finfo->sym_indices;
-                             oos = finfo->outsyms;
-                             while (iisp < iispend)
+                             if (*iindp >= 0
+                                 && (iisp->n_sclass == C_BINCL
+                                     || iisp->n_sclass == C_EINCL)
+                                 && iisp->n_value >= range_start
+                                 && iisp->n_value < range_end)
                                {
-                                 if (*iindp >= 0
-                                     && (iisp->n_sclass == C_BINCL
-                                         || iisp->n_sclass == C_EINCL)
-                                     && ((bfd_size_type) iisp->n_value
-                                         >= (bfd_size_type)(enclosing->line_filepos + linoff))
-                                     && ((bfd_size_type) iisp->n_value
-                                         < (enclosing->line_filepos
-                                            + enc_count * linesz)))
-                                   {
-                                     struct internal_syment iis;
-
-                                     bfd_coff_swap_sym_in (output_bfd,
-                                                           (void *) oos,
-                                                           (void *) &iis);
-                                     iis.n_value =
-                                       (iisp->n_value
-                                        - enclosing->line_filepos
-                                        - linoff
-                                        + aux.x_sym.x_fcnary.x_fcn.x_lnnoptr);
-                                     bfd_coff_swap_sym_out (output_bfd,
-                                                            (void *) &iis,
-                                                            (void *) oos);
-                                     --incls;
-                                   }
-
-                                 iiadd = 1 + iisp->n_numaux;
-                                 if (*iindp >= 0)
-                                   oos += iiadd * osymesz;
-                                 iisp += iiadd;
-                                 iindp += iiadd;
+                                 struct internal_syment iis;
+
+                                 bfd_coff_swap_sym_in (output_bfd, oos, &iis);
+                                 iis.n_value = (iisp->n_value
+                                                - range_start
+                                                + pos);
+                                 bfd_coff_swap_sym_out (output_bfd,
+                                                        &iis, oos);
+                                 --incls;
                                }
+
+                             iiadd = 1 + iisp->n_numaux;
+                             if (*iindp >= 0)
+                               oos += iiadd * osymesz;
+                             iisp += iiadd;
+                             iindp += iiadd;
                            }
                        }
                    }
@@ -4208,6 +4348,8 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
       indexp += add;
       isymp += add;
       csectpp += add;
+      lineno_counts += add;
+      debug_index += add;
     }
 
   /* If we swapped out a C_FILE symbol, guess that the next C_FILE
@@ -4310,7 +4452,6 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
            {
              struct xcoff_link_hash_entry *h = NULL;
              struct internal_ldrel ldrel;
-             bfd_boolean quiet;
 
              *rel_hash = NULL;
 
@@ -4443,7 +4584,6 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
                    }
                }
 
-             quiet = FALSE;
              switch (irel->r_type)
                {
                default:
@@ -4457,6 +4597,11 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
                case R_NEG:
                case R_RL:
                case R_RLA:
+                 if (h != NULL
+                     && (h->root.type == bfd_link_hash_defined
+                         || h->root.type == bfd_link_hash_defweak)
+                     && bfd_is_abs_section (h->root.u.def.section))
+                   break;
                  /* This reloc needs to be copied into the .loader
                     section.  */
                  ldrel.l_vaddr = irel->r_vaddr;
@@ -4494,17 +4639,7 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
                    }
                  else
                    {
-                     if (! finfo->info->relocatable
-                         && (h->flags & XCOFF_DEF_DYNAMIC) == 0
-                         && (h->flags & XCOFF_IMPORT) == 0)
-                       {
-                         /* We already called the undefined_symbol
-                            callback for this relocation, in
-                            _bfd_ppc_xcoff_relocate_section.  Don't
-                            issue any more warnings.  */
-                         quiet = TRUE;
-                       }
-                     if (h->ldindx < 0 && ! quiet)
+                     if (h->ldindx < 0)
                        {
                          (*_bfd_error_handler)
                            (_("%B: `%s' in loader reloc but not loader sym"),
@@ -4518,8 +4653,7 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *finfo,
                  ldrel.l_rtype = (irel->r_size << 8) | irel->r_type;
                  ldrel.l_rsecnm = o->output_section->target_index;
                  if (xcoff_hash_table (finfo->info)->textro
-                     && strcmp (o->output_section->name, ".text") == 0
-                     && ! quiet)
+                     && strcmp (o->output_section->name, ".text") == 0)
                    {
                      (*_bfd_error_handler)
                        (_("%B: loader reloc in read-only section %A"),
@@ -4584,6 +4718,144 @@ xcoff_sort_relocs (const void * p1, const void * p2)
     return 0;
 }
 
+/* Return true if section SEC is a TOC section.  */
+
+static inline bfd_boolean
+xcoff_toc_section_p (asection *sec)
+{
+  const char *name;
+
+  name = sec->name;
+  if (name[0] == '.' && name[1] == 't')
+    {
+      if (name[2] == 'c')
+       {
+         if (name[3] == '0' && name[4] == 0)
+           return TRUE;
+         if (name[3] == 0)
+           return TRUE;
+       }
+      if (name[2] == 'd' && name[3] == 0)
+       return TRUE;
+    }
+  return FALSE;
+}
+
+/* See if the link requires a TOC (it usually does!).  If so, find a
+   good place to put the TOC anchor csect, and write out the associated
+   symbol.  */
+
+static bfd_boolean
+xcoff_find_tc0 (bfd *output_bfd, struct xcoff_final_link_info *finfo)
+{
+  bfd_vma toc_start, toc_end, start, end, best_address;
+  asection *sec;
+  bfd *input_bfd;
+  int section_index;
+  struct internal_syment irsym;
+  union internal_auxent iraux;
+  file_ptr pos;
+  size_t size;
+
+  /* Set [TOC_START, TOC_END) to the range of the TOC.  Record the
+     index of a csect at the beginning of the TOC.  */
+  toc_start = ~(bfd_vma) 0;
+  toc_end = 0;
+  section_index = -1;
+  for (input_bfd = finfo->info->input_bfds;
+       input_bfd != NULL;
+       input_bfd = input_bfd->link_next)
+    for (sec = input_bfd->sections; sec != NULL; sec = sec->next)
+      if ((sec->flags & SEC_MARK) != 0 && xcoff_toc_section_p (sec))
+       {
+         start = sec->output_section->vma + sec->output_offset;
+         if (toc_start > start)
+           {
+             toc_start = start;
+             section_index = sec->output_section->target_index;
+           }
+
+         end = start + sec->size;
+         if (toc_end < end)
+           toc_end = end;
+       }
+
+  /* There's no need for a TC0 symbol if we don't have a TOC.  */
+  if (toc_end < toc_start)
+    {
+      xcoff_data (output_bfd)->toc = toc_start;
+      return TRUE;
+    }
+
+  if (toc_end - toc_start < 0x8000)
+    /* Every TOC csect can be accessed from TOC_START.  */
+    best_address = toc_start;
+  else
+    {
+      /* Find the lowest TOC csect that is still within range of TOC_END.  */
+      best_address = toc_end;
+      for (input_bfd = finfo->info->input_bfds;
+          input_bfd != NULL;
+          input_bfd = input_bfd->link_next)
+       for (sec = input_bfd->sections; sec != NULL; sec = sec->next)
+         if ((sec->flags & SEC_MARK) != 0 && xcoff_toc_section_p (sec))
+           {
+             start = sec->output_section->vma + sec->output_offset;
+             if (start < best_address
+                 && start + 0x8000 >= toc_end)
+               {
+                 best_address = start;
+                 section_index = sec->output_section->target_index;
+               }
+           }
+
+      /* Make sure that the start of the TOC is also within range.  */
+      if (best_address > toc_start + 0x8000)
+       {
+         (*_bfd_error_handler)
+           (_("TOC overflow: 0x%lx > 0x10000; try -mminimal-toc "
+              "when compiling"),
+            (unsigned long) (toc_end - toc_start));
+         bfd_set_error (bfd_error_file_too_big);
+         return FALSE;
+       }
+    }
+
+  /* Record the chosen TOC value.  */
+  finfo->toc_symindx = obj_raw_syment_count (output_bfd);
+  xcoff_data (output_bfd)->toc = best_address;
+  xcoff_data (output_bfd)->sntoc = section_index;
+
+  /* Fill out the TC0 symbol.  */
+  if (!bfd_xcoff_put_symbol_name (output_bfd, finfo->strtab, &irsym, "TOC"))
+    return FALSE;
+  irsym.n_value = best_address;
+  irsym.n_scnum = section_index;
+  irsym.n_sclass = C_HIDEXT;
+  irsym.n_type = T_NULL;
+  irsym.n_numaux = 1;
+  bfd_coff_swap_sym_out (output_bfd, &irsym, finfo->outsyms);
+
+  /* Fill out the auxillary csect information.  */
+  memset (&iraux, 0, sizeof iraux);
+  iraux.x_csect.x_smtyp = XTY_SD;
+  iraux.x_csect.x_smclas = XMC_TC0;
+  iraux.x_csect.x_scnlen.l = 0;
+  bfd_coff_swap_aux_out (output_bfd, &iraux, T_NULL, C_HIDEXT, 0, 1,
+                        finfo->outsyms + bfd_coff_symesz (output_bfd));
+
+  /* Write the contents to the file.  */
+  pos = obj_sym_filepos (output_bfd);
+  pos += obj_raw_syment_count (output_bfd) * bfd_coff_symesz (output_bfd);
+  size = 2 * bfd_coff_symesz (output_bfd);
+  if (bfd_seek (output_bfd, pos, SEEK_SET) != 0
+      || bfd_bwrite (finfo->outsyms, size, output_bfd) != size)
+    return FALSE;
+  obj_raw_syment_count (output_bfd) += 2;
+
+  return TRUE;
+}
+
 /* Write out a non-XCOFF global symbol.  */
 
 static bfd_boolean
@@ -5042,7 +5314,11 @@ xcoff_write_global_symbol (struct xcoff_link_hash_entry *h, void * inf)
     {
       isym.n_value = 0;
       isym.n_scnum = N_UNDEF;
-      isym.n_sclass = C_EXT;
+      if (h->root.type == bfd_link_hash_undefweak
+         && C_WEAKEXT == C_AIX_WEAKEXT)
+       isym.n_sclass = C_WEAKEXT;
+      else
+       isym.n_sclass = C_EXT;
       aux.x_csect.x_smtyp = XTY_ER;
     }
   else if ((h->root.type == bfd_link_hash_defined
@@ -5052,7 +5328,11 @@ xcoff_write_global_symbol (struct xcoff_link_hash_entry *h, void * inf)
       BFD_ASSERT (bfd_is_abs_section (h->root.u.def.section));
       isym.n_value = h->root.u.def.value;
       isym.n_scnum = N_UNDEF;
-      isym.n_sclass = C_EXT;
+      if (h->root.type == bfd_link_hash_undefweak
+         && C_WEAKEXT == C_AIX_WEAKEXT)
+       isym.n_sclass = C_WEAKEXT;
+      else
+       isym.n_sclass = C_EXT;
       aux.x_csect.x_smtyp = XTY_ER;
     }
   else if (h->root.type == bfd_link_hash_defined
@@ -5114,7 +5394,11 @@ xcoff_write_global_symbol (struct xcoff_link_hash_entry *h, void * inf)
       /* We just output an SD symbol.  Now output an LD symbol.  */
       h->indx += 2;
 
-      isym.n_sclass = C_EXT;
+      if (h->root.type == bfd_link_hash_undefweak
+         && C_WEAKEXT == C_AIX_WEAKEXT)
+       isym.n_sclass = C_WEAKEXT;
+      else
+       isym.n_sclass = C_EXT;
       bfd_coff_swap_sym_out (output_bfd, (void *) &isym, (void *) outsym);
       outsym += bfd_coff_symesz (output_bfd);
 
@@ -5371,15 +5655,15 @@ _bfd_xcoff_bfd_final_link (bfd *abfd, struct bfd_link_info *info)
   if (finfo.strtab == NULL)
     goto error_return;
 
-  /* Count the line number and relocation entries required for the
-     output file.  Determine a few maximum sizes.  */
+  /* Count the relocation entries required for the output file.
+     (We've already counted the line numbers.)  Determine a few
+     maximum sizes.  */
   max_contents_size = 0;
   max_lineno_count = 0;
   max_reloc_count = 0;
   for (o = abfd->sections; o != NULL; o = o->next)
     {
       o->reloc_count = 0;
-      o->lineno_count = 0;
       for (p = o->map_head.link_order; p != NULL; p = p->next)
        {
          if (p->type == bfd_indirect_link_order)
@@ -5394,18 +5678,12 @@ _bfd_xcoff_bfd_final_link (bfd *abfd, struct bfd_link_info *info)
                 the linker has decided to not include.  */
              sec->linker_mark = TRUE;
 
-             if (info->strip == strip_none
-                 || info->strip == strip_some)
-               o->lineno_count += sec->lineno_count;
-
              o->reloc_count += sec->reloc_count;
 
              if (sec->rawsize > max_contents_size)
                max_contents_size = sec->rawsize;
              if (sec->size > max_contents_size)
                max_contents_size = sec->size;
-             if (sec->lineno_count > max_lineno_count)
-               max_lineno_count = sec->lineno_count;
              if (coff_section_data (sec->owner, sec) != NULL
                  && xcoff_section_data (sec->owner, sec) != NULL
                  && (xcoff_section_data (sec->owner, sec)->lineno_count
@@ -5459,8 +5737,8 @@ _bfd_xcoff_bfd_final_link (bfd *abfd, struct bfd_link_info *info)
                         that needs padding.  This requires unlinking and
                         relinking the bfd's section list.  */
 
-                     n = bfd_make_section_anyway (abfd, ".pad");
-                     n->flags = SEC_HAS_CONTENTS;
+                     n = bfd_make_section_anyway_with_flags (abfd, ".pad",
+                                                             SEC_HAS_CONTENTS);
                      n->alignment_power = 0;
 
                      bfd_section_list_remove (abfd, n);
@@ -5660,7 +5938,10 @@ _bfd_xcoff_bfd_final_link (bfd *abfd, struct bfd_link_info *info)
     goto error_return;
 
   obj_raw_syment_count (abfd) = 0;
-  xcoff_data (abfd)->toc = (bfd_vma) -1;
+
+  /* Find a TOC symbol, if we need one.  */
+  if (!xcoff_find_tc0 (abfd, &finfo))
+    goto error_return;
 
   /* We now know the position of everything in the file, except that
      we don't know the size of the symbol table and therefore we don't
This page took 0.058341 seconds and 4 git commands to generate.