[V1 20/36] [SFrame-V3] libsframe: textual dump of fde type SFRAME_FDE_TYPE_FLEX
Indu Bhagat
indu.bhagat@oracle.com
Mon Dec 29 09:28:53 GMT 2025
More information about the Binutils mailing list
Mon Dec 29 09:28:53 GMT 2025
- Previous message (by thread): [V1 19/36] [SFrame-V3] libsframe: testsuite: add new argument to offset access APIs
- Next message (by thread): [V1 21/36] [SFrame-V3] sframe: gas: s390: aarch64: x86: add new backend hooks for FLEX FDE
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Refactor the SFrame textual dumper in sframe-dump.c to properly handle
the new FDE type.
In SFrame V2, the textual dumper could afford to be oblivious to the
exact DWARF register number for stack-pointer and frame-pointer
registers in each ABI. This is because a single bit was used to
differentiate between the two (irrespective of the ABI), and the dumper
could easily just use a:
const char *base_reg_str[] = {"fp", "sp"};
to get the register name.
With the introduction of new SFrame FDE type SFRAME_FDE_TYPE_FLEX, which
carry DWARF register numbers if applicable. E.g., for some patterns on
AMD64, one may see CFA is the value at 'r10+0'; or FP is the value at
'rbp+8'. For textual dump, we now need a mapping from:
- the ABI-specific frame-pointer to string "fp"
- the ABI-specific stack-pointer to string "sp"
This is done via the SFRAME_ABI_REG_MAP helper macros and the new
sframe_get_reg_name () API.
For registers other than stack-pointer and frame-pointer, the SFrame
textual dump does not print the register name (say, "rax", but just the
number (i.e., "r0").
Check the func_info2 byte and dispatch the stack frame row entry (FRE)
dumping to the correct function: either dump_sframe_func_fre_simple or
dump_sframe_func_fre_flex_topmost.
Ensure the display is consistent to previous semantics. When flex FDE
is in effect, there will always be an RA offset (after the CFA offsets)
present if FP offsets follow. So if a padding offset for RA is seen, we
will display "U". If no RA offset is seen, however, we will display a
"u" unless its an ABI where RA offset is fixed (in the latter case we
display "f").
libsframe/
* sframe-dump.c (SFRAME_SP): Define mapping from stack-pointer
register number to "sp".
(SFRAME_FP): Define mapping from frame-pointer register number
to "fp".
(SFRAME_ABI_REG_MAP): Helper macro to define per-ABI-arch
mappings.
(sframe_get_reg_name): Helper API to get register name.
(dump_sframe_func_with_fres): Refactor a bit...
(dump_sframe_func_fre_simple): ..into this.
(sframe_format_fre_disp): New definition.
(dump_sframe_func_fre_flex_topmost): Likewise.
(dump_sframe): Allow both SFrame version 2 and version 3.
---
[Changes from RFC]
- Display a "F" char for flexible FDE types.
- Fix dumping routines to account for one RA padding offset, when
present. In RFC, we were forcefully always emitting two offsets for
RA in all flex FDEs [Jens].
- Ensure the display is consistent to previous semantics. Added a
text in the commit log [Jens].
- Remove unnecessary inits of err variables with SFRAME_ERR [Jens].
- Rename out "flex_topmost" to "flex" in function name. Remove
references to "topmost" [Jens].
- Fix uninitialized errors in sframe-dump.c sframe_get_reg_name.
- Use SFRAME_V3_FDE_TYPE for flex_p.
[End of Changes from RFC]
---
libsframe/sframe-dump.c | 387 +++++++++++++++++++++++++++++++++-------
1 file changed, 320 insertions(+), 67 deletions(-)
diff --git a/libsframe/sframe-dump.c b/libsframe/sframe-dump.c
index e3ab98a655e..54a12266939 100644
--- a/libsframe/sframe-dump.c
+++ b/libsframe/sframe-dump.c
@@ -23,6 +23,75 @@
#include <inttypes.h>
#include "sframe-impl.h"
+typedef struct
+{
+ int reg_num;
+ const char *reg_name;
+} sframe_reg_map_entry;
+
+typedef struct
+{
+ const sframe_reg_map_entry *reg_map;
+ size_t map_size;
+} sframe_abi_reg_map;
+
+/* Register number - Register name pair.
+ Stack pointer (sp) and Frame pointer (fp) pairs. */
+#define SFRAME_SP(num) { num, "sp" }
+#define SFRAME_FP(num) { num, "fp" }
+
+#define SFRAME_ABI_REG_MAP(abi_str, ...) \
+ const sframe_reg_map_entry abi_str##_reg_map_entries[] = { __VA_ARGS__ }; \
+ const sframe_abi_reg_map abi_str##_sframe_abi_reg_map = { \
+ abi_str##_reg_map_entries, \
+ (sizeof (abi_str##_reg_map_entries) \
+ / sizeof (abi_str##_reg_map_entries[0])) \
+ };
+
+/* Create a map for each supported arch specifying DWARF register numbers for
+ stack pointer and frame pointer. */
+SFRAME_ABI_REG_MAP (amd64, SFRAME_SP (7), SFRAME_FP (6));
+SFRAME_ABI_REG_MAP (aarch64, SFRAME_SP (31), SFRAME_FP (29));
+SFRAME_ABI_REG_MAP (s390x, SFRAME_SP (15), SFRAME_FP (11));
+
+static const char *
+sframe_get_reg_name (uint8_t abi_arch, int reg_num, char *buf, size_t buf_size)
+{
+ const char *reg_name = NULL;
+ const sframe_abi_reg_map *abi_reg_map = NULL;
+
+ switch (abi_arch)
+ {
+ case SFRAME_ABI_AARCH64_ENDIAN_BIG:
+ case SFRAME_ABI_AARCH64_ENDIAN_LITTLE:
+ abi_reg_map = &aarch64_sframe_abi_reg_map;
+ break;
+ case SFRAME_ABI_AMD64_ENDIAN_LITTLE:
+ abi_reg_map = &amd64_sframe_abi_reg_map;
+ break;
+ case SFRAME_ABI_S390X_ENDIAN_BIG:
+ abi_reg_map = &s390x_sframe_abi_reg_map;
+ break;
+ default:
+ sframe_assert (false);
+ break;
+ }
+
+ if (abi_reg_map->reg_map[0].reg_num == reg_num)
+ reg_name = abi_reg_map->reg_map[0].reg_name;
+ else if (abi_reg_map->reg_map[1].reg_num == reg_num)
+ reg_name = abi_reg_map->reg_map[1].reg_name;
+
+ /* Handle fallback if name is missing or reg num is non-SP/FP. */
+ if (reg_name == NULL)
+ {
+ snprintf (buf, buf_size, "r%d", reg_num);
+ reg_name = buf;
+ }
+
+ return reg_name;
+}
+
/* Return TRUE if the SFrame section is associated with the aarch64 ABIs. */
static bool
@@ -142,18 +211,13 @@ dump_sframe_header (const sframe_decoder_ctx *sfd_ctx)
}
static void
-dump_sframe_func_with_fres (const sframe_decoder_ctx *sfd_ctx,
- unsigned int funcidx,
- uint64_t sec_addr)
+dump_sframe_func_fres_simple (const sframe_decoder_ctx *sfd_ctx,
+ unsigned int funcidx,
+ uint32_t num_fres,
+ int64_t func_start_pc_vma,
+ bool pc_mask_p)
{
uint32_t j = 0;
- uint32_t num_fres = 0;
- uint32_t func_size = 0;
- unsigned char func_info = 0;
- uint8_t rep_block_size = 0;
-
- uint64_t func_start_pc_vma = 0;
- uint64_t fre_start_pc_vma = 0;
const char *base_reg_str[] = {"fp", "sp"};
bool ra_undefined_p = false;
int32_t cfa_offset = 0;
@@ -165,68 +229,14 @@ dump_sframe_func_with_fres (const sframe_decoder_ctx *sfd_ctx,
sframe_frame_row_entry fre;
uint8_t ver = sframe_decoder_get_version (sfd_ctx);
- sframe_assert (ver == SFRAME_VERSION_3 || ver == SFRAME_VERSION_2);
- /* Get the SFrame function descriptor. */
- if (ver == SFRAME_VERSION_3)
- {
- int64_t func_start_addr = 0;
- sframe_decoder_get_funcdesc_v3 (sfd_ctx, funcidx, &num_fres,
- &func_size, &func_start_addr,
- &func_info, NULL,
- &rep_block_size);
- func_start_pc_vma = func_start_addr + sec_addr;
- }
- else
- {
- int32_t func_start_addr = 0;
- sframe_decoder_get_funcdesc_v2 (sfd_ctx, funcidx, &num_fres, &func_size,
- &func_start_addr, &func_info,
- &rep_block_size);
- func_start_pc_vma = func_start_addr + sec_addr;
- }
-
-/* Calculate the virtual memory address for function start pc. Some older
- SFrame V2 sections in ET_DYN or ET_EXEC may still have the
- SFRAME_F_FDE_FUNC_START_PCREL flag unset, and hence may be using the
- old encoding. Continue to support dumping the sections at least. */
- if (sframe_decoder_get_flags (sfd_ctx) & SFRAME_F_FDE_FUNC_START_PCREL)
- func_start_pc_vma += sframe_decoder_get_offsetof_fde_start_addr (sfd_ctx,
- funcidx,
- NULL);
-
- /* Gather all FDE attributes. */
- char attrs[16] = {0};
- bool fde_signal_p = SFRAME_V3_FDE_SIGNAL_P (func_info);
- sprintf (attrs, fde_signal_p ? "S" : "");
-
- printf ("\n func idx [%d]: pc = 0x%"PRIx64 ", size = %d bytes",
- funcidx,
- func_start_pc_vma,
- func_size);
- /* Print attributes if they exist. */
- if (attrs[0])
- printf (", attr = \"%s\"", attrs);
-
- if (is_sframe_abi_arch_aarch64 (sfd_ctx)
- && (SFRAME_V2_FUNC_PAUTH_KEY (func_info) == SFRAME_AARCH64_PAUTH_KEY_B))
- printf (", pauth = B key");
-
- /* Mark FDEs with [m] where the FRE start address is interpreted as a
- mask. */
- int pc_mask_p
- = (SFRAME_V2_FUNC_PC_TYPE (func_info) == SFRAME_V3_FDE_PCTYPE_MASK);
- const char *pc_mask_marker = (pc_mask_p ? "[m]" : " ");
- printf ("\n %-7s%-8s %-10s%-10s%-13s",
- "STARTPC", pc_mask_marker, "CFA", "FP", "RA");
-
char temp[100] = {0};
for (j = 0; j < num_fres; j++)
{
sframe_decoder_get_fre (sfd_ctx, funcidx, j, &fre);
- fre_start_pc_vma = (pc_mask_p
- ? fre.fre_start_addr
- : func_start_pc_vma + fre.fre_start_addr);
+ int64_t fre_start_pc_vma = (pc_mask_p
+ ? fre.fre_start_addr
+ : func_start_pc_vma + fre.fre_start_addr);
/* FIXME - fixup the err caching in array.
assert no error for base reg id and RA undefined. */
@@ -301,6 +311,249 @@ dump_sframe_func_with_fres (const sframe_decoder_ctx *sfd_ctx,
}
}
+/* Helper to safely format "reg+offset" or "(reg+offset)". */
+
+static void
+sframe_format_fre_disp (char *buf, size_t size, uint8_t abi_arch,
+ uint8_t reg_num, bool reg_p, int32_t offset,
+ bool deref_p)
+{
+ /* Initialize to string for CFA-based. */
+ const char *reg_name = "c";
+
+ /* Allocate space for the potential fallback name (e.g., "r12") */
+ char temp_reg_name[32] = {0};
+ if (reg_p)
+ reg_name = sframe_get_reg_name (abi_arch, reg_num, temp_reg_name,
+ sizeof (temp_reg_name));
+
+ if (deref_p)
+ snprintf (buf, size, "(%s%+d)", reg_name, offset);
+ else
+ snprintf (buf, size, "%s%+d", reg_name, offset);
+}
+
+static void
+dump_sframe_func_fres_flex (const sframe_decoder_ctx *sfd_ctx,
+ unsigned int funcidx,
+ uint32_t num_fres,
+ int64_t func_start_pc_vma,
+ bool pc_mask_p)
+{
+ uint32_t j = 0;
+ bool ra_undefined_p = false;
+ uint8_t cfa_reg, ra_reg, fp_reg;
+ bool cfa_deref_p, ra_deref_p, fp_deref_p;
+ int64_t fre_start_pc_vma = 0;
+ uint32_t fde_type = SFRAME_FDE_TYPE_FLEX;
+
+ sframe_frame_row_entry fre;
+ char temp[100] = {0};
+
+ for (j = 0; j < num_fres; j++)
+ {
+ sframe_decoder_get_fre (sfd_ctx, funcidx, j, &fre);
+
+ fre_start_pc_vma = (pc_mask_p
+ ? fre.fre_start_addr
+ : func_start_pc_vma + fre.fre_start_addr);
+
+ cfa_reg = 0;
+ ra_reg = 0;
+ fp_reg = 0;
+ cfa_deref_p = 0;
+ ra_deref_p = 0;
+ fp_deref_p = 0;
+
+ sframe_decoder_get_fre (sfd_ctx, funcidx, j, &fre);
+
+ fre_start_pc_vma = (pc_mask_p
+ ? fre.fre_start_addr
+ : func_start_pc_vma + fre.fre_start_addr);
+
+ int err_cfa_reg = 0;
+ int err_cfa_offset = 0;
+ int32_t cfa_reg_data = sframe_get_fre_offset (&fre,
+ SFRAME_FRE_CFA_OFFSET_IDX,
+ &err_cfa_reg);
+ int32_t cfa_offset = sframe_fre_get_cfa_offset (sfd_ctx, &fre, fde_type,
+ &err_cfa_offset);
+ sframe_assert (!err_cfa_reg && !err_cfa_offset);
+ bool cfa_reg_p = SFRAME_V3_FLEX_FDE_OFFSET_REG_P (cfa_reg_data);
+ if (cfa_reg_p)
+ {
+ cfa_reg = SFRAME_V3_FLEX_FDE_OFFSET_REG_NUM (cfa_reg_data);
+ cfa_deref_p = SFRAME_V3_FLEX_FDE_OFFSET_REG_DEREF_P (cfa_reg_data);
+ }
+
+ int err_ra_reg = 0;
+ int err_ra_offset = 0;
+ int32_t ra_reg_data = sframe_get_fre_offset (&fre,
+ SFRAME_FRE_RA_OFFSET_IDX * 2,
+ &err_ra_reg);
+ int32_t ra_offset = sframe_fre_get_ra_offset (sfd_ctx, &fre, fde_type,
+ &err_ra_offset);
+ // sframe_assert (!err_ra_reg && !err_ra_offset);
+ bool ra_reg_p = SFRAME_V3_FLEX_FDE_OFFSET_REG_P (ra_reg_data);
+ if (ra_reg_p)
+ {
+ ra_reg = SFRAME_V3_FLEX_FDE_OFFSET_REG_NUM (ra_reg_data);
+ ra_deref_p = SFRAME_V3_FLEX_FDE_OFFSET_REG_DEREF_P (ra_reg_data);
+ }
+
+ int err_fp_reg = 0;
+ int err_fp_offset = 0;
+ int fp_idx = SFRAME_FRE_FP_OFFSET_IDX * 2;
+ if (!err_ra_reg && ra_reg_data == SFRAME_FRE_RA_OFFSET_INVALID)
+ fp_idx -= 1;
+
+ int32_t fp_reg_data = sframe_get_fre_offset (&fre, fp_idx, &err_fp_reg);
+ int32_t fp_offset = sframe_fre_get_fp_offset (sfd_ctx, &fre, fde_type,
+ &err_fp_offset);
+ bool fp_reg_p = SFRAME_V3_FLEX_FDE_OFFSET_REG_P (fp_reg_data);
+ if (fp_reg_p)
+ {
+ fp_reg = SFRAME_V3_FLEX_FDE_OFFSET_REG_NUM (fp_reg_data);
+ fp_deref_p = SFRAME_V3_FLEX_FDE_OFFSET_REG_DEREF_P (fp_reg_data);
+ }
+
+ /* Dump VMA. */
+ printf ("\n");
+ printf (" %016"PRIx64, fre_start_pc_vma);
+
+ /* Dump RA undefined (FRE without any offsets). */
+ ra_undefined_p = sframe_fre_get_ra_undefined_p (sfd_ctx, &fre,
+ &err_ra_offset);
+ sframe_assert (!err_ra_offset);
+ if (ra_undefined_p)
+ {
+ printf (" RA undefined");
+ continue;
+ }
+
+ /* Dump CFA info. */
+ uint8_t abi_arch = sframe_decoder_get_abi_arch (sfd_ctx);
+ sframe_format_fre_disp (temp, sizeof (temp), abi_arch, cfa_reg,
+ cfa_reg_p, cfa_offset, cfa_deref_p);
+ printf (" %-10s", temp);
+
+ /* Dump FP info. */
+ if (!err_fp_reg && !err_fp_offset)
+ sframe_format_fre_disp (temp, sizeof (temp), abi_arch, fp_reg,
+ fp_reg_p, fp_offset, fp_deref_p);
+ else
+ strcpy (temp, "u");
+ printf ("%-10s", temp);
+
+ /* Dump RA info.
+ Even if an ABI does not track RA offset, e.g., AMD64, for flex
+ frame, it may have RA recovery from register. Else, display 'f'. */
+ if (err_ra_reg)
+ {
+ if (sframe_decoder_get_fixed_ra_offset (sfd_ctx)
+ != SFRAME_CFA_FIXED_RA_INVALID)
+ strcpy (temp, "f");
+ else
+ strcpy (temp, "u");
+ }
+ else if (ra_reg_data == SFRAME_FRE_RA_OFFSET_INVALID)
+ strcpy (temp, "U");
+ else
+ sframe_format_fre_disp (temp, sizeof (temp), abi_arch, ra_reg,
+ ra_reg_p, ra_offset, ra_deref_p);
+
+ /* Mark SFrame FRE's RA information with "[s]" if the RA is mangled
+ with signature bits. */
+ err_ra_offset = 0;
+ const char *ra_mangled_p_str
+ = ((sframe_fre_get_ra_mangled_p (sfd_ctx, &fre, &err_ra_offset))
+ ? "[s]" : " ");
+ sframe_assert (!err_ra_offset);
+ strcat (temp, ra_mangled_p_str);
+ printf ("%-13s", temp);
+ }
+}
+
+static void
+dump_sframe_func_with_fres (const sframe_decoder_ctx *sfd_ctx,
+ unsigned int funcidx,
+ uint64_t sec_addr)
+{
+ uint32_t num_fres = 0;
+ uint32_t func_size = 0;
+ uint64_t func_start_pc_vma = 0;
+ unsigned char func_info = 0;
+ unsigned char func_info2 = 0;
+ uint8_t rep_block_size = 0;
+
+ uint8_t ver = sframe_decoder_get_version (sfd_ctx);
+ sframe_assert (ver == SFRAME_VERSION_3 || ver == SFRAME_VERSION_2);
+ /* Get the SFrame function descriptor - all data including the index and
+ attributes. */
+ if (ver == SFRAME_VERSION_3)
+ {
+ int64_t func_start_addr = 0;
+ sframe_decoder_get_funcdesc_v3 (sfd_ctx, funcidx, &num_fres, &func_size,
+ &func_start_addr, &func_info,
+ &func_info2, &rep_block_size);
+ func_start_pc_vma = func_start_addr + sec_addr;
+ }
+ else
+ {
+ int32_t func_start_addr = 0;
+ sframe_decoder_get_funcdesc_v2 (sfd_ctx, funcidx, &num_fres, &func_size,
+ &func_start_addr, &func_info,
+ &rep_block_size);
+ func_start_pc_vma = func_start_addr + sec_addr;
+ }
+
+ /* Calculate the virtual memory address for function start pc. Some older
+ SFrame V2 sections in ET_DYN or ET_EXEC may still have the
+ SFRAME_F_FDE_FUNC_START_PCREL flag unset, and hence may be using the old
+ encoding. Continue to support dumping the sections at least. */
+ if (sframe_decoder_get_flags (sfd_ctx) & SFRAME_F_FDE_FUNC_START_PCREL)
+ func_start_pc_vma += sframe_decoder_get_offsetof_fde_start_addr (sfd_ctx,
+ funcidx,
+ NULL);
+
+ /* Gather all FDE attributes. Use a single snprintf to:
+ - Include 'S', if fde_signal_p is true
+ - Include 'F', if flex_p is true. */
+ char attrs[16] = {0};
+ bool fde_signal_p = SFRAME_V3_FDE_SIGNAL_P (func_info);
+ bool flex_p = (SFRAME_V3_FDE_TYPE (func_info2) == SFRAME_FDE_TYPE_FLEX);
+ snprintf (attrs, sizeof (attrs), "%s%s",
+ fde_signal_p ? "S" : "",
+ flex_p ? "F" : "");
+
+ printf ("\n func idx [%d]: pc = 0x%"PRIx64 ", size = %d bytes",
+ funcidx,
+ func_start_pc_vma,
+ func_size);
+ /* Print attributes if they exist. */
+ if (attrs[0])
+ printf (", attr = \"%s\"", attrs);
+
+ if (is_sframe_abi_arch_aarch64 (sfd_ctx)
+ && (SFRAME_V2_FUNC_PAUTH_KEY (func_info) == SFRAME_AARCH64_PAUTH_KEY_B))
+ printf (", pauth = B key");
+
+ /* Mark FDEs with [m] where the FRE start address is interpreted as a
+ mask. */
+ bool pc_mask_p
+ = (SFRAME_V2_FUNC_PC_TYPE (func_info) == SFRAME_V3_FDE_PCTYPE_MASK);
+ const char *pc_mask_marker = (pc_mask_p ? "[m]" : " ");
+ printf ("\n %-7s%-8s %-10s%-10s%-13s",
+ "STARTPC", pc_mask_marker, "CFA", "FP", "RA");
+
+ if (!flex_p)
+ dump_sframe_func_fres_simple (sfd_ctx, funcidx, num_fres,
+ func_start_pc_vma, pc_mask_p);
+ else
+ dump_sframe_func_fres_flex (sfd_ctx, funcidx, num_fres, func_start_pc_vma,
+ pc_mask_p);
+}
+
static void
dump_sframe_functions (const sframe_decoder_ctx *sfd_ctx, uint64_t sec_addr)
{
--
2.43.0
- Previous message (by thread): [V1 19/36] [SFrame-V3] libsframe: testsuite: add new argument to offset access APIs
- Next message (by thread): [V1 21/36] [SFrame-V3] sframe: gas: s390: aarch64: x86: add new backend hooks for FLEX FDE
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
More information about the Binutils mailing list