* elf64-alpha.c: Initial implementation of relaxation --
[binutils-gdb.git] / bfd / cofflink.c
index 8e11b09b740a2c21307cff9c3bd9c6b21059121b..58c6d94d6482ad9b56ec0cf53bf8faa26a64a315 100644 (file)
@@ -1,5 +1,5 @@
 /* COFF specific linker code.
-   Copyright 1994, 1995, 1996 Free Software Foundation, Inc.
+   Copyright 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Cygnus Support.
 
 This file is part of BFD, the Binary File Descriptor library.
@@ -34,6 +34,11 @@ static boolean coff_link_check_archive_element
 static boolean coff_link_check_ar_symbols
   PARAMS ((bfd *, struct bfd_link_info *, boolean *));
 static boolean coff_link_add_symbols PARAMS ((bfd *, struct bfd_link_info *));
+static char *dores_com PARAMS ((char *, bfd *, int));
+static char *get_name PARAMS ((char *, char **));
+static int process_embedded_commands
+  PARAMS ((bfd *, struct bfd_link_info *, bfd *));
+static void mark_relocs PARAMS ((struct coff_final_link_info *, bfd *));
 
 /* Create an entry in a COFF linker hash table.  */
 
@@ -369,7 +374,8 @@ coff_link_add_symbols (abfd, info)
            {
              flags = BSF_EXPORT | BSF_GLOBAL;
              section = coff_section_from_bfd_index (abfd, sym.n_scnum);
-             value -= section->vma;
+             if (! obj_pe (abfd))
+               value -= section->vma;
            }
 
          if (! (bfd_coff_link_add_one_symbol
@@ -378,6 +384,13 @@ coff_link_add_symbols (abfd, info)
                  (struct bfd_link_hash_entry **) sym_hash)))
            goto error_return;
 
