check sframe version in _bfd_elf_parse_sframe

Alan Modra amodra@gmail.com
Thu Mar 12 23:33:59 GMT 2026


Prompted by ld/bootstrap test failues on Ubuntu 25.10
ld-new: error in /lib/x86_64-linux-gnu/crt1.o (.sframe); unexpected SFrame format version 2
ld-new: final link failed
collect2: error: ld returned 1 exit status

Currently if you attempt to link an object file with sframe version 2
contents, the linker will complain in _bfd_elf_merge_section_sframe
and fail to produce an output.  This affects anyone who has objects
assembled by binutils-2.45 with .sframe sections.  A work-around is to
pass --discard-sframe to ld.

Ideally the linker would rewrite version 2 sframe input to version 3,
but in the absence of that support it would be nicer if ld just
ignored version 2 (or 1) sframe sections without needing to pass
--discard-sframe.  That is the aim of this patch.

It does so by checking the sframe version in _bfd_elf_parse_sframe
where various failing checks result in an error being printed and the
section not being marked as SEC_INFO_TYPE_SFRAME, but causes no
overall linker failure.  In addition, an sframe version failure now
also marks the sframe section with SEC_EXCLUDE.  This is also done for
other sframe failures.  See the changelog below for other fixes.

bfd/
	* elf-sframe.c (_bfd_elf_merge_section_sframe): Don't check
	sframe version here.
	(_bfd_elf_parse_sframe): Check sframe version here instead.
	Do the "already parsed" check first.  Reorder various other
	checks.  Do not bother parsing or print errors when
	discard_sframe is set.  Free section contents on failure
	paths.  Set SEC_EXCLUDE for fails.  Check for NULL bfd_zalloc
	return, and only allocate after successfully parsing sframe.
	On errors report that the section will be ignored, not "no
	.sframe will be created".
binutils/
	* testsuite/lib/binutils-common.exp: Ignore sframe version
	errors.

