]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - bfd/elflink.c
Fix: A potential bug of null pointer dereference
[thirdparty/binutils-gdb.git] / bfd / elflink.c
index 1613bc748195cdf188fecd9139c0199fc34e701f..99f4cdd5527139f3af9f75e5e932b05fd0986046 100644 (file)
@@ -1,5 +1,5 @@
 /* ELF linking support for BFD.
-   Copyright (C) 1995-2022 Free Software Foundation, Inc.
+   Copyright (C) 1995-2023 Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
@@ -1090,6 +1090,7 @@ _bfd_elf_merge_symbol (bfd *abfd,
   const struct elf_backend_data *bed;
   char *new_version;
   bool default_sym = *matched;
+  struct elf_link_hash_table *htab;
 
   *skip = false;
   *override = NULL;
@@ -1220,6 +1221,8 @@ _bfd_elf_merge_symbol (bfd *abfd,
      symbols.  */
   bfd_elf_link_mark_dynamic_symbol (info, h, sym);
 
+  htab = elf_hash_table (info);
+
   /* NEWDYN and OLDDYN indicate whether the new or old symbol,
      respectively, is from a dynamic object.  */
 
@@ -1283,7 +1286,9 @@ _bfd_elf_merge_symbol (bfd *abfd,
       olddyn = (oldsec->symbol->flags & BSF_DYNAMIC) != 0;
     }
 
-  if (oldbfd != NULL
+  /* Set non_ir_ref_dynamic only when not handling DT_NEEDED entries.  */
+  if (!htab->handling_dt_needed
+      && oldbfd != NULL
       && (oldbfd->flags & BFD_PLUGIN) != (abfd->flags & BFD_PLUGIN))
     {
       if (newdyn != olddyn)
@@ -1294,9 +1299,8 @@ _bfd_elf_merge_symbol (bfd *abfd,
          h->root.non_ir_ref_dynamic = true;
          hi->root.non_ir_ref_dynamic = true;
        }
-
-      if ((oldbfd->flags & BFD_PLUGIN) != 0
-         && hi->root.type == bfd_link_hash_indirect)
+      else if ((oldbfd->flags & BFD_PLUGIN) != 0
+              && hi->root.type == bfd_link_hash_indirect)
        {
          /* Change indirect symbol from IR to undefined.  */
          hi->root.type = bfd_link_hash_undefined;
@@ -2213,6 +2217,85 @@ _bfd_elf_export_symbol (struct elf_link_hash_entry *h, void *data)
   return true;
 }
 \f
+/* Return true if GLIBC_ABI_DT_RELR is added to the list of version
+   dependencies successfully.  GLIBC_ABI_DT_RELR will be put into the
+   .gnu.version_r section.  */
+
+static bool
+elf_link_add_dt_relr_dependency (struct elf_find_verdep_info *rinfo)
+{
+  bfd *glibc_bfd = NULL;
+  Elf_Internal_Verneed *t;
+  Elf_Internal_Vernaux *a;
+  size_t amt;
+  const char *relr = "GLIBC_ABI_DT_RELR";
+
+  /* See if we already know about GLIBC_PRIVATE_DT_RELR.  */
+  for (t = elf_tdata (rinfo->info->output_bfd)->verref;
+       t != NULL;
+       t = t->vn_nextref)
+    {
+      const char *soname = bfd_elf_get_dt_soname (t->vn_bfd);
+      /* Skip the shared library if it isn't libc.so.  */
+      if (!soname || !startswith (soname, "libc.so."))
+       continue;
+
+      for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr)
+       {
+         /* Return if GLIBC_PRIVATE_DT_RELR dependency has been
+            added.  */
+         if (a->vna_nodename == relr
+             || strcmp (a->vna_nodename, relr) == 0)
+           return true;
+
+         /* Check if libc.so provides GLIBC_2.XX version.  */
+         if (!glibc_bfd && startswith (a->vna_nodename, "GLIBC_2."))
+           glibc_bfd = t->vn_bfd;
+       }
+
+      break;
+    }
+
+  /* Skip if it isn't linked against glibc.  */
+  if (glibc_bfd == NULL)
+    return true;
+
+  /* This is a new version.  Add it to tree we are building.  */
+  if (t == NULL)
+    {
+      amt = sizeof *t;
+      t = (Elf_Internal_Verneed *) bfd_zalloc (rinfo->info->output_bfd,
+                                              amt);
+      if (t == NULL)
+       {
+         rinfo->failed = true;
+         return false;
+       }
+
+      t->vn_bfd = glibc_bfd;
+      t->vn_nextref = elf_tdata (rinfo->info->output_bfd)->verref;
+      elf_tdata (rinfo->info->output_bfd)->verref = t;
+    }
+
+  amt = sizeof *a;
+  a = (Elf_Internal_Vernaux *) bfd_zalloc (rinfo->info->output_bfd, amt);
+  if (a == NULL)
+    {
+      rinfo->failed = true;
+      return false;
+    }
+
+  a->vna_nodename = relr;
+  a->vna_flags = 0;
+  a->vna_nextptr = t->vn_auxptr;
+  a->vna_other = rinfo->vers + 1;
+  ++rinfo->vers;
+
+  t->vn_auxptr = a;
+
+  return true;
+}
+
 /* Look through the symbols which are defined in other shared
    libraries and referenced here.  Update the list of version
    dependencies.  This will be put into the .gnu.version_r section.
@@ -2568,7 +2651,7 @@ elf_link_read_relocs_from_section (bfd *abfd,
     return false;
 
   /* Read the relocations.  */
-  if (bfd_bread (external_relocs, shdr->sh_size, abfd) != shdr->sh_size)
+  if (bfd_read (external_relocs, shdr->sh_size, abfd) != shdr->sh_size)
     return false;
 
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
@@ -3503,10 +3586,19 @@ elf_link_is_defined_archive_symbol (bfd * abfd, carsym * symdef)
       abfd = abfd->plugin_dummy_bfd;
       hdr = &elf_tdata (abfd)->symtab_hdr;
     }
