]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
[SFrame-V3] bfd: ld: sframe: avoid unnecessary decoding of SFrame FREs at link time
authorIndu Bhagat <indu.bhagat@oracle.com>
Fri, 16 Jan 2026 00:43:20 +0000 (16:43 -0800)
committerIndu Bhagat <indu.bhagat@oracle.com>
Fri, 16 Jan 2026 01:02:26 +0000 (17:02 -0800)
At link time, in _bfd_elf_merge_section_sframe (), it suffices to bring
over the all per-function stack trace metadata (all FREs) as a blob into
the SFrame encoder object.  There is no need to "decode" each SFrame
FRE, only to add them in a serial fashion.

This is an optimization, and not directly related to any SFrame V3
related changes to the specification.  This should also bring us a step
closer to supporting SFrame for targets which use linker relaxations.
Removing the need to decode the input FREs can allow the linker to
simply use the available set of FREs from (relocated) contents.  To
support targets using linker relaxations in SFrame, other changes in the
SFrame parse functionality time may also be necessary, but this brings
us just a step closer.

Add two new APIs to accomplish this:
 - sframe_decoder_get_fres_buf, and
 - sframe_encoder_add_fres_buf

bfd/
* elf-sframe.c (_bfd_elf_merge_section_sframe): Get all FRE data
and add it all in bulk.
libsframe/
* libsframe.ver: Add new APIs.
* sframe.c (sframe_buf_fre_entry_size): New internal API to get
size of one SFrame FRE at the indicated buffer location, without
fully "decoding" the SFrame FRE.
(sframe_decoder_get_fres_buf): New definition.
(sframe_encoder_add_fres_buf): New definition.
include/
* sframe-api.h (sframe_decoder_get_fres_buf): New declaration.
(sframe_encoder_add_fres_buf): New declaration.

bfd/elf-sframe.c
include/sframe-api.h
libsframe/libsframe.ver
libsframe/sframe.c

index 1ef53120543019768bea53a8ee9da526c4ceb358..af6356f49fe0d15df7047813dc91b5dc94c059fa 100644 (file)
@@ -475,7 +475,7 @@ _bfd_elf_merge_section_sframe (bfd *abfd,
   /* Iterate over the function descriptor entries and the FREs of the
      function from the decoder context.  Add each of them to the encoder
      context, if suitable.  */
-  uint32_t i = 0, j = 0, cur_fidx = 0;
+  uint32_t i = 0, cur_fidx = 0;
 
   uint32_t num_fidx = sframe_decoder_get_num_fidx (sfd_ctx);
   uint32_t num_enc_fidx = sframe_encoder_get_num_fidx (sfe_ctx);
@@ -569,18 +569,18 @@ _bfd_elf_merge_section_sframe (bfd *abfd,
          BFD_ASSERT (!err);
        }
 
-      for (j = 0; j < num_fres; j++)
-       {
-         sframe_frame_row_entry fre;
-         if (!sframe_decoder_get_fre (sfd_ctx, i, j, &fre))
-           {
-             int err = sframe_encoder_add_fre (sfe_ctx,
-                                               cur_fidx-1+num_enc_fidx,
-                                               &fre);
-             BFD_ASSERT (!err);
-           }
-       }
+      uint32_t fde_num_fres = 0;
+      char *fres_buf = NULL;
+      size_t fres_buf_size = 0;
+
+      int err = sframe_decoder_get_fres_buf (sfd_ctx, i, &fres_buf,
+                                            &fres_buf_size, &fde_num_fres);
+      BFD_ASSERT (!err && fde_num_fres == num_fres);
+      err = sframe_encoder_add_fres_buf (sfe_ctx, cur_fidx - 1 + num_enc_fidx,
+                                        num_fres, fres_buf, fres_buf_size);
+      BFD_ASSERT (!err);
     }
+
   sfd_info->sfd_state = SFRAME_SEC_MERGED;
   /* Free the SFrame decoder context.  */
   sframe_decoder_free (&sfd_ctx);
index 4d5c9dccadb7004fbea10ebbe9ae9d51f68771c2..455738b5ef1058ee387ad0b726b9339297460c16 100644 (file)
@@ -169,6 +169,19 @@ sframe_decoder_get_fre (const sframe_decoder_ctx *ctx,
                        unsigned int fre_idx,
                        sframe_frame_row_entry *fre);
 
