]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
[SFrame-V3] libsframe: textual dump of fde type SFRAME_FDE_TYPE_FLEX_TOPMOST_FRAME
authorIndu Bhagat <indu.bhagat@oracle.com>
Mon, 17 Nov 2025 18:39:14 +0000 (10:39 -0800)
committerIndu Bhagat <indu.bhagat@oracle.com>
Tue, 9 Dec 2025 08:26:14 +0000 (00:26 -0800)
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_TOPMOST_FRAME, 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.

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.

libsframe/sframe-dump.c

index 6da571a841cd37f05cdac47d1f52ebe4a6db5acb..b125c7ae7e7d7d87a286032c823df51d510a74e3 100644 (file)
 #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 reg_name_buf[32];
+
+  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 (reg_name_buf, sizeof (reg_name_buf), "r%d", reg_num);
+      reg_name = reg_name_buf;
+    }
+
+  return reg_name;
+}
+
 /* Return TRUE if the SFrame section is associated with the aarch64 ABIs.  */
 
 static bool
@@ -142,18 +213,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_fre_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 +231,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_FUNC_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_FDE_TYPE (func_info) == SFRAME_FDE_TYPE_PCMASK);
-  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.  */
@@ -297,6 +309,234 @@ 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";
+
+  if (reg_p)
+    reg_name = sframe_get_reg_name (abi_arch, reg_num);
+
+  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_fre_flex_topmost (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_TOPMOST_FRAME;
+
+  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;
+      /* FP is not tracked, unless necessary for the FRE.  Assume no FP offset
+        by default.  */
+      int err_fp_offset = SFRAME_ERR;
+      int32_t fp_reg_data = sframe_get_fre_offset (&fre,
+                                                  SFRAME_FRE_FP_OFFSET_IDX * 2,
+                                                  &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 topmost
+        frame, it may have RA recovery from register.  Else, display 'f'.  */
+      if (!ra_reg_data && !ra_offset)
+       strcpy (temp, "f");
+      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.  */
+  char attrs[16] = {0};
+  bool fde_signal_p = SFRAME_V3_FUNC_SIGNAL_P (func_info);
+  snprintf (attrs, sizeof (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.  */
+  bool pc_mask_p
+    = (SFRAME_V2_FUNC_FDE_TYPE (func_info) == SFRAME_FDE_TYPE_PCMASK);
+  const char *pc_mask_marker = (pc_mask_p ? "[m]" : "   ");
+  printf ("\n    %-7s%-8s %-10s%-10s%-13s",
+         "STARTPC", pc_mask_marker, "CFA", "FP", "RA");
+
+  bool flex_p = (func_info2 == SFRAME_FDE_TYPE_FLEX_TOPMOST_FRAME);
+  if (!flex_p)
+    dump_sframe_func_fre_simple (sfd_ctx, funcidx, num_fres, func_start_pc_vma,
+                                pc_mask_p);
+  else
+    dump_sframe_func_fre_flex_topmost (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)
 {