diff --git a/debug/Makefile b/debug/Makefile
index c6c1069b40..d36f024d8b 100644
--- a/debug/Makefile
+++ b/debug/Makefile
@@ -291,6 +291,9 @@ $(objpfx)tst-backtrace1: $(shared-thread-library)
 
 # When SFrame is enabled, make sure the dwarf unwinder is also exercised.
 ifeq ($(enable-gsframe),yes)
+tests-internal += tst-backtrace-flex
+LDLIBS-tst-backtrace-flex += $(common-objpfx)elf/sframe-read.o
+
 dw_unwind_pair := \
   tst-backtrace1-nosframe:tst-backtrace1 \
   tst-backtrace2-nosframe:tst-backtrace2 \
diff --git a/debug/tst-backtrace-flex.c b/debug/tst-backtrace-flex.c
new file mode 100644
index 0000000000..572c427fd4
--- /dev/null
+++ b/debug/tst-backtrace-flex.c
@@ -0,0 +1,164 @@
+/* Test SFrame v3 FLEX FRE decoding in the backtrace test suite.
+   Copyright (C) 2026 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/>.  */
+
+#include <sframe-read.h>
+#include <support/check.h>
+
+#define TEST_FRE0_DATAWORDS 5
+#define TEST_FRE1_DATAWORDS 4
+#define TEST_FDE_COUNT 1
+#define TEST_FRE_COUNT 2
+#define TEST_FUNC_SIZE 0x10
+
+#define FLEX_CFA_SP_CTRL \
+  SFRAME_V3_FLEX_FDE_CTRLWORD_ENCODE (7, 0, 1)
+#define FLEX_FP_RBP_CTRL \
+  SFRAME_V3_FLEX_FDE_CTRLWORD_ENCODE (6, 1, 1)
+#define FLEX_RA_REG_CTRL \
+  SFRAME_V3_FLEX_FDE_CTRLWORD_ENCODE (2, 0, 1)
+
+#define TEST_FRE_SECTION_LEN \
+  (sizeof (sframe_func_desc_attr_v3) \
+   + sizeof (sframe_frame_row_entry_addr1) + TEST_FRE0_DATAWORDS \
+   + sizeof (sframe_frame_row_entry_addr1) + TEST_FRE1_DATAWORDS)
+
+struct sframe_test_section
+{
+  sframe_header header;
+  sframe_func_desc_idx_v3 fde;
+  sframe_func_desc_attr_v3 attr;
+  sframe_frame_row_entry_addr1 fre0;
+  int8_t fre0_data[TEST_FRE0_DATAWORDS];
+  sframe_frame_row_entry_addr1 fre1;
+  int8_t fre1_data[TEST_FRE1_DATAWORDS];
+} __attribute__ ((packed));
+
+static const struct sframe_test_section test_sframe_section =
+{
+  .header =
+    {
+      .sfh_preamble = { SFRAME_MAGIC, SFRAME_VERSION_3, SFRAME_F_FDE_SORTED },
+      .sfh_abi_arch = SFRAME_ABI_AMD64_ENDIAN_LITTLE,
+      .sfh_cfa_fixed_fp_offset = SFRAME_CFA_FIXED_FP_INVALID,
+      .sfh_cfa_fixed_ra_offset = -8,
+      .sfh_auxhdr_len = 0,
+      .sfh_num_fdes = TEST_FDE_COUNT,
+      .sfh_num_fres = TEST_FRE_COUNT,
+      .sfh_fre_len = TEST_FRE_SECTION_LEN,
+      .sfh_fdeoff = 0,
+      .sfh_freoff = sizeof (sframe_func_desc_idx_v3),
+    },
+  .fde =
+    {
+      .sfdi_func_start_offset = 0,
+      .sfdi_func_size = TEST_FUNC_SIZE,
+      .sfdi_func_start_fre_off = 0,
+    },
+  .attr =
+    {
+      .sfda_func_num_fres = TEST_FRE_COUNT,
+      .sfda_func_info = SFRAME_V1_FUNC_INFO (SFRAME_V3_FDE_PCTYPE_INC,
+					     SFRAME_FRE_TYPE_ADDR1),
+      .sfda_func_info2 = SFRAME_V3_SET_FDE_TYPE (0, SFRAME_FDE_TYPE_FLEX),
+      .sfda_func_rep_size = 0,
+    },
+  /* FRE0 (start 0): CFA via SP+8, RA padding, FP via deref(FP+0).  */
+  .fre0 =
+    {
+      .sfre_start_address = 0,
+      .sfre_info =
+	{
+	  .fre_info = SFRAME_V1_FRE_INFO (SFRAME_BASE_REG_SP,
+					  TEST_FRE0_DATAWORDS,
+					  SFRAME_FRE_DATAWORD_1B),
+	},
+    },
+  .fre0_data = { FLEX_CFA_SP_CTRL, 8, SFRAME_FRE_RA_OFFSET_INVALID,
+		 FLEX_FP_RBP_CTRL, 0 },
+  /* FRE1 (start 8): CFA via SP+16, RA via register rule r2+0.  */
+  .fre1 =
+    {
+      .sfre_start_address = 8,
+      .sfre_info =
+	{
+	  .fre_info = SFRAME_V1_FRE_INFO (SFRAME_BASE_REG_SP,
+					  TEST_FRE1_DATAWORDS,
+					  SFRAME_FRE_DATAWORD_1B),
+	},
+    },
+  .fre1_data = { FLEX_CFA_SP_CTRL, 16, FLEX_RA_REG_CTRL, 0 },
+};
+
+static int
+do_test (void)
+{
+  sframe_decoder_ctx dctx = { 0 };
+  sframe_frame_row_entry fre;
+  _Unwind_Reason_Code err;
+  int32_t off;
+
+  err = __sframe_decode (&dctx, (const char *) &test_sframe_section);
+  TEST_COMPARE (err, _URC_NO_REASON);
+
+  err = __sframe_find_fre (&dctx, 0, &fre);
+  TEST_COMPARE (err, _URC_NO_REASON);
+  TEST_COMPARE (__sframe_fre_get_fde_type (&fre), SFRAME_FDE_TYPE_FLEX);
+
+  TEST_COMPARE (__sframe_fre_get_udata (&fre, 0, &err), FLEX_CFA_SP_CTRL);
+  TEST_COMPARE (err, _URC_NO_REASON);
+  TEST_COMPARE (__sframe_fre_get_udata (&fre, 2, &err),
+		SFRAME_FRE_RA_OFFSET_INVALID);
+  TEST_COMPARE (err, _URC_NO_REASON);
+
+  off = __sframe_fre_get_cfa_offset (&dctx, &fre, &err);
+  TEST_COMPARE (err, _URC_NO_REASON);
+  TEST_COMPARE (off, 8);
+
+  off = __sframe_fre_get_fp_offset (&dctx, &fre, &err);
+  TEST_COMPARE (err, _URC_NO_REASON);
+  TEST_COMPARE (off, 0);
+
+  off = __sframe_fre_get_ra_offset (&dctx, &fre, &err);
+  TEST_COMPARE (err, _URC_NO_REASON);
+  TEST_COMPARE (off, -8);
+
+  err = __sframe_find_fre (&dctx, 8, &fre);
+  TEST_COMPARE (err, _URC_NO_REASON);
+
+  TEST_COMPARE (__sframe_fre_get_udata (&fre, 2, &err), FLEX_RA_REG_CTRL);
+  TEST_COMPARE (err, _URC_NO_REASON);
+
+  off = __sframe_fre_get_cfa_offset (&dctx, &fre, &err);
+  TEST_COMPARE (err, _URC_NO_REASON);
+  TEST_COMPARE (off, 16);
+
+  off = __sframe_fre_get_ra_offset (&dctx, &fre, &err);
+  TEST_COMPARE (err, _URC_NO_REASON);
+  TEST_COMPARE (off, 0);
+
+  off = __sframe_fre_get_fp_offset (&dctx, &fre, &err);
+  TEST_COMPARE (err, _URC_END_OF_STACK);
+  TEST_COMPARE (off, 0);
+
+  err = __sframe_find_fre (&dctx, TEST_FUNC_SIZE, &fre);
+  TEST_COMPARE (err, _URC_END_OF_STACK);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/generic/sframe-read.c b/sysdeps/generic/sframe-read.c
index 9464cd71e6..ec139a49a7 100644
--- a/sysdeps/generic/sframe-read.c
+++ b/sysdeps/generic/sframe-read.c
@@ -451,6 +451,49 @@ sframe_get_fre_offset (sframe_frame_row_entry *fre,
     }
 }
 
