Automatic date update in version.in
[binutils-gdb.git] / ld / ldelfgen.c
index 21739b19ca1c8b40cc8fe654329b371da7bc9c05..58b37c65bc92bcb765570436a7893d04908bf40c 100644 (file)
@@ -1,5 +1,5 @@
 /* Emulation code used by all ELF targets.
-   Copyright (C) 1991-2020 Free Software Foundation, Inc.
+   Copyright (C) 1991-2022 Free Software Foundation, Inc.
 
    This file is part of the GNU Binutils.
 
 #include "ldmisc.h"
 #include "ldexp.h"
 #include "ldlang.h"
+#include "ldctor.h"
 #include "elf-bfd.h"
+#include "elf/internal.h"
 #include "ldelfgen.h"
 
+/* Info attached to an output_section_statement about input sections,
+   used when sorting SHF_LINK_ORDER sections.  */
+
+struct os_sections
+{
+  /* Size allocated for isec.  */
+  unsigned int alloc;
+  /* Used entries in isec.  */
+  unsigned int count;
+  /* How many are SHF_LINK_ORDER.  */
+  unsigned int ordered;
+  /* Input sections attached to this output section.  */
+  struct os_sections_input {
+    lang_input_section_type *is;
+    unsigned int idx;
+  } isec[1];
+};
+
+/* Add IS to data kept for OS.  */
+
+static bool
+add_link_order_input_section (lang_input_section_type *is,
+                             lang_output_section_statement_type *os)
+{
+  struct os_sections *os_info = os->data;
+  asection *s;
+
+  if (os_info == NULL)
+    {
+      os_info = xmalloc (sizeof (*os_info) + 63 * sizeof (*os_info->isec));
+      os_info->alloc = 64;
+      os_info->count = 0;
+      os_info->ordered = 0;
+      os->data = os_info;
+    }
+  if (os_info->count == os_info->alloc)
+    {
+      size_t want;
+      os_info->alloc *= 2;
+      want = sizeof (*os_info) + (os_info->alloc - 1) * sizeof (*os_info->isec);
+      os_info = xrealloc (os_info, want);
+      os->data = os_info;
+    }
+  os_info->isec[os_info->count].is = is;
+  os_info->isec[os_info->count].idx = os_info->count;
+  os_info->count++;
+  s = is->section;
+  if (bfd_get_flavour (s->owner) == bfd_target_elf_flavour
+      && (s->flags & SEC_LINKER_CREATED) == 0
+      && elf_linked_to_section (s) != NULL)
+    os_info->ordered++;
+  return false;
+}
+
+/* Run over the linker's statement list, extracting info about input
+   sections attached to each output section.  */
+
+static bool
+link_order_scan (lang_statement_union_type *u,
+                lang_output_section_statement_type *os)
+{
+  asection *s;
+  bool ret = false;
+
+  for (; u != NULL; u = u->header.next)
+    {
+      switch (u->header.type)
+       {
+       case lang_wild_statement_enum:
+         if (link_order_scan (u->wild_statement.children.head, os))
+           ret = true;
+         break;
+       case lang_constructors_statement_enum:
+         if (link_order_scan (constructor_list.head, os))
+           ret = true;
+         break;
+       case lang_output_section_statement_enum:
+         if (u->output_section_statement.constraint != -1
+             && link_order_scan (u->output_section_statement.children.head,
+                                 &u->output_section_statement))
+           ret = true;
+         break;
+       case lang_group_statement_enum:
+         if (link_order_scan (u->group_statement.children.head, os))
+           ret = true;
+         break;
+       case lang_input_section_enum:
+         s = u->input_section.section;
+         if (s->output_section != NULL
+             && s->output_section->owner == link_info.output_bfd
+             && (s->output_section->flags & SEC_EXCLUDE) == 0
+             && ((s->output_section->flags & SEC_HAS_CONTENTS) != 0
+                 || ((s->output_section->flags & (SEC_LOAD | SEC_THREAD_LOCAL))
+                     == (SEC_LOAD | SEC_THREAD_LOCAL))))
+           if (add_link_order_input_section (&u->input_section, os))
+             ret = true;
+         break;
+       default:
+         break;
+       }
+    }
+  return ret;
+}
+
+/* Compare two sections based on the locations of the sections they are
+   linked to.  Used by fixup_link_order.  */
+
+static int
+compare_link_order (const void *a, const void *b)
+{
+  const struct os_sections_input *ai = a;
+  const struct os_sections_input *bi = b;
+  asection *asec = NULL;
+  asection *bsec = NULL;
+  bfd_vma apos, bpos;
+
+  if (bfd_get_flavour (ai->is->section->owner) == bfd_target_elf_flavour)
+    asec = elf_linked_to_section (ai->is->section);
+  if (bfd_get_flavour (bi->is->section->owner) == bfd_target_elf_flavour)
+    bsec = elf_linked_to_section (bi->is->section);
+
+  /* Place unordered sections before ordered sections.  */
+  if (asec == NULL || bsec == NULL)
+    {
+      if (bsec != NULL)
+       return -1;
+      else if (asec != NULL)
+       return 1;
+      return ai->idx - bi->idx;
+    }
+
+  apos = asec->output_section->lma + asec->output_offset;
+  bpos = bsec->output_section->lma + bsec->output_offset;
+
+  if (apos < bpos)
+    return -1;
+  else if (apos > bpos)
+    return 1;
+
+  if (! bfd_link_relocatable (&link_info))
+    {
+      /* The only way we should get matching LMAs is when the first of
+        the two sections has zero size, or asec and bsec are the
+        same section.  */
+      if (asec->size < bsec->size)
+       return -1;
+      else if (asec->size > bsec->size)
+       return 1;
+    }
+
+  /* If they are both zero size then they almost certainly have the same
+     VMA and thus are not ordered with respect to each other.  Test VMA
+     anyway, and fall back to idx to make the result reproducible across
+     qsort implementations.  */
+  apos = asec->output_section->vma + asec->output_offset;
+  bpos = bsec->output_section->vma + bsec->output_offset;
+  if (apos < bpos)
+    return -1;
+  else if (apos > bpos)
+    return 1;
+  else
+    return ai->idx - bi->idx;
+}
+
+/* Rearrange sections with SHF_LINK_ORDER into the same order as their
+   linked sections.  */
+
+static bool
+fixup_link_order (lang_output_section_statement_type *os)
+{
+  struct os_sections *os_info = os->data;
+  unsigned int i, j;
+  lang_input_section_type **orig_is;
+  asection **save_s;
+
+  for (i = 0; i < os_info->count; i = j)
+    {
+      /* Normally a linker script will select SHF_LINK_ORDER sections
+        with an input section wildcard something like the following:
+        *(.IA_64.unwind* .gnu.linkonce.ia64unw.*)
+        However if some other random sections are smashed into an
+        output section, or if SHF_LINK_ORDER are split up by the
+        linker script, then we only want to sort sections matching a
+        given wildcard.  That's the purpose of the pattern test.  */
+      for (j = i + 1; j < os_info->count; j++)
+       if (os_info->isec[j].is->pattern != os_info->isec[i].is->pattern)
+         break;
+      if (j - i > 1)
+       qsort (&os_info->isec[i], j - i, sizeof (*os_info->isec),
+              compare_link_order);
+    }
+  for (i = 0; i < os_info->count; i++)
+    if (os_info->isec[i].idx != i)
+      break;
+  if (i == os_info->count)
+    return false;
+
+  /* Now reorder the linker input section statements to reflect the
+     proper sorting.  The is done by rewriting the existing statements
+     rather than fiddling with lists, since the only thing we need to
+     change is the bfd section pointer.  */
+  orig_is = xmalloc (os_info->count * sizeof (*orig_is));
+  save_s = xmalloc (os_info->count * sizeof (*save_s));
+  for (i = 0; i < os_info->count; i++)
+    {
+      orig_is[os_info->isec[i].idx] = os_info->isec[i].is;
+      save_s[i] = os_info->isec[i].is->section;
+    }
+  for (i = 0; i < os_info->count; i++)
+    if (os_info->isec[i].idx != i)
+      {
+       orig_is[i]->section = save_s[i];
+       /* Restore os_info to pristine state before the qsort, for the
+          next pass over sections.  */
+       os_info->isec[i].is = orig_is[i];
+       os_info->isec[i].idx = i;
+      }
+  free (save_s);
+  free (orig_is);
+  return true;
+}
+
 void
