Home | History | Annotate | Line # | Download | only in libsframe
      1 /* sframe-dump.c - Textual dump of .sframe.
      2 
      3    Copyright (C) 2022-2026 Free Software Foundation, Inc.
      4 
      5    This file is part of libsframe.
      6 
      7    This program is free software; you can redistribute it and/or modify
      8    it under the terms of the GNU General Public License as published by
      9    the Free Software Foundation; either version 3 of the License, or
     10    (at your option) any later version.
     11 
     12    This program is distributed in the hope that it will be useful,
     13    but WITHOUT ANY WARRANTY; without even the implied warranty of
     14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15    GNU General Public License for more details.
     16 
     17    You should have received a copy of the GNU General Public License
     18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     19 
     20 #include <string.h>
     21 #include <stdio.h>
     22 #include <stdlib.h>
     23 #include <inttypes.h>
     24 #include "sframe-impl.h"
     25 
     26 typedef struct
     27 {
     28   unsigned int reg_num;
     29   const char *reg_name;
     30 } sframe_reg_map_entry;
     31 
     32 typedef struct
     33 {
     34   const sframe_reg_map_entry *reg_map;
     35   size_t map_size;
     36 } sframe_abi_reg_map;
     37 
     38 /* Register number - Register name pair.
     39    Stack pointer (sp) and Frame pointer (fp) pairs.  */
     40 #define SFRAME_SP(num) { num, "sp" }
     41 #define SFRAME_FP(num) { num, "fp" }
     42 
     43 #define SFRAME_ABI_REG_MAP(abi_str, ...) \
     44   const sframe_reg_map_entry abi_str##_reg_map_entries[] = { __VA_ARGS__ }; \
     45   const sframe_abi_reg_map abi_str##_sframe_abi_reg_map = { \
     46       abi_str##_reg_map_entries, \
     47       (sizeof (abi_str##_reg_map_entries) \
     48        / sizeof (abi_str##_reg_map_entries[0])) \
     49   };
     50 
     51 /* Create a map for each supported arch specifying DWARF register numbers for
     52    stack pointer and frame pointer.  */
     53 SFRAME_ABI_REG_MAP (amd64, SFRAME_SP (7), SFRAME_FP (6));
     54 SFRAME_ABI_REG_MAP (aarch64, SFRAME_SP (31), SFRAME_FP (29));
     55 SFRAME_ABI_REG_MAP (s390x, SFRAME_SP (15), SFRAME_FP (11));
     56 
     57 static const char *
     58 sframe_get_reg_name (uint8_t abi_arch, unsigned int reg_num, char *buf,
     59 		     size_t buf_size)
     60 {
     61   const char *reg_name = NULL;
     62   const sframe_abi_reg_map *abi_reg_map = NULL;
     63 
     64   switch (abi_arch)
     65     {
     66       case SFRAME_ABI_AARCH64_ENDIAN_BIG:
     67       case SFRAME_ABI_AARCH64_ENDIAN_LITTLE:
     68 	abi_reg_map = &aarch64_sframe_abi_reg_map;
     69 	break;
     70       case SFRAME_ABI_AMD64_ENDIAN_LITTLE:
     71 	abi_reg_map = &amd64_sframe_abi_reg_map;
     72 	break;
     73       case SFRAME_ABI_S390X_ENDIAN_BIG:
     74 	abi_reg_map = &s390x_sframe_abi_reg_map;
     75 	break;
     76       default:
     77 	sframe_assert (false);
     78 	break;
     79     }
     80 
     81   if (abi_reg_map->reg_map[0].reg_num == reg_num)
     82     reg_name = abi_reg_map->reg_map[0].reg_name;
     83   else if (abi_reg_map->reg_map[1].reg_num == reg_num)
     84     reg_name = abi_reg_map->reg_map[1].reg_name;
     85 
     86   /* Handle fallback if name is missing or reg num is non-SP/FP.  */
     87   if (reg_name == NULL)
     88     {
     89       snprintf (buf, buf_size, "r%u", reg_num);
     90       reg_name = buf;
     91     }
     92 
     93   return reg_name;
     94 }
     95 
     96 /* Return TRUE if the SFrame section is associated with the aarch64 ABIs.  */
     97 
     98 static bool
     99 is_sframe_abi_arch_aarch64 (const sframe_decoder_ctx *sfd_ctx)
    100 {
    101   bool aarch64_p = false;
    102 
    103   uint8_t abi_arch = sframe_decoder_get_abi_arch (sfd_ctx);
    104   if (abi_arch == SFRAME_ABI_AARCH64_ENDIAN_BIG
    105       || abi_arch == SFRAME_ABI_AARCH64_ENDIAN_LITTLE)
    106     aarch64_p = true;
    107 
    108   return aarch64_p;
    109 }
    110 
    111 /* Return TRUE if the SFrame section is associated with the s390x ABI.  */
    112 
    113 static bool
    114 is_sframe_abi_arch_s390x (const sframe_decoder_ctx *sfd_ctx)
    115 {
    116   return sframe_decoder_get_abi_arch (sfd_ctx) == SFRAME_ABI_S390X_ENDIAN_BIG;
    117 }
    118 
    119 static bool
    120 sframe_s390x_offset_regnum_p (int32_t offset, uint8_t ver)
    121 {
    122   if (ver == SFRAME_VERSION_2)
    123     return SFRAME_V2_S390X_OFFSET_IS_REGNUM (offset);
    124   else if (ver == SFRAME_VERSION_3)
    125     return false;
    126   else
    127     /* No other version is supported yet.  */
    128     sframe_assert (false);
    129 }
    130 
    131 static int
    132 sframe_s390x_offset_decode_regnum (int32_t offset, uint8_t ver)
    133 {
    134   if (ver == SFRAME_VERSION_2)
    135     return SFRAME_V2_S390X_OFFSET_DECODE_REGNUM (offset);
    136   else
    137     /* No other version is supported yet.  */
    138     sframe_assert (false);
    139 }
    140 
    141 static void
    142 dump_sframe_header_flags (const sframe_decoder_ctx *sfd_ctx)
    143 {
    144   const char *prefix = "Flags: ";
    145 
    146   uint8_t ver = sframe_decoder_get_version (sfd_ctx);
    147   uint8_t flags = sframe_decoder_get_flags (sfd_ctx);
    148 
    149   if (!flags)
    150     {
    151       printf ("%11sNONE\n", prefix);
    152       return;
    153     }
    154 
    155 #define PRINT_FLAG(x) \
    156   if (flags & (x)) \
    157     { flags = (flags & ~(x)); \
    158       printf ("%11s%s%s\n", prefix, #x, flags ? "," : ""); \
    159       prefix = " "; \
    160     }
    161 
    162   PRINT_FLAG (SFRAME_F_FDE_SORTED);
    163   /* SFRAME_F_FRAME_POINTER has been removed from SFrame V3 and beyond.  */
    164   if (ver == SFRAME_VERSION_2)
    165     PRINT_FLAG (SFRAME_F_FRAME_POINTER);
    166   PRINT_FLAG (SFRAME_F_FDE_FUNC_START_PCREL);
    167 #undef PRINT_FLAG
    168 
    169   /* Print any residual flags, should this implementation be out of sync when
    170      new flags are added.  */
    171   if (flags)
    172     printf ("%11s%d\n", prefix, flags);
    173 }
    174 
    175 static void
    176 dump_sframe_header (const sframe_decoder_ctx *sfd_ctx)
    177 {
    178   uint8_t ver;
    179   const char *ver_str = NULL;
    180   int8_t cfa_fixed_fp_offset;
    181   int8_t cfa_fixed_ra_offset;
    182   const sframe_header *header = &(sfd_ctx->sfd_header);
    183 
    184   /* Prepare SFrame section version string.  */
    185   const char *version_names[]
    186     = { "NULL",
    187 	"SFRAME_VERSION_1",
    188 	"SFRAME_VERSION_2",
    189 	"SFRAME_VERSION_3" };
    190 
    191   ver = sframe_decoder_get_version (sfd_ctx);
    192   if (ver <= SFRAME_VERSION)
    193     ver_str = version_names[ver];
    194 
    195   /* CFA fixed FP and RA offsets.  */
    196   cfa_fixed_fp_offset = header->sfh_cfa_fixed_fp_offset;
    197   cfa_fixed_ra_offset = header->sfh_cfa_fixed_ra_offset;
    198 
    199   const char* subsec_name = "Header";
    200   printf ("\n");
    201   printf ("  %s :\n", subsec_name);
    202   printf ("\n");
    203   printf ("    Version: %s\n", ver_str);
    204 
    205   dump_sframe_header_flags (sfd_ctx);
    206 
    207   if (cfa_fixed_fp_offset != SFRAME_CFA_FIXED_FP_INVALID)
    208     printf ("    CFA fixed FP offset: %d\n", cfa_fixed_fp_offset);
    209   if (cfa_fixed_ra_offset != SFRAME_CFA_FIXED_RA_INVALID)
    210     printf ("    CFA fixed RA offset: %d\n", cfa_fixed_ra_offset);
    211   printf ("    Num FDEs: %d\n", sframe_decoder_get_num_fidx (sfd_ctx));
    212   printf ("    Num FREs: %d\n", header->sfh_num_fres);
    213 }
    214 
    215 static void
    216 dump_sframe_func_fres_simple (const sframe_decoder_ctx *sfd_ctx,
    217 			      unsigned int funcidx,
    218 			      uint32_t num_fres,
    219 			      int64_t func_start_pc_vma,
    220 			      bool pc_mask_p)
    221 {
    222   uint32_t j = 0;
    223   const char *base_reg_str[] = {"fp", "sp"};
    224   bool ra_undefined_p = false;
    225   int32_t cfa_offset = 0;
    226   int32_t fp_offset = 0;
    227   int32_t ra_offset = 0;
    228   uint8_t base_reg_id = 0;
    229   int err[3] = {0, 0, 0};
    230 
    231   sframe_frame_row_entry fre;
    232 
    233   uint8_t ver = sframe_decoder_get_version (sfd_ctx);
    234   char temp[100] = {0};
    235   for (j = 0; j < num_fres; j++)
    236     {
    237       sframe_decoder_get_fre (sfd_ctx, funcidx, j, &fre);
    238 
    239       int64_t fre_start_pc_vma = (pc_mask_p
    240 				  ? fre.fre_start_addr
    241 				  : func_start_pc_vma + fre.fre_start_addr);
    242 
    243       /* FIXME - fixup the err caching in array.
    244 	 assert no error for base reg id and RA undefined.  */
    245       base_reg_id = sframe_fre_get_base_reg_id (&fre, &err[0]);
    246       ra_undefined_p = sframe_fre_get_ra_undefined_p (sfd_ctx, &fre, &err[0]);
    247       cfa_offset = sframe_fre_get_cfa_offset (sfd_ctx, &fre,
    248 					      SFRAME_FDE_TYPE_DEFAULT,
    249 					      &err[0]);
    250       fp_offset = sframe_fre_get_fp_offset (sfd_ctx, &fre,
    251 					    SFRAME_FDE_TYPE_DEFAULT, &err[1]);
    252       ra_offset = sframe_fre_get_ra_offset (sfd_ctx, &fre,
    253 					    SFRAME_FDE_TYPE_DEFAULT, &err[2]);
    254 
    255       /* Dump VMA.  */
    256       printf ("\n");
    257       printf ("    %016"PRIx64, fre_start_pc_vma);
    258 
    259       /* Dump RA undefined (FRE without any offsets).  */
    260       if (ra_undefined_p)
    261 	{
    262 	  printf ("  RA undefined");
    263 	  continue;
    264 	}
    265 
    266       /* Dump CFA info.  */
    267       sprintf (temp, "%s+%d", base_reg_str[base_reg_id], cfa_offset);
    268       printf ("  %-10s", temp);
    269 
    270       /* Dump SP/FP info.  */
    271       if (err[1] == 0)
    272 	{
    273 	  if (is_sframe_abi_arch_s390x (sfd_ctx)
    274 	      && sframe_s390x_offset_regnum_p (fp_offset, ver))
    275 	    sprintf (temp, "r%d",
    276 		     sframe_s390x_offset_decode_regnum (fp_offset, ver));
    277 	  else
    278 	    sprintf (temp, "c%+d", fp_offset);
    279 	}
    280       else
    281 	strcpy (temp, "u");
    282       printf ("%-10s", temp);
    283 
    284       /* Dump RA info.
    285 	 If an ABI does not track RA offset, e.g., AMD64, display 'f',
    286 	 else display the offset d as 'c+-d'.  */
    287       if (sframe_decoder_get_fixed_ra_offset (sfd_ctx)
    288 	  != SFRAME_CFA_FIXED_RA_INVALID)
    289 	strcpy (temp, "f");
    290       /* If an ABI does track RA offset, e.g. s390x, it can be a padding
    291 	 to represent FP without RA being saved on stack.  */
    292       else if (err[2] == 0 && ra_offset == SFRAME_FRE_RA_OFFSET_INVALID)
    293 	sprintf (temp, "U");
    294       else if (err[2] == 0)
    295 	{
    296 	  if (is_sframe_abi_arch_s390x (sfd_ctx)
    297 	      && sframe_s390x_offset_regnum_p (ra_offset, ver))
    298 	    sprintf (temp, "r%d",
    299 		     sframe_s390x_offset_decode_regnum (ra_offset, ver));
    300 	  else
    301 	    sprintf (temp, "c%+d", ra_offset);
    302 	}
    303       else
    304 	strcpy (temp, "u");
    305 
    306       /* Mark SFrame FRE's RA information with "[s]" if the RA is mangled
    307 	 with signature bits.  */
    308       const char *ra_mangled_p_str
    309 	= ((sframe_fre_get_ra_mangled_p (sfd_ctx, &fre, &err[2]))
    310 	   ? "[s]" : "   ");
    311       strcat (temp, ra_mangled_p_str);
    312       printf ("%-13s", temp);
    313     }
    314 }
    315 
    316 /* Helper to safely format "reg+offset" or "(reg+offset)". */
    317 
    318 static void
    319 sframe_format_fre_disp (char *buf, size_t size, uint8_t abi_arch,
    320 			unsigned int reg_num, bool reg_p, int32_t offset,
    321 			bool deref_p)
    322 {
    323   /* Initialize to string for CFA-based.  */
    324   const char *reg_name = "c";
    325 
    326   /* Allocate space for the potential fallback name (e.g., "r12") */
    327   char temp_reg_name[32] = {0};
    328   if (reg_p)
    329     reg_name = sframe_get_reg_name (abi_arch, reg_num, temp_reg_name,
    330 				    sizeof (temp_reg_name));
    331 
    332   if (deref_p)
    333     snprintf (buf, size, "(%s%+d)", reg_name, offset);
    334   else
    335     snprintf (buf, size, "%s%+d", reg_name, offset);
    336 }
    337 
    338 static void
    339 dump_sframe_func_fres_flex (const sframe_decoder_ctx *sfd_ctx,
    340 			    unsigned int funcidx,
    341 			    uint32_t num_fres,
    342 			    int64_t func_start_pc_vma,
    343 			    bool pc_mask_p)
    344 {
    345   uint32_t j = 0;
    346   bool ra_undefined_p = false;
    347   int64_t fre_start_pc_vma = 0;
    348   uint32_t fde_type = SFRAME_FDE_TYPE_FLEX;
    349 
    350   sframe_frame_row_entry fre;
    351   char temp[100] = {0};
    352 
    353   for (j = 0; j < num_fres; j++)
    354     {
    355       sframe_decoder_get_fre (sfd_ctx, funcidx, j, &fre);
    356 
    357       fre_start_pc_vma = (pc_mask_p
    358 			  ? fre.fre_start_addr
    359 			  : func_start_pc_vma + fre.fre_start_addr);
    360 
    361       /* Dump VMA.  */
    362       printf ("\n");
    363       printf ("    %016"PRIx64, fre_start_pc_vma);
    364 
    365       int err_ra_offset = 0;
    366       /* Dump RA undefined (FRE without any offsets).  */
    367       ra_undefined_p = sframe_fre_get_ra_undefined_p (sfd_ctx, &fre,
    368 						      &err_ra_offset);
    369       sframe_assert (!err_ra_offset);
    370       if (ra_undefined_p)
    371 	{
    372 	  printf ("  RA undefined");
    373 	  continue;
    374 	}
    375 
    376       unsigned int cfa_reg = 0, ra_reg = 0, fp_reg = 0;
    377       bool cfa_deref_p = 0, ra_deref_p = 0, fp_deref_p = 0;
    378 
    379       int err_cfa_reg = 0;
    380       int err_cfa_offset = 0;
    381       /* Read the Register/Control Data as unsigned.  */
    382       uint32_t cfa_reg_data
    383 	= sframe_get_fre_udata (&fre, SFRAME_FRE_CFA_OFFSET_IDX,
    384 				&err_cfa_reg);
    385       int32_t cfa_offset = sframe_fre_get_cfa_offset (sfd_ctx, &fre, fde_type,
    386 						      &err_cfa_offset);
    387       sframe_assert (!err_cfa_reg && !err_cfa_offset);
    388       bool cfa_reg_p = SFRAME_V3_FLEX_FDE_CTRLWORD_REG_P (cfa_reg_data);
    389       if (cfa_reg_p)
    390 	{
    391 	  cfa_reg = SFRAME_V3_FLEX_FDE_CTRLWORD_REGNUM (cfa_reg_data);
    392 	  cfa_deref_p = SFRAME_V3_FLEX_FDE_CTRLWORD_DEREF_P (cfa_reg_data);
    393 	}
    394 
    395       int err_ra_reg = 0;
    396       /* Read the Register/Control Data as unsigned.  */
    397       uint32_t ra_reg_data
    398 	= sframe_get_fre_udata (&fre, SFRAME_FRE_RA_OFFSET_IDX * 2,
    399 				&err_ra_reg);
    400       int32_t ra_offset = sframe_fre_get_ra_offset (sfd_ctx, &fre, fde_type,
    401 						    &err_ra_offset);
    402       bool ra_reg_p = SFRAME_V3_FLEX_FDE_CTRLWORD_REG_P (ra_reg_data);
    403       if (ra_reg_p)
    404 	{
    405 	  ra_reg = SFRAME_V3_FLEX_FDE_CTRLWORD_REGNUM (ra_reg_data);
    406 	  ra_deref_p = SFRAME_V3_FLEX_FDE_CTRLWORD_DEREF_P (ra_reg_data);
    407 	}
    408 
    409       int err_fp_reg = 0;
    410       int err_fp_offset = 0;
    411       int fp_idx = SFRAME_FRE_FP_OFFSET_IDX * 2;
    412       if (!err_ra_reg && ra_reg_data == SFRAME_FRE_RA_OFFSET_INVALID)
    413        fp_idx -= 1;
    414 
    415       /* Read the Register/Control Data as unsigned.  */
    416       uint32_t fp_reg_data = sframe_get_fre_udata (&fre, fp_idx, &err_fp_reg);
    417       int32_t fp_offset = sframe_fre_get_fp_offset (sfd_ctx, &fre, fde_type,
    418 						    &err_fp_offset);
    419       bool fp_reg_p = SFRAME_V3_FLEX_FDE_CTRLWORD_REG_P (fp_reg_data);
    420       if (fp_reg_p)
    421 	{
    422 	  fp_reg = SFRAME_V3_FLEX_FDE_CTRLWORD_REGNUM (fp_reg_data);
    423 	  fp_deref_p = SFRAME_V3_FLEX_FDE_CTRLWORD_DEREF_P (fp_reg_data);
    424 	}
    425 
    426       /* Dump CFA info.  */
    427       uint8_t abi_arch = sframe_decoder_get_abi_arch (sfd_ctx);
    428       sframe_format_fre_disp (temp, sizeof (temp), abi_arch, cfa_reg,
    429 			      cfa_reg_p, cfa_offset, cfa_deref_p);
    430       printf ("  %-10s", temp);
    431 
    432       /* Dump FP info.  */
    433       if (!err_fp_reg && !err_fp_offset)
    434 	sframe_format_fre_disp (temp, sizeof (temp), abi_arch, fp_reg,
    435 				fp_reg_p, fp_offset, fp_deref_p);
    436       else
    437 	strcpy (temp, "u");
    438       printf ("%-10s", temp);
    439 
    440       /* Dump RA info.
    441 	 Even if an ABI does not track RA offset, e.g., AMD64, for flex
    442 	 frame, it may have RA recovery from register.  Else, display 'f'.  */
    443       if (err_ra_reg)
    444 	{
    445 	  if (sframe_decoder_get_fixed_ra_offset (sfd_ctx)
    446 	      != SFRAME_CFA_FIXED_RA_INVALID)
    447 	    strcpy (temp, "f");
    448 	  else
    449 	    strcpy (temp, "u");
    450 	}
    451       else if (ra_reg_data == SFRAME_FRE_RA_OFFSET_INVALID)
    452 	strcpy (temp, "U");
    453       else
    454 	sframe_format_fre_disp (temp, sizeof (temp), abi_arch, ra_reg,
    455 				ra_reg_p, ra_offset, ra_deref_p);
    456 
    457       /* Mark SFrame FRE's RA information with "[s]" if the RA is mangled
    458 	 with signature bits.  */
    459       err_ra_offset = 0;
    460       const char *ra_mangled_p_str
    461 	= ((sframe_fre_get_ra_mangled_p (sfd_ctx, &fre, &err_ra_offset))
    462 	   ? "[s]" : "   ");
    463       sframe_assert (!err_ra_offset);
    464       strcat (temp, ra_mangled_p_str);
    465       printf ("%-13s", temp);
    466     }
    467 }
    468 
    469 static void
    470 dump_sframe_func_with_fres (const sframe_decoder_ctx *sfd_ctx,
    471 			    unsigned int funcidx,
    472 			    uint64_t sec_addr)
    473 {
    474   uint32_t num_fres = 0;
    475   uint32_t func_size = 0;
    476   uint64_t func_start_pc_vma = 0;
    477   unsigned char func_info = 0;
    478   unsigned char func_info2 = 0;
    479   uint8_t rep_block_size = 0;
    480 
    481   uint8_t ver = sframe_decoder_get_version (sfd_ctx);
    482   sframe_assert (ver == SFRAME_VERSION_3 || ver == SFRAME_VERSION_2);
    483   /* Get the SFrame function descriptor - all data including the index and
    484      attributes.  */
    485   if (ver == SFRAME_VERSION_3)
    486     {
    487       int64_t func_start_addr = 0;
    488       sframe_decoder_get_funcdesc_v3 (sfd_ctx, funcidx, &num_fres, &func_size,
    489 				      &func_start_addr, &func_info,
    490 				      &func_info2, &rep_block_size);
    491       func_start_pc_vma = func_start_addr + sec_addr;
    492     }
    493   else
    494     {
    495       int32_t func_start_addr = 0;
    496       sframe_decoder_get_funcdesc_v2 (sfd_ctx, funcidx, &num_fres, &func_size,
    497 				      &func_start_addr, &func_info,
    498 				      &rep_block_size);
    499       func_start_pc_vma = func_start_addr + sec_addr;
    500     }
    501 
    502   /* Calculate the virtual memory address for function start pc.  Some older
    503      SFrame V2 sections in ET_DYN or ET_EXEC may still have the
    504      SFRAME_F_FDE_FUNC_START_PCREL flag unset, and hence may be using the old
    505      encoding.  Continue to support dumping the sections at least.  */
    506   if (sframe_decoder_get_flags (sfd_ctx) & SFRAME_F_FDE_FUNC_START_PCREL)
    507     func_start_pc_vma += sframe_decoder_get_offsetof_fde_start_addr (sfd_ctx,
    508 								     funcidx,
    509 								     NULL);
    510 
    511   /* Gather all FDE attributes.  Use a single snprintf to:
    512      - Include 'S', if fde_signal_p is true
    513      - Include 'F', if flex_p is true.  */
    514   char attrs[16] = {0};
    515   bool fde_signal_p = SFRAME_V3_FDE_SIGNAL_P (func_info);
    516   bool flex_p = (SFRAME_V3_FDE_TYPE (func_info2) == SFRAME_FDE_TYPE_FLEX);
    517   snprintf (attrs, sizeof (attrs), "%s%s",
    518 	    fde_signal_p ? "S" : "",
    519 	    flex_p ? "F" : "");
    520 
    521   printf ("\n    func idx [%d]: pc = 0x%"PRIx64 ", size = %d bytes",
    522 	  funcidx,
    523 	  func_start_pc_vma,
    524 	  func_size);
    525   /* Print attributes if they exist.  */
    526   if (attrs[0])
    527     printf (", attr = \"%s\"", attrs);
    528 
    529   if (is_sframe_abi_arch_aarch64 (sfd_ctx)
    530       && (SFRAME_V2_FUNC_PAUTH_KEY (func_info) == SFRAME_AARCH64_PAUTH_KEY_B))
    531     printf (", pauth = B key");
    532 
    533   /* Mark FDEs with [m] where the FRE start address is interpreted as a
    534      mask.  */
    535   bool pc_mask_p
    536     = (SFRAME_V2_FUNC_PC_TYPE (func_info) == SFRAME_V3_FDE_PCTYPE_MASK);
    537   const char *pc_mask_marker = (pc_mask_p ? "[m]" : "   ");
    538   printf ("\n    %-7s%-8s %-10s%-10s%-13s",
    539 	  "STARTPC", pc_mask_marker, "CFA", "FP", "RA");
    540 
    541   if (!flex_p)
    542     dump_sframe_func_fres_simple (sfd_ctx, funcidx, num_fres,
    543 				  func_start_pc_vma, pc_mask_p);
    544   else
    545     dump_sframe_func_fres_flex (sfd_ctx, funcidx, num_fres, func_start_pc_vma,
    546 				pc_mask_p);
    547 }
    548 
    549 static void
    550 dump_sframe_functions (const sframe_decoder_ctx *sfd_ctx, uint64_t sec_addr)
    551 {
    552   uint32_t i;
    553   uint32_t num_fdes;
    554 
    555   const char* subsec_name = "Function Index";
    556   printf ("\n  %s :\n", subsec_name);
    557 
    558   num_fdes = sframe_decoder_get_num_fidx (sfd_ctx);
    559   for (i = 0; i < num_fdes; i++)
    560     {
    561       dump_sframe_func_with_fres (sfd_ctx, i, sec_addr);
    562       printf ("\n");
    563     }
    564 }
    565 
    566 void
    567 dump_sframe (const sframe_decoder_ctx *sfd_ctx, uint64_t sec_addr)
    568 {
    569   dump_sframe_header (sfd_ctx);
    570 
    571   uint8_t ver = sframe_decoder_get_version (sfd_ctx);
    572   /* Although newer gas and ld do not generate SFrame V2, continue to support
    573      textual dump of SFrame V2 sections ATM.  */
    574   if (ver == SFRAME_VERSION_3 || ver == SFRAME_VERSION_2)
    575     dump_sframe_functions (sfd_ctx, sec_addr);
    576   else
    577     printf ("\n No further information can be displayed.  %s",
    578 	    "SFrame version not supported\n");
    579 }
    580