]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - ld/ldwrite.c
Update year range in copyright notice of binutils files
[thirdparty/binutils-gdb.git] / ld / ldwrite.c
index c073263709b4b6343126fc9bffa8107412fb15fd..2db0b63b79224d4205e52f25be934a6ae30fe2e6 100644 (file)
-/* Copyright (C) 1991 Free Software Foundation, Inc.
-
-This file is part of GLD, the Gnu Linker.
-
-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
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-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.  */
-
-/*
-   This module writes out the final image by reading sections from the
-   input files, relocating them and writing them out
-
-   There are two main paths through this module, one for normal
-   operation and one for partial linking.
-
-   During  normal operation, raw section data is read along with the
-   associated relocation information, the relocation info applied and
-   the section data written out on a section by section basis.
+/* ldwrite.c -- write out the linked file
+   Copyright (C) 1991-2021 Free Software Foundation, Inc.
+   Written by Steve Chamberlain sac@cygnus.com
 
-   When partially linking, all the relocation records are read to work
-   out how big the output relocation vector will be. Then raw data is
-   read, relocated and written section by section.
+   This file is part of the GNU Binutils.
 
-   Written by Steve Chamberlain sac@cygnus.com
+   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 3 of the License, or
+   (at your option) any later version.
 
-*/
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   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., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
 
-#include "bfd.h"
 #include "sysdep.h"
+#include "bfd.h"
+#include "bfdlink.h"
+#include "libiberty.h"
+#include "ctf-api.h"
+#include "safe-ctype.h"
 
 #include "ld.h"
 #include "ldexp.h"
 #include "ldlang.h"
 #include "ldwrite.h"
 #include "ldmisc.h"
-#include "ldsym.h"
-#include "ldgram.h"
+#include <ldgram.h>
 #include "ldmain.h"
-#include "relax.h"
+
+/* Build link_order structures for the BFD linker.  */
 
 static void
