[V3,07/15] readelf/objdump: support for SFrame section

Message ID 20221030074450.1956074-8-indu.bhagat@oracle.com
State Not Applicable
Headers
Series Definition and support for SFrame unwind format |

Checks

Context Check Description
snail/binutils-gdb-check fail Git am fail log

Commit Message

Indu Bhagat Oct. 30, 2022, 7:44 a.m. UTC
  [Changes in V3]
  - Minor changes to uptake API signature changes in libsframe.
  - Fixed -Wformat warnings in dump_sframe_func_with_fres ().
[End of changes in V3]

[Changes in V2]
  - Fixed compilation errors seen on i686.
  - readelf: bugfix: --sframe=.eh_frame gives no error
    New behaviour:
    $ readelf --sframe=.eh_frame <Program>
    readelf: Error: SFrame decode failure: Buffer does not contain SFrame data.
    $ readelf --sframe=.foo <Program>
    readelf: Warning: Section '.foo' was not dumped because it does not exist
  - readelf: make --sframe arguments optional (similar to the command
    line option for objdump)
[End of changes in V2]

This patch adds support for SFrame in readelf and objdump.

include/ChangeLog:

	* sframe-api.h (dump_sframe): New function declaration.

ChangeLog:

	* binutils/Makefile.am: Add dependency on libsframe for
	readelf and objdump.
	* binutils/Makefile.in: Regenerate.
	* binutils/doc/binutils.texi: Document --sframe=[section].
	* binutils/doc/sframe.options.texi: New file.
	* binutils/objdump.c: Add support for SFrame format.
	* binutils/readelf.c: Likewise.
	* include/sframe-api.h: Add new API for dumping .sframe
	section.
	* libsframe/Makefile.am: Add sframe-dump.c.
	* libsframe/Makefile.in: Regenerate.
	* libsframe/sframe-dump.c: New file.
---
 binutils/Makefile.am             |   8 +-
 binutils/Makefile.in             |   8 +-
 binutils/doc/binutils.texi       |   4 +
 binutils/doc/sframe.options.texi |  10 ++
 binutils/objdump.c               |  75 +++++++++++++
 binutils/readelf.c               |  63 +++++++++++
 include/sframe-api.h             |   3 +
 libsframe/Makefile.am            |   2 +-
 libsframe/Makefile.in            |  12 +-
 libsframe/sframe-dump.c          | 181 +++++++++++++++++++++++++++++++
 10 files changed, 355 insertions(+), 11 deletions(-)
 create mode 100644 binutils/doc/sframe.options.texi
 create mode 100644 libsframe/sframe-dump.c
  

Patch

diff --git a/binutils/Makefile.am b/binutils/Makefile.am
index 6d974fd3f8a..ce5fa1d39bb 100644
--- a/binutils/Makefile.am
+++ b/binutils/Makefile.am
@@ -228,7 +228,7 @@  installcheck-local:
 # which depends on libintl, since we don't know whether LIBINTL_DEP will be
 # non-empty until configure time.  Ugh!
 size_DEPENDENCIES =      $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
-objdump_DEPENDENCIES =   $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB) $(OPCODES) $(LIBCTF) $(OBJDUMP_PRIVATE_OFILES)
+objdump_DEPENDENCIES =   $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB) $(OPCODES) $(LIBCTF) $(OBJDUMP_PRIVATE_OFILES) $(LIBSFRAME)
 nm_new_DEPENDENCIES =    $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
 ar_DEPENDENCIES =        $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
 strings_DEPENDENCIES =   $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
@@ -243,7 +243,7 @@  dlltool_DEPENDENCIES =   $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
 windres_DEPENDENCIES =   $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
 windmc_DEPENDENCIES =    $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
 addr2line_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
-readelf_DEPENDENCIES =   $(LIBINTL_DEP) $(LIBIBERTY) $(LIBCTF_NOBFD)
+readelf_DEPENDENCIES =   $(LIBINTL_DEP) $(LIBIBERTY) $(LIBCTF_NOBFD) $(LIBSFRAME)
 elfedit_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY)
 dllwrap_DEPENDENCIES =   $(LIBINTL_DEP) $(LIBIBERTY)
 bfdtest1_DEPENDENCIES =  $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