diff --git a/bfd/elf-sframe.c b/bfd/elf-sframe.c
index 7b2eed81c9f..995987eea6d 100644
--- a/bfd/elf-sframe.c
+++ b/bfd/elf-sframe.c
@@ -220,7 +220,7 @@ _bfd_elf_sframe_present (struct bfd_link_info *info)
 
 bool
 _bfd_elf_parse_sframe (bfd *abfd,
-		       struct bfd_link_info *info ATTRIBUTE_UNUSED,
+		       struct bfd_link_info *info,
 		       asection *sec, struct elf_reloc_cookie *cookie)
 {
   bfd_byte *sfbuf = NULL;
@@ -229,74 +229,85 @@ _bfd_elf_parse_sframe (bfd *abfd,
   bfd_size_type sf_size;
   int decerr = 0;
 
+  /* Check if this section was already parsed.  */
+  if (sec->sec_info_type == SEC_INFO_TYPE_SFRAME)
+    return true;
+
   if (info->discard_sframe)
-    sec->flags |= SEC_EXCLUDE;
+    {
+      sec->flags |= SEC_EXCLUDE;
+      return false;
+    }
 
-  /* Prior versions of assembler and ld were generating SFrame sections with
-     section type SHT_PROGBITS.  Issue an error for lack of support for such
-     objects now.  Even if section size is zero, a valid section type is
-     expected.  */
-  if (elf_section_type (sec) != SHT_GNU_SFRAME)
+  if ((sec->flags & SEC_EXCLUDE) != 0
+      || bfd_is_abs_section (sec->output_section))
     {
-      _bfd_error_handler
-	(_("error in %pB(%pA); unexpected SFrame section type"),
-	 abfd, sec);
+      /* This sections is being discarded from the link, ignore it.  */
       return false;
     }
 
   if (sec->size == 0
       || (sec->flags & SEC_HAS_CONTENTS) == 0)
     {
-      /* This file does not contain .sframe information.  */
-      return false;
+      /* This section does not contain .sframe information.  */
+      goto fail4;
     }
 
-  /* Check if this section was already parsed.  */
-  if (sec->sec_info_type == SEC_INFO_TYPE_SFRAME)
-    return true;
-
-  if (bfd_is_abs_section (sec->output_section))
+  /* Prior versions of assembler and ld were generating SFrame sections with
+     section type SHT_PROGBITS.  Issue an error for lack of support for such
+     objects now.  Even if section size is zero, a valid section type is
+     expected.  */
+  if (elf_section_type (sec) != SHT_GNU_SFRAME)
     {
-      /* At least one of the sections is being discarded from the
-	 link, so we should just ignore them.  */
-      return false;
+      _bfd_error_handler
+	(_("error in %pB(%pA); unexpected SFrame section type; section ignored"),
+	 abfd, sec);
+      goto fail4;
     }
 
   /* Read the SFrame stack trace information from abfd.  */
   if (!_bfd_elf_mmap_section_contents (abfd, sec, &sfbuf))
-    goto fail_no_free;
+    goto fail3;
 
   /* Decode the buffer and keep decoded contents for later use.
      Relocations are performed later, but are such that the section's
      size is unaffected.  */
-  sfd_info = bfd_zalloc (abfd, sizeof (*sfd_info));
   sf_size = sec->size;
+  sfd_ctx = sframe_decode ((const char *) sfbuf, sf_size, &decerr);
+  if (!sfd_ctx)
+    goto fail2;
 
-  sfd_info->sfd_ctx = sframe_decode ((const char*)sfbuf, sf_size, &decerr);
+  uint8_t dctx_version = sframe_decoder_get_version (sfd_ctx);
+  if (dctx_version != SFRAME_VERSION)
+    {
+      _bfd_error_handler
+	(_("error in %pB(%pA); unexpected SFrame format version %" PRIu8),
+	 abfd, sec, dctx_version);
+      goto fail2;
+    }
+
+  sfd_info = bfd_zalloc (abfd, sizeof (*sfd_info));
+  if (!sfd_info)
+    goto fail1;
+  sfd_info->sfd_ctx = sfd_ctx;
   sfd_info->sfd_state = SFRAME_SEC_DECODED;
-  sfd_ctx = sfd_info->sfd_ctx;
-  if (!sfd_ctx)
-    /* Free'ing up any memory held by decoder context is done by
-       sframe_decode in case of error.  */
-    goto fail_no_free;
 
   if (!sframe_decoder_init_func_bfdinfo (abfd, sec, sfd_info, cookie))
     {
+    fail1:
       sframe_decoder_free (&sfd_info->sfd_ctx);
-      goto fail_no_free;
+    fail2:
+      _bfd_elf_munmap_section_contents (sec, sfbuf);
+    fail3:
+      _bfd_error_handler (_("error in %pB(%pA); SFrame section ignored"),
+			  abfd, sec);
+    fail4:
+      sec->flags |= SEC_EXCLUDE;
+      return false;
     }
 
   sec->sec_info = sfd_info;
   sec->sec_info_type = SEC_INFO_TYPE_SFRAME;
-
-  goto success;
-
-fail_no_free:
-  _bfd_error_handler
-   (_("error in %pB(%pA); no .sframe will be created"),
-    abfd, sec);
-  return false;
-success:
   _bfd_elf_munmap_section_contents (sec, sfbuf);
   return true;
 }
@@ -384,8 +395,6 @@ _bfd_elf_merge_section_sframe (bfd *abfd,
   uint8_t sfd_ctx_abi_arch;
   int8_t sfd_ctx_fixed_fp_offset;
   int8_t sfd_ctx_fixed_ra_offset;
-  uint8_t dctx_version;
-  uint8_t ectx_version;
   uint8_t dctx_flags;
   uint8_t ectx_flags;
   int encerr = 0;
@@ -472,17 +481,6 @@ _bfd_elf_merge_section_sframe (bfd *abfd,
       return false;
     }
 
-  /* Check that all .sframe sections being linked have the same version.  */
-  dctx_version = sframe_decoder_get_version (sfd_ctx);
-  ectx_version = sframe_encoder_get_version (sfe_ctx);
-  if (dctx_version != SFRAME_VERSION_3 || dctx_version != ectx_version)
-    {
-      _bfd_error_handler
-	(_("error in %pB (%pA); unexpected SFrame format version %" PRIu8),
-	 sec->owner, sec, dctx_version);
-      return false;
-    }
-
   /* Check that all SFrame sections being linked have the 'data encoding'
      related flags set.  The implementation does not support updating these
      data encodings on the fly; confirm by checking the ectx_flags.  */
diff --git a/binutils/testsuite/lib/binutils-common.exp b/binutils/testsuite/lib/binutils-common.exp
index 24215502069..2dafd14e3ee 100644
--- a/binutils/testsuite/lib/binutils-common.exp
+++ b/binutils/testsuite/lib/binutils-common.exp
@@ -792,6 +792,9 @@ proc prune_warnings_extra { text } {
     regsub -all {(^|\n)([^\n]*lto-wrapper: warning: using serial compilation of [0-9]+ LTRANS jobs[^\n]*\n?)+} $text {\1} text
     regsub -all {(^|\n)([^\n]*lto-wrapper: note: [^\n]*\n?)+} $text {\1} text
 
+    # Ignore warnings about linking objects with an old sframe format
+    regsub -all {(^|\n)([^\n]* unexpected SFrame format version [^\n]*\n[^\n]* SFrame section ignored\n?)+} $text {\1} text
+
     return $text
 }
 

-- 
Alan Modra


More information about the Binutils mailing list