+/* Get IDX'th data word from FRE as unsigned value.
+   Set ERRP as applicable.  */
+
+static uint32_t
+sframe_get_fre_udata (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)
+    {
+      uint8_t *sp = (uint8_t *)fre->fre_offsets;
+      return sp[idx];
+    }
+  else if (offset_size == SFRAME_FRE_OFFSET_2B)
+    {
+      uint16_t *sp = (uint16_t *)fre->fre_offsets;
+      return sp[idx];
+    }
+  else
+    {
+      uint32_t *ip = (uint32_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.  */
 
@@ -592,6 +635,26 @@ __sframe_fre_get_base_reg_id (sframe_frame_row_entry *fre)
   return SFRAME_V1_FRE_CFA_BASE_REG_ID (fre_info);
 }
 
+/* Get the FDE type associated with the FRE.  */
+
+uint8_t
+__sframe_fre_get_fde_type (sframe_frame_row_entry *fre)
+{
+  if (fre == NULL)
+    return SFRAME_FDE_TYPE_DEFAULT;
+
+  return fre->fre_fde_type;
+}
+
+/* Get IDX'th data word from FRE as unsigned value.  */
+
+uint32_t
+__sframe_fre_get_udata (sframe_frame_row_entry *fre, int idx,
+			_Unwind_Reason_Code *errp)
+{
+  return sframe_get_fre_udata (fre, idx, errp);
+}
+
 /* Get the CFA offset from the FRE.  If the offset is unavailable,
    sets errp.  */
 
@@ -600,7 +663,10 @@ __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);
+  bool flex_p = (__sframe_fre_get_fde_type (fre) == SFRAME_FDE_TYPE_FLEX);
+  uint32_t idx = flex_p ? SFRAME_FRE_CFA_OFFSET_IDX + 1
+			: SFRAME_FRE_CFA_OFFSET_IDX;
+  return sframe_get_fre_offset (fre, idx, errp);
 }
 
 /* Get the FP offset from the FRE.  If the offset is unavailable, sets
@@ -611,14 +677,10 @@ __sframe_fre_get_fp_offset (sframe_decoder_ctx *dctx,
 			    sframe_frame_row_entry *fre,
 			    _Unwind_Reason_Code *errp)
 {
+  _Unwind_Reason_Code fp_err = _URC_NO_REASON;
+  bool flex_p = (__sframe_fre_get_fde_type (fre) == SFRAME_FDE_TYPE_FLEX);
   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;
+  int8_t fixed_fp_offset = sframe_decoder_get_fixed_fp_offset (dctx);
 
   /* 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
@@ -627,7 +689,32 @@ __sframe_fre_get_fp_offset (sframe_decoder_ctx *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);
+  if (flex_p)
+    {
+      _Unwind_Reason_Code ra_reg_err;
+      uint32_t flex_ra_reg_data
+	= sframe_get_fre_udata (fre, SFRAME_FRE_RA_OFFSET_IDX * 2,
+				&ra_reg_err);
+      if (ra_reg_err == _URC_NO_REASON
+	  && flex_ra_reg_data == SFRAME_FRE_RA_OFFSET_INVALID)
+	fp_offset_idx = SFRAME_FRE_FP_OFFSET_IDX * 2;
+      else
+	fp_offset_idx = SFRAME_FRE_FP_OFFSET_IDX * 2 + 1;
+    }
+
+  int32_t fp_offset = sframe_get_fre_offset (fre, fp_offset_idx, &fp_err);
+
+  /* If the FP offset is fixed and the FRE does not provide one, use the
+     fixed offset in the SFrame header.  */
+  if ((!flex_p || (flex_p && fp_err != _URC_NO_REASON))
+      && fixed_fp_offset != SFRAME_CFA_FIXED_FP_INVALID)
+    {
+      *errp = _URC_NO_REASON;
+      return fixed_fp_offset;
+    }
+
+  *errp = fp_err;
+  return fp_offset;
 }
 
 /* Get the RA offset from the FRE.  If the offset is unavailable, sets
@@ -638,16 +725,48 @@ __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;
+  _Unwind_Reason_Code ra_err = _URC_NO_REASON;
+  bool flex_p = (__sframe_fre_get_fde_type (fre) == SFRAME_FDE_TYPE_FLEX);
+  int8_t fixed_ra_offset = sframe_decoder_get_fixed_ra_offset (dctx);
+
+  uint32_t ra_offset_idx = SFRAME_FRE_RA_OFFSET_IDX;
+  if (flex_p)
+    {
+      _Unwind_Reason_Code ra_reg_err;
+      uint32_t flex_ra_reg_data
+	= sframe_get_fre_udata (fre, SFRAME_FRE_RA_OFFSET_IDX * 2,
+				&ra_reg_err);
+
+      /* In FLEX rows, a missing RA rule means use the fixed RA offset from
+	 the SFrame header (if available).  */
+      if (ra_reg_err == _URC_NO_REASON
+	  && flex_ra_reg_data == SFRAME_FRE_RA_OFFSET_INVALID)
+	{
+	  if (fixed_ra_offset != SFRAME_CFA_FIXED_RA_INVALID)
+	    {
+	      *errp = _URC_NO_REASON;
+	      return fixed_ra_offset;
+	    }
+
+	  *errp = _URC_END_OF_STACK;
+	  return 0;
+	}
+
+      ra_offset_idx = SFRAME_FRE_RA_OFFSET_IDX * 2 + 1;
+    }
+  int32_t ra_offset = sframe_get_fre_offset (fre, ra_offset_idx, &ra_err);
 