@@ -258,7 +258,7 @@  objcopy_SOURCES = objcopy.c not-strip.c rename.c $(WRITE_DEBUG_SRCS) $(BULIBS)
 strings_SOURCES = strings.c $(BULIBS)
 
 readelf_SOURCES = readelf.c version.c unwind-ia64.c dwarf.c demanguse.c $(ELFLIBS)
-readelf_LDADD   = $(LIBCTF_NOBFD) $(LIBINTL) $(LIBIBERTY) $(ZLIB) $(ZSTD_LIBS) $(DEBUGINFOD_LIBS) $(MSGPACK_LIBS)
+readelf_LDADD   = $(LIBCTF_NOBFD) $(LIBINTL) $(LIBIBERTY) $(ZLIB) $(ZSTD_LIBS) $(DEBUGINFOD_LIBS) $(MSGPACK_LIBS) $(LIBSFRAME)
 
 elfedit_SOURCES = elfedit.c version.c $(ELFLIBS)
 elfedit_LDADD = $(LIBINTL) $(LIBIBERTY)
@@ -269,7 +269,7 @@  nm_new_SOURCES = nm.c demanguse.c $(BULIBS)
 
 objdump_SOURCES = objdump.c dwarf.c prdbg.c demanguse.c $(DEBUG_SRCS) $(BULIBS) $(ELFLIBS)
 EXTRA_objdump_SOURCES = od-xcoff.c
-objdump_LDADD = $(OBJDUMP_PRIVATE_OFILES) $(OPCODES) $(LIBCTF) $(BFDLIB) $(LIBIBERTY) $(LIBINTL) $(DEBUGINFOD_LIBS)
+objdump_LDADD = $(OBJDUMP_PRIVATE_OFILES) $(OPCODES) $(LIBCTF) $(BFDLIB) $(LIBIBERTY) $(LIBINTL) $(DEBUGINFOD_LIBS) $(LIBSFRAME)
 
 objdump.@OBJEXT@:objdump.c
 if am__fastdepCC