+/* Get the SFrame FRE data of the function at FUNC_IDX'th function index entry
+   in the SFrame decoder DCTX.  The reference to the buffer is returned in
+   FRES_BUF with FRES_BUF_SIZE indicating the size of the buffer.  The number
+   of FREs in the buffer are NUM_FRES.  In SFrame V3, this buffer also contains
+   the FDE attr data before the actual SFrame FREs.  Returns SFRAME_ERR in case
+   of error.  */
+extern int
+sframe_decoder_get_fres_buf (const sframe_decoder_ctx *dctx,
+                            uint32_t func_idx,
+                            char **fres_buf,
+                            size_t *fres_buf_size,
+                            uint32_t *num_fres);
+
 /* Get the data (NUM_FRES, FUNC_SIZE, FUNC_START_ADDRESS, FUNC_INFO,
    REP_BLOCK_SIZE) from the function descriptor entry at index I'th
    in the decoder CTX.  If failed, return error code.
@@ -300,6 +313,16 @@ sframe_encoder_add_fre (sframe_encoder_ctx *ectx,
                        unsigned int func_idx,
                        sframe_frame_row_entry *frep);
 
+/* Add SFrame FRE data given in the buffer FRES_BUF of size FRES_BUF_SIZE (for
+   function at index FUNC_IDX) to the encoder context ECTX.  The number of FREs
+   in the buffer are NUM_FRES.  */
+int
+sframe_encoder_add_fres_buf (sframe_encoder_ctx *ectx,
+                            unsigned int func_idx,
+                            uint32_t num_fres,
+                            const char *fres_buf,
+                            size_t fres_buf_size);
+
 /* Add a new SFrame function descriptor entry with START_ADDR and FUNC_SIZE to
    the encoder context ECTX.  */
 extern int
index 726c2ea9524db5a34a84cb01968c0002320ba95c..df88a2b28b183b536b001f8b22570396433caad5 100644 (file)
@@ -25,6 +25,7 @@ LIBSFRAME_3.0 {
     sframe_decoder_get_funcdesc_v2;
     sframe_decoder_get_funcdesc_v3;
     sframe_decoder_get_fre;
+    sframe_decoder_get_fres_buf;
     sframe_encode;
     sframe_encoder_free;
     sframe_encoder_get_flags;
@@ -37,6 +38,7 @@ LIBSFRAME_3.0 {
     sframe_encoder_add_funcdesc;
     sframe_encoder_add_funcdesc_v2;
     sframe_encoder_add_funcdesc_v3;
+    sframe_encoder_add_fres_buf;
     sframe_encoder_write;
     dump_sframe;
     sframe_errmsg;
index 38bdd55ac960ea6482bf27300a00e9080c5267b2..a32144d221bd19046fd5bbe63789490d359ae632 100644 (file)
@@ -491,6 +491,21 @@ sframe_fre_entry_size (sframe_frame_row_entry *frep, uint32_t fre_type)
          + sframe_fre_offset_bytes_size (fre_info));
 }
 
+/* Get total size in bytes in the SFrame FRE at FRE_BUF location, given the
+   type of FRE as FRE_TYPE.  */
+
+static size_t
+sframe_buf_fre_entry_size (const char *fre_buf, uint32_t fre_type)
+{
+  if (fre_buf == NULL)
+    return 0;
+
+  size_t addr_size = sframe_fre_start_addr_size (fre_type);
+  uint8_t fre_info = *(uint8_t *)(fre_buf + addr_size);
+
+  return (addr_size + sizeof (fre_info)
+         + sframe_fre_offset_bytes_size (fre_info));
+}
 /* Get the function descriptor entry at index FUNC_IDX in the decoder
    context CTX.  */
 
@@ -1700,6 +1715,52 @@ sframe_decoder_get_fre (const sframe_decoder_ctx *ctx,
   return sframe_set_errno (&err, SFRAME_ERR_FDE_NOTFOUND);
 }
 
+/* Get the SFrame FRE data of the function at FUNC_IDX'th function index entry
+   in the SFrame decoder DCTX.  The reference to the buffer is returned in
+   FRES_BUF with FRES_BUF_SIZE indicating the size of the buffer.  The number
+   of FREs in the buffer are NUM_FRES.  In SFrame V3, this buffer also contains
+   the FDE attr data before the actual SFrame FREs.  Returns SFRAME_ERR in case
+   of error.  */
+
+int
+sframe_decoder_get_fres_buf (const sframe_decoder_ctx *dctx,
+                            const uint32_t func_idx,
+                            char **fres_buf,
+                            size_t *fres_buf_size,
+                            uint32_t *num_fres)
+{
+  sframe_func_desc_entry_int *fdep;
+  uint32_t i = 0;
+  uint32_t fre_type;
+  size_t esz;
+  int err = 0;
+
+  if (dctx == NULL || fres_buf == NULL)
+    return sframe_set_errno (&err, SFRAME_ERR_INVAL);
+
+  /* Get function descriptor entry at index func_idx.  */
+  fdep = sframe_decoder_get_funcdesc_at_index (dctx, func_idx);
+  *num_fres = fdep->func_num_fres;
+
+  if (fdep == NULL)
+    return sframe_set_errno (&err, SFRAME_ERR_FDE_NOTFOUND);
+
+  fre_type = sframe_get_fre_type (fdep);
+  /* Update the pointer to (and total size of) the FRE entries.  */
+  *fres_buf = dctx->sfd_fres + fdep->func_start_fre_off;
+  const char *tmp_buf = *fres_buf;
+  while (i < *num_fres)
+    {
+      /* Avoid cost of full decoding at this time.  */
+      esz = sframe_buf_fre_entry_size (tmp_buf, fre_type);
+      tmp_buf += esz;
+      *fres_buf_size += esz;
+      i++;
+    }
+
+  return 0;
+}
+
 
 /* SFrame Encoder.  */
 