-read_relocs (abfd, section, symbols)
-     bfd * abfd;
-     asection * section;
-     asymbol ** symbols;
+build_link_order (lang_statement_union_type *statement)
+{
+  switch (statement->header.type)
+    {
+    case lang_data_statement_enum:
+      {
+       asection *output_section;
+       struct bfd_link_order *link_order;
+       bfd_vma value;
+
+       output_section = statement->data_statement.output_section;
+       ASSERT (output_section->owner == link_info.output_bfd);
+
+       if (!((output_section->flags & SEC_HAS_CONTENTS) != 0
+             || ((output_section->flags & SEC_LOAD) != 0
+                 && (output_section->flags & SEC_THREAD_LOCAL))))
+         break;
+
+       link_order = bfd_new_link_order (link_info.output_bfd, output_section);
+       if (link_order == NULL)
+         einfo (_("%F%P: bfd_new_link_order failed\n"));
+
+       link_order->type = bfd_data_link_order;
+       link_order->offset = statement->data_statement.output_offset;
+       link_order->u.data.contents = (bfd_byte *) xmalloc (QUAD_SIZE);
+
+       value = statement->data_statement.value;
+
+       /* By convention, the bfd_put routines for an unknown
+          endianness are big endian, so we must swap here if the
+          input is little endian.  */
+       if (!bfd_big_endian (link_info.output_bfd)
+           && !bfd_little_endian (link_info.output_bfd)
+           && !link_info.big_endian)
+         {
+           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 == link_info.output_bfd);
+       switch (statement->data_statement.type)
+         {
+         case QUAD:
+         case SQUAD:
+           if (sizeof (bfd_vma) >= QUAD_SIZE)
+             bfd_put_64 (link_info.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 (link_info.output_bfd, high,
+                           (link_order->u.data.contents
+                            + (link_info.big_endian ? 0 : 4)));
+               bfd_put_32 (link_info.output_bfd, value,
+                           (link_order->u.data.contents
+                            + (link_info.big_endian ? 4 : 0)));
+             }
+           link_order->size = QUAD_SIZE;
+           break;
+         case LONG:
+           bfd_put_32 (link_info.output_bfd, value,
+                       link_order->u.data.contents);
+           link_order->size = LONG_SIZE;
+           break;
+         case SHORT:
+           bfd_put_16 (link_info.output_bfd, value,
+                       link_order->u.data.contents);
+           link_order->size = SHORT_SIZE;
+           break;
+         case BYTE:
+           bfd_put_8 (link_info.output_bfd, value,
+                      link_order->u.data.contents);
+           link_order->size = BYTE_SIZE;
+           break;
+         default:
+           abort ();
+         }
+       link_order->u.data.size = link_order->size;
+      }
+      break;
+
+    case lang_reloc_statement_enum:
+      {
+       lang_reloc_statement_type *rs;
+       asection *output_section;
+       struct bfd_link_order *link_order;
+
+       rs = &statement->reloc_statement;
+
+       output_section = rs->output_section;
+       ASSERT (output_section->owner == link_info.output_bfd);
+
+       if (!((output_section->flags & SEC_HAS_CONTENTS) != 0
+             || ((output_section->flags & SEC_LOAD) != 0
+                 && (output_section->flags & SEC_THREAD_LOCAL))))
+         break;
+
+       link_order = bfd_new_link_order (link_info.output_bfd, output_section);
+       if (link_order == NULL)
+         einfo (_("%F%P: bfd_new_link_order failed\n"));
+
+       link_order->offset = rs->output_offset;
+       link_order->size = bfd_get_reloc_size (rs->howto);
+
+       link_order->u.reloc.p = (struct bfd_link_order_reloc *)
+         xmalloc (sizeof (struct bfd_link_order_reloc));
+
+       link_order->u.reloc.p->reloc = rs->reloc;
+       link_order->u.reloc.p->addend = rs->addend_value;
+
+       if (rs->name == NULL)
+         {
+           link_order->type = bfd_section_reloc_link_order;
+           if (rs->section->owner == link_info.output_bfd)
+             link_order->u.reloc.p->u.section = rs->section;
+           else
+             {
+               link_order->u.reloc.p->u.section = rs->section->output_section;
+               link_order->u.reloc.p->addend += rs->section->output_offset;
+             }
+         }
+       else
+         {
+           link_order->type = bfd_symbol_reloc_link_order;
+           link_order->u.reloc.p->u.name = rs->name;
+         }
+      }
+      break;
+
+    case lang_input_section_enum:
+      {
+       /* Create a new link_order in the output section with this
+          attached */
+       asection *i = statement->input_section.section;
+
+       if (i->sec_info_type != SEC_INFO_TYPE_JUST_SYMS
+           && (i->flags & SEC_EXCLUDE) == 0)
+         {
+           asection *output_section = i->output_section;
+           struct bfd_link_order *link_order;
+
+           ASSERT (output_section->owner == link_info.output_bfd);
+
+           if (!((output_section->flags & SEC_HAS_CONTENTS) != 0
+                 || ((output_section->flags & SEC_LOAD) != 0
+                     && (output_section->flags & SEC_THREAD_LOCAL))))
+             break;
+
+           link_order = bfd_new_link_order (link_info.output_bfd,
+                                            output_section);
+           if (link_order == NULL)
+             einfo (_("%F%P: bfd_new_link_order failed\n"));
+
+           if ((i->flags & SEC_NEVER_LOAD) != 0
+               && (i->flags & SEC_DEBUGGING) == 0)
+             {
+               /* We've got a never load section inside one which is
+                  going to be output, we'll change it into a fill.  */
+               link_order->type = bfd_data_link_order;
+               link_order->u.data.contents = (unsigned char *) "";
+               link_order->u.data.size = 1;
+             }
+           else
+             {
+               link_order->type = bfd_indirect_link_order;
+               link_order->u.indirect.section = i;
+               ASSERT (i->output_section == output_section);
+             }
+           link_order->size = i->size;
+           link_order->offset = i->output_offset;
+         }
+      }
+      break;
+
+    case lang_padding_statement_enum:
+      /* Make a new link_order with the right filler */
+      {
+       asection *output_section;
+       struct bfd_link_order *link_order;
+
+       output_section = statement->padding_statement.output_section;
+       ASSERT (statement->padding_statement.output_section->owner
+               == link_info.output_bfd);
+
+       if (!((output_section->flags & SEC_HAS_CONTENTS) != 0
+             || ((output_section->flags & SEC_LOAD) != 0
+                 && (output_section->flags & SEC_THREAD_LOCAL))))
+         break;
+
+       link_order = bfd_new_link_order (link_info.output_bfd,
+                                        output_section);
+       if (link_order == NULL)
+         einfo (_("%F%P: bfd_new_link_order failed\n"));
+       link_order->type = bfd_data_link_order;
+       link_order->size = statement->padding_statement.size;
+       link_order->offset = statement->padding_statement.output_offset;
+       link_order->u.data.contents = statement->padding_statement.fill->data;
+       link_order->u.data.size = statement->padding_statement.fill->size;
+      }
+      break;
+
+    default:
+      /* All the other ones fall through */
+      break;
+    }
+}
+
+/* Return true if NAME is the name of an unsplittable section. These
+   are the stabs strings, dwarf strings.  */
+
+static bfd_boolean
+unsplittable_name (const char *name)
+{
+  if (CONST_STRNEQ (name, ".stab"))
+    {
+      /* There are several stab like string sections. We pattern match on
+        ".stab...str"  */
+      unsigned len = strlen (name);
+      if (strcmp (&name[len-3], "str") == 0)
+       return TRUE;
+    }
+  else if (strcmp (name, "$GDB_STRINGS$") == 0)
+    return TRUE;
+  return FALSE;
+}
+
+/* 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 (bfd *abfd, asection *s, const char *name, int *count)
 {
-  /* Work out the output section ascociated with this input section */
-  asection *output_section = section->output_section;
+  char *tname;
+  char *sname;
+  unsigned int len;
+  asection *n;
+  struct bfd_link_hash_entry *h;
+
+  /* Invent a section name from the section name and a dotted numeric
+     suffix.   */
+  len = strlen (name);
+  tname = (char *) xmalloc (len + 1);
+  memcpy (tname, name, len + 1);
+  /* Remove a dotted number suffix, from a previous split link. */
+  while (len && ISDIGIT (tname[len-1]))
+    len--;
+  if (len > 1 && tname[len-1] == '.')
+    /* It was a dotted number. */
+    tname[len-1] = 0;
 
