[PATCH v3 2/2] sframe: Add support for SFRAME_F_FDE_FUNC_START_PCREL flag

claudiu.zissulescu-ianculescu@oracle.com claudiu.zissulescu-ianculescu@oracle.com
Thu Jul 17 13:43:28 GMT 2025


From: Claudiu Zissulescu <claudiu.zissulescu-ianculescu@oracle.com>

The Sframe V2 has a new errata which introduces the
SFRAME_F_FDE_FUNC_START_PCREL flag. This flag indicates the encoding
of the SFrame FDE function start address field like this:

- if set, sfde_func_start_address field contains the offset in bytes
to the start PC of the associated function from the field itself.

- if unset, sfde_func_start_address field contains the offset in bytes
to the start PC of the associated function from the start of the
SFrame section.

Signed-off-by: Claudiu Zissulescu <claudiu.zissulescu-ianculescu@oracle.com>
---
 sysdeps/generic/sframe-read.c | 128 ++++++++++++++++++++++++++--------
 sysdeps/generic/sframe-read.h |   6 ++
 sysdeps/generic/sframe.h      |  15 +++-
 3 files changed, 118 insertions(+), 31 deletions(-)

diff --git a/sysdeps/generic/sframe-read.c b/sysdeps/generic/sframe-read.c
index d536575b2d..a6ebc42d66 100644
--- a/sysdeps/generic/sframe-read.c
+++ b/sysdeps/generic/sframe-read.c
@@ -75,11 +75,10 @@ sframe_get_fde_type (sframe_func_desc_entry *fdep)
 static bool
 sframe_header_sanity_check_p (sframe_header *hp)
 {
-  uint8_t all_flags = SFRAME_F_FDE_SORTED | SFRAME_F_FRAME_POINTER;
   /* Check preamble is valid.  */
   if ((hp->sfh_preamble.sfp_magic != SFRAME_MAGIC)
       || (hp->sfh_preamble.sfp_version != SFRAME_VERSION_2)
-      || ((hp->sfh_preamble.sfp_flags | all_flags) != all_flags))
+      || (hp->sfh_preamble.sfp_flags & ~SFRAME_V2_F_ALL_FLAGS))
     return false;
 
   /* Check offsets are valid.  */
@@ -171,25 +170,103 @@ sframe_fre_entry_size (sframe_frame_row_entry *frep, size_t addr_size)
 	  + sframe_fre_offset_bytes_size (fre_info));
 }
 
-/* Check whether for the given FDEP, the SFrame Frame Row Entry identified via
-   the START_IP_OFFSET and the END_IP_OFFSET, provides the stack trace
-   information for the PC.  */
+/* Get SFrame header from the given decoder context DCTX.  */
+
+static inline sframe_header *
+sframe_decoder_get_header (sframe_decoder_ctx *dctx)
+{
+  sframe_header *hp = NULL;
+  if (dctx != NULL)
+    hp = &dctx->sfd_header;
+  return hp;
+}
+
+/* Get the offset of the sfde_func_start_address field (from the start of the
+   on-disk layout of the SFrame section) of the FDE at FUNC_IDX in the decoder
+   context DCTX.  */
+
+static uint32_t
+sframe_decoder_get_offsetof_fde_start_addr (sframe_decoder_ctx *dctx,
+					    uint32_t func_idx,
+					    _Unwind_Reason_Code *errp)
+{
+  sframe_header *dhp;
+
+  dhp = sframe_decoder_get_header (dctx);
+  if (dhp == NULL)
+    {
+      if (errp != NULL)
+	*errp = _URC_END_OF_STACK;
+      return 0;
+    }
+
+  if (func_idx >= dhp->sfh_num_fdes)
+    {
+      if (errp != NULL)
+	*errp = _URC_END_OF_STACK;
+      return 0;
+    }
+  else if (errp != NULL)
+    *errp = _URC_NO_REASON;
+
+  return (sframe_get_hdr_size (dhp)
+	  + func_idx * sizeof (sframe_func_desc_entry)
+	  + offsetof (sframe_func_desc_entry, sfde_func_start_address));
+}
+
+
+/* Get the offset of the start PC of the SFrame FDE at FUNC_IDX from
+   the start of the SFrame section. If the flag
+   SFRAME_F_FDE_FUNC_START_PCREL is set, sfde_func_start_address is
+   the offset of the start PC of the function from the field itself.
+
+   If FUNC_IDX is not a valid index in the given decoder object, returns 0.  */
+
+static int32_t
+sframe_decoder_get_secrel_func_start_addr (sframe_decoder_ctx *dctx,
+					   uint32_t func_idx)
+{
+  int32_t func_start_addr;
+  _Unwind_Reason_Code err = 0;
+  int32_t offsetof_fde_in_sec = 0;
+
+  /* Check if we have SFRAME_F_FDE_FUNC_START_PCREL.  */
+  sframe_header *sh = &dctx->sfd_header;
+  if ((sh->sfh_preamble.sfp_flags & SFRAME_F_FDE_FUNC_START_PCREL))
+    {
+      offsetof_fde_in_sec =
+	sframe_decoder_get_offsetof_fde_start_addr (dctx, func_idx, &err);
+      /* If func_idx is not a valid index, return 0.  */
+      if (err == _URC_END_OF_STACK)
+	return 0;
+    }
+
+  func_start_addr = dctx->sfd_funcdesc[func_idx].sfde_func_start_address;
+
+  return func_start_addr + offsetof_fde_in_sec;
+}
+
+/* Check if the SFrame Frame Row Entry identified via the
+   START_IP_OFFSET and the END_IP_OFFSET (for SFrame FDE at
+   FUNC_IDX).  */
 
 static bool