-ldelf_map_segments (bfd_boolean need_layout)
+ldelf_map_segments (bool need_layout)
 {
   int tries = 10;
+  static bool done_link_order_scan = false;
 
   do
     {
       lang_relax_sections (need_layout);
-      need_layout = FALSE;
+      need_layout = false;
 
-      if (link_info.output_bfd->xvec->flavour == bfd_target_elf_flavour
+      if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour)
+       {
+         lang_output_section_statement_type *os;
+         if (!done_link_order_scan)
+           {
+             link_order_scan (statement_list.head, NULL);
+             done_link_order_scan = true;
+           }
+         for (os = (void *) lang_os_list.head; os != NULL; os = os->next)
+           {
+             struct os_sections *os_info = os->data;
+             if (os_info != NULL && os_info->ordered != 0)
+               {
+                 if (os_info->ordered != os_info->count
+                     && bfd_link_relocatable (&link_info))
+                   {
+                     einfo (_("%F%P: "
+                              "%pA has both ordered and unordered sections\n"),
+                            os->bfd_section);
+                     return;
+                   }
+                 if (os_info->count > 1
+                     && fixup_link_order (os))
+                   need_layout = true;
+               }
+           }
+       }
+
+      if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour
          && !bfd_link_relocatable (&link_info))
        {
          bfd_size_type phdr_size;
@@ -51,7 +304,8 @@ ldelf_map_segments (bfd_boolean need_layout)
          if (lang_phdr_list == NULL)
            elf_seg_map (link_info.output_bfd) = NULL;
          if (!_bfd_elf_map_sections_to_segments (link_info.output_bfd,
-                                                 &link_info))
+                                                 &link_info,
+                                                 &need_layout))
            einfo (_("%F%P: map sections to segments failed: %E\n"));
 
          if (phdr_size != elf_program_header_size (link_info.output_bfd))