+         if (section == bfd_com_section_ptr
+             && (*sym_hash)->root.type == bfd_link_hash_common
+             && ((*sym_hash)->root.u.c.p->alignment_power
+                 > bfd_coff_default_section_alignment_power (abfd)))
+           (*sym_hash)->root.u.c.p->alignment_power
+             = bfd_coff_default_section_alignment_power (abfd);
+
          if (info->hash->creator->flavour == bfd_get_flavour (abfd))
            {
              if (((*sym_hash)->class == C_NULL
@@ -519,6 +532,7 @@ _bfd_coff_final_link (abfd, info)
   finfo.contents = NULL;
   finfo.external_relocs = NULL;
   finfo.internal_relocs = NULL;
+  finfo.global_to_static = false;
   debug_merge_allocated = false;
 
   coff_data (abfd)->link_info = info;
@@ -533,7 +547,10 @@ _bfd_coff_final_link (abfd, info)
 
   /* Compute the file positions for all the sections.  */
   if (! abfd->output_has_begun)
-    bfd_coff_compute_section_file_positions (abfd);
+    {
+      if (! bfd_coff_compute_section_file_positions (abfd))
+       goto error_return;
+    }
 
   /* Count the line numbers and relocation entries required for the
      output file.  Set the file positions for the relocs.  */
@@ -824,6 +841,18 @@ _bfd_coff_final_link (abfd, info)
        return false;
     }
 
+  /* If doing task linking (ld --task-link) then make a pass through the
+     global symbols, writing out any that are defined, and making them
+     static. */
+  if (info->task_link)
+    {
+      finfo.failed = false;
+      coff_link_hash_traverse (coff_hash_table (info), _bfd_coff_write_task_globals,
+                              (PTR) &finfo);
+      if (finfo.failed)
+       goto error_return;
+    }
+
   /* Write out the global symbols.  */
   finfo.failed = false;
   coff_link_hash_traverse (coff_hash_table (info), _bfd_coff_write_global_sym,
@@ -1103,6 +1132,60 @@ process_embedded_commands (output_bfd, info,  abfd)
   return 1;
 }
 
+/* Place a marker against all symbols which are used by relocations.
+   This marker can be picked up by the 'do we skip this symbol ?'
+   loop in _bfd_coff_link_input_bfd() and used to prevent skipping
+   that symbol. 
+   */
+
+static void
+mark_relocs (finfo, input_bfd)
+     struct coff_final_link_info *     finfo;
+     bfd *                             input_bfd;
+{
+  asection * a;
+
+  if ((bfd_get_file_flags (input_bfd) & HAS_SYMS) == 0)
+    return;
+  
+  for (a = input_bfd->sections; a != (asection *) NULL; a = a->next)
+    {
+      struct internal_reloc *  internal_relocs;
+      struct internal_reloc *  irel;
+      struct internal_reloc *  irelend;
+
+      
+      if ((a->flags & SEC_RELOC) == 0 || a->reloc_count  < 1)
+       continue;
+
+      /* Read in the relocs.  */
+      internal_relocs = _bfd_coff_read_internal_relocs
+       (input_bfd, a, false,
+        finfo->external_relocs,
+        finfo->info->relocateable,
+        (finfo->info->relocateable
+         ? (finfo->section_info[ a->output_section->target_index ].relocs + a->output_section->reloc_count)
+         : finfo->internal_relocs)
+       );
+      
+      if (internal_relocs == NULL)
+       continue;
+
+      irel     = internal_relocs;
+      irelend  = irel + a->reloc_count;
+
+      /* Place a mark in the sym_indices array (whose entries have
+        been initialised to 0) for all of the symbols that are used
+        in the relocation table.  This will then be picked up in the
+        skip/don't pass */
+      
+      for (; irel < irelend; irel++)
+       {
+         finfo->sym_indices[ irel->r_symndx ] = -1;
+       }
+    }
+}
+
 /* Link an input file into the linker output file.  This function
    handles all the sections and relocations of the input file at once.  */
 
@@ -1176,11 +1259,25 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
        return false;
     }
 
+  /* If we are going to perform relocations and also strip/discard some symbols
+     then we must make sure that we do not strip/discard those symbols that are
+     going to be involved in the relocations */
+  if ((   finfo->info->strip   != strip_none
+       || finfo->info->discard != discard_none)
+      && finfo->info->relocateable)
+    {
+      /* mark the symbol array as 'not-used' */
+      memset (indexp, 0, obj_raw_syment_count (input_bfd) * sizeof * indexp); 
+       
+      mark_relocs (finfo, input_bfd);
+    }
+
   while (esym < esym_end)
     {
       struct internal_syment isym;
       boolean skip;
       boolean global;
+      boolean dont_skip_symbol;
       int add;
 
       bfd_coff_swap_sym_in (input_bfd, (PTR) esym, (PTR) isymp);
@@ -1201,6 +1298,14 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
            *secpp = bfd_com_section_ptr;
        }
 
+      /* Extract the flag indicating if this symbol is used by a relocation */
+      if ((   finfo->info->strip   != strip_none
+          || finfo->info->discard != discard_none)
+         && finfo->info->relocateable)
+       dont_skip_symbol = *indexp;
+      else
+       dont_skip_symbol = false;
+      
       *indexp = -1;
 
       skip = false;
@@ -1208,7 +1313,7 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
       add = 1 + isym.n_numaux;
 
       /* If we are stripping all symbols, we want to skip this one.  */
-      if (finfo->info->strip == strip_all)
+      if (finfo->info->strip == strip_all && ! dont_skip_symbol)
        skip = true;
 
       if (! skip)
@@ -1228,7 +1333,7 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
            {
              /* This is a local symbol.  Skip it if we are discarding
                  local symbols.  */
-             if (finfo->info->discard == discard_all)
+             if (finfo->info->discard == discard_all && ! dont_skip_symbol)
                skip = true;
            }
        }
@@ -1237,6 +1342,7 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
          symbol, then skip it.  */
       if (! skip
          && finfo->info->strip == strip_debugger
+         && ! dont_skip_symbol
          && isym.n_scnum == N_DEBUG)
        skip = true;
 
