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

Sam James sam@gentoo.org
Tue Jul 22 08:22:42 GMT 2025


Adhemerval Zanella <adhemerval.zanella@linaro.org> writes:

> 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>

Reviewed-by: Sam James <sam@gentoo.org>

> ---
>  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


More information about the Libc-alpha mailing list