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