-  else if ((abfd->flags & DYNAMIC) == 0 || elf_dynsymtab (abfd) == 0)
-    hdr = &elf_tdata (abfd)->symtab_hdr;
   else
-    hdr = &elf_tdata (abfd)->dynsymtab_hdr;
+    {
+      if (elf_use_dt_symtab_p (abfd))
+       {
+         bfd_set_error (bfd_error_wrong_format);
+         return false;
+       }
+
+      if ((abfd->flags & DYNAMIC) == 0 || elf_dynsymtab (abfd) == 0)
+       hdr = &elf_tdata (abfd)->symtab_hdr;
+      else
+       hdr = &elf_tdata (abfd)->dynsymtab_hdr;
+    }
 
   symcount = hdr->sh_size / get_elf_backend_data (abfd)->s->sizeof_sym;
 
@@ -4150,6 +4242,12 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
   htab = elf_hash_table (info);
   bed = get_elf_backend_data (abfd);
 
+  if (elf_use_dt_symtab_p (abfd))
+    {
+      bfd_set_error (bfd_error_wrong_format);
+      return false;
+    }
+
   if ((abfd->flags & DYNAMIC) == 0)
     dynamic = false;
   else
@@ -4303,7 +4401,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
                       | DYN_NO_NEEDED)) == 0;
 
       s = bfd_get_section_by_name (abfd, ".dynamic");
