@@ -1,5 +1,5 @@
/* Return backtrace of current program state.
- Copyright (C) 2003-2024 Free Software Foundation, Inc.
+ Copyright (C) 2003-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
@@ -20,6 +20,7 @@
#include <stdlib.h>
#include <unwind.h>
#include <unwind-link.h>
+#include <sframe.h>
struct trace_arg
{
@@ -30,6 +31,26 @@ 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. */
+
+#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 +93,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)
@@ -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
new file mode 100644
@@ -0,0 +1,577 @@
+/* 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))
+
+/* 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)
+ 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)
+{
+ unsigned char 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);
+ 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,
+ int32_t start_ip_offset, int32_t end_ip_offset,
+ int32_t pc)
+{
+ int32_t start_ip, end_ip;
+ int32_t func_start_addr;
+ uint8_t rep_block_size;
+ uint32_t fde_type;
+ int32_t masked_pc;
+ bool mask_p;
+ bool ret;
+
+ ret = false;
+
+ if (!fdep)
+ return ret;
+
+ 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 (!mask_p)
+ {
+ start_ip = start_ip_offset + func_start_addr;
+ end_ip = end_ip_offset + func_start_addr;
+ ret = ((start_ip <= pc) && (end_ip >= pc));
+ }
+ else
+ {
+ /* For FDEs for repetitive pattern of insns, we need to return the FRE
+ where pc % rep_block_size is between start_ip_offset and
+ end_ip_offset. */
+ masked_pc = pc % rep_block_size;
+ ret = ((start_ip_offset <= masked_pc) && (end_ip_offset >= masked_pc));
+ }
+
+ return ret;
+}
+
+/* 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, cnt;
+
+ 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;
+ cnt = high;
+ while (low <= high)
+ {
+ int mid = low + (high - low) / 2;
+
+ if (fdp[mid].sfde_func_start_address == addr)
+ return fdp + mid;
+
+ if (fdp[mid].sfde_func_start_address < addr)
+ {
+ if (mid == (cnt - 1)) /* Check if it's the last one. */
+ return fdp + (cnt - 1);
+ else if (fdp[mid+1].sfde_func_start_address > addr)
+ return fdp + mid;
+ 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 invalid, sets errp. */
+
+int32_t
+sframe_fre_get_cfa_offset (sframe_decoder_ctx *dctx __attribute__ ((__unused__)),
+ sframe_frame_row_entry *fre)
+{
+ _Unwind_Reason_Code err;
+ return sframe_get_fre_offset (fre, SFRAME_FRE_CFA_OFFSET_IDX, &err);
+}
+
+/* Get the FP offset from the FRE. If the offset is invalid, 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 invalid, sets errp. */
+
+int32_t
+sframe_fre_get_ra_offset (sframe_decoder_ctx *dctx,
+ sframe_frame_row_entry *fre)
+{
+ _Unwind_Reason_Code err;
+ int8_t ra_offset = sframe_decoder_get_fixed_ra_offset (dctx);
+ /* 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, &err);
+}
+
+/* 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, fde_type, i;
+ int32_t start_ip_offset;
+ int32_t func_start_addr;
+ int32_t end_ip_offset;
+ const char *fres;
+ size_t size = 0;
+ int err = 0;
+ bool mask_p;
+
+ 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);
+ fde_type = sframe_get_fde_type (fdep);
+ mask_p = (fde_type == SFRAME_FDE_TYPE_PCMASK);
+
+ 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);
+
+ /* First FRE's start_ip must be more than pc for regular SFrame FDEs. */
+ if (i == 0 && !mask_p && (start_ip_offset + func_start_addr) > pc)
+ 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;
+}
new file mode 100644
@@ -0,0 +1,101 @@
+/* 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. */
+
+extern int32_t
+sframe_fre_get_cfa_offset (sframe_decoder_ctx *dtcx,
+ sframe_frame_row_entry *fre);
+
+/* Get the FP offset from the FRE. If the offset is invalid, 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. */
+
+extern int32_t
+sframe_fre_get_ra_offset (sframe_decoder_ctx *dctx,
+ sframe_frame_row_entry *fre);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SFRAME_API_H */
new file mode 100644
@@ -0,0 +1,157 @@
+/* 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>
+
+
+/* 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 uint64_t
+read_stack_value (uint64_t loc)
+{
+ uint64_t value = *((uint64_t *) loc);
+ return value;
+}
+
+/* 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;
+ 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 ((void *) frame->pc, &data) < 0)
+ return i;
+
+ 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
+ return i;
+ }
+
+ /* Decode the specified SFrame buffer populate sframe's decoder
+ context. */
+ if (sframe_decode (dctx, (char *) data.dlfo_sframe) == _URC_END_OF_STACK)
+ return i;
+
+ 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
+ return i;
+ }
+
+ /* Get the CFA offset from the FRE. */
+ cfa_offset = sframe_fre_get_cfa_offset (dctx, frep);
+
+ /* 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. */
+ ra_offset = sframe_fre_get_ra_offset (dctx, frep);
+
+ ra_stack_loc = cfa + ra_offset;
+ return_addr = read_stack_value (ra_stack_loc);
+
+ ra_lst[i] = (void *)return_addr;
+
+ /* Set up for the next frame. */
+ /* Get the FP offset from the FRE. If the offset is invalid,
+ sets errp. */
+ fp_offset = sframe_fre_get_fp_offset (dctx, frep, &err);
+ if (err == _URC_NO_REASON)
+ {
+ /* Frame is valid, get the value stored in the stack
+ location. */
+ fp_stack_loc = cfa + fp_offset;
+ frame->fp = read_stack_value (fp_stack_loc);
+ }
+
+ 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);
new file mode 100644
@@ -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 */
new file mode 100644
@@ -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/>. */