[PATCH v6 4/5] elf: Add SFrame stack tracing
Adhemerval Zanella Netto
adhemerval.zanella@linaro.org
Wed Jul 2 12:50:40 GMT 2025
On 10/06/25 04:33, claudiu.zissulescu-ianculescu@oracle.com wrote:
> From: Claudiu Zissulescu <claudiu.zissulescu-ianculescu@oracle.com>
>
> This patch adds the necessary bits to enable stack tracing using
> SFrame. In the case the new SFrame stack tracing procedure doesn't
> find SFrame related info, the stack tracing falls back on default
> Dwarf implementation.
>
> The new SFrame stack tracing procedure is added to bebug/backtrace.c
> file, the support functions are added in sysdeps folder, namely
> sframe.h, read-sframe.c and read-sfame.h.
>
> Signed-off-by: Claudiu Zissulescu <claudiu.zissulescu-ianculescu@oracle.com>
> ---
> debug/backtrace.c | 33 +-
> sysdeps/generic/Makefile | 1 +
> sysdeps/generic/sframe-read.c | 567 ++++++++++++++++++++++++++++++++++
> sysdeps/generic/sframe-read.h | 106 +++++++
> sysdeps/generic/sframe.c | 187 +++++++++++
> sysdeps/generic/sframe.h | 367 ++++++++++++++++++++++
> sysdeps/generic/uw-sigframe.h | 17 +
> 7 files changed, 1277 insertions(+), 1 deletion(-)
> create mode 100644 sysdeps/generic/sframe-read.c
> create mode 100644 sysdeps/generic/sframe-read.h
> create mode 100644 sysdeps/generic/sframe.c
> create mode 100644 sysdeps/generic/sframe.h
> create mode 100644 sysdeps/generic/uw-sigframe.h
>
> diff --git a/debug/backtrace.c b/debug/backtrace.c
> index 05cdc84e8d..aca0b5ade9 100644
> --- a/debug/backtrace.c
> +++ b/debug/backtrace.c
> @@ -20,6 +20,7 @@
> #include <stdlib.h>
> #include <unwind.h>
> #include <unwind-link.h>
> +#include <sframe.h>
>
> struct trace_arg
> {
> @@ -30,6 +31,29 @@ struct trace_arg
> int size;
> };
>
> +/* Initialize the SFrame backtrace routine, and try to backtrace the
> + current stack using SFrame info. The return value of SFrame stack
> + tracer must be larger than one to consider the SFrame backtrace
> + valid. Otherwise, there may be the case that glibc is compiled
> + using SFrame but the application not. The SFrame backtracer can
> + fallback on Dwarf unwinder also when it is a posibility to return a
> + truncated trace. This happens when a stack frame is detected
> + missing SFrame information. */
> +
> +#define DO_SFRAME_BACKTRACE(ARRAY, SIZE) \
> + do \
> + { \
> + int cnt; \
> + frame frame; \
> + frame.pc = getPC(); \
> + frame.sp = getSP(); \
> + frame.fp = (_Unwind_Ptr) __builtin_frame_address (0); \
> + cnt = stacktrace_sframe (ARRAY, SIZE, &frame); \
> + if (cnt > 1) \
> + return cnt; \
> + } \
> + while(0)
> +
> static _Unwind_Reason_Code
> backtrace_helper (struct _Unwind_Context *ctx, void *a)
> {
> @@ -72,7 +96,14 @@ __backtrace (void **array, int size)
> .cnt = -1
> };
>
> - if (size <= 0 || arg.unwind_link == NULL)
> + if (size <= 0)
> + return 0;
> +
> + /* Try first the SFrame backtracer. */
> + DO_SFRAME_BACKTRACE (array, size);
> +
> + /* Try the dwarf unwinder. */
> + if (arg.unwind_link == NULL)
> return 0;
>
> UNWIND_LINK_PTR (arg.unwind_link, _Unwind_Backtrace)
> diff --git a/sysdeps/generic/Makefile b/sysdeps/generic/Makefile
> index 3ed75ddf89..c48e713eb3 100644
> --- a/sysdeps/generic/Makefile
> +++ b/sysdeps/generic/Makefile
> @@ -21,6 +21,7 @@ CFLAGS-wordcopy.c += -Wno-uninitialized
> endif
>
> ifeq ($(subdir),elf)
> +sysdep_routines += sframe-read sframe
> ifeq (yes:yes,$(build-shared):$(unwind-find-fde))
> # This is needed to support g++ v2 and v3.
> sysdep_routines += framestate unwind-pe
> diff --git a/sysdeps/generic/sframe-read.c b/sysdeps/generic/sframe-read.c
> new file mode 100644
> index 0000000000..6cc1e6cb46
> --- /dev/null
> +++ b/sysdeps/generic/sframe-read.c
> @@ -0,0 +1,567 @@
> +/* Copyright (C) 2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public License as
> + published by the Free Software Foundation; either version 2.1 of the
> + License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program. If not, see <http://www.gnu.org/licenses/>. */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <stdarg.h>
> +#include <string.h>
> +#include <assert.h>
> +#include <sframe-read.h>
> +
> +#define sframe_assert(expr) (assert (expr))
This macro does not add much, just call assert.
> +
> +/* Get the SFrame header size. */
> +
> +static inline uint32_t
> +sframe_get_hdr_size (sframe_header *sfh)
> +{
> + return SFRAME_V1_HDR_SIZE (*sfh);
> +}
> +
> +/* Access functions for frame row entry data. */
> +
> +static inline uint8_t
> +sframe_fre_get_offset_count (uint8_t fre_info)
> +{
> + return SFRAME_V1_FRE_OFFSET_COUNT (fre_info);
> +}
> +
> +static inline uint8_t
> +sframe_fre_get_offset_size (uint8_t fre_info)
> +{
> + return SFRAME_V1_FRE_OFFSET_SIZE (fre_info);
> +}
> +
> +static inline bool
> +sframe_get_fre_ra_mangled_p (uint8_t fre_info)
> +{
> + return SFRAME_V1_FRE_MANGLED_RA_P (fre_info);
> +}
> +
> +/* Access functions for info from function descriptor entry. */
> +
> +static uint32_t
> +sframe_get_fre_type (sframe_func_desc_entry *fdep)
> +{
> + uint32_t fre_type = 0;
> + if (fdep)
> + fre_type = SFRAME_V1_FUNC_FRE_TYPE (fdep->sfde_func_info);
> + return fre_type;
> +}
> +
> +static uint32_t
> +sframe_get_fde_type (sframe_func_desc_entry *fdep)
> +{
> + uint32_t fde_type = 0;
> + if (fdep)
No implict check:
if (fdep != NULL)
There are some other cases below.
> + fde_type = SFRAME_V1_FUNC_FDE_TYPE (fdep->sfde_func_info);
> + return fde_type;
> +}
> +
> +/* Check if SFrame header has valid data. Only consider SFrame type
> + 2. */
> +
> +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))
> + return false;
> +
> + /* Check offsets are valid. */
> + if (hp->sfh_fdeoff > hp->sfh_freoff)
> + return false;
> +
> + return true;
> +}
> +
> +/* Get the FRE start address size. */
> +
> +static size_t
> +sframe_fre_start_addr_size (uint32_t fre_type)
> +{
> + size_t addr_size = 0;
> + switch (fre_type)
> + {
> + case SFRAME_FRE_TYPE_ADDR1:
> + addr_size = 1;
> + break;
> + case SFRAME_FRE_TYPE_ADDR2:
> + addr_size = 2;
> + break;
> + case SFRAME_FRE_TYPE_ADDR4:
> + addr_size = 4;
> + break;
> + default:
> + /* No other value is expected. */
> + sframe_assert (0);
Do we really want to abort the process de a wrong frame description?
If this is what libgcc does, it should be ok.
> + break;
> + }
> + return addr_size;
> +}
> +
> +/* Check if the FREP has valid data. */
> +
> +static bool
> +sframe_fre_sanity_check_p (sframe_frame_row_entry *frep)
> +{
> + uint8_t offset_size, offset_cnt;
> + uint8_t fre_info;
> +
> + if (frep == NULL)
> + return false;
> +
> + fre_info = frep->fre_info;
> + offset_size = sframe_fre_get_offset_size (fre_info);
> +
> + if (offset_size != SFRAME_FRE_OFFSET_1B
> + && offset_size != SFRAME_FRE_OFFSET_2B
> + && offset_size != SFRAME_FRE_OFFSET_4B)
> + return false;
> +
> + offset_cnt = sframe_fre_get_offset_count (fre_info);
> + if (offset_cnt > MAX_NUM_STACK_OFFSETS)
> + return false;
> +
> + return true;
> +}
> +
> +/* Get FRE_INFO's offset size in bytes. */
> +
> +static size_t
> +sframe_fre_offset_bytes_size (uint8_t fre_info)
> +{
> + uint8_t offset_size, offset_cnt;
> +
> + offset_size = sframe_fre_get_offset_size (fre_info);
> +
> + offset_cnt = sframe_fre_get_offset_count (fre_info);
> +
> + if (offset_size == SFRAME_FRE_OFFSET_2B
> + || offset_size == SFRAME_FRE_OFFSET_4B) /* 2 or 4 bytes. */
> + return (offset_cnt * (offset_size * 2));
> +
> + return (offset_cnt);
> +}
> +
> +/* Get total size in bytes to represent FREP in the binary format. This
> + includes the starting address, FRE info, and all the offsets. */
> +
> +static size_t
> +sframe_fre_entry_size (sframe_frame_row_entry *frep, uint32_t fre_type)
> +{
> + if (frep == NULL)
> + return 0;
> +
> + uint8_t fre_info = frep->fre_info;
> + size_t addr_size = sframe_fre_start_addr_size (fre_type);
> +
> + return (addr_size + sizeof (frep->fre_info)
> + + 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. */
> +
> +static bool
> +sframe_fre_check_range_p (sframe_func_desc_entry *fdep,
> + uint32_t start_ip_offset, uint32_t end_ip_offset,
> + int32_t pc)
> +{
> + int32_t func_start_addr;
> + uint8_t rep_block_size;
> + uint32_t fde_type;
> + uint32_t pc_offset;
> + bool mask_p;
> +
> + if (!fdep)
> + return false;
> +
> + func_start_addr = fdep->sfde_func_start_address;
> + fde_type = sframe_get_fde_type (fdep);
> + mask_p = (fde_type == SFRAME_FDE_TYPE_PCMASK);
> + rep_block_size = fdep->sfde_func_rep_size;
> +
> + if (func_start_addr > pc)
> + return false;
> +
> + /* Given func_start_addr <= pc, pc - func_start_addr must be positive. */
> + pc_offset = pc - func_start_addr;
> + /* For SFrame FDEs encoding information for repetitive pattern of insns,
> + masking with the rep_block_size is necessary to find the matching FRE. */
> + if (mask_p)
> + pc_offset = pc_offset % rep_block_size;
> +
> + 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
> +sframe_get_fre_offset (sframe_frame_row_entry *fre,
> + int idx,
> + _Unwind_Reason_Code *errp)
> +{
> + uint8_t offset_cnt, offset_size;
> +
> + if (!sframe_fre_sanity_check_p (fre))
> + {
> + *errp = _URC_END_OF_STACK;
> + return 0;
> + }
> +
> + offset_cnt = sframe_fre_get_offset_count (fre->fre_info);
> + offset_size = sframe_fre_get_offset_size (fre->fre_info);
> +
> + if (offset_cnt < (idx + 1))
> + {
> + *errp = _URC_END_OF_STACK;
> + return 0;
> + }
> + *errp = _URC_NO_REASON;
> +
> + if (offset_size == SFRAME_FRE_OFFSET_1B)
> + {
> + int8_t *sp = (int8_t *)fre->fre_offsets;
> + return sp[idx];
> + }
> + else if (offset_size == SFRAME_FRE_OFFSET_2B)
> + {
> + int16_t *sp = (int16_t *)fre->fre_offsets;
> + return sp[idx];
> + }
> + else
> + {
> + int32_t *ip = (int32_t *)fre->fre_offsets;
> + return ip[idx];
> + }
> +}
> +
> +/* Decode the SFrame FRE start address offset value from FRE_BUF in on-disk
> + binary format, given the FRE_TYPE. Updates the FRE_START_ADDR. */
> +
> +static void
> +sframe_decode_fre_start_address (const char *fre_buf,
> + uint32_t *fre_start_addr,
> + uint32_t fre_type)
> +{
> + uint32_t saddr = 0;
> +
> + if (fre_type == SFRAME_FRE_TYPE_ADDR1)
> + {
> + uint8_t *uc = (uint8_t *)fre_buf;
> + saddr = (uint32_t)*uc;
> + }
> + else if (fre_type == SFRAME_FRE_TYPE_ADDR2)
> + {
> + uint16_t *ust = (uint16_t *)fre_buf;
> + saddr = (uint32_t)*ust;
> + }
> + else if (fre_type == SFRAME_FRE_TYPE_ADDR4)
> + {
> + uint32_t *uit = (uint32_t *)fre_buf;
> + saddr = (uint32_t)*uit;
> + }
> + else
> + return;
> +
> + *fre_start_addr = saddr;
> +}
> +
> +/* Find the function descriptor entry starting which contains the specified
> + address ADDR. */
> +
> +static sframe_func_desc_entry *
> +sframe_get_funcdesc_with_addr_internal (sframe_decoder_ctx *ctx, int32_t addr,
> + int *errp)
> +{
> + sframe_header *dhp;
> + sframe_func_desc_entry *fdp;
> + int low, high;
> +
> + if (ctx == NULL)
> + return NULL;
> +
> + dhp = sframe_decoder_get_header (ctx);
> +
> + if (dhp == NULL || dhp->sfh_num_fdes == 0 || ctx->sfd_funcdesc == NULL)
> + return NULL;
> + /* If the FDE sub-section is not sorted on PCs, skip the lookup because
> + binary search cannot be used. */
> + if ((dhp->sfh_preamble.sfp_flags & SFRAME_F_FDE_SORTED) == 0)
> + return NULL;
> +
> + /* Do the binary search. */
> + fdp = (sframe_func_desc_entry *) ctx->sfd_funcdesc;
> + low = 0;
> + high = dhp->sfh_num_fdes;
> + 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)
> + < fdp[mid].sfde_func_size))
> + return fdp + mid;
> +
> + if (fdp[mid].sfde_func_start_address < addr)
> + low = mid + 1;
> + else
> + high = mid - 1;
> + }
> +
> + return NULL;
> +}
> +
> +/* Get the end IP offset for the FRE at index i in the FDEP. The buffer FRES
> + is the starting location for the FRE. */
> +
> +static uint32_t
> +sframe_fre_get_end_ip_offset (sframe_func_desc_entry *fdep, unsigned int i,
> + const char *fres)
> +{
> + uint32_t end_ip_offset = 0;
> + uint32_t fre_type;
> +
> + fre_type = sframe_get_fre_type (fdep);
> +
> + /* Get the start address of the next FRE in sequence. */
> + if (i < fdep->sfde_func_num_fres - 1)
> + {
> + sframe_decode_fre_start_address (fres, &end_ip_offset, fre_type);
> + end_ip_offset -= 1;
> + }
> + else
> + /* The end IP offset for the FRE needs to be deduced from the function
> + size. */
> + end_ip_offset = fdep->sfde_func_size - 1;
> +
> + return end_ip_offset;
> +}
> +
> +/* Get the SFrame's fixed FP offset given the decoder context CTX. */
> +
> +static int8_t
> +sframe_decoder_get_fixed_fp_offset (sframe_decoder_ctx *ctx)
> +{
> + sframe_header *dhp;
> + dhp = sframe_decoder_get_header (ctx);
> + return dhp->sfh_cfa_fixed_fp_offset;
> +}
> +
> +/* Get the SFrame's fixed RA offset given the decoder context CTX. */
> +
> +static int8_t
> +sframe_decoder_get_fixed_ra_offset (sframe_decoder_ctx *ctx)
> +{
> + sframe_header *dhp;
> + dhp = sframe_decoder_get_header (ctx);
> + return dhp->sfh_cfa_fixed_ra_offset;
> +}
> +
> +/* Get the base reg id from the FRE info. Set errp if failure. */
> +
> +uint8_t
> +sframe_fre_get_base_reg_id (sframe_frame_row_entry *fre)
> +{
> + uint8_t fre_info = fre->fre_info;
> + return SFRAME_V1_FRE_CFA_BASE_REG_ID (fre_info);
> +}
> +
> +/* Get the CFA offset from the FRE. If the offset is unavailable,
> + sets errp. */
> +
> +int32_t
> +sframe_fre_get_cfa_offset (sframe_decoder_ctx *dctx __attribute__ ((__unused__)),
> + sframe_frame_row_entry *fre,
> + _Unwind_Reason_Code *errp)
> +{
> + return sframe_get_fre_offset (fre, SFRAME_FRE_CFA_OFFSET_IDX, errp);
> +}
> +
> +/* Get the FP offset from the FRE. If the offset is unavailable, sets
> + errp. */
> +
> +int32_t
> +sframe_fre_get_fp_offset (sframe_decoder_ctx *dctx,
> + sframe_frame_row_entry *fre,
> + _Unwind_Reason_Code *errp)
> +{
> + uint32_t fp_offset_idx = 0;
> + int8_t fp_offset = sframe_decoder_get_fixed_fp_offset (dctx);
> +
> + *errp = _URC_NO_REASON;
> + /* If the FP offset is not being tracked, return the fixed FP offset
> + from the SFrame header. */
> + if (fp_offset != SFRAME_CFA_FIXED_FP_INVALID)
> + return fp_offset;
> +
> + /* In some ABIs, the stack offset to recover RA (using the CFA) from is
> + fixed (like AMD64). In such cases, the stack offset to recover FP will
> + appear at the second index. */
> + fp_offset_idx = ((sframe_decoder_get_fixed_ra_offset (dctx)
> + != SFRAME_CFA_FIXED_RA_INVALID)
> + ? SFRAME_FRE_RA_OFFSET_IDX
> + : SFRAME_FRE_FP_OFFSET_IDX);
> + return sframe_get_fre_offset (fre, fp_offset_idx, errp);
> +}
> +
> +/* Get the RA offset from the FRE. If the offset is unavailable, sets
> + errp. */
> +
> +int32_t
> +sframe_fre_get_ra_offset (sframe_decoder_ctx *dctx,
> + sframe_frame_row_entry *fre,
> + _Unwind_Reason_Code *errp)
> +{
> + int8_t ra_offset = sframe_decoder_get_fixed_ra_offset (dctx);
> + *errp = _URC_NO_REASON;
> +
> + /* If the RA offset was not being tracked, return the fixed RA offset
> + from the SFrame header. */
> + if (ra_offset != SFRAME_CFA_FIXED_RA_INVALID)
> + return ra_offset;
> +
> + /* Otherwise, get the RA offset from the FRE. */
> + return sframe_get_fre_offset (fre, SFRAME_FRE_RA_OFFSET_IDX, errp);
> +}
> +
> +/* Decode the specified SFrame buffer SF_BUF and return the new SFrame
> + decoder context. */
> +
> +_Unwind_Reason_Code
> +sframe_decode (sframe_decoder_ctx *dctx, const char *sf_buf)
> +{
> + const sframe_preamble *sfp;
> + size_t hdrsz;
> + sframe_header *sfheaderp;
> + char *frame_buf;
> +
> + int fidx_size;
> + uint32_t fre_bytes;
> +
> + if (sf_buf == NULL)
> + return _URC_END_OF_STACK;
> +
> + sfp = (const sframe_preamble *) sf_buf;
> +
> + /* Check for foreign endianness. */
> + if (sfp->sfp_magic != SFRAME_MAGIC)
> + return _URC_END_OF_STACK;
> +
> + frame_buf = (char *)sf_buf;
> +
> + /* Handle the SFrame header. */
> + dctx->sfd_header = *(sframe_header *) frame_buf;
> +
> + /* Validate the contents of SFrame header. */
> + sfheaderp = &dctx->sfd_header;
> + if (!sframe_header_sanity_check_p (sfheaderp))
> + return _URC_END_OF_STACK;
> +
> + hdrsz = sframe_get_hdr_size (sfheaderp);
> + frame_buf += hdrsz;
> +
> + /* Handle the SFrame Function Descriptor Entry section. */
> + fidx_size
> + = sfheaderp->sfh_num_fdes * sizeof (sframe_func_desc_entry);
> + dctx->sfd_funcdesc = (sframe_func_desc_entry *)frame_buf;
> + frame_buf += (fidx_size);
> +
> + dctx->sfd_fres = frame_buf;
> + fre_bytes = sfheaderp->sfh_fre_len;
> + dctx->sfd_fre_nbytes = fre_bytes;
> +
> + return _URC_NO_REASON;
> +}
> +
> +/* Find the SFrame Row Entry which contains the PC. Returns
> + _URC_END_OF_STACK if failure. */
> +
> +_Unwind_Reason_Code
> +sframe_find_fre (sframe_decoder_ctx *ctx, int32_t pc,
> + sframe_frame_row_entry *frep)
> +{
> + sframe_func_desc_entry *fdep;
> + uint32_t fre_type, i;
> + uint32_t start_ip_offset;
> + int32_t func_start_addr;
> + uint32_t end_ip_offset;
> + const char *fres;
> + size_t size = 0;
> + int err = 0;
> +
> + if ((ctx == NULL) || (frep == NULL))
> + 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);
> + 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;
> +
> + for (i = 0; i < fdep->sfde_func_num_fres; i++)
> + {
> + size_t addr_size;
> +
> + /* Partially decode the FRE. */
> + sframe_decode_fre_start_address (fres, &frep->fre_start_addr, fre_type);
> + addr_size = sframe_fre_start_addr_size (fre_type);
> + frep->fre_info = *(uint8_t *)(fres + addr_size);
> + size = sframe_fre_entry_size (frep, fre_type);
> +
> + start_ip_offset = frep->fre_start_addr;
> + end_ip_offset = sframe_fre_get_end_ip_offset (fdep, i, fres + size);
> +
> + /* Stop search if FRE's start_ip is greater than pc. Given
> + func_start_addr <= pc, pc - func_start_addr must be positive. */
> + 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))
> + {
> + /* Decode last FRE bits: offsets size. */
> + frep->fre_offsets = fres + addr_size + sizeof (frep->fre_info);
> + return _URC_NO_REASON;
> + }
> +
> + fres += size;
> + }
> + return _URC_END_OF_STACK;
> +}
> diff --git a/sysdeps/generic/sframe-read.h b/sysdeps/generic/sframe-read.h
> new file mode 100644
> index 0000000000..b09c156c0e
> --- /dev/null
> +++ b/sysdeps/generic/sframe-read.h
> @@ -0,0 +1,106 @@
> +/* Copyright (C) 2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public License as
> + published by the Free Software Foundation; either version 2.1 of the
> + License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program. If not, see <http://www.gnu.org/licenses/>. */
> +
> +#ifndef _SFRAME_API_H
> +#define _SFRAME_API_H
> +
> +#include <sframe.h>
> +#include <stdbool.h>
> +#include <unwind.h>
> +
> +#ifdef __cplusplus
> +extern "C"
> +{
> +#endif
> +
> +typedef struct sframe_decoder_ctx
> +{
> +
> + sframe_header sfd_header;
> +
> + sframe_func_desc_entry *sfd_funcdesc;
> + /* SFrame FRE table. */
> + char *sfd_fres;
> + /* Number of bytes needed for SFrame FREs. */
> + int sfd_fre_nbytes;
> +} sframe_decoder_ctx;
> +
> +#define MAX_NUM_STACK_OFFSETS 3
> +
> +/* User interfacing SFrame Row Entry.
> + An abstraction provided by libsframe so the consumer is decoupled from
> + the binary format representation of the same.
> +
> + The members are best ordered such that they are aligned at their natural
> + boundaries. This helps avoid usage of undesirable misaligned memory
> + accesses. See PR libsframe/29856. */
> +
> +typedef struct sframe_frame_row_entry
> +{
> + uint32_t fre_start_addr;
> + const char *fre_offsets;
> + unsigned char fre_info;
> +} sframe_frame_row_entry;
> +
> +/* The SFrame Decoder. */
> +
> +/* Decode the specified SFrame buffer CF_BUF and return the new SFrame
> + decoder context. */
> +
> +extern _Unwind_Reason_Code
> +sframe_decode (sframe_decoder_ctx *dctx, const char *cf_buf);
> +
> +/* Find the SFrame Frame Row Entry which contains the PC. Returns
> + _URC_END_OF_STACK if failure. */
> +
> +extern _Unwind_Reason_Code
> +sframe_find_fre (sframe_decoder_ctx *ctx, int32_t pc,
> + sframe_frame_row_entry *frep);
> +
> +/* Get the base reg id from the FRE info. */
> +
> +extern uint8_t
> +sframe_fre_get_base_reg_id (sframe_frame_row_entry *fre);
> +
> +/* Get the CFA offset from the FRE. Sets ERRP if an error is
> + detected. */
> +
> +extern int32_t
> +sframe_fre_get_cfa_offset (sframe_decoder_ctx *dtcx,
> + sframe_frame_row_entry *fre,
> + _Unwind_Reason_Code *errp);
> +
> +/* Get the FP offset from the FRE. If the offset is unavailable, sets
> + ERRP. */
> +
> +extern int32_t
> +sframe_fre_get_fp_offset (sframe_decoder_ctx *dctx,
> + sframe_frame_row_entry *fre,
> + _Unwind_Reason_Code *errp);
> +
> +/* Get the RA offset from the FRE. Sets ERRP if ra offset is
> + unavailable. */
> +
> +extern int32_t
> +sframe_fre_get_ra_offset (sframe_decoder_ctx *dctx,
> + sframe_frame_row_entry *fre,
> + _Unwind_Reason_Code *errp);
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif /* _SFRAME_API_H */
> diff --git a/sysdeps/generic/sframe.c b/sysdeps/generic/sframe.c
> new file mode 100644
> index 0000000000..abdda07def
> --- /dev/null
> +++ b/sysdeps/generic/sframe.c
> @@ -0,0 +1,187 @@
> +/* Copyright (C) 2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public License as
> + published by the Free Software Foundation; either version 2.1 of the
> + License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program. If not, see <http://www.gnu.org/licenses/>. */
> +
> +#include <sframe-read.h>
> +#include <stdlib.h>
> +#include <dlfcn.h>
> +#include <unwind.h>
> +#include <uw-sigframe.h>
> +#include <ldsodefs.h>
> +
> +/* Some arches like s390x needs an offset to correct the value where
> + SP is located in relation to CFA. */
> +#ifndef SFRAME_SP_VAL_OFFSET
> +#define SFRAME_SP_VAL_OFFSET 0
> +#endif
> +
> +static inline _Unwind_Ptr
> +read_stack_value (_Unwind_Ptr loc)
> +{
> + _Unwind_Ptr value = *((_Unwind_Ptr *) loc);
> + return value;
> +}
> +
> +/* Helper to avoid PLT call in libc. Fixes elf/check-localplt
> + errors. */
> +
> +static int
> +_dl_find_object_helper (void *address, struct dl_find_object *result)
> +{
> + return GLRO (dl_find_object) (address, result);
> +}
> +
> +/* Backtrace the stack and collect the stacktrace given SFrame info.
> + If successful, store the return addresses in RA_LST. The SIZE
> + argument specifies the maximum number of return addresses that can
> + be stored in RA_LST and contains the number of the addresses
> + collected. */
> +
> +int
> +stacktrace_sframe (void **ra_lst, int count, frame *frame)
> +{
> + _Unwind_Ptr sframe_vma, cfa, return_addr, ra_stack_loc, fp_stack_loc, pc,
> + frame_ptr;
> + int cfa_offset, fp_offset, ra_offset, i;
> + sframe_frame_row_entry fred, *frep = &fred;
> +
> + if (!ra_lst || !count)
> + return 0;
> +
> + for (i = 0; i < count; i++)
> + {
> + _Unwind_Reason_Code err;
> + struct dl_find_object data;
> + sframe_decoder_ctx decoder_context, *dctx = &decoder_context;
> +
> + /* Clean decoder context. */
> + memset (dctx, 0, sizeof (sframe_decoder_ctx));
> +
> + /* Load and set up the SFrame stack trace info for pc. */
> + if (_dl_find_object_helper ((void *) frame->pc, &data) < 0)
> + /* Force fallback to DWARF stacktracer. */
> + return 0;
> +
> + sframe_vma = (_Unwind_Ptr) data.dlfo_sframe;
> + if (!sframe_vma)
> + {
> +#ifdef MD_DECODE_SIGNAL_FRAME
> + /* I cannot find a valid SFrame section. Check if it is a
> + signal frame. */
> + if (MD_DECODE_SIGNAL_FRAME (frame) == _URC_NO_REASON)
> + {
> + ra_lst[i] = (void *) frame->pc;
> + continue;
> + }
> +#endif
> + /* Force fallback to DWARF stacktracer. */
> + return 0;
> + }
> +
> + /* Decode the specified SFrame buffer populate sframe's decoder
> + context. */
> + if (sframe_decode (dctx, (char *) data.dlfo_sframe) != _URC_NO_REASON)
> + /* Force fallback to DWARF stacktracer. */
> + return 0;
> +
> + pc = frame->pc - sframe_vma;
> + /* Find the SFrame Row Entry which contains the PC. */
> + if (sframe_find_fre (dctx, pc, frep) == _URC_END_OF_STACK)
> + {
> +#ifdef MD_DECODE_SIGNAL_FRAME
> + /* I cannot find any FREs, try to see if it is a signal
> + frame, and if so decode it. */
> + if (MD_DECODE_SIGNAL_FRAME (frame) == _URC_NO_REASON)
> + {
> + ra_lst[i] = (void *) frame->pc;
> + continue;
> + }
> +#endif
> +#ifdef MD_DETECT_OUTERMOST_FRAME
> + if (MD_DETECT_OUTERMOST_FRAME (frame) == _URC_END_OF_STACK)
> + return i;
> +#endif
> + /* Force fallback to DWARF stacktracer. */
> + return 0;
> + }
> +
> + /* Get the CFA offset from the FRE. If offset is unavailable,
> + sets err. */
> + cfa_offset = sframe_fre_get_cfa_offset (dctx, frep, &err);
> + if (err != _URC_NO_REASON)
> + /* Force fallback to DWARF stacktracer. */
> + return 0;
> +
> + /* Get CFA using base reg id from the FRE info. */
> + cfa = ((sframe_fre_get_base_reg_id (frep)
> + == SFRAME_BASE_REG_SP) ? frame->sp : frame->fp) + cfa_offset;
> +
> + /* Get the RA offset from the FRE. If the offset is
> + unavailable, sets err. */
> + ra_offset = sframe_fre_get_ra_offset (dctx, frep, &err);
> + if (err != _URC_NO_REASON)
> + /* Force fallback to DWARF stacktracer. */
> + return 0;
> +
> + /* RA offset is available, get the value stored in the stack
> + location. */
> + ra_stack_loc = cfa + ra_offset;
> + return_addr = read_stack_value (ra_stack_loc);
> +
> + ra_lst[i] = (void *) return_addr;
> +
> + /* Get the FP offset from the FRE. If the offset is
> + unavailable, sets err. */
> + fp_offset = sframe_fre_get_fp_offset (dctx, frep, &err);
> + frame_ptr = frame->fp;
> + if (err == _URC_NO_REASON)
> + {
> + /* FP offset is available, get the value stored in the stack
> + location. */
> + fp_stack_loc = cfa + fp_offset;
> + frame_ptr = read_stack_value (fp_stack_loc);
> + }
> +
> + /* Set up for the next frame. */
> + frame->fp = frame_ptr;
> + frame->sp = cfa + SFRAME_SP_VAL_OFFSET;
> + frame->pc = return_addr;
> + }
> + return i;
> +}
> +
> +libc_hidden_def (stacktrace_sframe);
> +
> +/* A noinline helper used to obtain the caller's current PC. */
> +
> +_Unwind_Ptr __attribute__ ((noinline))
> +getPC (void)
> +{
> + return (_Unwind_Ptr)
> + __builtin_extract_return_addr (__builtin_return_address (0));
> +}
> +
> +libc_hidden_def (getPC);
> +
> +/* A noinline helper used to obtain the caller's current SP. It
> + mimics gcc14's __builtin_stack_address() functionality. */
> +
> +_Unwind_Ptr __attribute__ ((noinline))
> +getSP (void)
> +{
> + return (_Unwind_Ptr) __builtin_dwarf_cfa() + SFRAME_SP_VAL_OFFSET;
> +}
> +
> +libc_hidden_def (getSP);
> diff --git a/sysdeps/generic/sframe.h b/sysdeps/generic/sframe.h
> new file mode 100644
> index 0000000000..ce8f6cdf7f
> --- /dev/null
> +++ b/sysdeps/generic/sframe.h
> @@ -0,0 +1,367 @@
> +/* SFrame format description.
> + Copyright (C) 2022-2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program; see the file COPYING. If not see
> + <http://www.gnu.org/licenses/>. */
> +
> +#ifndef _SFRAME_H
> +#define _SFRAME_H
> +
> +#include <sys/types.h>
> +#include <limits.h>
> +#include <stdint.h>
> +#include <unwind.h>
> +
> +#ifdef __cplusplus
> +extern "C"
> +{
> +#endif
> +
> +/* SFrame format.
> +
> + SFrame format is a simple format to represent the information needed
> + for generating vanilla backtraces. SFrame format keeps track of the
> + minimal necessary information needed for stack tracing:
> + - Canonical Frame Address (CFA)
> + - Frame Pointer (FP)
> + - Return Address (RA)
> +
> + The SFrame section itself has the following structure:
> +
> + +--------+------------+---------+
> + | file | function | frame |
> + | header | descriptor | row |
> + | | entries | entries |
> + +--------+------------+---------+
> +
> + The file header stores a magic number and version information, flags, and
> + the byte offset of each of the sections relative to the end of the header
> + itself. The file header also specifies the total number of Function
> + Descriptor Entries, Frame Row Entries and length of the FRE sub-section.
> +
> + Following the header is a list of Function Descriptor Entries (FDEs).
> + This list may be sorted if the flags in the file header indicate it to be
> + so. The sort order, if applicable, is the order of functions in the
> + .text.* sections in the resulting binary artifact. Each Function
> + Descriptor Entry specifies the start PC of a function, the size in bytes
> + of the function and an offset to its first Frame Row Entry (FRE). Each FDE
> + additionally also specifies the type of FRE it uses to encode the stack
> + trace information.
> +
> + Next, the SFrame Frame Row Entry sub-section is a list of variable size
> + records. Each entry represents stack trace information for a set of PCs
> + of the function. A singular Frame Row Entry is a self-sufficient record
> + which contains information on how to generate stack trace from the
> + applicable set of PCs.
> +
> + */
> +
> +
> +/* SFrame format versions. */
> +#define SFRAME_VERSION_1 1
> +#define SFRAME_VERSION_2 2
> +/* SFrame magic number. */
> +#define SFRAME_MAGIC 0xdee2
> +/* Current version of SFrame format. */
> +#define SFRAME_VERSION SFRAME_VERSION_2
> +
> +/* Various flags for SFrame. */
> +
> +/* Function Descriptor Entries are sorted on PC. */
> +#define SFRAME_F_FDE_SORTED 0x1
> +/* Functions preserve frame pointer. */
> +#define SFRAME_F_FRAME_POINTER 0x2
> +
> +#define SFRAME_CFA_FIXED_FP_INVALID 0
> +#define SFRAME_CFA_FIXED_RA_INVALID 0
> +
> +/* Supported ABIs/Arch. */
> +#define SFRAME_ABI_AARCH64_ENDIAN_BIG 1 /* AARCH64 big endian. */
> +#define SFRAME_ABI_AARCH64_ENDIAN_LITTLE 2 /* AARCH64 little endian. */
> +#define SFRAME_ABI_AMD64_ENDIAN_LITTLE 3 /* AMD64 little endian. */
> +
> +/* SFrame FRE types. */
> +#define SFRAME_FRE_TYPE_ADDR1 0
> +#define SFRAME_FRE_TYPE_ADDR2 1
> +#define SFRAME_FRE_TYPE_ADDR4 2
> +
> +/* SFrame Function Descriptor Entry types.
> +
> + The SFrame format has two possible representations for functions. The
> + choice of which type to use is made according to the instruction patterns
> + in the relevant program stub.
> +
> + An SFrame FDE of type SFRAME_FDE_TYPE_PCINC is an indication
> + that the PCs in the FREs should be treated as increments in bytes. This is
> + used for a bulk of the executable code of a program, which contains
> + instructions with no specific pattern.
> +
> + An SFrame FDE of type SFRAME_FDE_TYPE_PCMASK is an indication
> + that the PCs in the FREs should be treated as masks. This type is useful
> + for the cases when a small pattern of instructions in a program stub is
> + repeatedly to cover a specific functionality. Typical usescases are pltN
> + entries, trampolines etc. */
> +
> +/* Unwinders perform a (PC >= FRE_START_ADDR) to look up a matching FRE. */
> +#define SFRAME_FDE_TYPE_PCINC 0
> +/* Unwinders perform a (PC % REP_BLOCK_SIZE >= FRE_START_ADDR) to look up a
> + matching FRE. */
> +#define SFRAME_FDE_TYPE_PCMASK 1
> +
> +typedef struct sframe_preamble
> +{
> + uint16_t sfp_magic; /* Magic number (SFRAME_MAGIC). */
> + uint8_t sfp_version; /* Data format version number (SFRAME_VERSION). */
> + uint8_t sfp_flags; /* Flags. */
> +} __attribute__ ((packed)) sframe_preamble;
> +
> +typedef struct sframe_header
> +{
> + sframe_preamble sfh_preamble;
> + /* Information about the arch (endianness) and ABI. */
> + uint8_t sfh_abi_arch;
> + /* Offset for the Frame Pointer (FP) from CFA may be fixed for some
> + ABIs (e.g, in AMD64 when -fno-omit-frame-pointer is used). When fixed,
> + this field specifies the fixed stack frame offset and the individual
> + FREs do not need to track it. When not fixed, it is set to
> + SFRAME_CFA_FIXED_FP_INVALID, and the individual FREs may provide
> + the applicable stack frame offset, if any. */
> + int8_t sfh_cfa_fixed_fp_offset;
> + /* Offset for the Return Address from CFA is fixed for some ABIs
> + (e.g., AMD64 has it as CFA-8). When fixed, the header specifies the
> + fixed stack frame offset and the individual FREs do not track it. When
> + not fixed, it is set to SFRAME_CFA_FIXED_RA_INVALID, and individual
> + FREs provide the applicable stack frame offset, if any. */
> + int8_t sfh_cfa_fixed_ra_offset;
> + /* Number of bytes making up the auxiliary header, if any.
> + Some ABI/arch, in the future, may use this space for extending the
> + information in SFrame header. Auxiliary header is contained in
> + bytes sequentially following the sframe_header. */
> + uint8_t sfh_auxhdr_len;
> + /* Number of SFrame FDEs in this SFrame section. */
> + uint32_t sfh_num_fdes;
> + /* Number of SFrame Frame Row Entries. */
> + uint32_t sfh_num_fres;
> + /* Number of bytes in the SFrame Frame Row Entry section. */
> + uint32_t sfh_fre_len;
> + /* Offset of SFrame Function Descriptor Entry section. */
> + uint32_t sfh_fdeoff;
> + /* Offset of SFrame Frame Row Entry section. */
> + uint32_t sfh_freoff;
> +} __attribute__ ((packed)) sframe_header;
> +
> +#define SFRAME_V1_HDR_SIZE(sframe_hdr) \
> + ((sizeof (sframe_header) + (sframe_hdr).sfh_auxhdr_len))
> +
> +/* Two possible keys for executable (instruction) pointers signing. */
> +#define SFRAME_AARCH64_PAUTH_KEY_A 0 /* Key A. */
> +#define SFRAME_AARCH64_PAUTH_KEY_B 1 /* Key B. */
> +
> +typedef struct sframe_func_desc_entry
> +{
> + /* Function start address. Encoded as a signed offset, relative to the
> + beginning of the current FDE. */
> + int32_t sfde_func_start_address;
> + /* Size of the function in bytes. */
> + uint32_t sfde_func_size;
> + /* Offset of the first SFrame Frame Row Entry of the function, relative to the
> + beginning of the SFrame Frame Row Entry sub-section. */
> + uint32_t sfde_func_start_fre_off;
> + /* Number of frame row entries for the function. */
> + uint32_t sfde_func_num_fres;
> + /* Additional information for stack tracing from the function:
> + - 4-bits: Identify the FRE type used for the function.
> + - 1-bit: Identify the FDE type of the function - mask or inc.
> + - 1-bit: PAC authorization A/B key (aarch64).
> + - 2-bits: Unused.
> + ------------------------------------------------------------------------
> + | Unused | PAC auth A/B key (aarch64) | FDE type | FRE type |
> + | | Unused (amd64) | | |
> + ------------------------------------------------------------------------
> + 8 6 5 4 0 */
> + uint8_t sfde_func_info;
> + /* Size of the block of repeating insns. Used for SFrame FDEs of type
> + SFRAME_FDE_TYPE_PCMASK. */
> + uint8_t sfde_func_rep_size;
> + uint16_t sfde_func_padding2;
> +} __attribute__ ((packed)) sframe_func_desc_entry;
> +
> +/* Macros to compose and decompose function info in FDE. */
> +
> +/* Note: Set PAC auth key to SFRAME_AARCH64_PAUTH_KEY_A by default. */
> +#define SFRAME_V1_FUNC_INFO(fde_type, fre_enc_type) \
> + (((SFRAME_AARCH64_PAUTH_KEY_A & 0x1) << 5) | \
> + (((fde_type) & 0x1) << 4) | ((fre_enc_type) & 0xf))
> +
> +#define SFRAME_V1_FUNC_FRE_TYPE(data) ((data) & 0xf)
> +#define SFRAME_V1_FUNC_FDE_TYPE(data) (((data) >> 4) & 0x1)
> +#define SFRAME_V1_FUNC_PAUTH_KEY(data) (((data) >> 5) & 0x1)
> +
> +/* Set the pauth key as indicated. */
> +#define SFRAME_V1_FUNC_INFO_UPDATE_PAUTH_KEY(pauth_key, fde_info) \
> + ((((pauth_key) & 0x1) << 5) | ((fde_info) & 0xdf))
> +
> +/* Size of stack frame offsets in an SFrame Frame Row Entry. A single
> + SFrame FRE has all offsets of the same size. Offset size may vary
> + across frame row entries. */
> +#define SFRAME_FRE_OFFSET_1B 0
> +#define SFRAME_FRE_OFFSET_2B 1
> +#define SFRAME_FRE_OFFSET_4B 2
> +
> +/* An SFrame Frame Row Entry can be SP or FP based. */
> +#define SFRAME_BASE_REG_FP 0
> +#define SFRAME_BASE_REG_SP 1
> +
> +/* The index at which a specific offset is presented in the variable length
> + bytes of an FRE. */
> +#define SFRAME_FRE_CFA_OFFSET_IDX 0
> +/* The RA stack offset, if present, will always be at index 1 in the variable
> + length bytes of the FRE. */
> +#define SFRAME_FRE_RA_OFFSET_IDX 1
> +/* The FP stack offset may appear at offset 1 or 2, depending on the ABI as RA
> + may or may not be tracked. */
> +#define SFRAME_FRE_FP_OFFSET_IDX 2
> +
> +typedef struct sframe_fre_info
> +{
> + /* Information about
> + - 1 bit: base reg for CFA
> + - 4 bits: Number of offsets (N). A value of upto 3 is allowed to track
> + all three of CFA, FP and RA (fixed implicit order).
> + - 2 bits: information about size of the offsets (S) in bytes.
> + Valid values are SFRAME_FRE_OFFSET_1B, SFRAME_FRE_OFFSET_2B,
> + SFRAME_FRE_OFFSET_4B
> + - 1 bit: Mangled RA state bit (aarch64 only).
> + ----------------------------------------------------------------------------------
> + | Mangled-RA (aarch64) | Size of offsets | Number of offsets | base_reg |
> + | Unused (amd64) | | | |
> + ----------------------------------------------------------------------------------
> + 8 7 5 1 0
> +
> + */
> + uint8_t fre_info;
> +} sframe_fre_info;
> +
> +/* Macros to compose and decompose FRE info. */
> +
> +/* Note: Set mangled_ra_p to zero by default. */
> +#define SFRAME_V1_FRE_INFO(base_reg_id, offset_num, offset_size) \
> + (((0 & 0x1) << 7) | (((offset_size) & 0x3) << 5) | \
> + (((offset_num) & 0xf) << 1) | ((base_reg_id) & 0x1))
> +
> +/* Set the mangled_ra_p bit as indicated. */
> +#define SFRAME_V1_FRE_INFO_UPDATE_MANGLED_RA_P(mangled_ra_p, fre_info) \
> + ((((mangled_ra_p) & 0x1) << 7) | ((fre_info) & 0x7f))
> +
> +#define SFRAME_V1_FRE_CFA_BASE_REG_ID(data) ((data) & 0x1)
> +#define SFRAME_V1_FRE_OFFSET_COUNT(data) (((data) >> 1) & 0xf)
> +#define SFRAME_V1_FRE_OFFSET_SIZE(data) (((data) >> 5) & 0x3)
> +#define SFRAME_V1_FRE_MANGLED_RA_P(data) (((data) >> 7) & 0x1)
> +
> +/* SFrame Frame Row Entry definitions.
> +
> + Used for both AMD64 and AARCH64.
> +
> + An SFrame Frame Row Entry is a self-sufficient record which contains
> + information on how to generate the stack trace for the specified range of
> + PCs. Each SFrame Frame Row Entry is followed by S*N bytes, where:
> + S is the size of the stack frame offset for the FRE, and
> + N is the number of stack frame offsets in the FRE
> +
> + The interpretation of FRE stack offsets is ABI-specific:
> +
> + AMD64:
> + offset1 (interpreted as CFA = BASE_REG + offset1)
> + if FP is being tracked
> + offset2 (intrepreted as FP = CFA + offset2)
> + fi
> +
> + AARCH64:
> + offset1 (interpreted as CFA = BASE_REG + offset1)
> + if FP is being tracked (in other words, if frame record created)
> + offset2 (interpreted as RA = CFA + offset2)
> + offset3 (intrepreted as FP = CFA + offset3)
> + fi
> + Note that in AAPCS64, a frame record, if created, will save both FP and
> + LR on stack.
> +*/
> +
> +/* Used when SFRAME_FRE_TYPE_ADDR1 is specified as FRE type. */
> +typedef struct sframe_frame_row_entry_addr1
> +{
> + /* Start address of the frame row entry. Encoded as an 1-byte unsigned
> + offset, relative to the start address of the function. */
> + uint8_t sfre_start_address;
> + sframe_fre_info sfre_info;
> +} __attribute__ ((packed)) sframe_frame_row_entry_addr1;
> +
> +/* Upper limit of start address in sframe_frame_row_entry_addr1
> + is 0x100 (not inclusive). */
> +#define SFRAME_FRE_TYPE_ADDR1_LIMIT \
> + (1ULL << ((SFRAME_FRE_TYPE_ADDR1 + 1) * 8))
> +
> +/* Used when SFRAME_FRE_TYPE_ADDR2 is specified as FRE type. */
> +typedef struct sframe_frame_row_entry_addr2
> +{
> + /* Start address of the frame row entry. Encoded as an 2-byte unsigned
> + offset, relative to the start address of the function. */
> + uint16_t sfre_start_address;
> + sframe_fre_info sfre_info;
> +} __attribute__ ((packed)) sframe_frame_row_entry_addr2;
> +
> +/* Upper limit of start address in sframe_frame_row_entry_addr2
> + is 0x10000 (not inclusive). */
> +#define SFRAME_FRE_TYPE_ADDR2_LIMIT \
> + (1ULL << ((SFRAME_FRE_TYPE_ADDR2 * 2) * 8))
> +
> +/* Used when SFRAME_FRE_TYPE_ADDR4 is specified as FRE type. */
> +typedef struct sframe_frame_row_entry_addr4
> +{
> + /* Start address of the frame row entry. Encoded as a 4-byte unsigned
> + offset, relative to the start address of the function. */
> + uint32_t sfre_start_address;
> + sframe_fre_info sfre_info;
> +} __attribute__ ((packed)) sframe_frame_row_entry_addr4;
> +
> +/* Upper limit of start address in sframe_frame_row_entry_addr2
> + is 0x100000000 (not inclusive). */
> +#define SFRAME_FRE_TYPE_ADDR4_LIMIT \
> + (1ULL << ((SFRAME_FRE_TYPE_ADDR4 * 2) * 8))
> +
> +/* Used to pass frame information to stack trace routine. */
> +typedef struct cframe
> +{
> + _Unwind_Ptr pc;
> + _Unwind_Ptr sp;
> + _Unwind_Ptr fp;
> +} frame;
> +
> +/* SFrame stack tracing support */
> +int stacktrace_sframe (void **, int, frame *);
> +libc_hidden_proto (stacktrace_sframe);
> +
> +/* Helper used by SFrame tracing algorithm. */
> +_Unwind_Ptr getPC (void);
> +libc_hidden_proto (getPC);
> +
> +/* Helper used by SFrame tracing algorithm. */
> +_Unwind_Ptr getSP (void);
> +libc_hidden_proto (getSP);
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif /* _SFRAME_H */
> diff --git a/sysdeps/generic/uw-sigframe.h b/sysdeps/generic/uw-sigframe.h
> new file mode 100644
> index 0000000000..77dbff78b6
> --- /dev/null
> +++ b/sysdeps/generic/uw-sigframe.h
> @@ -0,0 +1,17 @@
> +/* Internal header file for handling signal frames. Generic version.
> + Copyright (C) 2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
Usually for generic headers we add some information of what kinf of
definition each architecture should provide (like MD_DETECT_OUTERMOST_FRAME
on aarch64).
More information about the Libc-alpha
mailing list