-      if (s != NULL && s->size != 0)
+      if (s != NULL && s->size != 0 && (s->flags & SEC_HAS_CONTENTS) != 0)
        {
          bfd_byte *dynbuf;
          bfd_byte *extdyn;
@@ -4323,7 +4421,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
          shlink = elf_elfsections (abfd)[elfsec]->sh_link;
 
          for (extdyn = dynbuf;
-              extdyn <= dynbuf + s->size - bed->s->sizeof_dyn;
+              (size_t) (dynbuf + s->size - extdyn) >= bed->s->sizeof_dyn;
               extdyn += bed->s->sizeof_dyn)
            {
              Elf_Internal_Dyn dyn;
@@ -5219,10 +5317,14 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
                  else
                    _bfd_error_handler
                      /* xgettext:c-format */
-                     (_("warning: alignment %u of symbol `%s' in %pB"
-                        " is smaller than %u in %pB"),
+                     (_("warning: alignment %u of normal symbol `%s' in %pB"
+                        " is smaller than %u used by the common definition in %pB"),
                       1 << normal_align, name, normal_bfd,
                       1 << common_align, common_bfd);
+
+                 /* PR 30499: make sure that users understand that this warning is serious.  */
+                 _bfd_error_handler
+                   (_("warning: NOTE: alignment discrepancies can cause real problems.  Investigation is advised."));
                }
            }
 
@@ -5234,12 +5336,18 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
              if (h->size != 0
                  && h->size != isym->st_size
                  && ! size_change_ok)
-               _bfd_error_handler
-                 /* xgettext:c-format */
-                 (_("warning: size of symbol `%s' changed"
-                    " from %" PRIu64 " in %pB to %" PRIu64 " in %pB"),
-                  name, (uint64_t) h->size, old_bfd,
-                  (uint64_t) isym->st_size, abfd);
+               {
+                 _bfd_error_handler
+                   /* xgettext:c-format */
+                   (_("warning: size of symbol `%s' changed"
+                      " from %" PRIu64 " in %pB to %" PRIu64 " in %pB"),
+                    name, (uint64_t) h->size, old_bfd,
+                    (uint64_t) isym->st_size, abfd);
+
+                 /* PR 30499: make sure that users understand that this warning is serious.  */
+                 _bfd_error_handler
+                   (_("warning: NOTE: size discrepancies can cause real problems.  Investigation is advised."));
+               }
 
              h->size = isym->st_size;
            }
@@ -5299,7 +5407,14 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
              h->unique_global = (flags & BSF_GNU_UNIQUE) != 0;
            }
 