@@ -1253,13 +1359,13 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
          if (name == NULL)
            return false;
 
-         if ((finfo->info->strip == strip_some
-              && (bfd_hash_lookup (finfo->info->keep_hash, name, false,
+         if (! dont_skip_symbol
+             && ((finfo->info->strip == strip_some
+                  && (bfd_hash_lookup (finfo->info->keep_hash, name, false,
                                    false) == NULL))
-             || (! global
-                 && finfo->info->discard == discard_l
-                 && strncmp (name, finfo->info->lprefix,
-                             finfo->info->lprefix_len) == 0))
+                  || (! global
+                      && finfo->info->discard == discard_l
+                      && bfd_is_local_label_name (input_bfd, name))))
            skip = true;
        }
 
@@ -1459,9 +1565,11 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
          if (isym.n_scnum > 0)
            {
              isym.n_scnum = (*secpp)->output_section->target_index;
-             isym.n_value += ((*secpp)->output_section->vma
-                              + (*secpp)->output_offset
-                              - (*secpp)->vma);
+             isym.n_value += (*secpp)->output_offset;
+             if (! obj_pe (input_bfd))
+               isym.n_value -= (*secpp)->vma;
+             if (! obj_pe (finfo->output_bfd))
+               isym.n_value += (*secpp)->output_section->vma;
            }
 
          /* The value of a C_FILE symbol is the symbol index of the
@@ -1512,6 +1620,12 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
              finfo->last_file = isym;
            }
 
+         /* If doing task linking, convert normal global function symbols to
+            static functions. */
+
+         if (finfo->info->task_link && isym.n_sclass == C_EXT)
+           isym.n_sclass = C_STAT;
+
          /* Output the symbol.  */
 
          bfd_coff_swap_sym_out (output_bfd, (PTR) &isym, (PTR) outsym);
@@ -2053,9 +2167,10 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
                          char buf[SYMNMLEN + 1];
 
                          /* This reloc is against a symbol we are
-                             stripping.  It would be possible to
-                             handle this case, but I don't think it's
-                             worth it.  */
+                             stripping.  This should have been handled
+                            by the 'dont_skip_symbol' code in the while
+                            loop at the top of this function. */
+                         
                          is = finfo->internal_syms + irel->r_symndx;
 
                          name = (_bfd_coff_internal_syment_name
@@ -2087,8 +2202,9 @@ _bfd_coff_link_input_bfd (finfo, input_bfd)
        }
       else
        {
-         if (! _bfd_write_section_stabs (output_bfd, o, &secdata->stab_info,
-                                         contents))
+         if (! (_bfd_write_section_stabs
+                (output_bfd, &coff_hash_table (finfo->info)->stab_info,
+                 o, &secdata->stab_info, contents)))
            return false;
        }
     }
@@ -2152,8 +2268,9 @@ _bfd_coff_write_global_sym (h, data)
        else
          isym.n_scnum = sec->target_index;
        isym.n_value = (h->root.u.def.value
-                       + sec->vma
                        + h->root.u.def.section->output_offset);
+       if (! obj_pe (finfo->output_bfd))
+         isym.n_value += sec->vma;
       }
       break;
 
@@ -2195,6 +2312,18 @@ _bfd_coff_write_global_sym (h, data)
   if (isym.n_sclass == C_NULL)
     isym.n_sclass = C_EXT;
 
+  /* If doing task linking and this is the pass where we convert defined globals to
+     statics, then do that conversion now.  If the symbol is not being converted,
+     just ignore it and it will be output during a later pass. */
+  if (finfo->global_to_static)
+    {
+      if (isym.n_sclass != C_EXT)
+       {
+         return true;
+       }
+      isym.n_sclass = C_STAT;
+    }
+
   isym.n_numaux = h->numaux;
   
   bfd_coff_swap_sym_out (output_bfd, (PTR) &isym, (PTR) finfo->outsyms);
@@ -2233,6 +2362,33 @@ _bfd_coff_write_global_sym (h, data)
   return true;
 }
 
