Fix bug in previous delta
[deliverable/binutils-gdb.git] / ld / ldwrite.c
index 1dacb3de2a7a30c8abf5888739e002bce90c4a59..b56119a39ac124ae410cca6183afc2371c2d3fdb 100644 (file)
@@ -1,5 +1,6 @@
 /* ldwrite.c -- write out the linked file
-   Copyright (C) 1993 Free Software Foundation, Inc.
+   Copyright (C) 1991, 92, 93, 94, 95, 96, 97, 1998
+   Free Software Foundation, Inc.
    Written by Steve Chamberlain sac@cygnus.com
 
 This file is part of GLD, the Gnu Linker.
@@ -16,11 +17,12 @@ GNU General Public License for more details.
 
 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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include "bfd.h"
 #include "sysdep.h"
 #include "bfdlink.h"
+#include "libiberty.h"
 
 #include "ld.h"
 #include "ldexp.h"
@@ -31,9 +33,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "ldmain.h"
 
 static void build_link_order PARAMS ((lang_statement_union_type *));
-static void print_symbol_table PARAMS ((void));
-static void print_file_stuff PARAMS ((lang_input_statement_type *));
-static boolean print_symbol PARAMS ((struct bfd_link_hash_entry *, PTR));
+static asection *clone_section PARAMS ((bfd *, asection *, int *));
+static void split_sections PARAMS ((bfd *, struct bfd_link_info *));
 
 /* Build link_order structures for the BFD linker.  */
 