-  bfd_size_type reloc_size = bfd_get_reloc_upper_bound (abfd, section);
-  arelent **reloc_vector = (arelent **) ldmalloc (reloc_size);
+  /* We want to use the whole of the original section name for the
+     split name, but coff can be restricted to 8 character names.  */
+  if (bfd_family_coff (abfd) && strlen (tname) > 5)
+    {
+      /* Some section names cannot be truncated, as the name is
+        used to locate some other section.  */
+      if (CONST_STRNEQ (name, ".stab")
+         || strcmp (name, "$GDB_SYMBOLS$") == 0)
+       {
+         einfo (_ ("%F%P: cannot create split section name for %s\n"), name);
+         /* Silence gcc warnings.  einfo exits, so we never reach here.  */
+         return NULL;
+       }
+      tname[5] = 0;
+    }
 
-  if (bfd_canonicalize_reloc (abfd,
-                             section,
-                             reloc_vector,
-                             symbols))
+  if ((sname = bfd_get_unique_section_name (abfd, tname, count)) == NULL
+      || (n = bfd_make_section_anyway (abfd, sname)) == NULL
+      || (h = bfd_link_hash_lookup (link_info.hash,
+                                   sname, TRUE, TRUE, FALSE)) == NULL)
     {
-      output_section->reloc_count += section->reloc_count;
+      einfo (_("%F%P: clone section failed: %E\n"));
+      /* Silence gcc warnings.  einfo exits, so we never reach here.  */
+      return NULL;
     }
+  free (tname);
+
+  /* Set up section symbol.  */
+  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->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;
+
+  bfd_copy_private_section_data (abfd, s, abfd, n);
+
+  return n;
 }
 
