]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
unwinder: generate backtrace using SFrame format
authorWeimin Pan <weimin.pan@oracle.com>
Tue, 27 Sep 2022 21:58:04 +0000 (14:58 -0700)
committerIndu Bhagat <indu.bhagat@oracle.com>
Sun, 23 Mar 2025 18:32:25 +0000 (11:32 -0700)
[Changes in V4]
  - Renamed ESFRAME_* enum error code names to SFRAME_ERR_*.
  - Addressed review comments by Mike.
    - Use AC_CACHE_CHECK macro in sframe.m4
    - Delete config/sframe.m4. Add into libsframe/acinclude.m4.
    - Code fixups.
[End of changes in V4]

[Changes in V3]
  - Use the updated APIs from libsframe.
  - Use sframe_decoder_get_fixed_ra_offset on AMD64 instead of magic
    number -8.
[End of changes in V3]

[Changes in V2]
  - Minor formatting fixes.
[End of changes in V2]

A simple unwinder based on SFrame format.

The unwinder is made available via libsframebt library.

Buildsystem changes have been made to build libsframebt only when
--gsframe support is available in the assembler. These buildsystem
changes are necessary because the SFrame based unwinder the SFrame
unwind info for itself to work.

include/ChangeLog:

* sframe-backtrace-api.h: New file.

ChangeLog:

* libsframe/acinclude.m4: New file.
* libsframe/Makefile.am: Build backtrace functionality in its
own library.  Install libsframebt conditionally.
* libsframe/Makefile.in: Regenerate.
* libsframe/aclocal.m4: Regenerate.
* libsframe/configure: Regenerate.
* libsframe/configure.ac: Check if gas supports --gsframe
command line option.
* libsframe/sframe-backtrace-err.c: New file.
* libsframe/sframe-backtrace.c: New file.

include/sframe-backtrace-api.h [new file with mode: 0644]
libsframe/Makefile.am
libsframe/Makefile.in
libsframe/acinclude.m4 [new file with mode: 0644]
libsframe/aclocal.m4
libsframe/configure
libsframe/configure.ac
libsframe/sframe-backtrace-err.c [new file with mode: 0644]
libsframe/sframe-backtrace.c [new file with mode: 0644]

