@@ -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 \
new file mode 100644
@@ -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>
@@ -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;
}
@@ -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. */
@@ -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. */
@@ -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
@@ -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
@@ -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__