+#if TESTING
+static void
+ds (asection *s)
+{
+  struct bfd_link_order *l = s->map_head.link_order;
+  printf ("vma %x size %x\n", s->vma, s->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 (char *s, asection *a1, asection *a2)
+{
+  printf ("%s\n", s);
+  ds (a1);
+  ds (a2);
+}
 
 static void
-setup_rel ()
+sanity_check (bfd *abfd)
 {
-  /*
-    Run through each section of each file and work work out the total
-    number of relocation records which will finally be in each output
-    section
-    */
-
-  LANG_FOR_EACH_INPUT_SECTION
-  (statement, abfd, section,
-   (read_relocs (abfd, section, statement->asymbols)));
-
-
-
-  /*
-    Now run though all the output sections and allocate the space for
-    all the relocations
-    */
-  LANG_FOR_EACH_OUTPUT_SECTION
-    (section,
-     (section->orelocation =
-      (arelent **) ldmalloc ((bfd_size_type) (sizeof (arelent **) *
-                                             section->reloc_count)),
-      section->reloc_count = 0));
+  asection *s;
+  for (s = abfd->sections; s; s = s->next)
+    {
+      struct bfd_link_order *p;
+      bfd_vma prev = 0;
+      for (p = s->map_head.link_order; p; p = p->next)
+       {
+         if (p->offset > 100000)
+           abort ();
+         if (p->offset < prev)
+           abort ();
+         prev = p->offset;
+       }
+    }
 }
+#else
+#define sanity_check(a)
+#define dump(a, b, c)
+#endif
 
-void
-ldwrite ()
+static void
+split_sections (bfd *abfd, struct bfd_link_info *info)
 {
-  PTR data_area = (PTR) ldmalloc (largest_section);
+  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--)
+    {
+      int count = 0;
+      unsigned int lines = 0;
+      unsigned int relocs = 0;
+      bfd_size_type sec_size = 0;
+      struct bfd_link_order *l;
+      struct bfd_link_order *p;
+      bfd_vma vma = original_sec->vma;
+      asection *cursor = original_sec;
+
+      /* Count up the relocations and line entries to see if anything
+        would be too big to fit.  Accumulate section size too.  */
+      for (l = NULL, p = cursor->map_head.link_order; p != NULL; p = l->next)
+       {
+         unsigned int thislines = 0;
+         unsigned int thisrelocs = 0;
+         bfd_size_type thissize = 0;
+         if (p->type == bfd_indirect_link_order)
+           {
+             asection *sec;
+
+             sec = p->u.indirect.section;
+
+             if (info->strip == strip_none
+                 || info->strip == strip_some)
+               thislines = sec->lineno_count;
+
+             if (bfd_link_relocatable (info))
+               thisrelocs = sec->reloc_count;
+
+             thissize = sec->size;
+
+           }
+         else if (bfd_link_relocatable (info)
+                  && (p->type == bfd_section_reloc_link_order
+                      || p->type == bfd_symbol_reloc_link_order))
+           thisrelocs++;
+
+         if (l != NULL
+             && (thisrelocs + relocs >= config.split_by_reloc
+                 || thislines + lines >= config.split_by_reloc
+                 || (thissize + sec_size >= config.split_by_file))
+             && !unsplittable_name (cursor->name))
+           {
+             /* Create a new section and put this link order and the
+                following link orders into it.  */
+             bfd_vma shift_offset;
+             asection *n;
 
-  ldsym_write ();
+             n = clone_section (abfd, cursor, original_sec->name, &count);
 
-  if (config.relocateable_output == true)
-    setup_rel ();
+             /* Attach the link orders to the new section and snip
+                them off from the old section.  */
+             n->map_head.link_order = p;
+             n->map_tail.link_order = cursor->map_tail.link_order;
+             cursor->map_tail.link_order = l;
+             l->next = NULL;
+             l = p;
 
-  write_relax (output_bfd, data_area, config.relocateable_output);
+             /* Change the size of the original section and
+                update the vma of the new one.  */
 
-  free (data_area);
+             dump ("before snip", cursor, n);
 
-  /* Output the symbol table (both globals and locals).  */
+             shift_offset = p->offset;
+             n->size = cursor->size - shift_offset;
+             cursor->size = shift_offset;
 
-  /* Print a map, if requested and possible.  */
+             vma += shift_offset;
+             n->lma = n->vma = vma;
 
-  if (config.map_file)
-  {
-    ldsym_print_symbol_table ();
-    lang_map ();
-  }
+             /* Run down the chain and change the output section to
+                the right one, update the offsets too.  */
+             do
+               {
+                 p->offset -= shift_offset;
+                 if (p->type == bfd_indirect_link_order)
+                   {
+                     p->u.indirect.section->output_section = n;
+                     p->u.indirect.section->output_offset = p->offset;
+                   }
+                 p = p->next;
+               }
+             while (p);
+
+             dump ("after snip", cursor, n);
+             cursor = n;
+             relocs = thisrelocs;
+             lines = thislines;
+             sec_size = thissize;
+           }
+         else
+           {
+             l = p;
+             relocs += thisrelocs;
+             lines += thislines;
+             sec_size += thissize;
+           }
+       }
+    }
+  sanity_check (abfd);
+}
+
+/* Call BFD to write out the linked file.  */
+
+void
+ldwrite (void)
+{
+  /* Reset error indicator, which can typically something like invalid
+     format from opening up the .o files.  */
+  bfd_set_error (bfd_error_no_error);
+  lang_clear_os_map ();
+  lang_for_each_statement (build_link_order);
+
+  if (config.split_by_reloc != (unsigned) -1
+      || config.split_by_file != (bfd_size_type) -1)
+    split_sections (link_info.output_bfd, &link_info);
+  if (!bfd_final_link (link_info.output_bfd, &link_info))
+    {
+      /* If there was an error recorded, print it out.  Otherwise assume
+        an appropriate error message like unknown symbol was printed
+        out.  */
+
+      if (bfd_get_error () != bfd_error_no_error)
+       einfo (_("%F%P: final link failed: %E\n"));
+      else
+       xexit (1);
+    }
 }