diff --git a/binutils/Makefile.in b/binutils/Makefile.in
index 05aca3d3965..30b78a961dd 100644
--- a/binutils/Makefile.in
+++ b/binutils/Makefile.in
@@ -766,7 +766,7 @@  CC_FOR_TARGET = ` \
 # which depends on libintl, since we don't know whether LIBINTL_DEP will be
 # non-empty until configure time.  Ugh!
 size_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
-objdump_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB) $(OPCODES) $(LIBCTF) $(OBJDUMP_PRIVATE_OFILES)
+objdump_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB) $(OPCODES) $(LIBCTF) $(OBJDUMP_PRIVATE_OFILES) $(LIBSFRAME)
 nm_new_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
 ar_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
 strings_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
@@ -781,7 +781,7 @@  dlltool_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
 windres_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
 windmc_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
 addr2line_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
-readelf_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(LIBCTF_NOBFD)
+readelf_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(LIBCTF_NOBFD) $(LIBSFRAME)
 elfedit_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY)
 dllwrap_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY)
 bfdtest1_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
@@ -791,14 +791,14 @@  size_SOURCES = size.c $(BULIBS)
 objcopy_SOURCES = objcopy.c not-strip.c rename.c $(WRITE_DEBUG_SRCS) $(BULIBS)
 strings_SOURCES = strings.c $(BULIBS)
 readelf_SOURCES = readelf.c version.c unwind-ia64.c dwarf.c demanguse.c $(ELFLIBS)
-readelf_LDADD = $(LIBCTF_NOBFD) $(LIBINTL) $(LIBIBERTY) $(ZLIB) $(ZSTD_LIBS) $(DEBUGINFOD_LIBS) $(MSGPACK_LIBS)
+readelf_LDADD = $(LIBCTF_NOBFD) $(LIBINTL) $(LIBIBERTY) $(ZLIB) $(ZSTD_LIBS) $(DEBUGINFOD_LIBS) $(MSGPACK_LIBS) $(LIBSFRAME)
 elfedit_SOURCES = elfedit.c version.c $(ELFLIBS)
 elfedit_LDADD = $(LIBINTL) $(LIBIBERTY)
 strip_new_SOURCES = objcopy.c is-strip.c rename.c $(WRITE_DEBUG_SRCS) $(BULIBS)
 nm_new_SOURCES = nm.c demanguse.c $(BULIBS)
 objdump_SOURCES = objdump.c dwarf.c prdbg.c demanguse.c $(DEBUG_SRCS) $(BULIBS) $(ELFLIBS)
 EXTRA_objdump_SOURCES = od-xcoff.c
-objdump_LDADD = $(OBJDUMP_PRIVATE_OFILES) $(OPCODES) $(LIBCTF) $(BFDLIB) $(LIBIBERTY) $(LIBINTL) $(DEBUGINFOD_LIBS)
+objdump_LDADD = $(OBJDUMP_PRIVATE_OFILES) $(OPCODES) $(LIBCTF) $(BFDLIB) $(LIBIBERTY) $(LIBINTL) $(DEBUGINFOD_LIBS) $(LIBSFRAME)
 cxxfilt_SOURCES = cxxfilt.c $(BULIBS)
 ar_SOURCES = arparse.y arlex.l ar.c not-ranlib.c arsup.c rename.c binemul.c \
 	emul_$(EMULATION).c $(BULIBS)
diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi
index 6eea08fb91b..0f3860abaaa 100644
--- a/binutils/doc/binutils.texi
+++ b/binutils/doc/binutils.texi
@@ -2262,6 +2262,7 @@  objdump [@option{-a}|@option{--archive-headers}]
         [@option{-wE}|@option{--dwarf=do-not-use-debuginfod}]
         [@option{-L}|@option{--process-links}]
         [@option{--ctf=}@var{section}]
+        [@option{--sframe=}@var{section}]
         [@option{-G}|@option{--stabs}]
         [@option{-t}|@option{--syms}]
         [@option{-T}|@option{--dynamic-syms}]
@@ -2843,6 +2844,8 @@  Enable additional checks for consistency of Dwarf information.
 
 @include ctf.options.texi
 
+@include sframe.options.texi
+
 @item -G
 @itemx --stabs
 @cindex stab
@@ -4940,6 +4943,7 @@  readelf [@option{-a}|@option{--all}]
         [@option{--ctf-parent=}@var{section}]
         [@option{--ctf-symbols=}@var{section}]
         [@option{--ctf-strings=}@var{section}]
+        [@option{--sframe=}@var{section}]
         [@option{-I}|@option{--histogram}]
         [@option{-v}|@option{--version}]
         [@option{-W}|@option{--wide}]
diff --git a/binutils/doc/sframe.options.texi b/binutils/doc/sframe.options.texi
new file mode 100644
index 00000000000..9e23679a3da
--- /dev/null
+++ b/binutils/doc/sframe.options.texi
@@ -0,0 +1,10 @@ 
+@c This file contains the entry for the --sframe option that is
+@c common to both readelf and objdump.
+
+@item --sframe[=@var{section}]
+@cindex SFrame
+
+Display the contents of the specified SFrame section.
+
+By default, display the name of the section named @var{.sframe}, which is the
+name emitted by @command{ld}.
diff --git a/binutils/objdump.c b/binutils/objdump.c
index 0825e051ef6..dac5d73009f 100644
--- a/binutils/objdump.c
+++ b/binutils/objdump.c
@@ -58,6 +58,7 @@ 
 #include "demanguse.h"
 #include "dwarf.h"
 #include "ctf-api.h"
+#include "sframe-api.h"
 #include "getopt.h"
 #include "safe-ctype.h"
 #include "dis-asm.h"
@@ -108,6 +109,8 @@  static int dump_stab_section_info;	/* --stabs */
 static int dump_ctf_section_info;       /* --ctf */
 static char *dump_ctf_section_name;
 static char *dump_ctf_parent_name;	/* --ctf-parent */
+static int dump_sframe_section_info;	/* --sframe */
+static char *dump_sframe_section_name;
 static int do_demangle;			/* -C, --demangle */
 static bool disassemble;		/* -d */
 static bool disassemble_all;		/* -D */
@@ -316,6 +319,8 @@  usage (FILE *stream, int status)
   fprintf (stream, _("\
       --ctf[=SECTION]      Display CTF info from SECTION, (default `.ctf')\n"));
 #endif