@@ -59,11 +313,11 @@ ldelf_map_segments (bfd_boolean need_layout)
              if (tries > 6)
                /* The first few times we allow any change to
                   phdr_size .  */
-               need_layout = TRUE;
+               need_layout = true;
              else if (phdr_size
                       < elf_program_header_size (link_info.output_bfd))
                /* After that we only allow the size to grow.  */
-               need_layout = TRUE;
+               need_layout = true;
              else
                elf_program_header_size (link_info.output_bfd) = phdr_size;
            }
@@ -72,9 +326,23 @@ ldelf_map_segments (bfd_boolean need_layout)
   while (need_layout && --tries);
 
   if (tries == 0)
-    einfo (_("%F%P: looping in map_segments"));
+    einfo (_("%F%P: looping in map_segments\n"));
+
+  if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour
+      && lang_phdr_list == NULL)
+    {
+      /* If we don't have user supplied phdrs, strip zero-sized dynamic
+        sections and regenerate program headers.  */
+      const struct elf_backend_data *bed
+       = get_elf_backend_data (link_info.output_bfd);
+      if (bed->elf_backend_strip_zero_sized_dynamic_sections
+         && !bed->elf_backend_strip_zero_sized_dynamic_sections
+               (&link_info))
+         einfo (_("%F%P: failed to strip zero-sized dynamic sections\n"));
+    }
 }
 
+#ifdef ENABLE_LIBCTF
 /* We want to emit CTF early if and only if we are not targetting ELF with this
    invocation.  */
 
@@ -89,11 +357,9 @@ ldelf_emit_ctf_early (void)
 /* Callbacks used to map from bfd types to libctf types, under libctf's
    control.  */
 
-struct ctf_strsym_iter_cb_arg
+struct ctf_strtab_iter_cb_arg
 {
-  struct elf_sym_strtab *syms;
-  bfd_size_type symcount;
-  struct elf_strtab_hash *symstrtab;
+  struct elf_strtab_hash *strtab;
   size_t next_i;
   size_t next_idx;
 };
@@ -107,20 +373,27 @@ ldelf_ctf_strtab_iter_cb (uint32_t *offset, void *arg_)
   bfd_size_type off;
   const char *ret;
 
-  struct ctf_strsym_iter_cb_arg *arg =
-    (struct ctf_strsym_iter_cb_arg *) arg_;
+  struct ctf_strtab_iter_cb_arg *arg =
+    (struct ctf_strtab_iter_cb_arg *) arg_;
 
   /* There is no zeroth string.  */
   if (arg->next_i == 0)
     arg->next_i = 1;
 
-  if (arg->next_i >= _bfd_elf_strtab_len (arg->symstrtab))
+  /* Hunt through strings until we fall off the end or find one with
+     a nonzero refcount.  */
+  do
     {
-      arg->next_i = 0;
-      return NULL;
+      if (arg->next_i >= _bfd_elf_strtab_len (arg->strtab))
+       {
+         arg->next_i = 0;
+         return NULL;
+       }
+
+      ret = _bfd_elf_strtab_str (arg->strtab, arg->next_i++, &off);
     }
+  while (ret == NULL);
 