-sframe_fre_check_range_p (sframe_func_desc_entry *fdep,
+sframe_fre_check_range_p (sframe_decoder_ctx *dctx, uint32_t func_idx,
 			  uint32_t start_ip_offset, uint32_t end_ip_offset,
 			  int32_t pc)
 {
+  sframe_func_desc_entry *fdep;
   int32_t func_start_addr;
   uint8_t rep_block_size;
   uint32_t fde_type;
   uint32_t pc_offset;
   bool mask_p;
 
+  fdep = &dctx->sfd_funcdesc[func_idx];
   if (fdep == NULL)
     return false;
 
-  func_start_addr = fdep->sfde_func_start_address;
+  func_start_addr = sframe_decoder_get_secrel_func_start_addr (dctx, func_idx);
   fde_type = sframe_get_fde_type (fdep);
   mask_p = (fde_type == SFRAME_FDE_TYPE_PCMASK);
   rep_block_size = fdep->sfde_func_rep_size;
@@ -207,19 +284,6 @@ sframe_fre_check_range_p (sframe_func_desc_entry *fdep,
   return (start_ip_offset <= pc_offset) && (end_ip_offset >= pc_offset);
 }
 
-/* The SFrame Decoder.  */
-
-/* Get SFrame header from the given decoder context DCTX.  */
-
-static inline sframe_header *
-sframe_decoder_get_header (sframe_decoder_ctx *dctx)
-{
-  sframe_header *hp = NULL;
-  if (dctx != NULL)
-    hp = &dctx->sfd_header;
-  return hp;
-}
-
 /* Get IDX'th offset from FRE.  Set ERRP as applicable.  */
 
 static int32_t
@@ -298,7 +362,7 @@ sframe_decode_fre_start_address (const char *fre_buf,
 
 static sframe_func_desc_entry *
 sframe_get_funcdesc_with_addr_internal (sframe_decoder_ctx *ctx, int32_t addr,
-					int *errp)
+					int *errp, uint32_t *func_idx)
 {
   sframe_header *dhp;
   sframe_func_desc_entry *fdp;
@@ -319,19 +383,23 @@ sframe_get_funcdesc_with_addr_internal (sframe_decoder_ctx *ctx, int32_t addr,
   /* Do the binary search.  */
   fdp = (sframe_func_desc_entry *) ctx->sfd_funcdesc;
   low = 0;
-  high = dhp->sfh_num_fdes;
+  high = dhp->sfh_num_fdes - 1;
   while (low <= high)
     {
       int mid = low + (high - low) / 2;
 
       /* Given sfde_func_start_address <= addr,
 	 addr - sfde_func_start_address must be positive.  */
-      if (fdp[mid].sfde_func_start_address <= addr
-	  && ((uint32_t)(addr - fdp[mid].sfde_func_start_address)
+      if (sframe_decoder_get_secrel_func_start_addr (ctx, mid) <= addr
+	  && ((uint32_t)(addr - sframe_decoder_get_secrel_func_start_addr (ctx,
+									   mid))
 	      < fdp[mid].sfde_func_size))
-	return fdp + mid;
+	{
+	  *func_idx = mid;
+	  return fdp + mid;
+	}
 
-      if (fdp[mid].sfde_func_start_address < addr)
+      if (sframe_decoder_get_secrel_func_start_addr (ctx, mid) < addr)
 	low = mid + 1;
       else
 	high = mid - 1;
@@ -510,6 +578,7 @@ __sframe_find_fre (sframe_decoder_ctx *ctx, int32_t pc,
 		   sframe_frame_row_entry *frep)
 {
   sframe_func_desc_entry *fdep;
+  uint32_t func_idx;
   uint32_t fre_type, i;
   uint32_t start_ip_offset;
   int32_t func_start_addr;
@@ -522,14 +591,14 @@ __sframe_find_fre (sframe_decoder_ctx *ctx, int32_t pc,
     return _URC_END_OF_STACK;
 
   /* Find the FDE which contains the PC, then scan its fre entries.  */
-  fdep = sframe_get_funcdesc_with_addr_internal (ctx, pc, &err);
+  fdep = sframe_get_funcdesc_with_addr_internal (ctx, pc, &err, &func_idx);
   if (fdep == NULL || ctx->sfd_fres == NULL)
     return _URC_END_OF_STACK;
 
   fre_type = sframe_get_fre_type (fdep);
 
   fres = ctx->sfd_fres + fdep->sfde_func_start_fre_off;
-  func_start_addr = fdep->sfde_func_start_address;
+  func_start_addr = sframe_decoder_get_secrel_func_start_addr (ctx, func_idx);
 
   for (i = 0; i < fdep->sfde_func_num_fres; i++)
     {
@@ -553,7 +622,8 @@ __sframe_find_fre (sframe_decoder_ctx *ctx, int32_t pc,
       if (start_ip_offset > (uint32_t) (pc - func_start_addr))
 	return _URC_END_OF_STACK;
 
-      if (sframe_fre_check_range_p (fdep, start_ip_offset, end_ip_offset, pc))
+      if (sframe_fre_check_range_p (ctx, func_idx, start_ip_offset,
+				    end_ip_offset, pc))
 	{
 	  /* Decode last FRE bits: offsets size.  */
 	  frep->fre_offsets = fres + addr_size + sizeof (frep->fre_info);
diff --git a/sysdeps/generic/sframe-read.h b/sysdeps/generic/sframe-read.h
index 1db1886bce..146142143c 100644
--- a/sysdeps/generic/sframe-read.h
+++ b/sysdeps/generic/sframe-read.h
@@ -99,6 +99,12 @@ __sframe_fre_get_ra_offset (sframe_decoder_ctx *dctx,
 			    sframe_frame_row_entry *fre,
 			    _Unwind_Reason_Code *errp);
 
+/* Get the offset of the sfde_func_start_address field.  */
+
+extern uint32_t
+__sframe_decoder_get_offsetof_fde_start_addr (sframe_decoder_ctx *dctx,
+					      uint32_t func_idx,
+					      _Unwind_Reason_Code *errp);
 #ifdef	__cplusplus
 }
 #endif
diff --git a/sysdeps/generic/sframe.h b/sysdeps/generic/sframe.h
index 3f8ff1c80e..e38adcfe17 100644
--- a/sysdeps/generic/sframe.h
+++ b/sysdeps/generic/sframe.h
@@ -80,9 +80,20 @@ extern "C"
 /* Various flags for SFrame.  */
 
 /* Function Descriptor Entries are sorted on PC.  */
-#define SFRAME_F_FDE_SORTED	0x1
+#define SFRAME_F_FDE_SORTED		    0x1
 /* Functions preserve frame pointer.  */
-#define SFRAME_F_FRAME_POINTER 0x2
+#define SFRAME_F_FRAME_POINTER		    0x2
+/* Function start address in SFrame FDE is encoded as the distance from the
+   location of the sfde_func_start_address to the start PC of the function.
+   If absent, the function start address in SFrame FDE is encoded as the
+   distance from the start of the SFrame FDE section to the start PC of the
+   function.  */
+#define SFRAME_F_FDE_FUNC_START_PCREL	    0x4
+
+/* Set of all defined flags in SFrame V2.  */
+#define SFRAME_V2_F_ALL_FLAGS \
+  (SFRAME_F_FDE_SORTED | SFRAME_F_FRAME_POINTER \
+   | SFRAME_F_FDE_FUNC_START_PCREL)
 
 #define SFRAME_CFA_FIXED_FP_INVALID 0
 #define SFRAME_CFA_FIXED_RA_INVALID 0
-- 
2.50.1



More information about the Libc-alpha mailing list