Coverage Report

Created: 2026-03-10 08:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/binutils-gdb/libsframe/sframe-dump.c
Line
Count
Source
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
0
{
61
0
  const char *reg_name = NULL;
62
0
  const sframe_abi_reg_map *abi_reg_map = NULL;
63
64
0
  switch (abi_arch)
65
0
    {
66
0
      case SFRAME_ABI_AARCH64_ENDIAN_BIG:
67
0
      case SFRAME_ABI_AARCH64_ENDIAN_LITTLE:
68
0
  abi_reg_map = &aarch64_sframe_abi_reg_map;
69
0
  break;
70
0
      case SFRAME_ABI_AMD64_ENDIAN_LITTLE:
71
0
  abi_reg_map = &amd64_sframe_abi_reg_map;
72
0
  break;
73
0
      case SFRAME_ABI_S390X_ENDIAN_BIG:
74
0
  abi_reg_map = &s390x_sframe_abi_reg_map;
75
0
  break;
76
0
      default:
77
0
  sframe_assert (false);
78
0
  break;
79
0
    }
80
81
0
  if (abi_reg_map->reg_map[0].reg_num == reg_num)
82
0
    reg_name = abi_reg_map->reg_map[0].reg_name;
83
0
  else if (abi_reg_map->reg_map[1].reg_num == reg_num)
84
0
    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
0
  if (reg_name == NULL)
88
0
    {
89
0
      snprintf (buf, buf_size, "r%u", reg_num);
90
0
      reg_name = buf;
91
0
    }
92
93
0
  return reg_name;
94
0
}
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
0
{
101
0
  bool aarch64_p = false;
102
103
0
  uint8_t abi_arch = sframe_decoder_get_abi_arch (sfd_ctx);
104
0
  if (abi_arch == SFRAME_ABI_AARCH64_ENDIAN_BIG
105
0
      || abi_arch == SFRAME_ABI_AARCH64_ENDIAN_LITTLE)
106
0
    aarch64_p = true;
107
108
0
  return aarch64_p;
109
0
}
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
0
{
116
0
  return sframe_decoder_get_abi_arch (sfd_ctx) == SFRAME_ABI_S390X_ENDIAN_BIG;
117
0
}
118
119
static bool
120
sframe_s390x_offset_regnum_p (int32_t offset, uint8_t ver)
121
0
{
122
0
  if (ver == SFRAME_VERSION_2)
123
0
    return SFRAME_V2_S390X_OFFSET_IS_REGNUM (offset);
124
0
  else if (ver == SFRAME_VERSION_3)
125
0
    return false;
126
0
  else
127
    /* No other version is supported yet.  */
128
0
    sframe_assert (false);
129
0
}
130
131
static int
132
sframe_s390x_offset_decode_regnum (int32_t offset, uint8_t ver)
133
0
{
134
0
  if (ver == SFRAME_VERSION_2)
135
0
    return SFRAME_V2_S390X_OFFSET_DECODE_REGNUM (offset);
136
0
  else
137
    /* No other version is supported yet.  */
138
0
    sframe_assert (false);
139
0
}
140
141
static void
142
dump_sframe_header_flags (const sframe_decoder_ctx *sfd_ctx)
143
0
{
144
0
  const char *prefix = "Flags: ";
145
146
0
  uint8_t ver = sframe_decoder_get_version (sfd_ctx);
147
0
  uint8_t flags = sframe_decoder_get_flags (sfd_ctx);
148
149
0
  if (!flags)
150
0
    {
151
0
      printf ("%11sNONE\n", prefix);
152
0
      return;
153
0
    }
154
155
0
#define PRINT_FLAG(x) \
156
0
  if (flags & (x)) \
157
0
    { flags = (flags & ~(x)); \
158
0
      printf ("%11s%s%s\n", prefix, #x, flags ? "," : ""); \
159
0
      prefix = " "; \
160
0
    }
161
162
0
  PRINT_FLAG (SFRAME_F_FDE_SORTED);
163
  /* SFRAME_F_FRAME_POINTER has been removed from SFrame V3 and beyond.  */
164
0
  if (ver == SFRAME_VERSION_2)
165
0
    PRINT_FLAG (SFRAME_F_FRAME_POINTER);
166
0
  PRINT_FLAG (SFRAME_F_FDE_FUNC_START_PCREL);
167
0
#undef PRINT_FLAG
168
169
  /* Print any residual flags, should this implementation be out of sync when
170
     new flags are added.  */
171
0
  if (flags)
172
0
    printf ("%11s%d\n", prefix, flags);
173
0
}
174
175
static void
176
dump_sframe_header (const sframe_decoder_ctx *sfd_ctx)
177
0
{
178
0
  uint8_t ver;
179
0
  const char *ver_str = NULL;
180
0
  int8_t cfa_fixed_fp_offset;
181
0
  int8_t cfa_fixed_ra_offset;
182
0
  const sframe_header *header = &(sfd_ctx->sfd_header);
183
184
  /* Prepare SFrame section version string.  */
185
0
  const char *version_names[]
186
0
    = { "NULL",
187
0
  "SFRAME_VERSION_1",
188
0
  "SFRAME_VERSION_2",
189
0
  "SFRAME_VERSION_3" };
190
191
0
  ver = sframe_decoder_get_version (sfd_ctx);
192
0
  if (ver <= SFRAME_VERSION)
193
0
    ver_str = version_names[ver];
194
195
  /* CFA fixed FP and RA offsets.  */
196
0
  cfa_fixed_fp_offset = header->sfh_cfa_fixed_fp_offset;
197
0
  cfa_fixed_ra_offset = header->sfh_cfa_fixed_ra_offset;
198
199
0
  const char* subsec_name = "Header";
200
0
  printf ("\n");
201
0
  printf ("  %s :\n", subsec_name);
202
0
  printf ("\n");
203
0
  printf ("    Version: %s\n", ver_str);
204
205
0
  dump_sframe_header_flags (sfd_ctx);
206
207
0
  if (cfa_fixed_fp_offset != SFRAME_CFA_FIXED_FP_INVALID)
208
0
    printf ("    CFA fixed FP offset: %d\n", cfa_fixed_fp_offset);
209
0
  if (cfa_fixed_ra_offset != SFRAME_CFA_FIXED_RA_INVALID)
210
0
    printf ("    CFA fixed RA offset: %d\n", cfa_fixed_ra_offset);
211
0
  printf ("    Num FDEs: %d\n", sframe_decoder_get_num_fidx (sfd_ctx));
212
0
  printf ("    Num FREs: %d\n", header->sfh_num_fres);
213
0
}
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
0
{
222
0
  uint32_t j = 0;
223
0
  const char *base_reg_str[] = {"fp", "sp"};
224
0
  bool ra_undefined_p = false;
225
0
  int32_t cfa_offset = 0;
226
0
  int32_t fp_offset = 0;
227
0
  int32_t ra_offset = 0;
228
0
  uint8_t base_reg_id = 0;
229
0
  int err[3] = {0, 0, 0};
230
231
0
  sframe_frame_row_entry fre;
232
233
0
  uint8_t ver = sframe_decoder_get_version (sfd_ctx);
234
0
  char temp[100] = {0};
235
0
  for (j = 0; j < num_fres; j++)
236
0
    {
237
0
      sframe_decoder_get_fre (sfd_ctx, funcidx, j, &fre);
238
239
0
      int64_t fre_start_pc_vma = (pc_mask_p
240
0
          ? fre.fre_start_addr
241
0
          : 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
0
      base_reg_id = sframe_fre_get_base_reg_id (&fre, &err[0]);
246
0
      ra_undefined_p = sframe_fre_get_ra_undefined_p (sfd_ctx, &fre, &err[0]);
247
0
      cfa_offset = sframe_fre_get_cfa_offset (sfd_ctx, &fre,
248
0
                SFRAME_FDE_TYPE_DEFAULT,
249
0
                &err[0]);
250
0
      fp_offset = sframe_fre_get_fp_offset (sfd_ctx, &fre,
251
0
              SFRAME_FDE_TYPE_DEFAULT, &err[1]);
252
0
      ra_offset = sframe_fre_get_ra_offset (sfd_ctx, &fre,
253
0
              SFRAME_FDE_TYPE_DEFAULT, &err[2]);
254
255
      /* Dump VMA.  */
256
0
      printf ("\n");
257
0
      printf ("    %016"PRIx64, fre_start_pc_vma);
258
259
      /* Dump RA undefined (FRE without any offsets).  */
260
0
      if (ra_undefined_p)
261
0
  {
262
0
    printf ("  RA undefined");
263
0
    continue;
264
0
  }
265
266
      /* Dump CFA info.  */
267
0
      sprintf (temp, "%s+%d", base_reg_str[base_reg_id], cfa_offset);
268
0
      printf ("  %-10s", temp);
269
270
      /* Dump SP/FP info.  */
271
0
      if (err[1] == 0)
272
0
  {
273
0
    if (is_sframe_abi_arch_s390x (sfd_ctx)
274
0
        && sframe_s390x_offset_regnum_p (fp_offset, ver))
275
0
      sprintf (temp, "r%d",
276
0
         sframe_s390x_offset_decode_regnum (fp_offset, ver));
277
0
    else
278
0
      sprintf (temp, "c%+d", fp_offset);
279
0
  }
280
0
      else
281
0
  strcpy (temp, "u");
282
0
      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
0
      if (sframe_decoder_get_fixed_ra_offset (sfd_ctx)
288
0
    != SFRAME_CFA_FIXED_RA_INVALID)
289
0
  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
0
      else if (err[2] == 0 && ra_offset == SFRAME_FRE_RA_OFFSET_INVALID)
293
0
  sprintf (temp, "U");
294
0
      else if (err[2] == 0)
295
0
  {
296
0
    if (is_sframe_abi_arch_s390x (sfd_ctx)
297
0
        && sframe_s390x_offset_regnum_p (ra_offset, ver))
298
0
      sprintf (temp, "r%d",
299
0
         sframe_s390x_offset_decode_regnum (ra_offset, ver));
300
0
    else
301
0
      sprintf (temp, "c%+d", ra_offset);
302
0
  }
303
0
      else
304
0
  strcpy (temp, "u");
305
306
      /* Mark SFrame FRE's RA information with "[s]" if the RA is mangled
307
   with signature bits.  */
308
0
      const char *ra_mangled_p_str
309
0
  = ((sframe_fre_get_ra_mangled_p (sfd_ctx, &fre, &err[2]))
310
0
     ? "[s]" : "   ");
311
0
      strcat (temp, ra_mangled_p_str);
312
0
      printf ("%-13s", temp);
313
0
    }
314
0
}
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
0
{
323
  /* Initialize to string for CFA-based.  */
324
0
  const char *reg_name = "c";
325
326
  /* Allocate space for the potential fallback name (e.g., "r12") */
327
0
  char temp_reg_name[32] = {0};
328
0
  if (reg_p)
329
0
    reg_name = sframe_get_reg_name (abi_arch, reg_num, temp_reg_name,
330
0
            sizeof (temp_reg_name));
331
332
0
  if (deref_p)
333
0
    snprintf (buf, size, "(%s%+d)", reg_name, offset);
334
0
  else
335
0
    snprintf (buf, size, "%s%+d", reg_name, offset);
336
0
}
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
0
{
345
0
  uint32_t j = 0;
346
0
  bool ra_undefined_p = false;
347
0
  int64_t fre_start_pc_vma = 0;
348
0
  uint32_t fde_type = SFRAME_FDE_TYPE_FLEX;
349
350
0
  sframe_frame_row_entry fre;
351
0
  char temp[100] = {0};
352
353
0
  for (j = 0; j < num_fres; j++)
354
0
    {
355
0
      sframe_decoder_get_fre (sfd_ctx, funcidx, j, &fre);
356
357
0
      fre_start_pc_vma = (pc_mask_p
358
0
        ? fre.fre_start_addr
359
0
        : func_start_pc_vma + fre.fre_start_addr);
360
361
      /* Dump VMA.  */
362
0
      printf ("\n");
363
0
      printf ("    %016"PRIx64, fre_start_pc_vma);
364
365
0
      int err_ra_offset = 0;
366
      /* Dump RA undefined (FRE without any offsets).  */
367
0
      ra_undefined_p = sframe_fre_get_ra_undefined_p (sfd_ctx, &fre,
368
0
                  &err_ra_offset);
369
0
      sframe_assert (!err_ra_offset);
370
0
      if (ra_undefined_p)
371
0
  {
372
0
    printf ("  RA undefined");
373
0
    continue;
374
0
  }
375
376
0
      unsigned int cfa_reg = 0, ra_reg = 0, fp_reg = 0;
377
0
      bool cfa_deref_p = 0, ra_deref_p = 0, fp_deref_p = 0;
378
379
0
      int err_cfa_reg = 0;
380
0
      int err_cfa_offset = 0;
381
      /* Read the Register/Control Data as unsigned.  */
382
0
      uint32_t cfa_reg_data
383
0
  = sframe_get_fre_udata (&fre, SFRAME_FRE_CFA_OFFSET_IDX,
384
0
        &err_cfa_reg);
385
0
      int32_t cfa_offset = sframe_fre_get_cfa_offset (sfd_ctx, &fre, fde_type,
386
0
                  &err_cfa_offset);
387
0
      sframe_assert (!err_cfa_reg && !err_cfa_offset);
388
0
      bool cfa_reg_p = SFRAME_V3_FLEX_FDE_CTRLWORD_REG_P (cfa_reg_data);
389
0
      if (cfa_reg_p)
390
0
  {
391
0
    cfa_reg = SFRAME_V3_FLEX_FDE_CTRLWORD_REGNUM (cfa_reg_data);
392
0
    cfa_deref_p = SFRAME_V3_FLEX_FDE_CTRLWORD_DEREF_P (cfa_reg_data);
393
0
  }
394
395
0
      int err_ra_reg = 0;
396
      /* Read the Register/Control Data as unsigned.  */
397
0
      uint32_t ra_reg_data
398
0
  = sframe_get_fre_udata (&fre, SFRAME_FRE_RA_OFFSET_IDX * 2,
399
0
        &err_ra_reg);
400
0
      int32_t ra_offset = sframe_fre_get_ra_offset (sfd_ctx, &fre, fde_type,
401
0
                &err_ra_offset);
402
0
      bool ra_reg_p = SFRAME_V3_FLEX_FDE_CTRLWORD_REG_P (ra_reg_data);
403
0
      if (ra_reg_p)
404
0
  {
405
0
    ra_reg = SFRAME_V3_FLEX_FDE_CTRLWORD_REGNUM (ra_reg_data);
406
0
    ra_deref_p = SFRAME_V3_FLEX_FDE_CTRLWORD_DEREF_P (ra_reg_data);
407
0
  }
408
409
0
      int err_fp_reg = 0;
410
0
      int err_fp_offset = 0;
411
0
      int fp_idx = SFRAME_FRE_FP_OFFSET_IDX * 2;
412
0
      if (!err_ra_reg && ra_reg_data == SFRAME_FRE_RA_OFFSET_INVALID)
413
0
       fp_idx -= 1;
414
415
      /* Read the Register/Control Data as unsigned.  */
416
0
      uint32_t fp_reg_data = sframe_get_fre_udata (&fre, fp_idx, &err_fp_reg);
417
0
      int32_t fp_offset = sframe_fre_get_fp_offset (sfd_ctx, &fre, fde_type,
418
0
                &err_fp_offset);
419
0
      bool fp_reg_p = SFRAME_V3_FLEX_FDE_CTRLWORD_REG_P (fp_reg_data);
420
0
      if (fp_reg_p)
421
0
  {
422
0
    fp_reg = SFRAME_V3_FLEX_FDE_CTRLWORD_REGNUM (fp_reg_data);
423
0
    fp_deref_p = SFRAME_V3_FLEX_FDE_CTRLWORD_DEREF_P (fp_reg_data);
424
0
  }
425
426
      /* Dump CFA info.  */
427
0
      uint8_t abi_arch = sframe_decoder_get_abi_arch (sfd_ctx);
428
0
      sframe_format_fre_disp (temp, sizeof (temp), abi_arch, cfa_reg,
429
0
            cfa_reg_p, cfa_offset, cfa_deref_p);
430
0
      printf ("  %-10s", temp);
431
432
      /* Dump FP info.  */
433
0
      if (!err_fp_reg && !err_fp_offset)
434
0
  sframe_format_fre_disp (temp, sizeof (temp), abi_arch, fp_reg,
435
0
        fp_reg_p, fp_offset, fp_deref_p);
436
0
      else
437
0
  strcpy (temp, "u");
438
0
      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
0
      if (err_ra_reg)
444
0
  {
445
0
    if (sframe_decoder_get_fixed_ra_offset (sfd_ctx)
446
0
        != SFRAME_CFA_FIXED_RA_INVALID)
447
0
      strcpy (temp, "f");
448
0
    else
449
0
      strcpy (temp, "u");
450
0
  }
451
0
      else if (ra_reg_data == SFRAME_FRE_RA_OFFSET_INVALID)
452
0
  strcpy (temp, "U");
453
0
      else
454
0
  sframe_format_fre_disp (temp, sizeof (temp), abi_arch, ra_reg,
455
0
        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
0
      err_ra_offset = 0;
460
0
      const char *ra_mangled_p_str
461
0
  = ((sframe_fre_get_ra_mangled_p (sfd_ctx, &fre, &err_ra_offset))
462
0
     ? "[s]" : "   ");
463
0
      sframe_assert (!err_ra_offset);
464
0
      strcat (temp, ra_mangled_p_str);
465
0
      printf ("%-13s", temp);
466
0
    }
467
0
}
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
0
{
474
0
  uint32_t num_fres = 0;
475
0
  uint32_t func_size = 0;
476
0
  uint64_t func_start_pc_vma = 0;
477
0
  unsigned char func_info = 0;
478
0
  unsigned char func_info2 = 0;
479
0
  uint8_t rep_block_size = 0;
480
481
0
  uint8_t ver = sframe_decoder_get_version (sfd_ctx);
482
0
  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
0
  if (ver == SFRAME_VERSION_3)
486
0
    {
487
0
      int64_t func_start_addr = 0;
488
0
      sframe_decoder_get_funcdesc_v3 (sfd_ctx, funcidx, &num_fres, &func_size,
489
0
              &func_start_addr, &func_info,
490
0
              &func_info2, &rep_block_size);
491
0
      func_start_pc_vma = func_start_addr + sec_addr;
492
0
    }
493
0
  else
494
0
    {
495
0
      int32_t func_start_addr = 0;
496
0
      sframe_decoder_get_funcdesc_v2 (sfd_ctx, funcidx, &num_fres, &func_size,
497
0
              &func_start_addr, &func_info,
498
0
              &rep_block_size);
499
0
      func_start_pc_vma = func_start_addr + sec_addr;
500
0
    }
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
0
  if (sframe_decoder_get_flags (sfd_ctx) & SFRAME_F_FDE_FUNC_START_PCREL)
507
0
    func_start_pc_vma += sframe_decoder_get_offsetof_fde_start_addr (sfd_ctx,
508
0
                     funcidx,
509
0
                     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
0
  char attrs[16] = {0};
515
0
  bool fde_signal_p = SFRAME_V3_FDE_SIGNAL_P (func_info);
516
0
  bool flex_p = (SFRAME_V3_FDE_TYPE (func_info2) == SFRAME_FDE_TYPE_FLEX);
517
0
  snprintf (attrs, sizeof (attrs), "%s%s",
518
0
      fde_signal_p ? "S" : "",
519
0
      flex_p ? "F" : "");
520
521
0
  printf ("\n    func idx [%d]: pc = 0x%"PRIx64 ", size = %d bytes",
522
0
    funcidx,
523
0
    func_start_pc_vma,
524
0
    func_size);
525
  /* Print attributes if they exist.  */
526
0
  if (attrs[0])
527
0
    printf (", attr = \"%s\"", attrs);
528
529
0
  if (is_sframe_abi_arch_aarch64 (sfd_ctx)
530
0
      && (SFRAME_V2_FUNC_PAUTH_KEY (func_info) == SFRAME_AARCH64_PAUTH_KEY_B))
531
0
    printf (", pauth = B key");
532
533
  /* Mark FDEs with [m] where the FRE start address is interpreted as a
534
     mask.  */
535
0
  bool pc_mask_p
536
0
    = (SFRAME_V2_FUNC_PC_TYPE (func_info) == SFRAME_V3_FDE_PCTYPE_MASK);
537
0
  const char *pc_mask_marker = (pc_mask_p ? "[m]" : "   ");
538
0
  printf ("\n    %-7s%-8s %-10s%-10s%-13s",
539
0
    "STARTPC", pc_mask_marker, "CFA", "FP", "RA");
540
541
0
  if (!flex_p)
542
0
    dump_sframe_func_fres_simple (sfd_ctx, funcidx, num_fres,
543
0
          func_start_pc_vma, pc_mask_p);
544
0
  else
545
0
    dump_sframe_func_fres_flex (sfd_ctx, funcidx, num_fres, func_start_pc_vma,
546
0
        pc_mask_p);
547
0
}
548
549
static void
550
dump_sframe_functions (const sframe_decoder_ctx *sfd_ctx, uint64_t sec_addr)
551
0
{
552
0
  uint32_t i;
553
0
  uint32_t num_fdes;
554
555
0
  const char* subsec_name = "Function Index";
556
0
  printf ("\n  %s :\n", subsec_name);
557
558
0
  num_fdes = sframe_decoder_get_num_fidx (sfd_ctx);
559
0
  for (i = 0; i < num_fdes; i++)
560
0
    {
561
0
      dump_sframe_func_with_fres (sfd_ctx, i, sec_addr);
562
0
      printf ("\n");
563
0
    }
564
0
}
565
566
void
567
dump_sframe (const sframe_decoder_ctx *sfd_ctx, uint64_t sec_addr)
568
0
{
569
0
  dump_sframe_header (sfd_ctx);
570
571
0
  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
0
  if (ver == SFRAME_VERSION_3 || ver == SFRAME_VERSION_2)
575
0
    dump_sframe_functions (sfd_ctx, sec_addr);
576
0
  else
577
0
    printf ("\n No further information can be displayed.  %s",
578
0
      "SFrame version not supported\n");
579
0
}