1999-09-11 Donn Terry <donn@interix.com>
[deliverable/binutils-gdb.git] / bfd / cofflink.c
index 679e7a84d87abf5c485b498d7f4958246cfa229f..9344a04e4409e28697079281b5830f914cfa3ef3 100644 (file)
@@ -40,6 +40,16 @@ static int process_embedded_commands
   PARAMS ((bfd *, struct bfd_link_info *, bfd *));
 static void mark_relocs PARAMS ((struct coff_final_link_info *, bfd *));
 
+/* Define macros so that the ISFCN, et. al., macros work correctly.
+   These macros are defined in include/coff/internal.h in terms of
+   N_TMASK, etc.  These definitions require a user to define local
+   variables with the appropriate names, and with values from the
+   coff_data (abfd) structure.  */
+
+#define N_TMASK n_tmask
+#define N_BTSHFT n_btshft
+#define N_BTMASK n_btmask
+
 /* Create an entry in a COFF linker hash table.  */
 
 struct bfd_hash_entry *
@@ -225,32 +235,25 @@ coff_link_check_ar_symbols (abfd, info, pneeded)
      struct bfd_link_info *info;
      boolean *pneeded;
 {
-  boolean (*sym_is_global) PARAMS ((bfd *, struct internal_syment *));
   bfd_size_type symesz;
   bfd_byte *esym;
   bfd_byte *esym_end;
 
   *pneeded = false;
 
-  sym_is_global = coff_backend_info (abfd)->_bfd_coff_sym_is_global;
-
   symesz = bfd_coff_symesz (abfd);
   esym = (bfd_byte *) obj_coff_external_syms (abfd);
   esym_end = esym + obj_raw_syment_count (abfd) * symesz;
   while (esym < esym_end)
     {
       struct internal_syment sym;
+      enum coff_symbol_classification classification;
 
       bfd_coff_swap_sym_in (abfd, (PTR) esym, (PTR) &sym);
 
-      if ((sym.n_sclass == C_EXT
-          || sym.n_sclass == C_WEAKEXT
-          || (obj_pe (abfd) && sym.n_sclass == C_NT_WEAK)
-#ifdef C_SYSTEM
-          || sym.n_sclass == C_SYSTEM
-#endif
-          || (sym_is_global && (*sym_is_global) (abfd, &sym)))
-         && (sym.n_scnum != 0 || sym.n_value != 0))
+      classification = bfd_coff_classify_symbol (abfd, &sym);
+      if (classification == COFF_SYMBOL_GLOBAL
+         || classification == COFF_SYMBOL_COMMON)
        {
          const char *name;
          char buf[SYMNMLEN + 1];
@@ -292,7 +295,9 @@ coff_link_add_symbols (abfd, info)
      bfd *abfd;
      struct bfd_link_info *info;
 {
-  boolean (*sym_is_global) PARAMS ((bfd *, struct internal_syment *));
+  unsigned int n_tmask = coff_data (abfd)->local_n_tmask;
+  unsigned int n_btshft = coff_data (abfd)->local_n_btshft;
+  unsigned int n_btmask = coff_data (abfd)->local_n_btmask;
   boolean keep_syms;
   boolean default_copy;
   bfd_size_type symcount;
@@ -306,8 +311,6 @@ coff_link_add_symbols (abfd, info)
   keep_syms = obj_coff_keep_syms (abfd);
   obj_coff_keep_syms (abfd) = true;
 
-  sym_is_global = coff_backend_info (abfd)->_bfd_coff_sym_is_global;
-
   if (info->keep_memory)
     default_copy = false;
   else
@@ -334,23 +337,20 @@ coff_link_add_symbols (abfd, info)
   while (esym < esym_end)
     {
       struct internal_syment sym;
+      enum coff_symbol_classification classification;
       boolean copy;
 
       bfd_coff_swap_sym_in (abfd, (PTR) esym, (PTR) &sym);
 
-      if (sym.n_sclass == C_EXT
-          || sym.n_sclass == C_WEAKEXT
-          || (obj_pe (abfd) && sym.n_sclass == C_NT_WEAK)
-#ifdef C_SYSTEM
-         || sym.n_sclass == C_SYSTEM
-#endif
-         || (sym_is_global && (*sym_is_global) (abfd, &sym)))
+      classification = bfd_coff_classify_symbol (abfd, &sym);
+      if (classification != COFF_SYMBOL_LOCAL)
        {
          const char *name;
          char buf[SYMNMLEN + 1];
          flagword flags;
          asection *section;
          bfd_vma value;
+         boolean addit;
 
          /* This symbol is externally visible.  */
 
@@ -367,37 +367,79 @@ coff_link_add_symbols (abfd, info)
 
          value = sym.n_value;
 
-         if (sym.n_scnum == 0)
-           {
-             if (value == 0)
-               {
-                 flags = 0;
-                 section = bfd_und_section_ptr;
-               }
-             else
-               {
-                 flags = BSF_GLOBAL;
-                 section = bfd_com_section_ptr;
-               }
-           }
-         else
+         switch (classification)
            {
+           default:
+             abort ();
+
+           case COFF_SYMBOL_GLOBAL:
              flags = BSF_EXPORT | BSF_GLOBAL;
              section = coff_section_from_bfd_index (abfd, sym.n_scnum);
              if (! obj_pe (abfd))
                value -= section->vma;
+             break;
+
+           case COFF_SYMBOL_UNDEFINED:
+             flags = 0;
+             section = bfd_und_section_ptr;
+             break;
+
+           case COFF_SYMBOL_COMMON:
+             flags = BSF_GLOBAL;
+             section = bfd_com_section_ptr;
+             break;
+
+           case COFF_SYMBOL_PE_SECTION:
+             flags = BSF_SECTION_SYM | BSF_GLOBAL;
+             section = coff_section_from_bfd_index (abfd, sym.n_scnum);
+             break;
            }
 
          if (sym.n_sclass == C_WEAKEXT
              || (obj_pe (abfd) && sym.n_sclass == C_NT_WEAK))
            flags = BSF_WEAK;
 
-         if (! (bfd_coff_link_add_one_symbol
-                (info, abfd, name, flags, section, value,
-                 (const char *) NULL, copy, false,
-                 (struct bfd_link_hash_entry **) sym_hash)))
-           goto error_return;
+         addit = true;
+
+         /* In the PE format, section symbols actually refer to the
+             start of the output section.  We handle them specially
+             here.  */
+         if (obj_pe (abfd) && (flags & BSF_SECTION_SYM) != 0)
+           {
+             *sym_hash = coff_link_hash_lookup (coff_hash_table (info),
+                                                name, false, copy, false);
+             if (*sym_hash != NULL)
+               {
+                 if (((*sym_hash)->coff_link_hash_flags
+                      & COFF_LINK_HASH_PE_SECTION_SYMBOL) == 0
+                     && (*sym_hash)->root.type != bfd_link_hash_undefined
+                     && (*sym_hash)->root.type != bfd_link_hash_undefweak)
+                   (*_bfd_error_handler)
+                     ("Warning: symbol `%s' is both section and non-section",
+                      name);
+
+                 addit = false;
+               }
+           }
 
+         if (addit)
+           {
+             if (! (bfd_coff_link_add_one_symbol
+                    (info, abfd, name, flags, section, value,
+                     (const char *) NULL, copy, false,
+                     (struct bfd_link_hash_entry **) sym_hash)))
+               goto error_return;
+           }
+
+         if (obj_pe (abfd) && (flags & BSF_SECTION_SYM) != 0)
+           (*sym_hash)->coff_link_hash_flags |=
+             COFF_LINK_HASH_PE_SECTION_SYMBOL;
+
+         /* Limit the alignment of a common symbol to the possible
+             alignment of a section.  There is no point to permitting
+             a higher alignment for a common symbol: we can not
+             guarantee it, and it may cause us to allocate extra space
+             in the common section.  */
          if (section == bfd_com_section_ptr
              && (*sym_hash)->root.type == bfd_link_hash_common
              && ((*sym_hash)->root.u.c.p->alignment_power
@@ -407,25 +449,44 @@ coff_link_add_symbols (abfd, info)
 
          if (info->hash->creator->flavour == bfd_get_flavour (abfd))
            {
-             if (((*sym_hash)->class == C_NULL
-                  && (*sym_hash)->type == T_NULL)
-                 || sym.n_scnum != 0
-                 || (sym.n_value != 0
-                     && (*sym_hash)->root.type != bfd_link_hash_defined
-                     && (*sym_hash)->root.type != bfd_link_hash_defweak))
-               {
-                 (*sym_hash)->class = sym.n_sclass;
-                 if (sym.n_type != T_NULL)
-                   {
-                     if ((*sym_hash)->type != T_NULL
-                         && (*sym_hash)->type != sym.n_type)
-                       (*_bfd_error_handler)
-                         (_("Warning: type of symbol `%s' changed from %d to %d in %s"),
-                          name, (*sym_hash)->type, sym.n_type,
-                          bfd_get_filename (abfd));
-                     (*sym_hash)->type = sym.n_type;
-                   }
-                 (*sym_hash)->auxbfd = abfd;
+             /* If we don't have any symbol information currently in
+                 the hash table, or if we are looking at a symbol
+                 definition, then update the symbol class and type in
+                 the hash table.  */
+             if (((*sym_hash)->class == C_NULL
+                  && (*sym_hash)->type == T_NULL)
+                 || sym.n_scnum != 0
+                 || (sym.n_value != 0
+                     && (*sym_hash)->root.type != bfd_link_hash_defined
+                     && (*sym_hash)->root.type != bfd_link_hash_defweak))
+               {
+                 (*sym_hash)->class = sym.n_sclass;
+                 if (sym.n_type != T_NULL)
+                   {
+                     /* We want to warn if the type changed, but not
+                        if it changed from an unspecified type.
+                        Testing the whole type byte may work, but the
+                        change from (e.g.) a function of unspecified
+                        type to function of known type also wants to
+                        skip the warning.  */
+                     if ((*sym_hash)->type != T_NULL
+                         && (*sym_hash)->type != sym.n_type
+                         && !(DTYPE ((*sym_hash)->type) == DTYPE (sym.n_type)
+                              && (BTYPE ((*sym_hash)->type) == T_NULL
+                                  || BTYPE (sym.n_type) == T_NULL)))
+                       (*_bfd_error_handler)
+                         (_("Warning: type of symbol `%s' changed from %d to %d in %s"),
+                          name, (*sym_hash)->type, sym.n_type,
+                          bfd_get_filename (abfd));
+
+                     /* We don't want to change from a meaningful
+                        base type to a null one, but if we know
+                        nothing, take what little we might now know.  */
+                     if (BTYPE (sym.n_type) != T_NULL
+                         || (*sym_hash)->type == T_NULL)
+                       (*sym_hash)->type = sym.n_type;
+                   }
+                 (*sym_hash)->auxbfd = abfd;
                  if (sym.n_numaux != 0)
                    {
                      union internal_auxent *alloc;
@@ -450,6 +511,26 @@ coff_link_add_symbols (abfd, info)
                    }
                }
            }
+
+         if (classification == COFF_SYMBOL_PE_SECTION
+             && (*sym_hash)->numaux != 0)
+           {
+             /* Some PE sections (such as .bss) have a zero size in
+                 the section header, but a non-zero size in the AUX
+                 record.  Correct that here.
+
+                FIXME: This is not at all the right place to do this.
+                For example, it won't help objdump.  This needs to be
+                done when we swap in the section header.  */
+
+             BFD_ASSERT ((*sym_hash)->numaux == 1);
+             if (section->_raw_size == 0)
+               section->_raw_size = (*sym_hash)->aux[0].x_scn.x_scnlen;
+
+             /* FIXME: We could test whether the section sizes
+                 matches the size in the aux entry, but apparently
+                 that sometimes fails unexpectedly.  */
+           }
        }
 
       esym += (sym.n_numaux + 1) * symesz;
@@ -1212,15 +1293,17 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
      struct coff_final_link_info *finfo;
      bfd *input_bfd;
 {
-  boolean (*sym_is_global) PARAMS ((bfd *, struct internal_syment *));
+  unsigned int n_tmask = coff_data (input_bfd)->local_n_tmask;
+  unsigned int n_btshft = coff_data (input_bfd)->local_n_btshft;
+#if 0
+  unsigned int n_btmask = coff_data (input_bfd)->local_n_btmask;
+#endif
   boolean (*adjust_symndx) PARAMS ((bfd *, struct bfd_link_info *, bfd *,
                                    asection *, struct internal_reloc *,
                                    boolean *));
   bfd *output_bfd;
   const char *strings;
   bfd_size_type syment_base;
-  unsigned int n_tmask;
-  unsigned int n_btshft;
   boolean copy, hash;
   bfd_size_type isymesz;
   bfd_size_type osymesz;
@@ -1238,7 +1321,6 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
   /* Move all the symbols to the output file.  */
 
   output_bfd = finfo->output_bfd;
-  sym_is_global = coff_backend_info (input_bfd)->_bfd_coff_sym_is_global;
   strings = NULL;
   syment_base = obj_raw_syment_count (output_bfd);
   isymesz = bfd_coff_symesz (input_bfd);
@@ -1246,13 +1328,6 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
   linesz = bfd_coff_linesz (input_bfd);
   BFD_ASSERT (linesz == bfd_coff_linesz (output_bfd));
 
-  n_tmask = coff_data (input_bfd)->local_n_tmask;
-  n_btshft = coff_data (input_bfd)->local_n_btshft;
-
-  /* Define macros so that ISFCN, et. al., macros work correctly.  */
-#define N_TMASK n_tmask
-#define N_BTSHFT n_btshft
-
   copy = false;
   if (! finfo->info->keep_memory)
     copy = true;
@@ -1293,6 +1368,7 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
   while (esym < esym_end)
     {
       struct internal_syment isym;
+      enum coff_symbol_classification classification;
       boolean skip;
       boolean global;
       boolean dont_skip_symbol;
@@ -1306,14 +1382,22 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
         the symbol.  */
       isym = *isymp;
 
-      if (isym.n_scnum != 0)
-       *secpp = coff_section_from_bfd_index (input_bfd, isym.n_scnum);
-      else
+      classification = bfd_coff_classify_symbol (input_bfd, &isym);
+      switch (classification)
        {
-         if (isym.n_value == 0)
-           *secpp = bfd_und_section_ptr;
-         else
-           *secpp = bfd_com_section_ptr;
+       default:
+         abort ();
+       case COFF_SYMBOL_GLOBAL:
+       case COFF_SYMBOL_PE_SECTION:
+       case COFF_SYMBOL_LOCAL:
+         *secpp = coff_section_from_bfd_index (input_bfd, isym.n_scnum);
+         break;
+       case COFF_SYMBOL_COMMON:
+         *secpp = bfd_com_section_ptr;
+         break;
+       case COFF_SYMBOL_UNDEFINED:
+         *secpp = bfd_und_section_ptr;
+         break;
        }
 
       /* Extract the flag indicating if this symbol is used by a
@@ -1337,28 +1421,34 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
 
       if (! skip)
        {
-         if (isym.n_sclass == C_EXT
-             || isym.n_sclass == C_WEAKEXT
-             || (obj_pe (input_bfd) && isym.n_sclass == C_NT_WEAK)
-#ifdef C_SYSTEM
-             || isym.n_sclass == C_SYSTEM
-#endif
-             || (sym_is_global && (*sym_is_global) (input_bfd, &isym)))
+         switch (classification)
            {
+           default:
+             abort ();
+           case COFF_SYMBOL_GLOBAL:
+           case COFF_SYMBOL_COMMON:
+           case COFF_SYMBOL_PE_SECTION:
              /* This is a global symbol.  Global symbols come at the
                 end of the symbol table, so skip them for now.
                 Locally defined function symbols, however, are an
                 exception, and are not moved to the end.  */
              global = true;
-             if (! ISFCN (isym.n_type) || isym.n_scnum == 0)
+             if (! ISFCN (isym.n_type))
                skip = true;
-           }
-         else
-           {
+             break;
+
+           case COFF_SYMBOL_UNDEFINED:
+             /* Undefined symbols are left for the end.  */
+             global = true;
+             skip = true;
+             break;
+
+           case COFF_SYMBOL_LOCAL:
              /* This is a local symbol.  Skip it if we are discarding
                  local symbols.  */
              if (finfo->info->discard == discard_all && ! dont_skip_symbol)
                skip = true;
+             break;
            }
        }
 
@@ -1466,7 +1556,7 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
            {
              const char *elename;
              char elebuf[SYMNMLEN + 1];
-             char *copy;
+             char *name_copy;
 
              bfd_coff_swap_sym_in (input_bfd, (PTR) esl, (PTR) islp);
 
@@ -1481,12 +1571,13 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
              if (elename == NULL)
                return false;
 
-             copy = (char *) bfd_alloc (input_bfd, strlen (elename) + 1);
-             if (copy == NULL)
+             name_copy = (char *) bfd_alloc (input_bfd,
+                                             strlen (elename) + 1);
+             if (name_copy == NULL)
                return false;
-             strcpy (copy, elename);
+             strcpy (name_copy, elename);
 
-             (*epp)->name = copy;
+             (*epp)->name = name_copy;
              (*epp)->type = islp->n_type;
              (*epp)->tagndx = 0;
              if (islp->n_numaux >= 1
@@ -1934,6 +2025,8 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
          bfd_vma offset;
          bfd_byte *eline;
          bfd_byte *elineend;
+         bfd_byte *oeline;
+         boolean skipping;
 
          /* FIXME: If SEC_HAS_CONTENTS is not for the section, then
             build_link_order in ldwrite.c will not have created a
@@ -1955,7 +2048,9 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
 
          offset = o->output_section->vma + o->output_offset - o->vma;
          eline = finfo->linenos;
+         oeline = finfo->linenos;
          elineend = eline + linesz * o->lineno_count;
+         skipping = false;
          for (; eline < elineend; eline += linesz)
            {
              struct internal_lineno iline;
@@ -1975,11 +2070,13 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
                  if (indx < 0)
                    {
                      /* These line numbers are attached to a symbol
-                        which we are stripping.  We should really
-                        just discard the line numbers, but that would
-                        be a pain because we have already counted
-                        them.  */
-                     indx = 0;
+                        which we are stripping.  We must discard the
+                        line numbers because reading them back with
+                        no associated symbol (or associating them all
+                        with symbol #0) will fail.  We can't regain
+                        the space in the output file, but at least
+                        they're dense.  */
+                     skipping = true;
                    }
                  else
                    {
@@ -2018,23 +2115,32 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
                                                 is.n_type, is.n_sclass, 0,
                                                 is.n_numaux, auxptr);
                        }
+
+                       skipping = false;
                    }
 
                  iline.l_addr.l_symndx = indx;
                }
 
-             bfd_coff_swap_lineno_out (output_bfd, (PTR) &iline, (PTR) eline);
+             if (!skipping)
+               {
+                 bfd_coff_swap_lineno_out (output_bfd, (PTR) &iline,
+                                           (PTR) oeline);
+                 oeline += linesz;
+               }
            }
 
          if (bfd_seek (output_bfd,
                        (o->output_section->line_filepos
                         + o->output_section->lineno_count * linesz),
                        SEEK_SET) != 0
-             || bfd_write (finfo->linenos, linesz, o->lineno_count,
-                           output_bfd) != linesz * o->lineno_count)
+             || (bfd_write (finfo->linenos, 1, oeline - finfo->linenos,
+                            output_bfd)
+                 != (bfd_size_type) (oeline - finfo->linenos)))
            return false;
 
-         o->output_section->lineno_count += o->lineno_count;
+         o->output_section->lineno_count +=
+           (oeline - finfo->linenos) / linesz;
        }
     }
 
@@ -2386,11 +2492,64 @@ _bfd_coff_write_global_sym (h, data)
 
   ++obj_raw_syment_count (output_bfd);
 
-  /* Write out any associated aux entries.  There normally will be
-     none.  If there are any, I have no idea how to modify them.  */
+  /* Write out any associated aux entries.  Most of the aux entries
+     will have been modified in _bfd_coff_link_input_bfd.  We have to
+     handle section aux entries here, now that we have the final
+     relocation and line number counts.  */
   for (i = 0; i < isym.n_numaux; i++)
     {
-      bfd_coff_swap_aux_out (output_bfd, (PTR) (h->aux + i), isym.n_type,
+      union internal_auxent *auxp;
+
+      auxp = h->aux + i;
+
+      /* Look for a section aux entry here using the same tests that
+         coff_swap_aux_out uses.  */
+      if (i == 0
+         && (isym.n_sclass == C_STAT
+             || isym.n_sclass == C_HIDDEN)
+         && isym.n_type == T_NULL
+         && (h->root.type == bfd_link_hash_defined
+             || h->root.type == bfd_link_hash_defweak))
+       {
+         asection *sec;
+
+         sec = h->root.u.def.section->output_section;
+         if (sec != NULL)
+           {
+             auxp->x_scn.x_scnlen = (sec->_cooked_size != 0
+                                     ? sec->_cooked_size
+                                     : sec->_raw_size);
+
+             /* For PE, an overflow on the final link reportedly does
+                 not matter.  FIXME: Why not?  */
+
+             if (sec->reloc_count > 0xffff
+                 && (! obj_pe (output_bfd)
+                     || finfo->info->relocateable))
+               (*_bfd_error_handler)
+                 (_("%s: %s: reloc overflow: 0x%lx > 0xffff"),
+                  bfd_get_filename (output_bfd),
+                  bfd_get_section_name (output_bfd, sec),
+                  sec->reloc_count);
+
+             if (sec->lineno_count > 0xffff
+                 && (! obj_pe (output_bfd)
+                     || finfo->info->relocateable))
+               (*_bfd_error_handler)
+                 (_("%s: warning: %s: line number overflow: 0x%lx > 0xffff"),
+                  bfd_get_filename (output_bfd),
+                  bfd_get_section_name (output_bfd, sec),
+                  sec->lineno_count);
+
+             auxp->x_scn.x_nreloc = sec->reloc_count;
+             auxp->x_scn.x_nlinno = sec->lineno_count;
+             auxp->x_scn.x_checksum = 0;
+             auxp->x_scn.x_associated = 0;
+             auxp->x_scn.x_comdat = 0;
+           }
+       }
+
+      bfd_coff_swap_aux_out (output_bfd, (PTR) auxp, isym.n_type,
                             isym.n_sclass, i, isym.n_numaux,
                             (PTR) finfo->outsyms);
       if (bfd_write (finfo->outsyms, symesz, 1, output_bfd) != symesz)
This page took 0.047643 seconds and 4 git commands to generate.