-  /* 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;
+  /* If the RA offset is fixed and the FRE does not provide one, use the
+     fixed offset in the SFrame header.  */
+  if ((!flex_p || (flex_p && ra_err != _URC_NO_REASON))
+      && fixed_ra_offset != SFRAME_CFA_FIXED_RA_INVALID)
+    {
+      *errp = _URC_NO_REASON;
+      return fixed_ra_offset;
+    }
 
-  /* Otherwise, get the RA offset from the FRE.  */
-  return sframe_get_fre_offset (fre, SFRAME_FRE_RA_OFFSET_IDX, errp);
+  *errp = ra_err;
+  return ra_offset;
 }
 
 /* Decode the specified SFrame buffer SF_BUF and return the new SFrame
@@ -737,10 +856,6 @@ __sframe_find_fre (sframe_decoder_ctx *ctx, int64_t pc,
       || ctx->sfd_fres == NULL)
     return _URC_END_OF_STACK;
 
-  /* Glibc's SFrame unwinder currently supports only DEFAULT FDEs.  */
-  if (sframe_get_fde_type (&fdep, version) != SFRAME_FDE_TYPE_DEFAULT)
-    return _URC_END_OF_STACK;
-
   fre_type = sframe_get_fre_type (&fdep, version);
   fres = ctx->sfd_fres + fdep.func_start_fre_off;
   if (version == SFRAME_VERSION_3)