diff --git a/include/sframe-backtrace-api.h b/include/sframe-backtrace-api.h
new file mode 100644 (file)
index 0000000..b9537bf
--- /dev/null
@@ -0,0 +1,60 @@
+/* Public API to SFrame backtrace.
+
+   Copyright (C) 2022 Free Software Foundation, Inc.
+
+   This file is part of libsframebt.
+
+   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/>.  */
+
+#ifndef        _SFRAME_BACKTRACE_API_H
+#define        _SFRAME_BACKTRACE_API_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+enum sframe_bt_errcode
+{
+  SFRAME_BT_OK,
+  SFRAME_BT_ERR_NOTPRESENT,
+  SFRAME_BT_ERR_ARG,
+  SFRAME_BT_ERR_MALLOC,
+  SFRAME_BT_ERR_REALLOC,
+  SFRAME_BT_ERR_OPEN,
+  SFRAME_BT_ERR_READLINK,
+  SFRAME_BT_ERR_LSEEK,
+  SFRAME_BT_ERR_READ,
+  SFRAME_BT_ERR_GETCONTEXT,
+  SFRAME_BT_ERR_DECODE,
+  SFRAME_BT_ERR_CFA_OFFSET,
+};
+
+#define NUM_OF_DSOS    32                      /* Number of DSOs.  */
+
+#define ENUM_ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
+
+/* Get the backtrace of the calling program by storing return addresses
+   in BUFFER. The SIZE argument specifies the maximum number of addresses
+   that can be stored in the buffer. Return the number of return addresses
+   collected or -1 if there is any error.  */
+extern int sframe_backtrace (void **buffer, int size, int *errp);
+
+extern const char *sframe_bt_errmsg (enum sframe_bt_errcode ecode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif                         /* _SFRAME_BACKTRACE_API_H */
index 89e471fd3d5ca3532e8e9505660aa056aea6a113..5f25abe0bb7eba416ab200cea6a9fd91a6e84ca7 100644 (file)
@@ -56,6 +56,18 @@ libsframe_la_LDFLAGS = $(libsframe_version_info) $(libsframe_version_script)
 EXTRA_DIST = libtool-version libsframe.ver
 diststuff: $(EXTRA_DIST) info
 
+if HAVE_SFRAME_AS
+  libsframebt_la_SOURCES = sframe-backtrace.c sframe-backtrace-err.c
+  libsframebt_la_CPPFLAGS = -I$(srcdir) -I$(srcdir)/../include
+  libsframebt_la_CFLAGS = -Wa,--gsframe
+if INSTALL_LIBBFD
+  lib_LTLIBRARIES += libsframebt.la
+  include_HEADERS += $(INCDIR)/sframe-backtrace-api.h
+else
+  noinst_LTLIBRARIES += libsframebt.la
+endif
+endif
+
 include doc/local.mk
 
 include testsuite/local.mk
index 13f4c26e2f146ae9200e760949986d725b33bcf0..f9384c1c2666b03d5ed8c49150c2d21ef122535a 100644 (file)
@@ -108,11 +108,14 @@ POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
 target_triplet = @target@
-@BUILD_INFO_TRUE@am__append_1 = doc/sframe-spec.texi
-@BUILD_INFO_TRUE@am__append_2 = texput.log
-@BUILD_INFO_TRUE@am__append_3 = doc/sframe-spec.info
+@HAVE_SFRAME_AS_TRUE@@INSTALL_LIBBFD_TRUE@am__append_1 = libsframebt.la
+@HAVE_SFRAME_AS_TRUE@@INSTALL_LIBBFD_TRUE@am__append_2 = $(INCDIR)/sframe-backtrace-api.h
+@HAVE_SFRAME_AS_TRUE@@INSTALL_LIBBFD_FALSE@am__append_3 = libsframebt.la
+@BUILD_INFO_TRUE@am__append_4 = doc/sframe-spec.texi
+@BUILD_INFO_TRUE@am__append_5 = texput.log
+@BUILD_INFO_TRUE@am__append_6 = doc/sframe-spec.info
 check_PROGRAMS = $(am__EXEEXT_1)
-@HAVE_COMPAT_DEJAGNU_TRUE@am__append_4 = testsuite/libsframe.decode/be-flipping \
+@HAVE_COMPAT_DEJAGNU_TRUE@am__append_7 = testsuite/libsframe.decode/be-flipping \
 @HAVE_COMPAT_DEJAGNU_TRUE@     testsuite/libsframe.decode/frecnt-1 \
 @HAVE_COMPAT_DEJAGNU_TRUE@     testsuite/libsframe.decode/frecnt-2 \
 @HAVE_COMPAT_DEJAGNU_TRUE@     testsuite/libsframe.encode/encode-1 \
@@ -132,7 +135,8 @@ am__aclocal_m4_deps = $(top_srcdir)/../bfd/acinclude.m4 \
        $(top_srcdir)/../config/warnings.m4 \
        $(top_srcdir)/../libtool.m4 $(top_srcdir)/../ltoptions.m4 \
        $(top_srcdir)/../ltsugar.m4 $(top_srcdir)/../ltversion.m4 \
-       $(top_srcdir)/../lt~obsolete.m4 $(top_srcdir)/configure.ac
+       $(top_srcdir)/../lt~obsolete.m4 $(top_srcdir)/acinclude.m4 \
+       $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
 DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \
@@ -187,6 +191,20 @@ libsframe_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
        $(libsframe_la_LDFLAGS) $(LDFLAGS) -o $@
 @INSTALL_LIBBFD_FALSE@am_libsframe_la_rpath =
 @INSTALL_LIBBFD_TRUE@am_libsframe_la_rpath = -rpath $(libdir)
+libsframebt_la_LIBADD =
+am__libsframebt_la_SOURCES_DIST = sframe-backtrace.c \
+       sframe-backtrace-err.c
+@HAVE_SFRAME_AS_TRUE@am_libsframebt_la_OBJECTS =  \
+@HAVE_SFRAME_AS_TRUE@  libsframebt_la-sframe-backtrace.lo \
+@HAVE_SFRAME_AS_TRUE@  libsframebt_la-sframe-backtrace-err.lo
+libsframebt_la_OBJECTS = $(am_libsframebt_la_OBJECTS)
+libsframebt_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+       $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+       $(libsframebt_la_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o \
+       $@
+@HAVE_SFRAME_AS_TRUE@@INSTALL_LIBBFD_FALSE@am_libsframebt_la_rpath =
+@HAVE_SFRAME_AS_TRUE@@INSTALL_LIBBFD_TRUE@am_libsframebt_la_rpath =  \
+@HAVE_SFRAME_AS_TRUE@@INSTALL_LIBBFD_TRUE@     -rpath $(libdir)
 @HAVE_COMPAT_DEJAGNU_TRUE@am__EXEEXT_1 = testsuite/libsframe.decode/be-flipping$(EXEEXT) \
 @HAVE_COMPAT_DEJAGNU_TRUE@     testsuite/libsframe.decode/frecnt-1$(EXEEXT) \
 @HAVE_COMPAT_DEJAGNU_TRUE@     testsuite/libsframe.decode/frecnt-2$(EXEEXT) \
@@ -264,7 +282,7 @@ AM_V_CCLD = $(am__v_CCLD_@AM_V@)
 am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
 am__v_CCLD_0 = @echo "  CCLD    " $@;
 am__v_CCLD_1 = 
-SOURCES = $(libsframe_la_SOURCES) \
+SOURCES = $(libsframe_la_SOURCES) $(libsframebt_la_SOURCES) \
        $(testsuite_libsframe_decode_be_flipping_SOURCES) \
        $(testsuite_libsframe_decode_frecnt_1_SOURCES) \
        $(testsuite_libsframe_decode_frecnt_2_SOURCES) \
@@ -273,6 +291,7 @@ SOURCES = $(libsframe_la_SOURCES) \
        $(testsuite_libsframe_find_findfunc_1_SOURCES) \
        $(testsuite_libsframe_find_plt_findfre_1_SOURCES)
 DIST_SOURCES = $(libsframe_la_SOURCES) \
+       $(am__libsframebt_la_SOURCES_DIST) \
        $(testsuite_libsframe_decode_be_flipping_SOURCES) \
        $(testsuite_libsframe_decode_frecnt_1_SOURCES) \
        $(testsuite_libsframe_decode_frecnt_2_SOURCES) \
@@ -314,7 +333,7 @@ DVIS = doc/sframe-spec.dvi
 PDFS = doc/sframe-spec.pdf
 PSS = doc/sframe-spec.ps
 HTMLS = doc/sframe-spec.html
-TEXINFOS = $(am__append_1)
+TEXINFOS = $(am__append_4)
 TEXI2DVI = texi2dvi
 TEXI2PDF = $(TEXI2DVI) --pdf --batch
 MAKEINFOHTML = $(MAKEINFO) --html
@@ -325,7 +344,8 @@ am__can_run_installinfo = \
     n|no|NO) false;; \
     *) (install-info --version) >/dev/null 2>&1;; \
   esac
-am__include_HEADERS_DIST = $(INCDIR)/sframe.h $(INCDIR)/sframe-api.h
+am__include_HEADERS_DIST = $(INCDIR)/sframe-backtrace-api.h \
+       $(INCDIR)/sframe.h $(INCDIR)/sframe-api.h
 HEADERS = $(include_HEADERS)
 am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \
        $(LISP)config.h.in
@@ -521,9 +541,9 @@ ACLOCAL_AMFLAGS = -I .. -I ../config -I ../bfd
 AUTOMAKE_OPTIONS = dejagnu foreign no-texinfo.tex info-in-builddir subdir-objects
 
 # Variables that we might accumulate conditionally or in subdirs.
