aboutsummaryrefslogtreecommitdiff
path: root/sysdeps/generic
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/generic')
-rw-r--r--sysdeps/generic/Makefile3
-rw-r--r--sysdeps/generic/getrandom-internal.h2
-rw-r--r--sysdeps/generic/ldsodefs.h35
-rw-r--r--sysdeps/generic/libc-tsd.h60
-rw-r--r--sysdeps/generic/math-type-macros-double.h2
-rw-r--r--sysdeps/generic/math-type-macros-float.h2
-rw-r--r--sysdeps/generic/math-type-macros-float128.h9
-rw-r--r--sysdeps/generic/math-type-macros-ldouble.h2
-rw-r--r--sysdeps/generic/math-type-macros.h1
-rw-r--r--sysdeps/generic/sframe-read.c636
-rw-r--r--sysdeps/generic/sframe-read.h112
-rw-r--r--sysdeps/generic/sframe.c187
-rw-r--r--sysdeps/generic/sframe.h378
-rw-r--r--sysdeps/generic/sysdep.h3
-rw-r--r--sysdeps/generic/uw-sigframe.h31
15 files changed, 1397 insertions, 66 deletions
diff --git a/sysdeps/generic/Makefile b/sysdeps/generic/Makefile
index 3ed75dd..1be63b7 100644
--- a/sysdeps/generic/Makefile
+++ b/sysdeps/generic/Makefile
@@ -21,6 +21,9 @@ CFLAGS-wordcopy.c += -Wno-uninitialized
endif
ifeq ($(subdir),elf)
+ifeq ($(enable-gsframe),yes)
+sysdep_routines += sframe-read sframe
+endif
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/getrandom-internal.h b/sysdeps/generic/getrandom-internal.h
index 7c54194..4872598 100644
--- a/sysdeps/generic/getrandom-internal.h
+++ b/sysdeps/generic/getrandom-internal.h
@@ -19,7 +19,7 @@
#ifndef _GETRANDOM_INTERNAL_H
#define _GETRANDOM_INTERNAL_H
-static inline void __getrandom_early_init (_Bool)
+static inline void __getrandom_early_init (_Bool initial)
{
}
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 5b12a41..74025f1 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -368,8 +368,6 @@ struct rtld_global
size_t n_elements;
void (*free) (void *);
} _ns_unique_sym_table;
- /* Keep track of changes to each namespace' list. */
- struct r_debug_extended _ns_debug;
} _dl_ns[DL_NNS];
/* One higher than index of last used namespace. */
EXTERN size_t _dl_nns;
@@ -717,10 +715,23 @@ extern const ElfW(Phdr) *_dl_phdr;
extern size_t _dl_phnum;
#endif
+/* Possible values for the glibc.rtld.execstack tunable. */
+enum stack_tunable_mode
+ {
+ /* Do not allow executable stacks, even if program requires it. */
+ stack_tunable_mode_disable = 0,
+ /* Follows either ABI requirement, or the PT_GNU_STACK value. */
+ stack_tunable_mode_enable = 1,
+ /* Always enable an executable stack. */
+ stack_tunable_mode_force = 2
+ };
+
+void _dl_handle_execstack_tunable (void) attribute_hidden;
+
/* This function changes the permission of the memory region pointed
by STACK_ENDP to executable and update the internal memory protection
flags for future thread stack creation. */
-int _dl_make_stack_executable (void **stack_endp) attribute_hidden;
+int _dl_make_stack_executable (const void *stack_endp) attribute_hidden;
/* Variable pointing to the end of the stack (or close to it). This value
must be constant over the runtime of the application. Some programs
@@ -1076,15 +1087,29 @@ extern void _dl_debug_state (void);
rtld_hidden_proto (_dl_debug_state)
/* Initialize `struct r_debug_extended' for the namespace NS. LDBASE
- is the run-time load address of the dynamic linker, to be put in the
- `r_ldbase' member. Return the address of the structure. */
+ is the run-time load address of the dynamic linker, to be put in
+ the `r_ldbase' member.
+
+ Return the address of the r_debug structure for the namespace.
+ This is not merely a convenience or optimization, but it is
+ necessary for the LIBC_PROBE Systemtap/debugger probes to work
+ reliably: direct variable access can create probes that tools
+ cannot consume. */
extern struct r_debug *_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
attribute_hidden;
+/* This is called after relocation processing to handle a potential
+ copy relocation for _r_debug. */
+void _dl_debug_post_relocate (struct link_map *main_map) attribute_hidden;
+
/* Update the `r_map' member and return the address of `struct r_debug'
of the namespace NS. */
extern struct r_debug *_dl_debug_update (Lmid_t ns) attribute_hidden;
+/* Update R->r_state to STATE and notify the debugger by calling
+ _dl_debug_state. */
+void _dl_debug_change_state (struct r_debug *r, int state) attribute_hidden;
+
/* Initialize the basic data structure for the search paths. SOURCE
is either "LD_LIBRARY_PATH" or "--library-path".
GLIBC_HWCAPS_PREPEND adds additional glibc-hwcaps subdirectories to
diff --git a/sysdeps/generic/libc-tsd.h b/sysdeps/generic/libc-tsd.h
deleted file mode 100644
index b95e409..0000000
--- a/sysdeps/generic/libc-tsd.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/* libc-internal interface for thread-specific data. Stub or TLS version.
- Copyright (C) 1998-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/>. */
-
-#ifndef _GENERIC_LIBC_TSD_H
-#define _GENERIC_LIBC_TSD_H 1
-
-/* This file defines the following macros for accessing a small fixed
- set of thread-specific `void *' data used only internally by libc.
-
- __libc_tsd_define(CLASS, TYPE, KEY) -- Define or declare a datum with TYPE
- for KEY. CLASS can be `static' for
- keys used in only one source file,
- empty for global definitions, or
- `extern' for global declarations.
- __libc_tsd_address(TYPE, KEY) -- Return the `TYPE *' pointing to
- the current thread's datum for KEY.
- __libc_tsd_get(TYPE, KEY) -- Return the `TYPE' datum for KEY.
- __libc_tsd_set(TYPE, KEY, VALUE) -- Set the datum for KEY to VALUE.
-
- The set of available KEY's will usually be provided as an enum,
- and contains (at least):
- _LIBC_TSD_KEY_MALLOC
- _LIBC_TSD_KEY_DL_ERROR
- _LIBC_TSD_KEY_RPC_VARS
- All uses must be the literal _LIBC_TSD_* name in the __libc_tsd_* macros.
- Some implementations may not provide any enum at all and instead
- using string pasting in the macros. */
-
-#include <tls.h>
-
-/* When full support for __thread variables is available, this interface is
- just a trivial wrapper for it. Without TLS, this is the generic/stub
- implementation for wholly single-threaded systems.
-
- We don't define an enum for the possible key values, because the KEYs
- translate directly into variables by macro magic. */
-
-#define __libc_tsd_define(CLASS, TYPE, KEY) \
- CLASS __thread TYPE __libc_tsd_##KEY attribute_tls_model_ie;
-
-#define __libc_tsd_address(TYPE, KEY) (&__libc_tsd_##KEY)
-#define __libc_tsd_get(TYPE, KEY) (__libc_tsd_##KEY)
-#define __libc_tsd_set(TYPE, KEY, VALUE) (__libc_tsd_##KEY = (VALUE))
-
-#endif /* libc-tsd.h */
diff --git a/sysdeps/generic/math-type-macros-double.h b/sysdeps/generic/math-type-macros-double.h
index 1e84d3f..f03aea1 100644
--- a/sysdeps/generic/math-type-macros-double.h
+++ b/sysdeps/generic/math-type-macros-double.h
@@ -28,6 +28,8 @@
#define M_STRTO_NAN __strtod_nan
#define M_USE_BUILTIN(c) USE_ ##c ##_BUILTIN
+#define M_SET_RESTORE_ROUND(RM) SET_RESTORE_ROUND (RM)
+
#include <libm-alias-double.h>
#include <math-nan-payload-double.h>
diff --git a/sysdeps/generic/math-type-macros-float.h b/sysdeps/generic/math-type-macros-float.h
index 4aac524..445535a 100644
--- a/sysdeps/generic/math-type-macros-float.h
+++ b/sysdeps/generic/math-type-macros-float.h
@@ -30,6 +30,8 @@
/* GNU extension float constant macros. */
#define M_MLIT(c) c ## f
+#define M_SET_RESTORE_ROUND(RM) SET_RESTORE_ROUNDF (RM)
+
#include <libm-alias-float.h>
#include <math-nan-payload-float.h>
diff --git a/sysdeps/generic/math-type-macros-float128.h b/sysdeps/generic/math-type-macros-float128.h
index ad2310b..f64c1d3 100644
--- a/sysdeps/generic/math-type-macros-float128.h
+++ b/sysdeps/generic/math-type-macros-float128.h
@@ -19,6 +19,8 @@
#ifndef _MATH_TYPE_MACROS_FLOAT128
#define _MATH_TYPE_MACROS_FLOAT128
+#include <fenv_private.h>
+
#define M_LIT(c) __f128 (c)
#define M_PFX FLT128
#define M_SUF(c) c ## f128
@@ -30,6 +32,13 @@
#define M_MLIT(c) c ## f128
+/* fenv_private.h may not define SET_RESTORE_ROUNDF128. */
+#ifdef SET_RESTORE_ROUNDF128
+# define M_SET_RESTORE_ROUND(RM) SET_RESTORE_ROUNDF128 (RM)
+#else
+# define M_SET_RESTORE_ROUND(RM) SET_RESTORE_ROUNDL (RM)
+#endif
+
#include <libm-alias-float128.h>
#include <math-nan-payload-float128.h>
diff --git a/sysdeps/generic/math-type-macros-ldouble.h b/sysdeps/generic/math-type-macros-ldouble.h
index 931d5ec..00309cb 100644
--- a/sysdeps/generic/math-type-macros-ldouble.h
+++ b/sysdeps/generic/math-type-macros-ldouble.h
@@ -28,6 +28,8 @@
#define M_STRTO_NAN __strtold_nan
#define M_USE_BUILTIN(c) USE_ ##c ##L_BUILTIN
+#define M_SET_RESTORE_ROUND(RM) SET_RESTORE_ROUNDL (RM)
+
#include <libm-alias-ldouble.h>
#include <math-nan-payload-ldouble.h>
diff --git a/sysdeps/generic/math-type-macros.h b/sysdeps/generic/math-type-macros.h
index e3224a0..21c5fee 100644
--- a/sysdeps/generic/math-type-macros.h
+++ b/sysdeps/generic/math-type-macros.h
@@ -33,6 +33,7 @@
M_STRTO_NAN - Resolves to the internal libc function which
converts a string into the appropriate FLOAT nan
value.
+ M_SET_RESTORE_ROUND - Resolves to a SET_RESTORE_ROUND call for M_TYPE.
declare_mgen_alias(from,to)
This exposes the appropriate symbol(s) for a
diff --git a/sysdeps/generic/sframe-read.c b/sysdeps/generic/sframe-read.c
new file mode 100644
index 0000000..a6ebc42
--- /dev/null
+++ b/sysdeps/generic/sframe-read.c
@@ -0,0 +1,636 @@
+/* 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>
+
+/* 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 != NULL)
+ 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 != NULL)
+ 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)
+{
+ /* 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 & ~SFRAME_V2_F_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:
+ 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, size_t addr_size)
+{
+ if (frep == NULL)
+ return 0;
+
+ uint8_t fre_info = frep->fre_info;
+
+ return (addr_size + sizeof (frep->fre_info)
+ + sframe_fre_offset_bytes_size (fre_info));
+}
+
+/* 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_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 = 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;
+
+ 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);
+}
+
+/* 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, uint32_t *func_idx)
+{
+ 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 - 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 (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))
+ {
+ *func_idx = mid;
+ return fdp + mid;
+ }
+
+ if (sframe_decoder_get_secrel_func_start_addr (ctx, mid) < 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 func_idx;
+ 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, &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 = sframe_decoder_get_secrel_func_start_addr (ctx, func_idx);
+
+ 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);
+ if (addr_size == 0)
+ return _URC_END_OF_STACK;
+
+ frep->fre_info = *(uint8_t *)(fres + addr_size);
+ size = sframe_fre_entry_size (frep, addr_size);
+
+ 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 (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);
+ 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 0000000..1461421
--- /dev/null
+++ b/sysdeps/generic/sframe-read.h
@@ -0,0 +1,112 @@
+/* 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);
+
+/* 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
+
+#endif /* _SFRAME_API_H */
diff --git a/sysdeps/generic/sframe.c b/sysdeps/generic/sframe.c
new file mode 100644
index 0000000..ba0830d
--- /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 || !(data.dlfo_flags & DLFO_FLAG_SFRAME))
+ {
+#ifdef MD_DECODE_SIGNAL_FRAME
+ /* If there is no valid SFrame section or SFrame section is
+ corrupted then 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
+ /* If there are no valid FREs, check 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 0000000..e38adcf
--- /dev/null
+++ b/sysdeps/generic/sframe.h
@@ -0,0 +1,378 @@
+/* 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
+/* 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
+
+/* 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/sysdep.h b/sysdeps/generic/sysdep.h
index 4c0dda4..ef5eba2 100644
--- a/sysdeps/generic/sysdep.h
+++ b/sysdeps/generic/sysdep.h
@@ -45,6 +45,7 @@
# define cfi_adjust_cfa_offset(off) .cfi_adjust_cfa_offset off
# define cfi_offset(reg, off) .cfi_offset reg, off
# define cfi_rel_offset(reg, off) .cfi_rel_offset reg, off
+# define cfi_val_offset(reg, off) .cfi_val_offset reg, off
# define cfi_register(r1, r2) .cfi_register r1, r2
# define cfi_return_column(reg) .cfi_return_column reg
# define cfi_restore(reg) .cfi_restore reg
@@ -74,6 +75,8 @@
".cfi_offset " CFI_STRINGIFY(reg) "," CFI_STRINGIFY(off)
# define CFI_REL_OFFSET(reg, off) \
".cfi_rel_offset " CFI_STRINGIFY(reg) "," CFI_STRINGIFY(off)
+# define CFI_VAL_OFFSET(reg, off) \
+ ".cfi_val_offset " CFI_STRINGIFY(reg) "," CFI_STRINGIFY(off)
# define CFI_REGISTER(r1, r2) \
".cfi_register " CFI_STRINGIFY(r1) "," CFI_STRINGIFY(r2)
# define CFI_RETURN_COLUMN(reg) \
diff --git a/sysdeps/generic/uw-sigframe.h b/sysdeps/generic/uw-sigframe.h
new file mode 100644
index 0000000..b357f8a
--- /dev/null
+++ b/sysdeps/generic/uw-sigframe.h
@@ -0,0 +1,31 @@
+/* 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/>. */
+
+/* Each architecture that supports SFrame may need to define several
+ macros to handle exceptional cases during stack backtracing.
+
+ MD_DECODE_SIGNAL_FRAME(frame) should recover frame information when
+ a signal-related exception occurs. The input frame must contain a
+ valid program counter (PC) field. On success, the macro should
+ return _URC_NO_REASON.
+
+ MD_DETECT_OUTERMOST_FRAME(frame) is used to detect the outermost
+ stack frame. It returns _URC_NO_REASON upon successful
+ detection.
+
+ The FRAME structure is defined in sysdeps/generic/sframe.h */