@@ -795,6 +910,7 @@ __sframe_find_fre (sframe_decoder_ctx *ctx, int64_t pc,
 	{
 	  /* Decode last FRE bits: offsets size.  */
 	  frep->fre_offsets = fres + addr_size + sizeof (frep->fre_info);
+	  frep->fre_fde_type = sframe_get_fde_type (&fdep, version);
 	  return _URC_NO_REASON;
 	}
 
diff --git a/sysdeps/generic/sframe-read.h b/sysdeps/generic/sframe-read.h
index 9bda6b1b19..ffb6e46c3a 100644
--- a/sysdeps/generic/sframe-read.h
+++ b/sysdeps/generic/sframe-read.h
@@ -38,7 +38,8 @@ typedef struct sframe_decoder_ctx
   int sfd_fre_nbytes;
 } sframe_decoder_ctx;
 
-#define MAX_NUM_STACK_OFFSETS	3
+/* Default FDEs use up to 3 data words, FLEX FDEs may use up to 6.  */
+#define MAX_NUM_STACK_OFFSETS	6
 
 /* User interfacing SFrame Row Entry.
    An abstraction provided by libsframe so the consumer is decoupled from
@@ -52,6 +53,7 @@ typedef struct sframe_frame_row_entry
 {
   uint32_t fre_start_addr;
   const char *fre_offsets;
+  uint8_t fre_fde_type;
   unsigned char fre_info;
 } sframe_frame_row_entry;
 
@@ -75,6 +77,15 @@ __sframe_find_fre (sframe_decoder_ctx *ctx, int64_t pc,
 extern uint8_t
 __sframe_fre_get_base_reg_id (sframe_frame_row_entry *fre);
 
+/* Get the FDE type associated with the FRE.  */
+extern uint8_t
+__sframe_fre_get_fde_type (sframe_frame_row_entry *fre);
+
+/* Get IDX'th data word from the FRE as unsigned value.  */
+extern uint32_t
+__sframe_fre_get_udata (sframe_frame_row_entry *fre, int idx,
+			_Unwind_Reason_Code *errp);
+
 /* Get the CFA offset from the FRE.  Sets ERRP if an error is
    detected.  */
 