-info_TEXINFOS = $(am__append_1)
-DISTCLEANFILES = $(am__append_2)
-MAINTAINERCLEANFILES = $(am__append_3)
+info_TEXINFOS = $(am__append_4)
+DISTCLEANFILES = $(am__append_5)
+MAINTAINERCLEANFILES = $(am__append_6)
 INCDIR = $(srcdir)/../include
 # include libctf for swap.h
 AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/../include -I$(srcdir)/../libctf
@@ -534,14 +554,19 @@ libsframe_version_info = -version-info `grep -v '^\#' $(srcdir)/libtool-version`
 # not support symbol versioning, there is no need to fall back on libtool's
 # -export-symbols option.
 @HAVE_LD_VERSION_SCRIPT_TRUE@@HAVE_SOLARIS_LD_TRUE@libsframe_version_script = -Wl,-M -Wl,$(srcdir)/libsframe.ver
-@INSTALL_LIBBFD_TRUE@lib_LTLIBRARIES = libsframe.la
-@INSTALL_LIBBFD_FALSE@include_HEADERS = 
-@INSTALL_LIBBFD_TRUE@include_HEADERS = $(INCDIR)/sframe.h $(INCDIR)/sframe-api.h
-@INSTALL_LIBBFD_FALSE@noinst_LTLIBRARIES = libsframe.la
+@INSTALL_LIBBFD_TRUE@lib_LTLIBRARIES = libsframe.la $(am__append_1)
+@INSTALL_LIBBFD_FALSE@include_HEADERS = $(am__append_2)
+@INSTALL_LIBBFD_TRUE@include_HEADERS = $(INCDIR)/sframe.h \
+@INSTALL_LIBBFD_TRUE@  $(INCDIR)/sframe-api.h $(am__append_2)
+@INSTALL_LIBBFD_FALSE@noinst_LTLIBRARIES = libsframe.la \
+@INSTALL_LIBBFD_FALSE@ $(am__append_3)
 libsframe_la_SOURCES = sframe.c sframe-dump.c sframe-error.c
 libsframe_la_CPPFLAGS = $(AM_CPPFLAGS)
 libsframe_la_LDFLAGS = $(libsframe_version_info) $(libsframe_version_script)
 EXTRA_DIST = libtool-version libsframe.ver
+@HAVE_SFRAME_AS_TRUE@libsframebt_la_SOURCES = sframe-backtrace.c sframe-backtrace-err.c
+@HAVE_SFRAME_AS_TRUE@libsframebt_la_CPPFLAGS = -I$(srcdir) -I$(srcdir)/../include
+@HAVE_SFRAME_AS_TRUE@libsframebt_la_CFLAGS = -Wa,--gsframe
 @BUILD_INFO_TRUE@AM_MAKEINFOFLAGS = --no-split
 
 # Setup the testing framework
@@ -673,6 +698,9 @@ clean-noinstLTLIBRARIES:
 libsframe.la: $(libsframe_la_OBJECTS) $(libsframe_la_DEPENDENCIES) $(EXTRA_libsframe_la_DEPENDENCIES) 
        $(AM_V_CCLD)$(libsframe_la_LINK) $(am_libsframe_la_rpath) $(libsframe_la_OBJECTS) $(libsframe_la_LIBADD) $(LIBS)
 
+libsframebt.la: $(libsframebt_la_OBJECTS) $(libsframebt_la_DEPENDENCIES) $(EXTRA_libsframebt_la_DEPENDENCIES) 
+       $(AM_V_CCLD)$(libsframebt_la_LINK) $(am_libsframebt_la_rpath) $(libsframebt_la_OBJECTS) $(libsframebt_la_LIBADD) $(LIBS)
+
 clean-checkPROGRAMS:
        @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
        echo " rm -f" $$list; \
@@ -761,6 +789,8 @@ distclean-compile:
 @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@./$(DEPDIR)/libsframebt_la-sframe-backtrace-err.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsframebt_la-sframe-backtrace.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@testsuite/libsframe.decode/$(DEPDIR)/testsuite_libsframe_decode_be_flipping-be-flipping.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@testsuite/libsframe.decode/$(DEPDIR)/testsuite_libsframe_decode_frecnt_1-frecnt-1.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@testsuite/libsframe.decode/$(DEPDIR)/testsuite_libsframe_decode_frecnt_2-frecnt-2.Po@am__quote@
@@ -814,6 +844,20 @@ libsframe_la-sframe-error.lo: sframe-error.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-error.lo `test -f 'sframe-error.c' || echo '$(srcdir)/'`sframe-error.c
 
