#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
}
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;
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. */
}
}
+/* 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)
{