diff --git a/sysdeps/generic/sframe.c b/sysdeps/generic/sframe.c
index 3c5721f513..30644494bb 100644
--- a/sysdeps/generic/sframe.c
+++ b/sysdeps/generic/sframe.c
@@ -34,6 +34,52 @@ read_stack_value (_Unwind_Ptr loc)
   return value;
 }
 
+/* Resolve a FLEX rule register number to a known register value.  */
+
+static _Unwind_Reason_Code
+sframe_flex_get_reg_value (frame *frame, uint32_t reg_num, _Unwind_Ptr *value)
+{
+#if defined SFRAME_FLEX_REG_FP && defined SFRAME_FLEX_REG_SP
+  if (reg_num == SFRAME_FLEX_REG_SP)
+    *value = frame->sp;
+  else if (reg_num == SFRAME_FLEX_REG_FP)
+    *value = frame->fp;
+  else
+    return _URC_END_OF_STACK;
+
+  return _URC_NO_REASON;
+#else
+  return _URC_END_OF_STACK;
+#endif
+}
+
+/* Apply one FLEX control-word + offset recovery rule.  */
+
+static _Unwind_Reason_Code
+sframe_apply_flex_rule (frame *frame, _Unwind_Ptr cfa, uint32_t reg_data,
+			int32_t offset, bool cfa_p, _Unwind_Ptr *value)
+{
+  _Unwind_Ptr base;
+  bool reg_p = SFRAME_V3_FLEX_FDE_CTRLWORD_REG_P (reg_data);
+  bool deref_p = SFRAME_V3_FLEX_FDE_CTRLWORD_DEREF_P (reg_data);
+
+  if (cfa_p && !reg_p)
+    return _URC_END_OF_STACK;
+
+  if (reg_p)
+    {
+      uint32_t reg_num = SFRAME_V3_FLEX_FDE_CTRLWORD_REGNUM (reg_data);
+      if (sframe_flex_get_reg_value (frame, reg_num, &base) != _URC_NO_REASON)
+	return _URC_END_OF_STACK;
+    }
+  else
+    base = cfa;
+
+  _Unwind_Ptr loc = base + (_Unwind_Ptr) ((intptr_t) offset);
+  *value = deref_p ? read_stack_value (loc) : loc;
+  return _URC_NO_REASON;
+}
+
 /* Helper to avoid PLT call in libc.  Fixes elf/check-localplt
    errors.  */
 