+libsframebt_la-sframe-backtrace.lo: sframe-backtrace.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsframebt_la_CPPFLAGS) $(CPPFLAGS) $(libsframebt_la_CFLAGS) $(CFLAGS) -MT libsframebt_la-sframe-backtrace.lo -MD -MP -MF $(DEPDIR)/libsframebt_la-sframe-backtrace.Tpo -c -o libsframebt_la-sframe-backtrace.lo `test -f 'sframe-backtrace.c' || echo '$(srcdir)/'`sframe-backtrace.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) $(DEPDIR)/libsframebt_la-sframe-backtrace.Tpo $(DEPDIR)/libsframebt_la-sframe-backtrace.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='sframe-backtrace.c' object='libsframebt_la-sframe-backtrace.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) $(libsframebt_la_CPPFLAGS) $(CPPFLAGS) $(libsframebt_la_CFLAGS) $(CFLAGS) -c -o libsframebt_la-sframe-backtrace.lo `test -f 'sframe-backtrace.c' || echo '$(srcdir)/'`sframe-backtrace.c
+
+libsframebt_la-sframe-backtrace-err.lo: sframe-backtrace-err.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsframebt_la_CPPFLAGS) $(CPPFLAGS) $(libsframebt_la_CFLAGS) $(CFLAGS) -MT libsframebt_la-sframe-backtrace-err.lo -MD -MP -MF $(DEPDIR)/libsframebt_la-sframe-backtrace-err.Tpo -c -o libsframebt_la-sframe-backtrace-err.lo `test -f 'sframe-backtrace-err.c' || echo '$(srcdir)/'`sframe-backtrace-err.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) $(DEPDIR)/libsframebt_la-sframe-backtrace-err.Tpo $(DEPDIR)/libsframebt_la-sframe-backtrace-err.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='sframe-backtrace-err.c' object='libsframebt_la-sframe-backtrace-err.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) $(libsframebt_la_CPPFLAGS) $(CPPFLAGS) $(libsframebt_la_CFLAGS) $(CFLAGS) -c -o libsframebt_la-sframe-backtrace-err.lo `test -f 'sframe-backtrace-err.c' || echo '$(srcdir)/'`sframe-backtrace-err.c
+
 testsuite/libsframe.decode/testsuite_libsframe_decode_be_flipping-be-flipping.o: testsuite/libsframe.decode/be-flipping.c
 @am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testsuite_libsframe_decode_be_flipping_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT testsuite/libsframe.decode/testsuite_libsframe_decode_be_flipping-be-flipping.o -MD -MP -MF testsuite/libsframe.decode/$(DEPDIR)/testsuite_libsframe_decode_be_flipping-be-flipping.Tpo -c -o testsuite/libsframe.decode/testsuite_libsframe_decode_be_flipping-be-flipping.o `test -f 'testsuite/libsframe.decode/be-flipping.c' || echo '$(srcdir)/'`testsuite/libsframe.decode/be-flipping.c
 @am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) testsuite/libsframe.decode/$(DEPDIR)/testsuite_libsframe_decode_be_flipping-be-flipping.Tpo testsuite/libsframe.decode/$(DEPDIR)/testsuite_libsframe_decode_be_flipping-be-flipping.Po
diff --git a/libsframe/acinclude.m4 b/libsframe/acinclude.m4
new file mode 100644 (file)
index 0000000..a019ed8
--- /dev/null
@@ -0,0 +1,20 @@
+dnl SFRAME_CHECK_HAVE_SFRAME_AS
+dnl ----------------------
+dnl Defines:
+dnl sframe_cv_have_sframe_as
+dnl
+
+# Check whether the assembler supports generation of SFrame
+# unwind information.
+AC_DEFUN([SFRAME_CHECK_HAVE_SFRAME_AS],
+[
+  AC_CACHE_CHECK([if as supports --gsframe], sframe_cv_have_sframe_as,
+  [
+    ac_save_CFLAGS="$CFLAGS"
+    CFLAGS="$CFLAGS -Wa,--gsframe"
+    AC_MSG_CHECKING([for as that supports --gsframe])
+    AC_TRY_COMPILE([], [return 0;], [sframe_cv_have_sframe_as=yes], [sframe_cv_have_sframe_as=no])
+  ])
+  AC_MSG_RESULT($sframe_cv_have_sframe_as)
+  CFLAGS="$ac_save_CFLAGS"
+])
index 8804d9fb42b8d9b3342ca34071a80e0f08949f63..6cf58259abc0b5ffbb6016620c525aeded0d186b 100644 (file)
@@ -1241,3 +1241,4 @@ m4_include([../ltoptions.m4])
 m4_include([../ltsugar.m4])
 m4_include([../ltversion.m4])
 m4_include([../lt~obsolete.m4])
+m4_include([acinclude.m4])
index 4727eff533a2b10f503566233f1fabb369a43c96..cf84dc7db1b114539c0a687602b3f2d6d7caf42f 100755 (executable)
@@ -643,6 +643,8 @@ INSTALL_LIBBFD_TRUE
 MAINT
 MAINTAINER_MODE_FALSE
 MAINTAINER_MODE_TRUE
+HAVE_SFRAME_AS_FALSE
+HAVE_SFRAME_AS_TRUE
 HAVE_LD_VERSION_SCRIPT_FALSE
 HAVE_LD_VERSION_SCRIPT_TRUE
 HAVE_SOLARIS_LD_FALSE
@@ -11563,7 +11565,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11566 "configure"
+#line 11568 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -11669,7 +11671,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11672 "configure"
+#line 11674 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
 
 
 
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if as supports --gsframe" >&5
+$as_echo_n "checking if as supports --gsframe... " >&6; }
+if ${sframe_cv_have_sframe_as+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+    ac_save_CFLAGS="$CFLAGS"
+    CFLAGS="$CFLAGS -Wa,--gsframe"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for as that supports --gsframe" >&5
+$as_echo_n "checking for as that supports --gsframe... " >&6; }
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  sframe_cv_have_sframe_as=yes
+else
+  sframe_cv_have_sframe_as=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $sframe_cv_have_sframe_as" >&5
+$as_echo "$sframe_cv_have_sframe_as" >&6; }
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $sframe_cv_have_sframe_as" >&5
+$as_echo "$sframe_cv_have_sframe_as" >&6; }
+  CFLAGS="$ac_save_CFLAGS"
+
+ if test "x$sframe_cv_have_sframe_as" = "xyes"; then
+  HAVE_SFRAME_AS_TRUE=
+  HAVE_SFRAME_AS_FALSE='#'
+else
+  HAVE_SFRAME_AS_TRUE='#'
+  HAVE_SFRAME_AS_FALSE=
+fi
+
+
+
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5
 $as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; }
     # Check whether --enable-maintainer-mode was given.
@@ -12951,6 +12999,10 @@ if test -z "${HAVE_LD_VERSION_SCRIPT_TRUE}" && test -z "${HAVE_LD_VERSION_SCRIPT
   as_fn_error $? "conditional \"HAVE_LD_VERSION_SCRIPT\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${HAVE_SFRAME_AS_TRUE}" && test -z "${HAVE_SFRAME_AS_FALSE}"; then
