From: Indu Bhagat Date: Mon, 17 Nov 2025 18:39:14 +0000 (-0800) Subject: [SFrame-V3] libsframe: textual dump of fde type SFRAME_FDE_TYPE_FLEX_TOPMOST_FRAME X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=aa49b6c5cb454b608e26554bf82594ff2e376831;p=thirdparty%2Fbinutils-gdb.git [SFrame-V3] libsframe: textual dump of fde type SFRAME_FDE_TYPE_FLEX_TOPMOST_FRAME 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. --- diff --git a/libsframe/sframe-dump.c b/libsframe/sframe-dump.c index 6da571a841c..b125c7ae7e7 100644 --- a/libsframe/sframe-dump.c +++ b/libsframe/sframe-dump.c @@ -23,6 +23,77 @@ #include #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) {