@@ -52,6 +98,7 @@ _dl_find_object_helper (void *address, struct dl_find_object *result)
 int
 __stacktrace_sframe (void **ra_lst, int count, frame *frame)
 {
+  uint32_t fde_type;
   _Unwind_Ptr sframe_vma, cfa, return_addr, ra_stack_loc, fp_stack_loc, pc,
     frame_ptr;
   int cfa_offset, fp_offset, ra_offset, i;
@@ -121,6 +168,8 @@ __stacktrace_sframe (void **ra_lst, int count, frame *frame)
       if (SFRAME_V2_FRE_RA_UNDEFINED_P (frep->fre_info))
 	return i;
 
+      fde_type = __sframe_fre_get_fde_type (frep);
+
       /* Get the CFA offset from the FRE.  If offset is unavailable,
 	 sets err.  */
       cfa_offset = __sframe_fre_get_cfa_offset (dctx, frep, &err);
@@ -128,9 +177,21 @@ __stacktrace_sframe (void **ra_lst, int count, frame *frame)
 	/* 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;
+      if (fde_type == SFRAME_FDE_TYPE_FLEX)
+	{
+	  uint32_t cfa_reg_data
+	    = __sframe_fre_get_udata (frep, SFRAME_FRE_CFA_OFFSET_IDX, &err);
+	  if (err != _URC_NO_REASON
+	      || sframe_apply_flex_rule (frame, 0, cfa_reg_data,
+					 cfa_offset, true, &cfa)
+		 != _URC_NO_REASON)
+	    /* Force fallback to DWARF stacktracer.  */
+	    return 0;
+	}
+      else
+	/* 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.  */
@@ -139,10 +200,35 @@ __stacktrace_sframe (void **ra_lst, int count, frame *frame)
 	/* 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);
+      if (fde_type == SFRAME_FDE_TYPE_FLEX)
+	{
+	  _Unwind_Reason_Code ra_reg_err;
+	  uint32_t ra_reg_data
+	    = __sframe_fre_get_udata (frep, SFRAME_FRE_RA_OFFSET_IDX * 2,
+				      &ra_reg_err);
+	  if (ra_reg_err == _URC_NO_REASON
+	      && ra_reg_data != SFRAME_FRE_RA_OFFSET_INVALID)
+	    {
+	      if (sframe_apply_flex_rule (frame, cfa, ra_reg_data, ra_offset,
+					  false, &return_addr)
+		  != _URC_NO_REASON)
+		/* Force fallback to DWARF stacktracer.  */
+		return 0;
+	    }
+	  else
+	    {
+	      /* Fixed RA offset case in FLEX uses default stack rule.  */
+	      ra_stack_loc = cfa + ra_offset;
+	      return_addr = read_stack_value (ra_stack_loc);
+	    }
+	}
+      else
+	{
+	  /* 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;
 
@@ -152,10 +238,39 @@ __stacktrace_sframe (void **ra_lst, int count, frame *frame)
       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);
+	  if (fde_type == SFRAME_FDE_TYPE_FLEX)
+	    {
+	      _Unwind_Reason_Code ra_reg_err, fp_reg_err;
+	      int fp_idx = SFRAME_FRE_FP_OFFSET_IDX * 2;
+	      uint32_t ra_reg_data
+		= __sframe_fre_get_udata (frep, SFRAME_FRE_RA_OFFSET_IDX * 2,
+					  &ra_reg_err);
+	      if (ra_reg_err == _URC_NO_REASON
+		  && ra_reg_data == SFRAME_FRE_RA_OFFSET_INVALID)
+		fp_idx -= 1;
+	      uint32_t fp_reg_data = __sframe_fre_get_udata (frep, fp_idx,
+							     &fp_reg_err);
+	      if (fp_reg_err == _URC_NO_REASON)
+		{
+		  if (sframe_apply_flex_rule (frame, cfa, fp_reg_data, fp_offset,
+					      false, &frame_ptr)
+		      != _URC_NO_REASON)
+		    /* Force fallback to DWARF stacktracer.  */
+		    return 0;
+		}
+	      else
+		{
+		  fp_stack_loc = cfa + fp_offset;
+		  frame_ptr = read_stack_value (fp_stack_loc);
+		}
+	    }
+	  else
+	    {
+	      /* 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.  */
diff --git a/sysdeps/generic/sframe.h b/sysdeps/generic/sframe.h
index 2bb720bb43..240a452a20 100644
--- a/sysdeps/generic/sframe.h
+++ b/sysdeps/generic/sframe.h
@@ -323,6 +323,22 @@ typedef struct sframe_func_desc_attr_v3
   ((info2) & SFRAME_V3_FDE_TYPE_MASK)
 #define SFRAME_V3_FDE_FRE_TYPE(info) (SFRAME_V2_FUNC_FRE_TYPE (info))
 #define SFRAME_V3_FDE_PC_TYPE(info) (SFRAME_V2_FUNC_PC_TYPE (info))
+#define SFRAME_V3_FDE_SIGNAL_P(info) (((info) >> 7) & 0x1)
+
+/* Set the FDE type in the info2 byte, preserving upper bits.  */
+#define SFRAME_V3_SET_FDE_TYPE(info2, fde_type) \
+  (((info2) & ~SFRAME_V3_FDE_TYPE_MASK) \
+   | ((fde_type) & SFRAME_V3_FDE_TYPE_MASK))
+
+#define SFRAME_V3_FDE_UPDATE_SIGNAL_P(signal_p, info) \
+  ((((signal_p) & 0x1) << 7) | ((info) & 0x7f))
+
+#define SFRAME_V3_FLEX_FDE_CTRLWORD_ENCODE(reg, deref_p, reg_p) \
+  ((((reg) << 0x3) | (0 << 0x2) | (((deref_p) & 0x1) << 0x1) | ((reg_p) & 0x1)))
+
+#define SFRAME_V3_FLEX_FDE_CTRLWORD_REGNUM(data) ((data) >> 3)
+#define SFRAME_V3_FLEX_FDE_CTRLWORD_DEREF_P(data) (((data) >> 1) & 0x1)
+#define SFRAME_V3_FLEX_FDE_CTRLWORD_REG_P(data) ((data) & 0x1)
 
 /* 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
@@ -353,6 +369,9 @@ typedef struct sframe_func_desc_attr_v3
    may or may not be tracked.  */
 #define SFRAME_FRE_FP_OFFSET_IDX    2
 
+/* Invalid RA offset.  Used as padding in FLEX FREs when RA is not tracked.  */
+#define SFRAME_FRE_RA_OFFSET_INVALID 0
+
 typedef struct sframe_fre_info
 {
   /* Information about
diff --git a/sysdeps/unix/sysv/linux/aarch64/uw-sigframe.h b/sysdeps/unix/sysv/linux/aarch64/uw-sigframe.h
index 7aa9e4bd60..51dfa7456f 100644
--- a/sysdeps/unix/sysv/linux/aarch64/uw-sigframe.h
+++ b/sysdeps/unix/sysv/linux/aarch64/uw-sigframe.h
@@ -32,6 +32,12 @@
 #define SVC_0           0x010000d4
 #endif
 
+/* DWARF register number of the frame-pointer for SFrame CFA tracking.  */
+#define SFRAME_FLEX_REG_FP 29
+/* DWARF register number of the stack-pointer for SFrame CFA and FP
+   tracking.  */
+#define SFRAME_FLEX_REG_SP 31
+
 #define MD_DECODE_SIGNAL_FRAME aarch64_decode_signal_frame
 
 static _Unwind_Reason_Code
diff --git a/sysdeps/unix/sysv/linux/x86_64/uw-sigframe.h b/sysdeps/unix/sysv/linux/x86_64/uw-sigframe.h
index 7813667789..1eea78a224 100644
--- a/sysdeps/unix/sysv/linux/x86_64/uw-sigframe.h
+++ b/sysdeps/unix/sysv/linux/x86_64/uw-sigframe.h
@@ -27,6 +27,12 @@
 
 /* SFrame is only supported by x86_64 targets.  */
 
+/* DWARF register number of the frame-pointer for SFrame CFA tracking.  */
+#define SFRAME_FLEX_REG_FP 6
+/* DWARF register number of the stack-pointer for SFrame CFA and FP
+   tracking.  */
+#define SFRAME_FLEX_REG_SP 7
+
 #define MD_DECODE_SIGNAL_FRAME x86_64_decode_signal_frame
 
 #ifdef __LP64__