-  ret = _bfd_elf_strtab_str (arg->symstrtab, arg->next_i++, &off);
   *offset = off;
 
   /* If we've overflowed, we cannot share any further strings: the CTF
@@ -131,56 +404,74 @@ ldelf_ctf_strtab_iter_cb (uint32_t *offset, void *arg_)
   return ret;
 }
 
-/* Return symbols from the symbol table to libctf, one by one.  We assume (and
-   assert) that the symbols in the elf_link_hash_table are in strictly ascending
-   order, and that none will be added in between existing ones.  Returns NULL
-   when iteration is complete.  */
-
-static struct ctf_link_sym *
-ldelf_ctf_symbols_iter_cb (struct ctf_link_sym *dest,
-                                          void *arg_)
+void
+ldelf_acquire_strings_for_ctf
+  (struct ctf_dict *ctf_output, struct elf_strtab_hash *strtab)
 {
-  struct ctf_strsym_iter_cb_arg *arg =
-    (struct ctf_strsym_iter_cb_arg *) arg_;
+  struct ctf_strtab_iter_cb_arg args = { strtab, 0, 0 };
+  if (!ctf_output)
+    return;
 
-  if (arg->next_i > arg->symcount)
+  if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour)
     {
-      arg->next_i = 0;
-      arg->next_idx = 0;
-      return NULL;
+      if (ctf_link_add_strtab (ctf_output, ldelf_ctf_strtab_iter_cb,
+                              &args) < 0)
+       einfo (_("%F%P: warning: CTF strtab association failed; strings will "
+                "not be shared: %s\n"),
+              ctf_errmsg (ctf_errno (ctf_output)));
     }
-
-  ASSERT (arg->syms[arg->next_i].dest_index == arg->next_idx);
-  dest->st_name = _bfd_elf_strtab_str (arg->symstrtab, arg->next_i, NULL);
-  dest->st_shndx = arg->syms[arg->next_i].sym.st_shndx;
-  dest->st_type = ELF_ST_TYPE (arg->syms[arg->next_i].sym.st_info);
-  dest->st_value = arg->syms[arg->next_i].sym.st_value;
-  arg->next_i++;
-  return dest;
 }
 
 void
-ldelf_examine_strtab_for_ctf
-  (struct ctf_file *ctf_output, struct elf_sym_strtab *syms,
-   bfd_size_type symcount, struct elf_strtab_hash *symstrtab)
+ldelf_new_dynsym_for_ctf (struct ctf_dict *ctf_output, int symidx,
+                         struct elf_internal_sym *sym)
 {
-  struct ctf_strsym_iter_cb_arg args = { syms, symcount, symstrtab,
-                                         0, 0 };
-   if (!ctf_output)
+  ctf_link_sym_t lsym;
+
+  if (!ctf_output)
      return;
 
-   if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour
-       && !bfd_link_relocatable (&link_info))
+  /* New symbol.  */
+  if (sym != NULL)
     {
-      if (ctf_link_add_strtab (ctf_output, ldelf_ctf_strtab_iter_cb,
-                              &args) < 0)
-       einfo (_("%F%P: warning: CTF strtab association failed; strings will "
-                "not be shared: %s\n"),
-              ctf_errmsg (ctf_errno (ctf_output)));
+      lsym.st_name = NULL;
+      lsym.st_nameidx = sym->st_name;
+      lsym.st_nameidx_set = 1;
+      lsym.st_symidx = symidx;
+      lsym.st_shndx = sym->st_shndx;
+      lsym.st_type = ELF_ST_TYPE (sym->st_info);
+      lsym.st_value = sym->st_value;
+      if (ctf_link_add_linker_symbol (ctf_output, &lsym) < 0)
+       {
+         einfo (_("%F%P: warning: CTF symbol addition failed; CTF will "
+                  "not be tied to symbols: %s\n"),
+                ctf_errmsg (ctf_errno (ctf_output)));
+       }
+    }
+  else
+    {
+      /* Shuffle all the symbols.  */
 
-      if (ctf_link_shuffle_syms (ctf_output, ldelf_ctf_symbols_iter_cb,
-                                &args) < 0)
-       einfo (_("%F%P: warning: CTF symbol shuffling failed; slight space "
-                "cost: %s\n"), ctf_errmsg (ctf_errno (ctf_output)));
+      if (ctf_link_shuffle_syms (ctf_output) < 0)
+       einfo (_("%F%P: warning: CTF symbol shuffling failed; CTF will "
+                "not be tied to symbols: %s\n"),
+              ctf_errmsg (ctf_errno (ctf_output)));
     }
 }
+#else
+int
+ldelf_emit_ctf_early (void)
+{
+  return 0;
+}
+
+void
+ldelf_acquire_strings_for_ctf (struct ctf_dict *ctf_output ATTRIBUTE_UNUSED,
+                              struct elf_strtab_hash *strtab ATTRIBUTE_UNUSED)
+{}
+void
+ldelf_new_dynsym_for_ctf (struct ctf_dict *ctf_output ATTRIBUTE_UNUSED,
+                         int symidx ATTRIBUTE_UNUSED,
+                         struct elf_internal_sym *sym ATTRIBUTE_UNUSED)
+{}
+#endif