1999-09-11 Donn Terry <donn@interix.com>
[binutils-gdb.git] / bfd / cofflink.c
index f8748327f33dc8a0d1ce79a8c492e13db1d6cb7e..a4e18b32e8b5fb5e74a2c48c14a13ae185ec2cdd 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,17 +337,13 @@ 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];
@@ -368,25 +367,32 @@ 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
@@ -429,6 +435,11 @@ coff_link_add_symbols (abfd, info)
            (*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
@@ -438,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;
@@ -481,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;
@@ -1243,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;
@@ -1269,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);
@@ -1277,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;
@@ -1324,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;
@@ -1337,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
@@ -1368,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;
            }
        }
 
@@ -1497,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);
 
@@ -1512,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