+  fprintf (stream, _("\
+      --sframe[=SECTION]   Display SFrame info from SECTION, (default '.sframe')\n"));
   fprintf (stream, _("\
   -t, --syms               Display the contents of the symbol table(s)\n"));
   fprintf (stream, _("\
@@ -462,6 +467,7 @@  enum option_values
     OPTION_CTF,
     OPTION_CTF_PARENT,
 #endif
+    OPTION_SFRAME,
     OPTION_VISUALIZE_JUMPS,
     OPTION_DISASSEMBLER_COLOR
   };
@@ -516,6 +522,7 @@  static struct option long_options[]=
   {"reloc", no_argument, NULL, 'r'},
   {"section", required_argument, NULL, 'j'},
   {"section-headers", no_argument, NULL, 'h'},
+  {"sframe", optional_argument, NULL, OPTION_SFRAME},
   {"show-raw-insn", no_argument, &show_raw_insn, 1},
   {"source", no_argument, NULL, 'S'},
   {"source-comment", optional_argument, NULL, OPTION_SOURCE_COMMENT},
@@ -4801,6 +4808,66 @@  dump_ctf (bfd *abfd ATTRIBUTE_UNUSED, const char *sect_name ATTRIBUTE_UNUSED,
 	  const char *parent_name ATTRIBUTE_UNUSED) {}
 #endif
 
+static bfd_byte*
+read_section_sframe (bfd *abfd, const char *sect_name, bfd_size_type *size_ptr,
+		     bfd_vma *sframe_vma)
+{
+  asection *sframe_sect;
+  bfd_byte *contents;
+
+  sframe_sect = bfd_get_section_by_name (abfd, sect_name);
+  if (sframe_sect == NULL)
+    {
+      printf (_("No %s section present\n\n"),
+	      sanitize_string (sect_name));
+      return NULL;
+    }
+
+  if (!bfd_malloc_and_get_section (abfd, sframe_sect, &contents))
+    {
+      non_fatal (_("reading %s section of %s failed: %s"),
+		 sect_name, bfd_get_filename (abfd),
+		 bfd_errmsg (bfd_get_error ()));
+      exit_status = 1;
+      free (contents);
+      return NULL;
+    }
+
+  *size_ptr = bfd_section_size (sframe_sect);
+  *sframe_vma = bfd_section_vma (sframe_sect);
+
+  return contents;
+}
+
+static void
+dump_section_sframe (bfd *abfd ATTRIBUTE_UNUSED,
+		     const char * sect_name)
+{
+  sframe_decoder_ctx *sfd_ctx = NULL;
+  bfd_size_type sf_size;
+  bfd_byte *sframe_data = NULL;
+  bfd_vma sf_vma;
+  int err = 0;
+
+  if (sect_name == NULL)
+    sect_name = ".sframe";
+
+  sframe_data = read_section_sframe (abfd, sect_name, &sf_size, &sf_vma);
+
+  if (sframe_data == NULL)
+    bfd_fatal (bfd_get_filename (abfd));
+
+  /* Decode the contents of the section.  */
+  sfd_ctx = sframe_decode ((const char*)sframe_data, sf_size, &err);
+  if (!sfd_ctx)
+    bfd_fatal (bfd_get_filename (abfd));
+
+  printf (_("Contents of the SFrame section %s:"),
+	  sanitize_string (sect_name));
+  /* Dump the contents as text.  */
+  dump_sframe (sfd_ctx, sf_vma);
+}
+
 
 static void
 dump_bfd_private_header (bfd *abfd)
@@ -5554,6 +5621,8 @@  dump_bfd (bfd *abfd, bool is_mainfile)
     {
       if (dump_ctf_section_info)
 	dump_ctf (abfd, dump_ctf_section_name, dump_ctf_parent_name);
+      if (dump_sframe_section_info)
+	dump_section_sframe (abfd, dump_sframe_section_name);
       if (dump_stab_section_info)
 	dump_stabs (abfd);
       if (dump_reloc_info && ! disassemble)
@@ -6051,6 +6120,12 @@  main (int argc, char **argv)
 	  dump_ctf_parent_name = xstrdup (optarg);
 	  break;
 #endif
+	case OPTION_SFRAME:
+	  dump_sframe_section_info = true;
+	  if (optarg)
+	    dump_sframe_section_name = xstrdup (optarg);
+	  seenflag = true;
+	  break;
 	case 'G':
 	  dump_stab_section_info = true;
 	  seenflag = true;
diff --git a/binutils/readelf.c b/binutils/readelf.c
index e52060dbf91..8cfa6cb0a5c 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -63,6 +63,7 @@ 
 #include "demanguse.h"
 #include "dwarf.h"
 #include "ctf-api.h"
+#include "sframe-api.h"
 #include "demangle.h"
 
 #include "elf/common.h"
@@ -190,6 +191,7 @@  typedef struct elf_section_list
 #define STRING_DUMP     (1 << 3)	/* The -p command line switch.  */
 #define RELOC_DUMP      (1 << 4)	/* The -R command line switch.  */
 #define CTF_DUMP	(1 << 5)	/* The --ctf command line switch.  */
+#define SFRAME_DUMP	(1 << 6)	/* The --sframe command line switch.  */
 
 typedef unsigned char dump_type;
 
@@ -233,6 +235,7 @@  static bool do_version = false;
 static bool do_histogram = false;
 static bool do_debugging = false;
 static bool do_ctf = false;
+static bool do_sframe = false;
 static bool do_arch = false;
 static bool do_notes = false;
 static bool do_archive_index = false;
@@ -5071,6 +5074,7 @@  enum long_option_values
   OPTION_CTF_PARENT,
   OPTION_CTF_SYMBOLS,
   OPTION_CTF_STRINGS,
+  OPTION_SFRAME_DUMP,
   OPTION_WITH_SYMBOL_VERSIONS,
   OPTION_RECURSE_LIMIT,
   OPTION_NO_RECURSE_LIMIT,
@@ -5133,6 +5137,7 @@  static struct option options[] =
   {"ctf-strings",      required_argument, 0, OPTION_CTF_STRINGS},
   {"ctf-parent",       required_argument, 0, OPTION_CTF_PARENT},
 #endif
+  {"sframe",	       optional_argument, 0, OPTION_SFRAME_DUMP},
   {"sym-base",	       optional_argument, 0, OPTION_SYM_BASE},
 
   {0,		       no_argument, 0, 0}
@@ -5273,6 +5278,8 @@  usage (FILE * stream)
   --ctf-strings=<number|name>\n\
                          Use section <number|name> as the CTF external strtab\n"));
 #endif
+  fprintf (stream, _("\
+  --sframe[=NAME]        Display SFrame info from section NAME, (default '.sframe')\n"));
 
 #ifdef SUPPORT_DISASSEMBLY
   fprintf (stream, _("\
@@ -5546,6 +5553,19 @@  parse_args (struct dump_data *dumpdata, int argc, char ** argv)
 	  free (dump_ctf_parent_name);
 	  dump_ctf_parent_name = strdup (optarg);
 	  break;
+	case OPTION_SFRAME_DUMP:
+	  do_sframe = true;
+	  /* Providing section name is optional.  request_dump (), however,
+	     thrives on non NULL optarg.  Handle it explicitly here.  */
+	  if (optarg != NULL)
+	    request_dump (dumpdata, SFRAME_DUMP);
+	  else
+	    {
+	      do_dump = true;
+	      const char *sframe_sec_name = strdup (".sframe");
+	      request_dump_byname (sframe_sec_name, SFRAME_DUMP);
+	    }
+	  break;
 	case OPTION_DYN_SYMS:
 	  do_dyn_syms = true;
 	  break;
@@ -15857,6 +15877,44 @@  dump_section_as_ctf (Elf_Internal_Shdr * section, Filedata * filedata)
 }
 #endif
 
+static bool
+dump_section_as_sframe (Elf_Internal_Shdr * section, Filedata * filedata)
+{
+  void *		  data = NULL;
+  sframe_decoder_ctx	  *sfd_ctx = NULL;
+  const char *print_name = printable_section_name (filedata, section);
+
+  bool ret = true;
+  size_t sf_size;
+  int err = 0;
+
+  if (strcmp (print_name, "") == 0)
+    {
+      error (_("Section name must be provided \n"));
+      ret = false;
+      return ret;
+    }
+
+  data = get_section_contents (section, filedata);
+  sf_size = section->sh_size;
+  /* Decode the contents of the section.  */
+  sfd_ctx = sframe_decode ((const char*)data, sf_size, &err);
+  if (!sfd_ctx)
+    {
+      ret = false;
+      error (_("SFrame decode failure: %s\n"), sframe_errmsg (err));
+      goto fail;
+    }
+
+  printf (_("Contents of the SFrame section %s:"), print_name);
+  /* Dump the contents as text.  */
+  dump_sframe (sfd_ctx, section->sh_addr);
+
+ fail:
+  free (data);
+  return ret;
+}
+
 static bool
 load_specific_debug_section (enum dwarf_section_display_enum  debug,
 			     const Elf_Internal_Shdr *        sec,
@@ -16363,6 +16421,11 @@  process_section_contents (Filedata * filedata)
 	    res = false;
 	}
 #endif
+      if (dump & SFRAME_DUMP)
+	{
+	  if (! dump_section_as_sframe (section, filedata))
+	    res = false;
+	}
     }
 
   if (! filedata->is_separate)
diff --git a/include/sframe-api.h b/include/sframe-api.h
index 0fbed017ecd..077985b2017 100644
--- a/include/sframe-api.h
+++ b/include/sframe-api.h
@@ -157,6 +157,9 @@  sframe_decoder_get_funcdesc (sframe_decoder_ctx *ctx,
 			     int32_t *func_start_address,
 			     unsigned char *func_info);
 
+/* SFrame textual dump.  */
+extern void
+dump_sframe (sframe_decoder_ctx *decoder, uint64_t addr);
 
 /* Get the base reg id from the FRE info.  Sets errp if fails.  */
 extern unsigned int
diff --git a/libsframe/Makefile.am b/libsframe/Makefile.am
index 940494dba1e..d8198a166c5 100644
--- a/libsframe/Makefile.am
+++ b/libsframe/Makefile.am
@@ -33,7 +33,7 @@  include_HEADERS =
 noinst_LTLIBRARIES = libsframe.la
 endif
 
-libsframe_la_SOURCES = sframe.c sframe-error.c
+libsframe_la_SOURCES = sframe.c sframe-dump.c sframe-error.c
 libsframe_la_CPPFLAGS = $(AM_CPPFLAGS)
 
 include testsuite/local.mk
diff --git a/libsframe/Makefile.in b/libsframe/Makefile.in
index 2d1ea896471..340bfe88060 100644
--- a/libsframe/Makefile.in
+++ b/libsframe/Makefile.in
@@ -149,7 +149,7 @@  am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"
 LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES)
 libsframe_la_LIBADD =
 am_libsframe_la_OBJECTS = libsframe_la-sframe.lo \
-	libsframe_la-sframe-error.lo
+	libsframe_la-sframe-dump.lo libsframe_la-sframe-error.lo
 libsframe_la_OBJECTS = $(am_libsframe_la_OBJECTS)
 AM_V_lt = $(am__v_lt_@AM_V@)
 am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
@@ -427,7 +427,7 @@  AM_CFLAGS = @ac_libsframe_warn_cflags@
 @INSTALL_LIBBFD_FALSE@include_HEADERS = 
 @INSTALL_LIBBFD_TRUE@include_HEADERS = $(INCDIR)/sframe.h $(INCDIR)/sframe-api.h
 @INSTALL_LIBBFD_FALSE@noinst_LTLIBRARIES = libsframe.la
-libsframe_la_SOURCES = sframe.c sframe-error.c
+libsframe_la_SOURCES = sframe.c sframe-dump.c sframe-error.c
 libsframe_la_CPPFLAGS = $(AM_CPPFLAGS)
 
 # Setup the testing framework
@@ -607,6 +607,7 @@  mostlyclean-compile:
 distclean-compile:
 	-rm -f *.tab.c
 
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsframe_la-sframe-dump.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsframe_la-sframe-error.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsframe_la-sframe.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@testsuite/libsframe.decode/$(DEPDIR)/testsuite_libsframe_decode_be_flipping-be-flipping.Po@am__quote@
@@ -645,6 +646,13 @@  libsframe_la-sframe.lo: sframe.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsframe_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsframe_la-sframe.lo `test -f 'sframe.c' || echo '$(srcdir)/'`sframe.c
 
+libsframe_la-sframe-dump.lo: sframe-dump.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsframe_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsframe_la-sframe-dump.lo -MD -MP -MF $(DEPDIR)/libsframe_la-sframe-dump.Tpo -c -o libsframe_la-sframe-dump.lo `test -f 'sframe-dump.c' || echo '$(srcdir)/'`sframe-dump.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libsframe_la-sframe-dump.Tpo $(DEPDIR)/libsframe_la-sframe-dump.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='sframe-dump.c' object='libsframe_la-sframe-dump.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsframe_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsframe_la-sframe-dump.lo `test -f 'sframe-dump.c' || echo '$(srcdir)/'`sframe-dump.c
+
 libsframe_la-sframe-error.lo: sframe-error.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsframe_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsframe_la-sframe-error.lo -MD -MP -MF $(DEPDIR)/libsframe_la-sframe-error.Tpo -c -o libsframe_la-sframe-error.lo `test -f 'sframe-error.c' || echo '$(srcdir)/'`sframe-error.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libsframe_la-sframe-error.Tpo $(DEPDIR)/libsframe_la-sframe-error.Plo
diff --git a/libsframe/sframe-dump.c b/libsframe/sframe-dump.c
new file mode 100644
index 00000000000..5b063c9962d
--- /dev/null
+++ b/libsframe/sframe-dump.c
@@ -0,0 +1,181 @@ 
+/* sframe-dump.c - Textual dump of .sframe.
+
+   Copyright (C) 2022 Free Software Foundation, Inc.
+
+   his file is part of libsframe.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include "sframe-impl.h"
+
+#define SFRAME_HEADER_FLAGS_STR_MAX_LEN 50
+
+static void
+dump_sframe_header (sframe_decoder_ctx *sfd_ctx)
+{
+  const char *verstr = NULL;
+  const sframe_header *header = &(sfd_ctx->sfd_header);
+
+  /* Prepare SFrame section version string.  */
+  const char *version_names[]
+    = { "NULL",
+	"SFRAME_VERSION_1" };
+  unsigned char ver = header->sfh_preamble.sfp_version;
+  if (ver <= SFRAME_VERSION)
+    verstr = version_names[ver];
+
+  /* Prepare SFrame section flags string.  */
+  unsigned char flags = header->sfh_preamble.sfp_flags;
+  char *flags_str
+    = (char*) calloc (sizeof (char), SFRAME_HEADER_FLAGS_STR_MAX_LEN);
+  if (flags)
+    {
+      const char *flag_names[]
+	= { "SFRAME_F_FDE_SORTED",
+	    "SFRAME_F_FRAME_POINTER" };
+      unsigned char flags = header->sfh_preamble.sfp_flags;
+      if (flags & SFRAME_F_FDE_SORTED)
+	strcpy (flags_str, flag_names[0]);
+      if (flags & SFRAME_F_FRAME_POINTER)
+	{
+	  if (strlen (flags_str) > 0)
+	    strcpy (flags_str, ",");
+	  strcpy (flags_str, flag_names[1]);
+	}
+    }
+  else
+    strcpy (flags_str, "NONE");
+
+  const char* subsec_name = "Header";
+  printf ("\n");
+  printf ("  %s :\n", subsec_name);
+  printf ("\n");
+  printf ("    Version: %s\n", verstr);
+  printf ("    Flags: %s\n", flags_str);
+  printf ("    Num FDEs: %d\n", header->sfh_num_fdes);
+  printf ("    Num FREs: %d\n", header->sfh_num_fres);
+
+  free (flags_str);
+}
+
+static void
+dump_sframe_func_with_fres (sframe_decoder_ctx *sfd_ctx,
+			    unsigned int funcidx,
+			    uint64_t sec_addr)
+{
+  uint32_t j = 0;
+  uint32_t num_fres = 0;
+  uint32_t func_size = 0;
+  int32_t func_start_address = 0;
+  unsigned char func_info = 0;
+
+  uint64_t func_start_pc_vma = 0;
+  uint64_t fre_start_pc_vma = 0;
+  const char *base_reg_str[] = {"fp", "sp"};
+  int32_t cfa_offset = 0;
+  int32_t fp_offset = 0;
+  int32_t ra_offset = 0;
+  unsigned int base_reg_id = 0;
+  int err[3] = {0, 0, 0};
+
+  sframe_frame_row_entry fre;
+
+  /* Get the SFrame function descriptor.  */
+  sframe_decoder_get_funcdesc (sfd_ctx, funcidx, &num_fres,
+			       &func_size, &func_start_address, &func_info);
+  /* Calculate the virtual memory address for function start pc.  */
+  func_start_pc_vma = func_start_address + sec_addr;
+
+  /* Mark FDEs with [m] where the FRE start address is interpreted as a
+     mask.  */
+  int fde_type_addrmask_p = (SFRAME_V1_FUNC_FDE_TYPE (func_info)
+			     == SFRAME_FDE_TYPE_PCMASK);
+  const char *fde_type_marker
+    = (fde_type_addrmask_p ? "[m]" : "   ");
+
+  printf ("\n    func idx [%d]: pc = 0x%"PRIx64 ", size = %d bytes",
+	  funcidx,
+	  func_start_pc_vma,
+	  func_size);
+
+  char temp[100];
+  memset (temp, 0, 100);
+
+  printf ("\n    %-7s%-8s %-10s%-10s%-10s", "STARTPC", fde_type_marker, "CFA", "FP", "RA");
+  for (j = 0; j < num_fres; j++)
+    {
+      sframe_decoder_get_fre (sfd_ctx, funcidx, j, &fre);
+
+      fre_start_pc_vma = (fde_type_addrmask_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.  */
+      base_reg_id = sframe_fre_get_base_reg_id (&fre, &err[0]);
+      cfa_offset = sframe_fre_get_cfa_offset (sfd_ctx, &fre, &err[0]);
+      fp_offset = sframe_fre_get_fp_offset (sfd_ctx, &fre, &err[1]);
+      ra_offset = sframe_fre_get_ra_offset (sfd_ctx, &fre, &err[2]);
+
+      /* Dump CFA info.  */
+      printf ("\n");
+      printf ("    %016"PRIx64, fre_start_pc_vma);
+      sprintf (temp, "%s+%d", base_reg_str[base_reg_id], cfa_offset);
+      printf ("  %-10s", temp);
+
+      /* Dump SP/FP info.  */
+      memset (temp, 0, 100);
+      if (err[1] == 0)
+	sprintf (temp, "c%+d", fp_offset);
+      else
+	strcpy (temp, "u");
+      printf ("%-10s", temp);
+
+      /* Dump RA info.  */
+      memset (temp, 0, 100);
+      if (err[2] == 0)
+	sprintf (temp, "c%+d", ra_offset);
+      else
+	strcpy (temp, "u");
+      printf ("%-10s", temp);
+    }
+}
+
+static void
+dump_sframe_functions (sframe_decoder_ctx *sfd_ctx, uint64_t sec_addr)
+{
+  uint32_t i;
+  uint32_t num_fdes;
+
+  const char* subsec_name = "Function Index";
+  printf ("\n  %s :\n", subsec_name);
+
+  num_fdes = sframe_decoder_get_num_fidx (sfd_ctx);
+  for (i = 0; i < num_fdes; i++)
+    {
+      dump_sframe_func_with_fres (sfd_ctx, i, sec_addr);
+      printf ("\n");
+    }
+}
+
+void
+dump_sframe (sframe_decoder_ctx *sfd_ctx, uint64_t sec_addr)
+{
+  dump_sframe_header (sfd_ctx);
+  dump_sframe_functions (sfd_ctx, sec_addr);
+}