@@ -1950,6 +2011,88 @@ bad:
   return -1;
 }
 
+/* Add SFrame FRE data given in the buffer FRES_BUF of size FRES_BUF_SIZE (for
+   function at index FUNC_IDX) to the encoder context ECTX.  The number of FREs
+   in the buffer are NUM_FRES.  Returns SFRAME_ERR if failure.  */
+
+int
+sframe_encoder_add_fres_buf (sframe_encoder_ctx *ectx,
+                            unsigned int func_idx,
+                            uint32_t num_fres,
+                            const char *fres_buf,
+                            size_t fres_buf_size)
+{
+  sframe_frame_row_entry *ectx_frep;
+  size_t esz = 0;
+
+  int err = 0;
+  if (ectx == NULL || ((fres_buf == NULL) != (fres_buf_size == 0)))
+    return sframe_set_errno (&err, SFRAME_ERR_INVAL);
+
+  /* Use func_idx to gather the function descriptor entry.  */
+  sframe_func_desc_entry_int *fdep
+    = sframe_encoder_get_funcdesc_at_index (ectx, func_idx);
+  if (fdep == NULL)
+    return sframe_set_errno (&err, SFRAME_ERR_FDE_NOTFOUND);
+
+  sf_fre_tbl *fre_tbl = ectx->sfe_fres;
+  if (fre_tbl == NULL || fre_tbl->count + num_fres >= fre_tbl->alloced)
+    {
+      sf_fre_tbl *tmp = sframe_grow_fre_tbl (fre_tbl, num_fres, &err);
+      if (err)
+       {
+         sframe_set_errno (&err, SFRAME_ERR_NOMEM);
+         goto bad;             /* OOM.  */
+       }
+      fre_tbl = tmp;
+    }
+
+  uint32_t fre_type = sframe_get_fre_type (fdep);
+  uint32_t remaining = num_fres;
+  size_t buf_size = 0;
+  while (remaining)
+    {
+      ectx_frep = &fre_tbl->entry[fre_tbl->count];
+      /* Copy the SFrame FRE data over to the encoder object's fre_tbl.  */
+      sframe_decode_fre (fres_buf, ectx_frep, fre_type, &esz);
+
+      if (!sframe_fre_sanity_check_p (ectx_frep))
+       return sframe_set_errno (&err, SFRAME_ERR_FRE_INVAL);
+
+      /* Although a stricter sanity check on fre_start_addr like:
+          if (fdep->func_size)
+            sframe_assert (frep->fre_start_addr < fdep->func_size);
+        is more suitable, some code has been seen to not abide by it.  See PR
+        libsframe/33131.  */
+      sframe_assert (ectx_frep->fre_start_addr <= fdep->func_size);
+
+      ectx->sfe_fre_nbytes += esz;
+
+      fre_tbl->count++;
+      fres_buf += esz;
+      buf_size += esz;
+      remaining--;
+    }
+
+  sframe_assert (fres_buf_size == buf_size);
+  ectx->sfe_fres = fre_tbl;
+
+  sframe_header *ehp = sframe_encoder_get_header (ectx);
+  ehp->sfh_num_fres = fre_tbl->count;
+
+  /* Update the value of the number of FREs for the function.  */
+  fdep->func_num_fres = num_fres;
+
+  return 0;
+
+bad:
+  if (fre_tbl != NULL)
+    free (fre_tbl);
+  ectx->sfe_fres = NULL;
+  ectx->sfe_fre_nbytes = 0;
+  return -1;
+}
+
 /* Add a new SFrame function descriptor entry with START_ADDR, FUNC_SIZE and
    FUNC_INFO to the encoder context ECTX.  Caller must make sure that ECTX
    exists.  */