-         if (definition && !dynamic)
+         /* Don't add indirect symbols for .symver x, x@FOO aliases
+            in IR.  Since all data or text symbols in IR have the
+            same type, value and section, we can't tell if a symbol
+            is an alias of another symbol by their types, values and
+            sections.  */
+         if (definition
+             && !dynamic
+             && (abfd->flags & BFD_PLUGIN) == 0)
            {
              char *p = strchr (name, ELF_VER_CHR);
              if (p != NULL && p[1] != ELF_VER_CHR)
@@ -6271,15 +6386,11 @@ compute_bucket_count (struct bfd_link_info *info ATTRIBUTE_UNUSED,
   size_t best_size = 0;
   unsigned long int i;
 
-  /* We have a problem here.  The following code to optimize the table
-     size requires an integer type with more the 32 bits.  If
-     BFD_HOST_U_64_BIT is set we know about such a type.  */
-#ifdef BFD_HOST_U_64_BIT
   if (info->optimize)
     {
       size_t minsize;
       size_t maxsize;
-      BFD_HOST_U_64_BIT best_chlen = ~((BFD_HOST_U_64_BIT) 0);
+      uint64_t best_chlen = ~((uint64_t) 0);
       bfd *dynobj = elf_hash_table (info)->dynobj;
       size_t dynsymcount = elf_hash_table (info)->dynsymcount;
       const struct elf_backend_data *bed = get_elf_backend_data (dynobj);
@@ -6316,7 +6427,7 @@ compute_bucket_count (struct bfd_link_info *info ATTRIBUTE_UNUSED,
       for (i = minsize; i < maxsize; ++i)
        {
          /* Walk through the array of hashcodes and count the collisions.  */
-         BFD_HOST_U_64_BIT max;
+         uint64_t max;
          unsigned long int j;
          unsigned long int fact;
 
@@ -6381,11 +6492,7 @@ compute_bucket_count (struct bfd_link_info *info ATTRIBUTE_UNUSED,
       free (counts);
     }
   else
-#endif /* defined (BFD_HOST_U_64_BIT) */
     {
-      /* This is the fallback solution if no 64bit type is available or if we
-        are not supposed to spend much time on optimizations.  We select the
-        bucket count using a fixed set of numbers.  */
       for (i = 0; elf_buckets[i] != 0; i++)
        {
          best_size = elf_buckets[i];
@@ -6940,6 +7047,13 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
       if (sinfo.failed)
        return false;
 
+      if (info->enable_dt_relr)
+       {
+         elf_link_add_dt_relr_dependency (&sinfo);
+         if (sinfo.failed)
+           return false;
+       }
+
       if (elf_tdata (output_bfd)->verref == NULL)
        s->flags |= SEC_EXCLUDE;
       else
@@ -7034,13 +7148,23 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
   /* Determine any GNU_STACK segment requirements, after the backend
      has had a chance to set a default segment size.  */
   if (info->execstack)
-    elf_stack_flags (output_bfd) = PF_R | PF_W | PF_X;
+    {
+      /* If the user has explicitly requested warnings, then generate one even
+        though the choice is the result of another command line option.  */
+      if (info->warn_execstack == 1)
+       _bfd_error_handler
+         (_("\
+warning: enabling an executable stack because of -z execstack command line option"));
+      elf_stack_flags (output_bfd) = PF_R | PF_W | PF_X;
+    }
   else if (info->noexecstack)
     elf_stack_flags (output_bfd) = PF_R | PF_W;
   else
     {
       bfd *inputobj;
       asection *notesec = NULL;
+      bfd *noteobj = NULL;
+      bfd *emptyobj = NULL;
       int exec = 0;
 
       for (inputobj = info->input_bfds;
@@ -7059,15 +7183,49 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
          s = bfd_get_section_by_name (inputobj, ".note.GNU-stack");
          if (s)
            {
-             if (s->flags & SEC_CODE)
-               exec = PF_X;
              notesec = s;
+             if (s->flags & SEC_CODE)
+               {
+                 noteobj = inputobj;
+                 exec = PF_X;
+                 /* There is no point in scanning the remaining bfds.  */
+                 break;
+               }
+           }
+         else if (bed->default_execstack && info->default_execstack)
+           {
+             exec = PF_X;
+             emptyobj = inputobj;
            }
-         else if (bed->default_execstack)
-           exec = PF_X;
        }
+
       if (notesec || info->stacksize > 0)
-       elf_stack_flags (output_bfd) = PF_R | PF_W | exec;
+       {
+         if (exec)
+           {
+             if (info->warn_execstack != 0)
+               {
+                 /* PR 29072: Because an executable stack is a serious
+                    security risk, make sure that the user knows that it is
+                    being enabled despite the fact that it was not requested
+                    on the command line.  */
+                 if (noteobj)
+                   _bfd_error_handler (_("\
+warning: %s: requires executable stack (because the .note.GNU-stack section is executable)"),
+                      bfd_get_filename (noteobj));
+                 else if (emptyobj)
+                   {
+                     _bfd_error_handler (_("\
+warning: %s: missing .note.GNU-stack section implies executable stack"),
+                                         bfd_get_filename (emptyobj));
+                     _bfd_error_handler (_("\
+NOTE: This behaviour is deprecated and will be removed in a future version of the linker"));
+                   }
+               }
+           }
+         elf_stack_flags (output_bfd) = PF_R | PF_W | exec;
+       }
+
       if (notesec && exec && bfd_link_relocatable (info)
          && notesec->output_section != bfd_abs_section_ptr)
        notesec->output_section->flags |= SEC_CODE;
@@ -8078,7 +8236,7 @@ bfd_elf_get_bfd_needed_list (bfd *abfd,
     return true;
 
   s = bfd_get_section_by_name (abfd, ".dynamic");
-  if (s == NULL || s->size == 0)
+  if (s == NULL || s->size == 0 || (s->flags & SEC_HAS_CONTENTS) == 0)
     return true;
 
   if (!bfd_malloc_and_get_section (abfd, s, &dynbuf))
@@ -8093,9 +8251,9 @@ bfd_elf_get_bfd_needed_list (bfd *abfd,
   extdynsize = get_elf_backend_data (abfd)->s->sizeof_dyn;
   swap_dyn_in = get_elf_backend_data (abfd)->s->swap_dyn_in;
 
-  extdyn = dynbuf;
-  extdynend = extdyn + s->size;
-  for (; extdyn < extdynend; extdyn += extdynsize)
+  for (extdyn = dynbuf, extdynend = dynbuf + s->size;
+       (size_t) (extdynend - extdyn) >= extdynsize;
+       extdyn += extdynsize)
     {
       Elf_Internal_Dyn dyn;
 
@@ -8246,8 +8404,7 @@ elf_create_symbuf (size_t symcount, Elf_Internal_Sym *isymbuf)
       ssymhead->count++;
     }
   BFD_ASSERT ((size_t) (ssymhead - ssymbuf) == shndx_count
-             && (((bfd_hostptr_t) ssym - (bfd_hostptr_t) ssymbuf)
-                 == total_size));
+             && (uintptr_t) ssym - (uintptr_t) ssymbuf == total_size);
 
   free (indbuf);
   return ssymbuf;
@@ -9220,7 +9377,6 @@ ext32b_r_offset (const void *p)
   return aval;
 }
 
-#ifdef BFD_HOST_64_BIT
 static bfd_vma
 ext64l_r_offset (const void *p)
 {
@@ -9264,7 +9420,6 @@ ext64b_r_offset (const void *p)
                   | (uint64_t) a->c[7]);
   return aval;
 }
-#endif
 
 /* When performing a relocatable link, the input relocations are
    preserved.  But, if they reference global symbols, the indices
@@ -9368,13 +9523,11 @@ elf_link_adjust_relocs (bfd *abfd,
        }
       else
        {
-#ifdef BFD_HOST_64_BIT
          if (abfd->xvec->header_byteorder == BFD_ENDIAN_LITTLE)
            ext_r_off = ext64l_r_offset;
          else if (abfd->xvec->header_byteorder == BFD_ENDIAN_BIG)
            ext_r_off = ext64b_r_offset;
          else
-#endif
            abort ();
        }
 
@@ -9427,12 +9580,20 @@ elf_link_adjust_relocs (bfd *abfd,
              size_t sortlen = p - loc;
              bfd_vma r_off2 = (*ext_r_off) (loc);
              size_t runlen = elt_size;
+             bfd_vma r_off_runend = r_off;
+             bfd_vma r_off_runend_next;
              size_t buf_size = 96 * 1024;
              while (p + runlen < end
                     && (sortlen <= buf_size
                         || runlen + elt_size <= buf_size)
-                    && r_off2 > (*ext_r_off) (p + runlen))
-               runlen += elt_size;
+                    /* run must not break the ordering of base..loc+1 */
+                    && r_off2 > (r_off_runend_next = (*ext_r_off) (p + runlen))
+                    /* run must be already sorted */
+                    && r_off_runend_next >= r_off_runend)
+               {
+                 runlen += elt_size;
+                 r_off_runend = r_off_runend_next;
+               }
              if (buf == NULL)
                {
                  buf = bfd_malloc (buf_size);
@@ -9866,9 +10027,7 @@ elf_link_output_symstrtab (void *finf,
   if (ELF_ST_BIND (elfsym->st_info) == STB_GNU_UNIQUE)
     elf_tdata (flinfo->output_bfd)->has_gnu_osabi |= elf_gnu_osabi_unique;
 
-  if (name == NULL
-      || *name == '\0'
-      || (input_sec->flags & SEC_EXCLUDE))
+  if (name == NULL || *name == '\0')
     elfsym->st_name = (unsigned long) -1;
   else
     {
@@ -10032,7 +10191,7 @@ elf_link_swap_symbols_out (struct elf_final_link_info *flinfo)
   pos = hdr->sh_offset + hdr->sh_size;
   amt = bed->s->sizeof_sym * flinfo->output_bfd->symcount;
   if (bfd_seek (flinfo->output_bfd, pos, SEEK_SET) == 0
-      && bfd_bwrite (symbuf, amt, flinfo->output_bfd) == amt)
+      && bfd_write (symbuf, amt, flinfo->output_bfd) == amt)
     {
       hdr->sh_size += amt;
       ret = true;
@@ -10770,6 +10929,7 @@ elf_section_ignore_discarded_relocs (asection *sec)
     case SEC_INFO_TYPE_STABS:
     case SEC_INFO_TYPE_EH_FRAME:
     case SEC_INFO_TYPE_EH_FRAME_ENTRY:
+    case SEC_INFO_TYPE_SFRAME:
       return true;
     default:
       break;
@@ -10795,12 +10955,22 @@ elf_section_ignore_discarded_relocs (asection *sec)
 unsigned int
 _bfd_elf_default_action_discarded (asection *sec)
 {
+  const struct elf_backend_data *bed;
+  bed = get_elf_backend_data (sec->owner);
+
   if (sec->flags & SEC_DEBUGGING)
     return PRETEND;
 
   if (strcmp (".eh_frame", sec->name) == 0)
     return 0;
 
+  if (bed->elf_backend_can_make_multiple_eh_frame
+      && strncmp (sec->name, ".eh_frame.", 10) == 0)
+    return 0;
+
+  if (strcmp (".sframe", sec->name) == 0)
+    return 0;
+
   if (strcmp (".gcc_except_table", sec->name) == 0)
     return 0;
 
@@ -11013,22 +11183,10 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
 
       /* If this symbol is defined in a section which we are
         discarding, we don't need to keep it.  */
-      if (isym->st_shndx != SHN_UNDEF
-         && isym->st_shndx < SHN_LORESERVE
-         && isec->output_section == NULL
-         && flinfo->info->non_contiguous_regions
-         && flinfo->info->non_contiguous_regions_warnings)
-       {
-         _bfd_error_handler (_("warning: --enable-non-contiguous-regions "
-                               "discards section `%s' from '%s'\n"),
-                             isec->name, bfd_get_filename (isec->owner));
-         continue;
-       }
-
-      if (isym->st_shndx != SHN_UNDEF
-         && isym->st_shndx < SHN_LORESERVE
-         && bfd_section_removed_from_list (output_bfd,
-                                           isec->output_section))
+      if (isym->st_shndx < SHN_LORESERVE
+         && (isec->output_section == NULL
+             || bfd_section_removed_from_list (output_bfd,
+                                               isec->output_section)))
        continue;
 
       /* Get the name of the symbol.  */
@@ -11235,6 +11393,13 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
              contents = flinfo->contents;
            }
        }
+      else if (!(o->flags & SEC_RELOC)
+              && !bed->elf_backend_write_section
+              && o->sec_info_type == SEC_INFO_TYPE_MERGE)
+       /* A MERGE section that has no relocations doesn't need the
+          contents anymore, they have been recorded earlier.  Except
+          if the backend has special provisions for writing sections.  */
+       contents = NULL;
       else
        {
          contents = flinfo->contents;
@@ -11734,6 +11899,16 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
              return false;
          }
          break;
+       case SEC_INFO_TYPE_SFRAME:
+           {
+             /* Merge .sframe sections into the ctf frame encoder
+                context of the output_bfd's section.  The final .sframe
+                output section will be written out later.  */
+             if (!_bfd_elf_merge_section_sframe (output_bfd, flinfo->info,
+                                                 o, contents))
+               return false;
+           }
+           break;
        default:
          {
            if (! (o->flags & SEC_EXCLUDE))
@@ -11748,9 +11923,10 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
                  {
                    /* Reverse-copy input section to output.  */
 
-                   if (o->reloc_count != 0
-                       && (o->size * bed->s->int_rels_per_ext_rel
-                           != o->reloc_count * address_size))
+                   if ((o->size & (address_size - 1)) != 0
+                       || (o->reloc_count != 0
+                           && (o->size * bed->s->int_rels_per_ext_rel
+                               != o->reloc_count * address_size)))
                      {
                        _bfd_error_handler
                          /* xgettext:c-format */
@@ -12122,7 +12298,8 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
   const struct elf_backend_data *bed = get_elf_backend_data (abfd);
   struct elf_outext_info eoinfo;
   bool merged;
-  size_t relativecount = 0;
+  size_t relativecount;
+  size_t relr_entsize;
   asection *reldyn = 0;
   bfd_size_type amt;
   asection *attr_section = NULL;
@@ -12282,8 +12459,10 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
                      && elf_symtab_shndx_list (sec->owner) != NULL)
                    max_sym_shndx_count = sym_count;
 
-                 if (esdo->this_hdr.sh_type == SHT_REL
-                     || esdo->this_hdr.sh_type == SHT_RELA)
+                 esdi = elf_section_data (sec);
+
+                 if (esdi->this_hdr.sh_type == SHT_REL
+                     || esdi->this_hdr.sh_type == SHT_RELA)
                    /* Some backends use reloc_count in relocation sections
                       to count particular types of relocs.  Of course,
                       reloc sections themselves can't have relocations.  */
@@ -12301,8 +12480,6 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
                  else if (bed->elf_backend_count_relocs)
                    reloc_count = (*bed->elf_backend_count_relocs) (info, sec);
 
-                 esdi = elf_section_data (sec);
-
                  if ((sec->flags & SEC_RELOC) != 0)
                    {
                      size_t ext_size = 0;
@@ -12409,7 +12586,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
             later.  Use bfd_malloc since it will be freed by
             bfd_compress_section_contents.  */
          unsigned char *contents = esdo->this_hdr.contents;
-         if ((o->flags & SEC_ELF_COMPRESS) == 0 || contents != NULL)
+         if (contents != NULL)
            abort ();
          contents
            = (unsigned char *) bfd_malloc (esdo->this_hdr.sh_size);
@@ -12724,8 +12901,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
 
   /* If backend needs to output some local symbols not present in the hash
      table, do it now.  */
-  if (bed->elf_backend_output_arch_local_syms
-      && (info->strip != strip_all || emit_relocs))
+  if (bed->elf_backend_output_arch_local_syms)
     {
       if (! ((*bed->elf_backend_output_arch_local_syms)
             (abfd, info, &flinfo, elf_link_output_symstrtab)))
@@ -12898,7 +13074,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
                                                               off, true);
 
              if (bfd_seek (abfd, symtab_shndx_hdr->sh_offset, SEEK_SET) != 0
-                 || (bfd_bwrite (flinfo.symshndxbuf, amt, abfd) != amt))
+                 || (bfd_write (flinfo.symshndxbuf, amt, abfd) != amt))
                {
                  ret = false;
                  goto return_local_hash_table;
@@ -12966,9 +13142,24 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
       o->reloc_count = 0;
     }
 
+  relativecount = 0;
   if (dynamic && info->combreloc && dynobj != NULL)
     relativecount = elf_link_sort_relocs (abfd, info, &reldyn);
 
+  relr_entsize = 0;
+  if (htab->srelrdyn != NULL
+      && htab->srelrdyn->output_section != NULL
+      && htab->srelrdyn->size != 0)
+    {
+      asection *s = htab->srelrdyn->output_section;
+      relr_entsize = elf_section_data (s)->this_hdr.sh_entsize;
+      if (relr_entsize == 0)
+       {
+         relr_entsize = bed->s->arch_size / 8;
+         elf_section_data (s)->this_hdr.sh_entsize = relr_entsize;
+       }
+    }
+
   /* If we are linking against a dynamic object, or generating a
      shared library, finish up the dynamic linking information.  */
   if (dynamic)
@@ -12996,17 +13187,44 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
            default:
              continue;
            case DT_NULL:
-             if (relativecount > 0 && dyncon + bed->s->sizeof_dyn < dynconend)
+             if (relativecount != 0)
                {
                  switch (elf_section_data (reldyn)->this_hdr.sh_type)
                    {
                    case SHT_REL: dyn.d_tag = DT_RELCOUNT; break;
                    case SHT_RELA: dyn.d_tag = DT_RELACOUNT; break;
-                   default: continue;
                    }
-                 dyn.d_un.d_val = relativecount;
+                 if (dyn.d_tag != DT_NULL
+                     && dynconend - dyncon >= bed->s->sizeof_dyn)
+                   {
+                     dyn.d_un.d_val = relativecount;
+                     relativecount = 0;
+                     break;
+                   }
                  relativecount = 0;
-                 break;
+               }
+             if (relr_entsize != 0)
+               {
+                 if (dynconend - dyncon >= 3 * bed->s->sizeof_dyn)
+                   {
+                     asection *s = htab->srelrdyn;
+                     dyn.d_tag = DT_RELR;
+                     dyn.d_un.d_ptr
+                       = s->output_section->vma + s->output_offset;
+                     bed->s->swap_dyn_out (dynobj, &dyn, dyncon);
+                     dyncon += bed->s->sizeof_dyn;
+
+                     dyn.d_tag = DT_RELRSZ;
+                     dyn.d_un.d_val = s->size;
+                     bed->s->swap_dyn_out (dynobj, &dyn, dyncon);
+                     dyncon += bed->s->sizeof_dyn;
+
+                     dyn.d_tag = DT_RELRENT;
+                     dyn.d_un.d_val = relr_entsize;
+                     relr_entsize = 0;
+                     break;
+                   }
+                 relr_entsize = 0;
                }
              continue;
 
@@ -13273,6 +13491,9 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
   if (! _bfd_elf_write_section_eh_frame_hdr (abfd, info))
     goto error_return;
 
+  if (! _bfd_elf_write_section_sframe (abfd, info))
+    goto error_return;
+
   if (info->callbacks->emit_ctf)
       info->callbacks->emit_ctf ();
 
@@ -13489,7 +13710,7 @@ elf_gc_mark_debug_section (asection *sec ATTRIBUTE_UNUSED,
       /* Return the local debug definition section.  */
       asection *isec = bfd_section_from_elf_index (sec->owner,
                                                   sym->st_shndx);
-      if ((isec->flags & SEC_DEBUGGING) != 0)
+      if (isec != NULL && (isec->flags & SEC_DEBUGGING) != 0)
        return isec;
     }
 
@@ -14728,6 +14949,41 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
                                _bfd_elf_adjust_eh_frame_global_symbol, NULL);
     }
 
+  o = bfd_get_section_by_name (output_bfd, ".sframe");
+  if (o != NULL)
+    {
+      asection *i;
+
+      for (i = o->map_head.s; i != NULL; i = i->map_head.s)
+       {
+         if (i->size == 0)
+           continue;
+
+         abfd = i->owner;
+         if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
+           continue;
+
+         if (!init_reloc_cookie_for_section (&cookie, info, i))
+           return -1;
+
+         if (_bfd_elf_parse_sframe (abfd, info, i, &cookie))
+           {
+             if (_bfd_elf_discard_section_sframe (i,
+                                                  bfd_elf_reloc_symbol_deleted_p,
+                                                  &cookie))
+               {
+                 if (i->size != i->rawsize)
+                   changed = 1;
+               }
+           }
+         fini_reloc_cookie_for_section (&cookie, i);
+       }
+      /* Update the reference to the output .sframe section.  Used to
+        determine later if PT_GNU_SFRAME segment is to be generated.  */
+      if (!_bfd_elf_set_section_sframe (output_bfd, info))
+       return -1;
+    }
+
   for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link.next)
     {
       const struct elf_backend_data *bed;
@@ -14758,7 +15014,7 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
 
   if (info->eh_frame_hdr_type
       && !bfd_link_relocatable (info)
-      && _bfd_elf_discard_section_eh_frame_hdr (output_bfd, info))
+      && _bfd_elf_discard_section_eh_frame_hdr (info))
     changed = 1;
 
   return changed;