+  as_fn_error $? "conditional \"HAVE_SFRAME_AS\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then
   as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
index cf4285fefac0baf736235a008e1c31ce5dd99f07..be9fbbec074eb5dca9072294a50f718ebb7e3973 100644 (file)
@@ -112,6 +112,13 @@ else
 fi
 AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$have_ld_version_script" = "yes")
 
+dnl The libsframebt library needs to be built with SFrame info.
+dnl If the build assembler is not capable of generate SFrame then
+dnl the library is not built.
+
+SFRAME_CHECK_HAVE_SFRAME_AS
+AM_CONDITIONAL([HAVE_SFRAME_AS], [test "x$sframe_cv_have_sframe_as" = "xyes"])
+
 AM_MAINTAINER_MODE
 AM_INSTALL_LIBBFD
 
diff --git a/libsframe/sframe-backtrace-err.c b/libsframe/sframe-backtrace-err.c
new file mode 100644 (file)
index 0000000..935509e
--- /dev/null
@@ -0,0 +1,49 @@
+/* sframe-backtrace-err.c - SFrame Backtrace Error table.
+
+   Copyright (C) 2022 Free Software Foundation, Inc.
+
+   This file is part of libsframebt.
+
+   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 "config.h"
+#include "sframe-backtrace-api.h"
+
+/* SFrame backtrace error messages.  */
+static const char *const sframe_bt_errlist[] =
+{
+  "",
+  "File does not contain SFrame data",
+  "Iterating shared object reading error",
+  "Failed to malloc memory space",
+  "Failed to realloc memory space",
+  "Failed to open file",
+  "Failed on resolve canonical file name",
+  "Failed to reposition file offset",
+  "Failed to read from a file descriptor",
+  "Failed to get the user context",
+  "Failed to set up decode data",
+  "Illegal CFA offset"
+};
+
+/* Return the error message associated with the error code.  */
+
+const char *
+sframe_bt_errmsg (enum sframe_bt_errcode ecode)
+{
+  if ((unsigned int)ecode >= ENUM_ARRAY_SIZE (sframe_bt_errlist))
+    return "Unknown error";
+
+  return sframe_bt_errlist[ecode];
+}
diff --git a/libsframe/sframe-backtrace.c b/libsframe/sframe-backtrace.c
new file mode 100644 (file)
index 0000000..1280b00
--- /dev/null
@@ -0,0 +1,616 @@
+/* sframe-backtrace.c - The SFrame backtracer.
+
+   Copyright (C) 2022 Free Software Foundation, Inc.
+
+   This file is part of libsframebt.
+
+   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 "config.h"
+#include <link.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <execinfo.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ucontext.h>
+#include <stdarg.h>
+#include "ansidecl.h"
+#include "sframe-api.h"
+#include "sframe-backtrace-api.h"
+
+#ifndef PT_SFRAME
+#define PT_SFRAME 0x6474e554           /* FIXME.  */
+#endif
+
+#define _sf_printflike_(string_index, first_to_check) ATTRIBUTE_PRINTF (1, 2)
+
+static bool _sframe_unwind_debug;      /* Control for printing out debug info.  */
+static int no_of_entries = NUM_OF_DSOS;
+
+/* SFrame decode data for the main module or a DSO.  */
+struct sframe_decode_data
+{
+  char *sfdd_data;                     /* SFrame decode data.  */
+  int sfdd_data_size;                  /* SFrame decode data size.  */
+  uint64_t sfdd_text_vma;              /* Text segment's virtual address.  */
+  int sfdd_text_size;                  /* Text segment's size.  */
+  uint64_t sfdd_sframe_vma;            /* SFrame segment's virtual address.  */
+  sframe_decoder_ctx *sfdd_sframe_ctx; /* SFrame decoder context.  */
+};
+
+/* List that holds SFrame info for the shared libraries.  */
+struct dso_cfi_list
+{
+  int alloced;                         /* Entries allocated.  */
+  int used;                            /* Entries used.  */
+  struct sframe_decode_data *entry;    /* DSO's decode data.  */
+};
+
+/* Data that's passed through sframe_callback.  */
+struct sframe_unwind_info
+{
+  int sui_fd;                          /* File descriptor.  */
+  struct sframe_decode_data sui_ctx;   /* The decode data.  */
+  struct dso_cfi_list sui_dsos;                /* The DSO list.  */
+};
+
+static void
+sframe_unwind_init_debug (void)
+{
+  static int inited;
+
+  if (!inited)
+    {
+      _sframe_unwind_debug = getenv ("SFRAME_UNWIND_DEBUG") != NULL;
+      inited = 1;
+    }
+}
+
+_sf_printflike_ (1, 2)
+static void
+debug_printf (const char *format, ...)
+{
+  if (_sframe_unwind_debug == true)
+    {
+      va_list args;
+
+      va_start (args, format);
+      __builtin_vprintf (format, args);
+      va_end (args);
+    }
+}
+
+/* sframe_bt_errno - Check if there is error code in ERRP.  */
+
+static int
+sframe_bt_errno (const int *errp)
+{
+  if (errp == NULL)
+    return 0;
+
+  return (*errp != SFRAME_BT_OK);
+}
+
+/* sframe_bt_set_errno - Store the specified error code ERROR into ERRP if
+   it is non-NULL.  */
+
+static void
+sframe_bt_set_errno (int *errp, int error)
+{
+  if (errp != NULL)
+    *errp = error;
+}
+
+/* sframe_add_dso - Add .sframe info in D_DATA, which is associated with
+   a dynamic shared object, to D_LIST.  */
+
+static void
+sframe_add_dso (struct dso_cfi_list *d_list,
+               struct sframe_decode_data d_data,
+               int *errp)
+{
+  if (d_list->alloced == 0)
+    {
+      d_list->entry = malloc (no_of_entries * sizeof (struct sframe_decode_data));
+      if (d_list->entry == NULL)
+       {
+         sframe_bt_set_errno (errp, SFRAME_BT_ERR_MALLOC);
+         return;
+       }
+      memset (d_list->entry, 0,
+             no_of_entries * sizeof (struct sframe_decode_data));
+      d_list->alloced = no_of_entries;
+    }
+  else if (d_list->used == d_list->alloced)
+    {
+      d_list->entry = realloc (d_list->entry,
+                              ((d_list->alloced + no_of_entries)
+                               * sizeof (struct sframe_decode_data)));
+      if (d_list->entry == NULL)
+       {
+         sframe_bt_set_errno (errp, SFRAME_BT_ERR_REALLOC);
+         return;
+       }
+
+      memset (&d_list->entry[d_list->alloced], 0,
+             no_of_entries * sizeof (struct sframe_decode_data));
+      d_list->alloced += no_of_entries;
+    }
+
+  sframe_bt_set_errno (errp, SFRAME_BT_OK);
+  d_list->entry[d_list->used++] = d_data;
+}
+
+/* sframe_free_cfi - Free up space allocated for .sframe info for CF.  */
+
+static void
+sframe_free_cfi (struct sframe_unwind_info *sf)
+{
+  struct dso_cfi_list *d_list;
+  int i;
+
+  if (sf == NULL)
+    return;
+
+  free (sf->sui_ctx.sfdd_data);
+  sframe_decoder_free (&sf->sui_ctx.sfdd_sframe_ctx);
+  close (sf->sui_fd);
+
+  d_list = &sf-> sui_dsos;
+  if (d_list == NULL)
+    return;
+
+  for (i = 0; i < d_list->used; ++i)
+    {
+      free (d_list->entry[i].sfdd_data);
+      sframe_decoder_free (&d_list->entry[i].sfdd_sframe_ctx);
+    }
+
+  free (d_list->entry);
+}
+
+/* sframe_find_context - Find the decode data that contains ADDR from CF.
+   Return the pointer to the decode data or NULL.  */
+
+static struct sframe_decode_data *
+sframe_find_context (struct sframe_unwind_info *sf, uint64_t addr)
+{
+  struct dso_cfi_list *d_list;
+  struct sframe_decode_data sdec_data;
+  int i;
+
+  if (sf == NULL)
+    return NULL;
+
+  if (sf->sui_ctx.sfdd_text_vma < addr
+      && sf->sui_ctx.sfdd_text_vma + sf->sui_ctx.sfdd_text_size > addr)
+    return &sf->sui_ctx;
+
+  d_list = &sf->sui_dsos;
+  for (i = 0; i < sf->sui_dsos.used; ++i)
+    {
+      sdec_data = d_list->entry[i];
+      if ((sdec_data.sfdd_text_vma <= addr)
+         && (sdec_data.sfdd_text_vma + sdec_data.sfdd_text_size >= addr))
+       return &d_list->entry[i];
+    }
+
+  return NULL;
+}
+
+/* sframe_valid_addr - Check if ADDR is valid in CF. The address is considered
+   invalid, with regards to SFrame, if it's not in any address range of the
+   main module or any of its DSO's. Return 1 if valid, 0 otherwise.  */
+
+static int
+sframe_valid_addr (struct sframe_unwind_info *sf, uint64_t addr)
+{
+  struct sframe_decode_data *cdp;
+
+  if (sf == NULL)
+    return 0;
+
+  cdp = sframe_find_context (sf, addr);
+  return cdp ? 1 : 0;
+}
+
+/* sframe_load_ctx - Call decoder to create and set up the SFrame info for
+   either the main module or one of the DSOs from CF, based on the input
+   RADDR argument.  Return the newly created decode context or NULL.  */
+
+static sframe_decoder_ctx *
+sframe_load_ctx (struct sframe_unwind_info *sf, uint64_t raddr)
+{
+  sframe_decoder_ctx *nctx;
+  struct sframe_decode_data *cdp;
+
+  if (sf == NULL)
+    return NULL;
+
+  cdp = sframe_find_context (sf, raddr);
+  if (cdp == NULL)
+    return NULL;
+
+  if (cdp->sfdd_sframe_ctx == NULL)
+    {
+      int err;
+      nctx = sframe_decode (cdp->sfdd_data, cdp->sfdd_data_size, &err);
+      if (nctx == NULL)
+       return NULL;
+      cdp->sfdd_sframe_ctx = nctx;
+      return nctx;
+    }
+
+  return NULL;
+}
+
+/* sframe_update_ctx - Check if need to do a decode context switch, based on
+   the input RADDR argument, from CF. A new decode context will be created
+   and set up if it isn't already done so. Return the new decode context in
+   CTX and vma in CFI_VMA.  */
+
+static void
+sframe_update_ctx (struct sframe_unwind_info *sf, uint64_t raddr,
+                  sframe_decoder_ctx **ctx, uint64_t *cfi_vma)
+{
+  sframe_decoder_ctx *nctx;
+  struct sframe_decode_data *cdp;
+
+  cdp = sframe_find_context (sf, raddr);
+  if (cdp != NULL)
+    {
+      if (cdp->sfdd_sframe_ctx == NULL)
+       {
+         int err;
+         nctx = sframe_decode (cdp->sfdd_data, cdp->sfdd_data_size, &err);
+         if (nctx == NULL)
+           {
+             *ctx = NULL;
+             return;
+           }
+         cdp->sfdd_sframe_ctx = nctx;
+       }
+       *ctx = cdp->sfdd_sframe_ctx;
+       *cfi_vma = cdp->sfdd_sframe_vma;
+    }
+}
+
+/* get_contents - Return contents at ADDR from file descriptor FD.  */
+
+static uint64_t
+get_contents (int fd, uint64_t addr, int *errp)
+{
+  uint64_t data;
+  size_t sz;
+
+  sframe_bt_set_errno (errp, SFRAME_BT_OK);
+  if (lseek (fd, addr, SEEK_SET) == -1)
+    {
+      sframe_bt_set_errno (errp, SFRAME_BT_ERR_LSEEK);
+      return 0;
+    }
+  sz = read (fd, &data, sizeof (uint64_t));
+  if (sz != sizeof (uint64_t))
+    {
+      sframe_bt_set_errno (errp, SFRAME_BT_ERR_READ);
+      return 0;
+    }
+
+  return data;
+}
+
+/* sframe_fd_open - Open /proc image associated with the process id and return
+   the file descriptor.  */
+
+static int
+sframe_fd_open (int *errp)
+{
+  int fd;
+
+  if ((fd = open ("/proc/self/mem", O_CLOEXEC)) == -1)
+    {
+      sframe_bt_set_errno (errp, SFRAME_BT_ERR_OPEN);
+      return -1;
+    }
+
+  return fd;
+}
+
+/* sframe_callback - The callback from dl_iterate_phdr with header info
+   in INFO.
+   Return SFrame info for either the main module or a DSO in DATA.  */
+
+static int
+sframe_callback (struct dl_phdr_info *info,
+                size_t size ATTRIBUTE_UNUSED,
+                void *data)
+{
+  struct sframe_unwind_info *sf = (struct sframe_unwind_info *) data;
+  int p_type, i, fd, sframe_err;
+  ssize_t len;
+  uint64_t text_vma = 0;
+  int text_size = 0;
+
+  if (data == NULL || info == NULL)
+    return 1;
+
+  debug_printf ("-- name: %s %14p\n", info->dlpi_name, (void *)info->dlpi_addr);
+
+  for (i = 0; i < info->dlpi_phnum; i++)
+    {
+      debug_printf ("  %2d: [%" PRIu64 "; memsz %" PRIu64 "] flags: 0x%x; \n", i,
+                  (uint64_t) info->dlpi_phdr[i].p_vaddr,
+                  (uint64_t) info->dlpi_phdr[i].p_memsz,
+                  info->dlpi_phdr[i].p_flags);
+
+      p_type = info->dlpi_phdr[i].p_type;
+      if (p_type == PT_LOAD && info->dlpi_phdr[i].p_flags & PF_X)
+       {
+         text_vma = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
+         text_size = info->dlpi_phdr[i].p_memsz;
+         continue;
+       }
+      if (p_type != PT_SFRAME)
+       continue;
+
+      if (info->dlpi_name[0] == '\0')          /* the main module.  */
+       {
+         fd = sframe_fd_open (&sframe_err);
+         if (fd == -1)
+           return 1;
+         if (lseek (fd, info->dlpi_addr + info->dlpi_phdr[i].p_vaddr,
+                    SEEK_SET) == -1)
+           {
+             sframe_bt_set_errno (&sframe_err, SFRAME_BT_ERR_LSEEK);
+             return 1;
+           }
+
+         sf->sui_ctx.sfdd_data = malloc (info->dlpi_phdr[i].p_memsz);
+         if (sf->sui_ctx.sfdd_data == NULL)
+           {
+             sframe_bt_set_errno (&sframe_err, SFRAME_BT_ERR_MALLOC);
+             return 1;
+           }
+
+         len = read (fd, sf->sui_ctx.sfdd_data, info->dlpi_phdr[i].p_memsz);
+         if (len == -1 || len != (ssize_t) info->dlpi_phdr[i].p_memsz)
+           {
+             sframe_bt_set_errno (&sframe_err, SFRAME_BT_ERR_READ);
+             return 1;
+           }
+
+         assert (text_vma);
+         sf->sui_ctx.sfdd_data_size = len;
+         sf->sui_ctx.sfdd_sframe_vma = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
+         sf->sui_fd = fd;
+         sf->sui_ctx.sfdd_text_vma = text_vma;
+         sf->sui_ctx.sfdd_text_size = text_size;
+         text_vma = 0;
+         return 0;
+       }
+      else
+       {                                       /* a dynamic shared object.  */
+         struct sframe_decode_data dt;
+         memset (&dt, 0, sizeof (struct sframe_decode_data));
+         assert (sf->sui_fd);
+         if (lseek (sf->sui_fd, info->dlpi_addr + info->dlpi_phdr[i].p_vaddr,
+                    SEEK_SET) == -1)
+           {
+             sframe_bt_set_errno (&sframe_err, SFRAME_BT_ERR_LSEEK);
+             return 1;
+           }
+
+         dt.sfdd_data = malloc (info->dlpi_phdr[i].p_memsz);
+         if (dt.sfdd_data == NULL)
+           {
+             sframe_bt_set_errno (&sframe_err, SFRAME_BT_ERR_MALLOC);
+             return 1;
+           }
+
+         len = read (sf->sui_fd, dt.sfdd_data, info->dlpi_phdr[i].p_memsz);
+         if (len == -1 || len != (ssize_t) info->dlpi_phdr[i].p_memsz)
+           {
+             sframe_bt_set_errno (&sframe_err, SFRAME_BT_ERR_READ);
+             return 1;
+           }
+
+         assert (text_vma);
+         dt.sfdd_data_size = len;
+         dt.sfdd_sframe_vma = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
+         dt.sfdd_text_vma = text_vma;
+         dt.sfdd_text_size = text_size;
+         text_vma = 0;
+         sframe_add_dso (&sf->sui_dsos, dt, &sframe_err);
+         if (sframe_err != SFRAME_BT_OK)
+           return 1;
+         return 0;
+       }
+    }
+
+    return 0;
+}
+
+/* sframe_unwind - Unwind the stack backtrace for CF. If successful,
+   store the return addresses in RA_LST. The RA_SIZE argument specifies
+   the maximum number of return addresses that can be stored in RA_LST
+   and contains the number of the addresses collected.  */
+
+static void
+sframe_unwind (struct sframe_unwind_info *sf, void **ra_lst,
+              int *ra_size, int *errp)
+{
+  uint64_t cfa, return_addr, ra_stack_loc, rfp_stack_loc;
+  int8_t fixed_ra_offset;
+  sframe_decoder_ctx *ctx;
+  int cfa_offset, rfp_offset, errnum, i, count;
+  sframe_frame_row_entry fred, *frep = &fred;
+  uint64_t pc, rfp, rsp, cfi_vma;
+  ucontext_t context, *cp = &context;
+
+  if (sf == NULL || ra_lst == NULL || ra_size == NULL)
+    {
+      sframe_bt_set_errno (errp, SFRAME_BT_ERR_ARG);
+      return;
+    }
+
+  /* Get the user context for its registers.  */
+  if (getcontext (cp) != 0)
+    {
+      sframe_bt_set_errno (errp, SFRAME_BT_ERR_GETCONTEXT);
+      return;
+    }
+  sframe_bt_set_errno (errp, SFRAME_BT_OK);
+
+#if defined (__x86_64__)
+  pc = cp->uc_mcontext.gregs[REG_RIP];
+  rsp = cp->uc_mcontext.gregs[REG_RSP];
+  rfp = cp->uc_mcontext.gregs[REG_RBP];
+#elif defined (__aarch64__)
+#define UNWIND_AARCH64_X29             29      /* 64-bit frame pointer.  */
+#define UNWIND_AARCH64_X30             30      /* 64-bit link pointer.  */
+  pc = cp->uc_mcontext.pc;
+  rsp = cp->uc_mcontext.sp;
+  rfp = cp->uc_mcontext.regs[UNWIND_AARCH64_X29];
+  uint64_t ra = cp->uc_mcontext.regs[UNWIND_AARCH64_X30];
+#endif
+
+  /* Load and set up the decoder.  */
+  ctx = sframe_load_ctx (sf, pc);
+  if (ctx == NULL)
+    {
+      sframe_bt_set_errno (errp, SFRAME_BT_ERR_DECODE);
+      return;
+    }
+  cfi_vma = sf->sui_ctx.sfdd_sframe_vma;
+  count = *ra_size;
+
+  for (i = 0; i < count; ++i)
+    {
+      pc -= cfi_vma;
+      errnum = sframe_find_fre (ctx, pc, frep);
+      if (errnum == 0)
+       {
+         cfa_offset = sframe_fre_get_cfa_offset (ctx, frep, &errnum);
+         if (errnum == SFRAME_ERR_FREOFFSET_NOPRESENT)
+           {
+             sframe_bt_set_errno (errp, SFRAME_BT_ERR_CFA_OFFSET);
+             return;
+           }
+
+         cfa = ((frep->fre_info & 0x1) == SFRAME_BASE_REG_SP
+           ? rsp : rfp) + cfa_offset;
+
+#ifdef __x86_64__
+         /* For x86, read the return address from the fixed RA offset from
+            the SFrame header.  RA must be at location CFA - 8.  */
+         fixed_ra_offset = sframe_decoder_get_fixed_ra_offset (ctx);
+         if (fixed_ra_offset == SFRAME_CFA_FIXED_RA_INVALID)
+          return;
+
+         ra_stack_loc = cfa + fixed_ra_offset;
+         return_addr = get_contents (sf->sui_fd, ra_stack_loc, errp);
+         if (sframe_bt_errno (errp))
+           return;
+#else
+#ifdef __aarch64__
+         int ra_offset = sframe_fre_get_ra_offset (ctx, frep, &errnum);
+         if (errnum == 0)
+           {
+             ra_stack_loc = cfa + ra_offset;
+             return_addr = get_contents (sf->sui_fd, ra_stack_loc, errp);
+             if (sframe_bt_errno (errp))
+               return;
+           }
+         else
+           return_addr = ra;
+#endif
+#endif
+
+         /* Validate and add return address to the list.  */
+         if (sframe_valid_addr (sf, return_addr) == 0)
+           {
+             i -= 1;
+             goto find_fre_ra_err;
+           }
+         if (i != 0)           /* exclude self.  */
+           ra_lst[i-1] = (void *)return_addr;
+
+         /* Set up for the next frame.  */
+         rfp_offset = sframe_fre_get_fp_offset (ctx, frep, &errnum);
+         if (errnum == 0)
+           {
+             rfp_stack_loc = cfa + rfp_offset;
+             rfp = get_contents (sf->sui_fd, rfp_stack_loc, errp);
+             if (sframe_bt_errno (errp))
+               return;
+           }
+         rsp = cfa;
+         pc = return_addr;
+
+         /* Check if need to update the decoder context and vma.  */
+         sframe_update_ctx (sf, return_addr, &ctx, &cfi_vma);
+         if (ctx == NULL)
+           {
+             sframe_bt_set_errno (errp, SFRAME_BT_ERR_DECODE);
+             return;
+           }
+       }
+      else
+       {
+         i -= 1;
+         goto find_fre_ra_err;
+       }
+    }
+
+find_fre_ra_err:
+  *ra_size = i;
+}
+
+/* sframe_backtrace - Main API that user program calls to get a backtrace.
+   The BUFFER argument provides space for the list of the return addresses
+   and the SIZE argument specifies the maximum number of addresses that
+   can be stored in the buffer.  Return the number of return addresses
+   collected or -1 if there is any error.  */
+
+int
+sframe_backtrace (void **buffer, int size, int *errp)
+{
+  struct sframe_unwind_info sframeinfo;
+
+  sframe_unwind_init_debug ();
+
+  memset (&sframeinfo, 0, sizeof (struct sframe_unwind_info));
+
+  /* find and set up the .sframe sections.  */
+  (void) dl_iterate_phdr (sframe_callback, (void *)&sframeinfo);
+  if (sframeinfo.sui_fd == 0)
+    {
+      sframe_bt_set_errno (errp, SFRAME_BT_ERR_NOTPRESENT);
+      return -1;
+    }
+
+  /* Do the stack unwinding.  */
+  sframe_unwind (&sframeinfo, buffer, &size, errp);
+  if (sframe_bt_errno (errp))
+    size = -1;
+
+  sframe_free_cfi (&sframeinfo);
+
+  return size;
+}