@@ -44,42 +45,137 @@ build_link_order (statement)
   switch (statement->header.type)
     {
     case lang_data_statement_enum:
-      /* FIXME: This should probably build a link_order, but instead
-        it just does the output directly.  */
       {
-       bfd_vma value = statement->data_statement.value;
-       bfd_byte play_area[QUAD_SIZE];
-       unsigned int size = 0;
-       asection *output_section = statement->data_statement.output_section;
+       asection *output_section;
+       struct bfd_link_order *link_order;
+       bfd_vma value;
+       boolean big_endian = false;
+
+       output_section = statement->data_statement.output_section;
+       ASSERT (output_section->owner == output_bfd);
+
+       link_order = bfd_new_link_order (output_bfd, output_section);
+       if (link_order == NULL)
+         einfo (_("%P%F: bfd_new_link_order failed\n"));
+
+       link_order->type = bfd_data_link_order;
+       link_order->offset = statement->data_statement.output_vma;
+       link_order->u.data.contents = (bfd_byte *) xmalloc (QUAD_SIZE);
+
+       value = statement->data_statement.value;
+
+       /* If the endianness of the output BFD is not known, then we
+          base the endianness of the data on the first input file.
+          By convention, the bfd_put routines for an unknown
+          endianness are big endian, so we must swap here if the
+          input file is little endian.  */
+       if (bfd_big_endian (output_bfd))
+         big_endian = true;
+       else if (bfd_little_endian (output_bfd))
+         big_endian = false;
+       else
+         {
+           boolean swap;
+
+           swap = false;
+           if (command_line.endian == ENDIAN_BIG)
+             big_endian = true;
+           else if (command_line.endian == ENDIAN_LITTLE)
+             {
+               big_endian = false;
+               swap = true;
+             }
+           else if (command_line.endian == ENDIAN_UNSET)
+             {
+               big_endian = true;
+               {
+                 LANG_FOR_EACH_INPUT_STATEMENT (s)
+                   {
+                     if (s->the_bfd != NULL)
+                       {
+                         if (bfd_little_endian (s->the_bfd))
+                           {
+                             big_endian = false;
+                             swap = true;
+                           }
+                         break;
+                       }
+                   }
+               }
+             }
+
+           if (swap)
+             {
+               bfd_byte buffer[8];
+
+               switch (statement->data_statement.type)
+                 {
+                 case QUAD:
+                 case SQUAD:
+                   if (sizeof (bfd_vma) >= QUAD_SIZE)
+                     {
+                       bfd_putl64 (value, buffer);
+                       value = bfd_getb64 (buffer);
+                       break;
+                     }
+                   /* Fall through.  */
+                 case LONG:
+                   bfd_putl32 (value, buffer);
+                   value = bfd_getb32 (buffer);
+                   break;
+                 case SHORT:
+                   bfd_putl16 (value, buffer);
+                   value = bfd_getb16 (buffer);
+                   break;
+                 case BYTE:
+                   break;
+                 default:
+                   abort ();
+                 }
+             }
+         }
 
        ASSERT (output_section->owner == output_bfd);
        switch (statement->data_statement.type)
          {
          case QUAD:
-           bfd_put_64 (output_bfd, value, play_area);
-           size = QUAD_SIZE;
+         case SQUAD:
+           if (sizeof (bfd_vma) >= QUAD_SIZE)
+             bfd_put_64 (output_bfd, value, link_order->u.data.contents);
+           else
+             {
+               bfd_vma high;
+
+               if (statement->data_statement.type == QUAD)
+                 high = 0;
+               else if ((value & 0x80000000) == 0)
+                 high = 0;
+               else
+                 high = (bfd_vma) -1;
+               bfd_put_32 (output_bfd, high,
+                           (link_order->u.data.contents
+                            + (big_endian ? 0 : 4)));
+               bfd_put_32 (output_bfd, value,
+                           (link_order->u.data.contents
+                            + (big_endian ? 4 : 0)));
+             }
+           link_order->size = QUAD_SIZE;
            break;
          case LONG:
-           bfd_put_32 (output_bfd, value, play_area);
-           size = LONG_SIZE;
+           bfd_put_32 (output_bfd, value, link_order->u.data.contents);
+           link_order->size = LONG_SIZE;
            break;
          case SHORT:
-           bfd_put_16 (output_bfd, value, play_area);
-           size = SHORT_SIZE;
+           bfd_put_16 (output_bfd, value, link_order->u.data.contents);
+           link_order->size = SHORT_SIZE;
            break;
          case BYTE:
-           bfd_put_8 (output_bfd, value, play_area);
-           size = BYTE_SIZE;
+           bfd_put_8 (output_bfd, value, link_order->u.data.contents);
+           link_order->size = BYTE_SIZE;
            break;
          default:
            abort ();
          }
-
-       if (! bfd_set_section_contents (output_bfd, output_section,
-                                       play_area,
-                                       statement->data_statement.output_vma,
-                                       size))
-         einfo ("%P%X: writing data failed: %E\n");
       }
       break;
 
@@ -96,7 +192,7 @@ build_link_order (statement)
 
        link_order = bfd_new_link_order (output_bfd, output_section);
        if (link_order == NULL)
-         einfo ("%P%F: bfd_new_link_order failed");
+         einfo (_("%P%F: bfd_new_link_order failed\n"));
 
        link_order->offset = rs->output_vma;
        link_order->size = bfd_get_reloc_size (rs->howto);
@@ -108,9 +204,8 @@ build_link_order (statement)
        link_order->u.reloc.p->reloc = rs->reloc;
        link_order->u.reloc.p->addend = rs->addend_value;
 
-       if (rs->section != (asection *) NULL)
+       if (rs->name == NULL)
          {
-           ASSERT (rs->name == (const char *) NULL);
            link_order->type = bfd_section_reloc_link_order;
            if (rs->section->owner == output_bfd)
              link_order->u.reloc.p->u.section = rs->section;
@@ -122,7 +217,6 @@ build_link_order (statement)
          }
        else
          {
-           ASSERT (rs->name != (const char *) NULL);
            link_order->type = bfd_symbol_reloc_link_order;
            link_order->u.reloc.p->u.name = rs->name;
          }
@@ -196,147 +290,241 @@ build_link_order (statement)
 
 /* Call BFD to write out the linked file.  */
 
-void
-ldwrite ()
-{
-  lang_for_each_statement (build_link_order);
 
-  if (! bfd_final_link (output_bfd, &link_info))
-    einfo ("%F%P: final link failed: %E\n", output_bfd);
+/**********************************************************************/
+
 
-  if (config.map_file)
+/* Wander around the input sections, make sure that
+   we'll never try and create an output section with more relocs
+   than will fit.. Do this by always assuming the worst case, and
+   creating new output sections with all the right bits */
+#define TESTIT 1
+static asection *
+clone_section (abfd, s, count)
+     bfd *abfd;
+     asection *s;
+     int *count;
+{
+#define SSIZE 8
+  char sname[SSIZE];           /* ??  find the name for this size */
+  asection *n;
+  struct bfd_link_hash_entry *h;
+  /* Invent a section name - use first five
+     chars of base section name and a digit suffix */
+  do
     {
-      print_symbol_table ();
-      lang_map ();
+      unsigned int i;
+      char b[6];
+      for (i = 0; i < sizeof (b) - 1 && s->name[i]; i++)
+       b[i] = s->name[i];
+      b[i] = 0;
+      sprintf (sname, "%s%d", b, (*count)++);
     }
+  while (bfd_get_section_by_name (abfd, sname));
+
+  n = bfd_make_section_anyway (abfd, xstrdup (sname));
+
+  /* Create a symbol of the same name */
+
+  h = bfd_link_hash_lookup (link_info.hash,
+                           sname, true, true, false);
+  h->type = bfd_link_hash_defined;
+  h->u.def.value = 0;
+  h->u.def.section = n   ;
+
+
+  n->flags = s->flags;
+  n->vma = s->vma;
+  n->user_set_vma = s->user_set_vma;
+  n->lma = s->lma;
+  n->_cooked_size = 0;
+  n->_raw_size = 0;
+  n->output_offset = s->output_offset;
+  n->output_section = n;
+  n->orelocation = 0;
+  n->reloc_count = 0;
+  n->alignment_power = s->alignment_power;
+  return n;
 }
 
-/* Print the symbol table.  */
-
-static void
-print_symbol_table ()
+#if TESTING
+static void 
+ds (s)
+     asection *s;
 {
-  fprintf (config.map_file, "**FILES**\n\n");
-  lang_for_each_file (print_file_stuff);
-
-  fprintf (config.map_file, "**GLOBAL SYMBOLS**\n\n");
-  fprintf (config.map_file, "offset    section    offset   symbol\n");
-  bfd_link_hash_traverse (link_info.hash, print_symbol, (PTR) NULL);
+  struct bfd_link_order *l = s->link_order_head;
+  printf ("vma %x size %x\n", s->vma, s->_raw_size);
+  while (l)
+    {
+      if (l->type == bfd_indirect_link_order)
+       {
+         printf ("%8x %s\n", l->offset, l->u.indirect.section->owner->filename);
+       }
+      else
+       {
+         printf (_("%8x something else\n"), l->offset);
+       }
+      l = l->next;
+    }
+  printf ("\n");
+}
+dump (s, a1, a2)
+     char *s;
+     asection *a1;
+     asection *a2;
+{
+  printf ("%s\n", s);
+  ds (a1);
+  ds (a2);
 }
 
-/* Print information about a file.  */
-
-static void
-print_file_stuff (f)
-     lang_input_statement_type * f;
+static void 
+sanity_check (abfd)
+     bfd *abfd;
 {
-  fprintf (config.map_file, "  %s\n", f->filename);
-  if (f->just_syms_flag)
+  asection *s;
+  for (s = abfd->sections; s; s = s->next)
     {
-      fprintf (config.map_file, " symbols only\n");
+      struct bfd_link_order *p;
+      bfd_vma prev = 0;
+      for (p = s->link_order_head; p; p = p->next)
+       {
+         if (p->offset > 100000)
+           abort ();
+         if (p->offset < prev)
+           abort ();
+         prev = p->offset;
+       }
     }
-  else
+}
+#else
+#define sanity_check(a)
+#define dump(a, b, c)
+#endif
+
+static void 
+split_sections (abfd, info)
+     bfd *abfd;
+     struct bfd_link_info *info;
+{
+  asection *original_sec;
+  int nsecs = abfd->section_count;
+  sanity_check (abfd);
+  /* look through all the original sections */
+  for (original_sec = abfd->sections;
+       original_sec && nsecs;
+       original_sec = original_sec->next, nsecs--)
     {
-      asection *s;
-      if (true)
+      boolean first = true;
+      int count = 0;
+      int lines = 0;
+      int relocs = 0;
+      struct bfd_link_order **pp;
+      bfd_vma vma = original_sec->vma;
+      bfd_vma shift_offset = 0;
+      asection *cursor = original_sec;
+
+      /* count up the relocations and line entries to see if
+        anything would be too big to fit */
+      for (pp = &(cursor->link_order_head); *pp; pp = &((*pp)->next))
        {
-         for (s = f->the_bfd->sections;
-              s != (asection *) NULL;
-              s = s->next)
+         struct bfd_link_order *p = *pp;
+         int thislines = 0;
+         int thisrelocs = 0;
+         if (p->type == bfd_indirect_link_order)
            {
-             print_address (s->output_offset);
-             if (s->reloc_done)
-               {
-                 fprintf (config.map_file, " %08x 2**%2ud %s\n",
-                          (unsigned) bfd_get_section_size_after_reloc (s),
-                          s->alignment_power, s->name);
-               }
+             asection *sec;
 
-             else
+             sec = p->u.indirect.section;
+
+             if (info->strip == strip_none
+                 || info->strip == strip_some)
+               thislines = sec->lineno_count;
+
+             if (info->relocateable)
+               thisrelocs = sec->reloc_count;
+
+           }
+         else if (info->relocateable
+                  && (p->type == bfd_section_reloc_link_order
+                      || p->type == bfd_symbol_reloc_link_order))
+           thisrelocs++;
+
+         if (! first
+             && (thisrelocs + relocs > config.split_by_reloc
+                 || thislines + lines > config.split_by_reloc
+                 || config.split_by_file))
+           {
+             /* create a new section and put this link order and the
+                following link orders into it */
+             struct bfd_link_order *l = p;
+             asection *n = clone_section (abfd, cursor, &count);
+             *pp = NULL;       /* Snip off link orders from old section */
+             n->link_order_head = l;   /* attach to new section */
+             pp = &n->link_order_head;
+
+             /* change the size of the original section and
+                update the vma of the new one */
+
+             dump ("before snip", cursor, n);
+
+             n->_raw_size = cursor->_raw_size - l->offset;
+             cursor->_raw_size = l->offset;
+
+             vma += cursor->_raw_size;
+             n->lma = n->vma = vma;
+
+             shift_offset = l->offset;
+
+             /* run down the chain and change the output section to
+                the right one, update the offsets too */
+
+             while (l)
                {
-                 fprintf (config.map_file, " %08x 2**%2ud %s\n",
-                          (unsigned) bfd_get_section_size_before_reloc (s),
-                          s->alignment_power, s->name);
+                 l->offset -= shift_offset;
+                 if (l->type == bfd_indirect_link_order)
+                   {
+                     l->u.indirect.section->output_section = n;
+                     l->u.indirect.section->output_offset = l->offset;
+                   }
+                 l = l->next;
                }
+             dump ("after snip", cursor, n);
+             cursor = n;
+             relocs = thisrelocs;
+             lines = thislines;
            }
-       }
-      else
-       {
-         for (s = f->the_bfd->sections;
-              s != (asection *) NULL;
-              s = s->next)
+         else
            {
-             fprintf (config.map_file, "%s ", s->name);
-             print_address (s->output_offset);
-             fprintf (config.map_file, "(%x)",
-                      (unsigned) bfd_get_section_size_after_reloc (s));
+             relocs += thisrelocs;
+             lines += thislines;
            }
-         fprintf (config.map_file, "hex \n");
+
+         first = false;
        }
     }
-  print_nl ();
+  sanity_check (abfd);
 }
-
-/* Print a symbol.  */
-
-/*ARGSUSED*/
-static boolean
-print_symbol (p, ignore)
-     struct bfd_link_hash_entry *p;
-     PTR ignore;
+/**********************************************************************/
+void
+ldwrite ()
 {
-  while (p->type == bfd_link_hash_indirect
-        || p->type == bfd_link_hash_warning)
-    p = p->u.i.link;
+  /* Reset error indicator, which can typically something like invalid
+     format from openning up the .o files */
+  bfd_set_error (bfd_error_no_error);
+  lang_for_each_statement (build_link_order);
 
-  switch (p->type) 
+  if (config.split_by_reloc || config.split_by_file)
+    split_sections (output_bfd, &link_info);
+  if (!bfd_final_link (output_bfd, &link_info))
     {
-    case bfd_link_hash_new:
-      abort ();
-
-    case bfd_link_hash_undefined:
-      fprintf (config.map_file, "undefined                     ");
-      fprintf (config.map_file, "%s ", p->root.string);
-      print_nl ();    
-      break;
+      /* If there was an error recorded, print it out.  Otherwise assume
+        an appropriate error message like unknown symbol was printed
+        out.  */
 
-    case bfd_link_hash_weak:
-      fprintf (config.map_file, "weak                          ");
-      fprintf (config.map_file, "%s ", p->root.string);
-      print_nl ();    
-      break;
-
-    case bfd_link_hash_defined:            
-      {
-       asection *defsec = p->u.def.section;
-
-       print_address (p->u.def.value);
-       if (defsec)
-         {
-           fprintf (config.map_file, "  %-10s",
-                    bfd_section_name (output_bfd, defsec));
-           print_space ();
-           print_address (p->u.def.value + defsec->vma);
-         }
-       else
-         {
-           fprintf (config.map_file, "         .......");
-         }
-       fprintf (config.map_file, " %s ", p->root.string);
-      }
-      print_nl ();    
-      break;
-
-    case bfd_link_hash_common:
-      fprintf (config.map_file, "common               ");
-      print_address (p->u.c.size);
-      fprintf (config.map_file, " %s ", p->root.string);
-      print_nl ();
-      break;
-
-    default:
-      abort ();
+      if (bfd_get_error () != bfd_error_no_error)
+       einfo (_("%F%P: final link failed: %E\n"), output_bfd);
+      else
+       xexit(1);
     }
-
-  return true;
 }
This page took 0.029848 seconds and 4 git commands to generate.