[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