+/* Write out task global symbols, converting them to statics.  Called
+   via coff_link_hash_traverse.  Calls bfd_coff_write_global_sym to do
+   the dirty work, if the symbol we are processing needs conversion. */
+
+boolean
+_bfd_coff_write_task_globals (h, data)
+     struct coff_link_hash_entry *h;
+     PTR data;
+{
+  struct coff_final_link_info *finfo = (struct coff_final_link_info *) data;
+  boolean rtnval = true;
+
+  if (h->indx < 0)
+    {
+      switch (h->root.type)
+       {
+       case bfd_link_hash_defined:
+       case bfd_link_hash_defweak:
+         finfo->global_to_static = true;
+         rtnval = _bfd_coff_write_global_sym (h, data);
+         finfo->global_to_static = false;
+         break;
+       }
+    }
+  return (rtnval);
+}
+
 /* Handle a link order which is supposed to generate a reloc.  */
 
 boolean
@@ -2423,6 +2579,18 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
       if (howto == NULL)
        return false;
 
+      /* If we are doing a relocateable link, then we can just ignore
+         a PC relative reloc that is pcrel_offset.  It will already
+         have the correct value.  If this is not a relocateable link,
+         then we should ignore the symbol value.  */
+      if (howto->pc_relative && howto->pcrel_offset)
+       {
+         if (info->relocateable)
+           continue;
+         if (sym != NULL && sym->n_scnum != 0)
+           addend += sym->n_value;
+       }
+
       val = 0;
 
       if (h == NULL)
@@ -2439,8 +2607,9 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
              sec = sections[symndx];
               val = (sec->output_section->vma
                     + sec->output_offset
-                    + sym->n_value
-                    - sec->vma);
+                    + sym->n_value);
+             if (! obj_pe (input_bfd))
+               val -= sec->vma;
            }
        }
       else
@@ -2468,19 +2637,26 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
       if (info->base_file)
        {
          /* Emit a reloc if the backend thinks it needs it. */
-         if (sym && pe_data(output_bfd)->in_reloc_p(output_bfd, howto))
+         if (sym && pe_data (output_bfd)->in_reloc_p (output_bfd, howto))
            {
-             /* relocation to a symbol in a section which
-                isn't absolute - we output the address here 
-                to a file */
-             bfd_vma addr = rel->r_vaddr 
-               - input_section->vma 
-               + input_section->output_offset 
-                 + input_section->output_section->vma;
-             if (coff_data(output_bfd)->pe)
+             /* Relocation to a symbol in a section which isn't
+                absolute.  We output the address here to a file.
+                This file is then read by dlltool when generating the
+                reloc section.  Note that the base file is not
+                portable between systems.  We write out a long here,
+                and dlltool reads in a long.  */
+             long addr = (rel->r_vaddr 
+                          - input_section->vma 
+                          + input_section->output_offset 
+                          + input_section->output_section->vma);
+             if (coff_data (output_bfd)->pe)
                addr -= pe_data(output_bfd)->pe_opthdr.ImageBase;
-             /* FIXME: Shouldn't 4 be sizeof (addr)?  */
-             fwrite (&addr, 1,4, (FILE *) info->base_file);
+             if (fwrite (&addr, 1, sizeof (long), (FILE *) info->base_file)
+                 != sizeof (long))
+               {
+                 bfd_set_error (bfd_error_system_call);
+                 return false;
+               }
            }
        }
   
@@ -2497,10 +2673,10 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
          break;
        case bfd_reloc_outofrange:
          (*_bfd_error_handler)
-           ("%s: bad reloc address in section `%s' at address 0x%lx",
+           ("%s: bad reloc address 0x%lx in section `%s'",
             bfd_get_filename (input_bfd),
-            bfd_get_section_name (input_bfd, input_section),
-            (unsigned long) rel->r_vaddr);
+            (unsigned long) rel->r_vaddr,
+            bfd_get_section_name (input_bfd, input_section));
          return false;
        case bfd_reloc_overflow:
          {