[V3,08/15] unwinder: generate backtrace using SFrame format

Message ID 20221030074450.1956074-9-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
  From: Weimin Pan <weimin.pan@oracle.com>

[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.

PS: libsframe/configure has NOT been included in the patch.  Please
regenerate.

config/ChangeLog:

	* sframe.m4: New file.

include/ChangeLog:

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

ChangeLog:

	* libsframe/Makefile.am: Build backtrace functionality in its
	own library.  Install libsframebt conditionally.
	* libsframe/Makefile.in: Regenerate.
	* libsframe/aclocal.m4: Regenerate.
	* libsframe/configure: Regenerate.  <-- [REMOVED FROM THE PATCH.
	  PLEASE 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.
---
 config/sframe.m4                 |  16 +
 include/sframe-backtrace-api.h   |  57 +++
 libsframe/Makefile.am            |  12 +
 libsframe/Makefile.in            |  58 ++-
 libsframe/aclocal.m4             |   1 +
 libsframe/configure.ac           |   7 +
 libsframe/sframe-backtrace-err.c |  46 +++
 libsframe/sframe-backtrace.c     | 626 +++++++++++++++++++++++++++++++
 8 files changed, 816 insertions(+), 7 deletions(-)
 create mode 100644 config/sframe.m4
 create mode 100644 include/sframe-backtrace-api.h
 create mode 100644 libsframe/sframe-backtrace-err.c
 create mode 100644 libsframe/sframe-backtrace.c
  

Comments

Mike Frysinger Oct. 30, 2022, 2:03 p.m. UTC | #1
On 30 Oct 2022 00:44, Indu Bhagat via Binutils wrote:
> --- /dev/null
> +++ b/config/sframe.m4
> @@ -0,0 +1,16 @@
> +# SFRAME_CHECK_AS_SFRAME
> +# ----------------------
> +# Check whether the assembler supports generation of SFrame
> +# unwind information.
> +#
> +# Defines:
> +# ac_cv_have_sframe
> +

you should be using `dnl` for comments in m4 files so they aren't copied
into the generated output.

> +AC_DEFUN([SFRAME_CHECK_AS_SFRAME],[

space after the ,

> +  ac_save_CFLAGS="$CFLAGS"
> +  CFLAGS="$CFLAGS -Wa,--gsframe"
> +  AC_MSG_CHECKING([for as that supports --gsframe])
> +  AC_TRY_COMPILE([], [return 0;], [ac_cv_have_sframe=yes], [ac_cv_have_sframe=no])
> +  AC_MSG_RESULT($ac_cv_have_sframe)
> +  CFLAGS="$ac_save_CFLAGS"
> +])

you call it "ac_cv_have_sframe" which implies it's an autoconf cached var,
but you aren't actually using the AC_CACHE_CHECK macro.

i'm guessing this isn't actually coming from autoconf, or will be merged
there, so shouldn't this be using a "gcc_cv_" prefix instead ?  i'm not
sure what the policy is on config/ when it comes to home-grown cache vars.

similarly, should the macro name lacks scoping ...

> --- a/libsframe/configure.ac
> +++ b/libsframe/configure.ac
>
>  COMPAT_DEJAGNU=$ac_cv_dejagnu_compat
>  AC_SUBST(COMPAT_DEJAGNU)
>  
> +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_AS_SFRAME
> +AM_CONDITIONAL([HAVE_SFRAME_AS], [test "x$ac_cv_have_sframe" = "xyes"])

hmm, is this macro only used by libsframe/ ?  if no one else is going to use
this macro, config/ isn't the right place for it.  you should put it into
libsframe/acinclude.m4 instead.

> --- /dev/null
> +++ b/libsframe/sframe-backtrace-err.c
>
> +/* 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)
> +{
> +  return sframe_bt_errlist[ecode];
> +}

this needs to make sure the ecode is within range, and if it isn't, at the
very least perform an assert/abort.
  assert((unsigned int)ecode < ARRAY_SIZE (sframe_bt_errlist))

> --- /dev/null
> +++ b/libsframe/sframe-backtrace.c
>
> +#ifndef _GNU_SOURCE
> +#define _GNU_SOURCE
> +#endif

i thought your configure script already took care of this ?

> +#ifndef PT_SFRAME
> +#define PT_SFRAME 0x6474e554		/* FIXME.  */
> +#endif

what excatly is the fix ?  that it should be defined in include/elf/common.h
first (among other places) ?  you should do that then instead of keeping this.

> +#define _sf_printflike_(string_index,first_to_check) \
> +    __attribute__ ((__format__ (__printf__, (string_index), (first_to_check))))

use ATTRIBUTE_PRINTF from ansidecl instead of defining your own

> +static int _sframe_unwind_debug;	/* Control for printing out debug info.  */

this should be a proper bool instead of an int-pretending-to-be-bool

> +static int no_of_entries = 32;

shouldn't this be a const ?

> +static int
> +sframe_bt_errno (int *errp)
> +{
> +  if (errp == NULL)
> +    return 0;
> +
> +  return (*errp != SFRAME_BT_OK);
> +}

shouldn't errp be const ?

> +static int
> +sframe_fd_open (int *errp)
> +{
> +  char filename[PATH_MAX];

don't rely on PATH_MAX.  use asprintf.

> +  pid = getpid ();
> +  snprintf (filename, sizeof filename, "/proc/%d/task/%d/mem", pid, pid);

/proc/<pid>/task/<pid>/ is the same thing as /proc/<pid>/

and /proc/<getpid>/ is simply /proc/self/

so can't you use the constant path /proc/self/mem ?
am i missing something obvious here ?

> +  if ((fd = open (filename, O_RDONLY)) == -1)

you should always use O_CLOEXEC when calling open().  if you don't want
that flag, you should always leave a comment explaining why.

> +static int
> +sframe_callback (struct dl_phdr_info *info,

this seems to be returning a bool, so use a bool instead of an int

> +  for (i = 0; i < info->dlpi_phnum; i++)
> +    {
> +      debug_printf("    %2d: [%14p; memsz:%7lx] flags: 0x%x; \n", i,

GNU style puts space before the (

> +		   (void *) info->dlpi_phdr[i].p_vaddr,

p_vaddr is an Elf_Addr right ?  you can't assume that sizeof(void*) is
>= sizeof(Elf_Addr).  a 64-bit BFD on a 32-bit host will break this.
cast it to a proper uint64_t and use proper stdint.h PRIx64 types.

> +		   info->dlpi_phdr[i].p_memsz,

i think this also has broken assumptions -- that sizeof(long) ==
sizeof(p_memsz).  cast it to 64-bit too.

> +	  sf->sui_ctx.sfdd_data = (char *) malloc (info->dlpi_phdr[i].p_memsz);

malloc returns void*.  in C, we don't have to cast that.  so you can omit it.

> +static void
> +sframe_unwind (struct sframe_unwind_info *sf, void **ra_lst,
> +	       int *ra_size, int *errp)
> +{
> ...
> +#ifdef __x86_64__

this really needs better delegation for porting than inlined in the middle
of a large portable file.  can you at least factor it out into a header ?

> +  pc = cp->uc_mcontext.gregs[REG_RIP];
> +  rsp = cp->uc_mcontext.gregs[REG_RSP];
> +  rfp = cp->uc_mcontext.gregs[REG_RBP];
> +#else
> +#ifdef __aarch64__

use `#elif defined` to avoid an ever-growing set of nested #else/#endif
-mike
  
Weimin Pan Nov. 1, 2022, 10:36 p.m. UTC | #2
Thanks for your comments.

On 10/30/2022 7:03 AM, Mike Frysinger via Binutils wrote:
> On 30 Oct 2022 00:44, Indu Bhagat via Binutils wrote:
>> --- /dev/null
>> +++ b/config/sframe.m4
>> @@ -0,0 +1,16 @@
>> +# SFRAME_CHECK_AS_SFRAME
>> +# ----------------------
>> +# Check whether the assembler supports generation of SFrame
>> +# unwind information.
>> +#
>> +# Defines:
>> +# ac_cv_have_sframe
>> +
> you should be using `dnl` for comments in m4 files so they aren't copied
> into the generated output.

OK.

>> +AC_DEFUN([SFRAME_CHECK_AS_SFRAME],[
> space after the ,

OK.

>> +  ac_save_CFLAGS="$CFLAGS"
>> +  CFLAGS="$CFLAGS -Wa,--gsframe"
>> +  AC_MSG_CHECKING([for as that supports --gsframe])
>> +  AC_TRY_COMPILE([], [return 0;], [ac_cv_have_sframe=yes], [ac_cv_have_sframe=no])
>> +  AC_MSG_RESULT($ac_cv_have_sframe)
>> +  CFLAGS="$ac_save_CFLAGS"
>> +])
> you call it "ac_cv_have_sframe" which implies it's an autoconf cached var,
> but you aren't actually using the AC_CACHE_CHECK macro.
>
> i'm guessing this isn't actually coming from autoconf, or will be merged
> there, so shouldn't this be using a "gcc_cv_" prefix instead ?  i'm not
> sure what the policy is on config/ when it comes to home-grown cache vars.

Indu should be able to address issue better.

> similarly, should the macro name lacks scoping ...
>
>> --- a/libsframe/configure.ac
>> +++ b/libsframe/configure.ac
>>
>>   COMPAT_DEJAGNU=$ac_cv_dejagnu_compat
>>   AC_SUBST(COMPAT_DEJAGNU)
>>   
>> +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_AS_SFRAME
>> +AM_CONDITIONAL([HAVE_SFRAME_AS], [test "x$ac_cv_have_sframe" = "xyes"])
> hmm, is this macro only used by libsframe/ ?  if no one else is going to use
> this macro, config/ isn't the right place for it.  you should put it into
> libsframe/acinclude.m4 instead.

Looks like libsframe/ is the only place that uses this macro.

>> --- /dev/null
>> +++ b/libsframe/sframe-backtrace-err.c
>>
>> +/* 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)
>> +{
>> +  return sframe_bt_errlist[ecode];
>> +}
> this needs to make sure the ecode is within range, and if it isn't, at the
> very least perform an assert/abort.
>    assert((unsigned int)ecode < ARRAY_SIZE (sframe_bt_errlist))

OK, added code to validate ecode. It prints "Unknown error" for invalid 
ecode.

>> --- /dev/null
>> +++ b/libsframe/sframe-backtrace.c
>>
>> +#ifndef _GNU_SOURCE
>> +#define _GNU_SOURCE
>> +#endif
> i thought your configure script already took care of this ?

Yes,  the above code is taken out.

>> +#ifndef PT_SFRAME
>> +#define PT_SFRAME 0x6474e554		/* FIXME.  */
>> +#endif
> what excatly is the fix ?  that it should be defined in include/elf/common.h
> first (among other places) ?  you should do that then instead of keeping this.

Since <link.h>, which is needed for dl_iterate_phdr, also includes <elf.h>.
using include/elf/common.h will cause multiple defined warnings, e.g. PT_HIPROC.
Looks like it should be defined in /usr/include/elf.h.

>> +#define _sf_printflike_(string_index,first_to_check) \
>> +    __attribute__ ((__format__ (__printf__, (string_index), (first_to_check))))
> use ATTRIBUTE_PRINTF from ansidecl instead of defining your own

OK.

>> +static int _sframe_unwind_debug;	/* Control for printing out debug info.  */
> this should be a proper bool instead of an int-pretending-to-be-bool

Change its type to bool.

>> +static int no_of_entries = 32;
> shouldn't this be a const ?

OK, done.

>> +static int
>> +sframe_bt_errno (int *errp)
>> +{
>> +  if (errp == NULL)
>> +    return 0;
>> +
>> +  return (*errp != SFRAME_BT_OK);
>> +}
> shouldn't errp be const ?

Done.

>> +static int
>> +sframe_fd_open (int *errp)
>> +{
>> +  char filename[PATH_MAX];
> don't rely on PATH_MAX.  use asprintf.
>
>> +  pid = getpid ();
>> +  snprintf (filename, sizeof filename, "/proc/%d/task/%d/mem", pid, pid);
> /proc/<pid>/task/<pid>/ is the same thing as /proc/<pid>/
>
> and /proc/<getpid>/ is simply /proc/self/
>
> so can't you use the constant path /proc/self/mem ?
> am i missing something obvious here ?

Nice. We can get rid of asprintf and filename[] altogether and just do:

         if ((fd = open ("/proc/self/mem", O_CLOEXEC)) == -1)
>> +  if ((fd = open (filename, O_RDONLY)) == -1)
> you should always use O_CLOEXEC when calling open().  if you don't want
> that flag, you should always leave a comment explaining why.
>
>> +static int
>> +sframe_callback (struct dl_phdr_info *info,
> this seems to be returning a bool, so use a bool instead of an int

Function sframe_callback is the "callback" of


        int dl_iterate_phdr(
                  int (*callback) (struct dl_phdr_info *info,
                                   size_t size, void *data), void *data);

which returns an int: 0 for success and 1 otherwise.

>> +  for (i = 0; i < info->dlpi_phnum; i++)
>> +    {
>> +      debug_printf("    %2d: [%14p; memsz:%7lx] flags: 0x%x; \n", i,
> GNU style puts space before the (

OK

>> +		   (void *) info->dlpi_phdr[i].p_vaddr,
> p_vaddr is an Elf_Addr right ?  you can't assume that sizeof(void*) is
>> = sizeof(Elf_Addr).  a 64-bit BFD on a 32-bit host will break this.
> cast it to a proper uint64_t and use proper stdint.h PRIx64 types.
>
>> +		   info->dlpi_phdr[i].p_memsz,
> i think this also has broken assumptions -- that sizeof(long) ==
> sizeof(p_memsz).  cast it to 64-bit too.

OK, cast it to uint64_t and use PRIu64 for both p_vaddr and p_memsz.

>> +	  sf->sui_ctx.sfdd_data = (char *) malloc (info->dlpi_phdr[i].p_memsz);
> malloc returns void*.  in C, we don't have to cast that.  so you can omit it.

OK, done.

>
>> +static void
>> +sframe_unwind (struct sframe_unwind_info *sf, void **ra_lst,
>> +	       int *ra_size, int *errp)
>> +{
>> ...
>> +#ifdef __x86_64__
> this really needs better delegation for porting than inlined in the middle
> of a large portable file.  can you at least factor it out into a header ?

Sorry, not sure I follow this comment. Would you please elaborate on 
"factoring it out into a header"?

>> +  pc = cp->uc_mcontext.gregs[REG_RIP];
>> +  rsp = cp->uc_mcontext.gregs[REG_RSP];
>> +  rfp = cp->uc_mcontext.gregs[REG_RBP];
>> +#else
>> +#ifdef __aarch64__
> use `#elif defined` to avoid an ever-growing set of nested #else/#endif

OK.

Thanks again.

> -mike
  
Indu Bhagat Nov. 2, 2022, 6:23 a.m. UTC | #3
(Adding Weimin Pan who is the author of the patch)

On 10/30/22 07:03, Mike Frysinger wrote:
> On 30 Oct 2022 00:44, Indu Bhagat via Binutils wrote:
>> --- /dev/null
>> +++ b/config/sframe.m4
>> @@ -0,0 +1,16 @@
>> +# SFRAME_CHECK_AS_SFRAME
>> +# ----------------------
>> +# Check whether the assembler supports generation of SFrame
>> +# unwind information.
>> +#
>> +# Defines:
>> +# ac_cv_have_sframe
>> +
> you should be using `dnl` for comments in m4 files so they aren't copied
> into the generated output.
> 
>> +AC_DEFUN([SFRAME_CHECK_AS_SFRAME],[
> space after the ,
> 
>> +  ac_save_CFLAGS="$CFLAGS"
>> +  CFLAGS="$CFLAGS -Wa,--gsframe"
>> +  AC_MSG_CHECKING([for as that supports --gsframe])
>> +  AC_TRY_COMPILE([], [return 0;], [ac_cv_have_sframe=yes], [ac_cv_have_sframe=no])
>> +  AC_MSG_RESULT($ac_cv_have_sframe)
>> +  CFLAGS="$ac_save_CFLAGS"
>> +])
> you call it "ac_cv_have_sframe" which implies it's an autoconf cached var,
> but you aren't actually using the AC_CACHE_CHECK macro.
> 

I will use the AC_CACHE_CHECK macro in V4. Also changed the prefix to 
"sframe_cv_" instead.

> i'm guessing this isn't actually coming from autoconf, or will be merged
> there, so shouldn't this be using a "gcc_cv_" prefix instead ?  i'm not
> sure what the policy is on config/ when it comes to home-grown cache vars.
> 
> similarly, should the macro name lacks scoping ...
> 

Can you please clarify what is meant by "should the macro name lack 
scoping.." in the current context ?

>> --- a/libsframe/configure.ac
>> +++ b/libsframe/configure.ac
>>
>>   COMPAT_DEJAGNU=$ac_cv_dejagnu_compat
>>   AC_SUBST(COMPAT_DEJAGNU)
>>   
>> +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_AS_SFRAME
>> +AM_CONDITIONAL([HAVE_SFRAME_AS], [test "x$ac_cv_have_sframe" = "xyes"])
> hmm, is this macro only used by libsframe/ ?  if no one else is going to use
> this macro, config/ isn't the right place for it.  you should put it into
> libsframe/acinclude.m4 instead.
> 

Yes, at this time the macro is only being used in libsframe. OK, I will 
move it to libsframe/acinclude.m4 in V4.

Thanks
  
Mike Frysinger Nov. 2, 2022, 3 p.m. UTC | #4
On 01 Nov 2022 15:36, Weimin Pan via Binutils wrote:
> On 10/30/2022 7:03 AM, Mike Frysinger via Binutils wrote:
> > On 30 Oct 2022 00:44, Indu Bhagat via Binutils wrote:
> >> +static void
> >> +sframe_unwind (struct sframe_unwind_info *sf, void **ra_lst,
> >> +	       int *ra_size, int *errp)
> >> +{
> >> ...
> >> +#ifdef __x86_64__
> > this really needs better delegation for porting than inlined in the middle
> > of a large portable file.  can you at least factor it out into a header ?
> 
> Sorry, not sure I follow this comment. Would you please elaborate on 
> "factoring it out into a header"?

common code like this should not be stuffed with arch-specific checks and
implementations.  it makes it hard for more ports to happen, and for code
to be checked/validated.

further, as-written, this code can only be run on a system with a matching
runtime architecture.  normally a library that is meant for processing of
files is host-agnostic.  that's like the whole design of libbfd and other
tools in the binutils project.  i don't know how important that use case
is to this particular bit of code though.

to at least address the code health, create an API between the common code
and the arch-specific code, and then add arch-specific files that implement
that API.  it could be a header of small inline functions.

for the host-agnostic part, i haven't really read the library API to make
suggestions for how to address it.  libbfd takes objects/arches as arguments
and then operates according to those.
-mike
  

Patch

diff --git a/config/sframe.m4 b/config/sframe.m4
new file mode 100644
index 00000000000..9f149fda9ef
--- /dev/null
+++ b/config/sframe.m4
@@ -0,0 +1,16 @@ 
+# SFRAME_CHECK_AS_SFRAME
+# ----------------------
+# Check whether the assembler supports generation of SFrame
+# unwind information.
+#
+# Defines:
+# ac_cv_have_sframe
+
+AC_DEFUN([SFRAME_CHECK_AS_SFRAME],[
+  ac_save_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -Wa,--gsframe"
+  AC_MSG_CHECKING([for as that supports --gsframe])
+  AC_TRY_COMPILE([], [return 0;], [ac_cv_have_sframe=yes], [ac_cv_have_sframe=no])
+  AC_MSG_RESULT($ac_cv_have_sframe)
+  CFLAGS="$ac_save_CFLAGS"
+])
diff --git a/include/sframe-backtrace-api.h b/include/sframe-backtrace-api.h
new file mode 100644
index 00000000000..ad8f6bed024
--- /dev/null
+++ b/include/sframe-backtrace-api.h
@@ -0,0 +1,57 @@ 
+/* 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_PHDR,
+  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,
+};
+
+/* 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 */
diff --git a/libsframe/Makefile.am b/libsframe/Makefile.am
index d8198a166c5..6b27ccdcdf6 100644
--- a/libsframe/Makefile.am
+++ b/libsframe/Makefile.am
@@ -36,4 +36,16 @@  endif
 libsframe_la_SOURCES = sframe.c sframe-dump.c sframe-error.c
 libsframe_la_CPPFLAGS = $(AM_CPPFLAGS)
 
+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 testsuite/local.mk
diff --git a/libsframe/Makefile.in b/libsframe/Makefile.in
index 340bfe88060..3d0b26000db 100644
--- a/libsframe/Makefile.in
+++ b/libsframe/Makefile.in
@@ -89,8 +89,11 @@  PRE_UNINSTALL = :
 POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
+@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
 check_PROGRAMS = $(am__EXEEXT_1)
-@HAVE_COMPAT_DEJAGNU_TRUE@am__append_1 = testsuite/libsframe.decode/be-flipping \
+@HAVE_COMPAT_DEJAGNU_TRUE@am__append_4 = 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
@@ -103,6 +106,7 @@  am__aclocal_m4_deps = $(top_srcdir)/../bfd/acinclude.m4 \
 	$(top_srcdir)/../config/jobserver.m4 \
 	$(top_srcdir)/../config/lead-dot.m4 \
 	$(top_srcdir)/../config/override.m4 \
+	$(top_srcdir)/../config/sframe.m4 \
 	$(top_srcdir)/../config/warnings.m4 \
 	$(top_srcdir)/../libtool.m4 $(top_srcdir)/../ltoptions.m4 \
 	$(top_srcdir)/../ltsugar.m4 $(top_srcdir)/../ltversion.m4 \
@@ -157,6 +161,20 @@  am__v_lt_0 = --silent
 am__v_lt_1 = 
 @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) \
@@ -216,12 +234,13 @@  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) \
 	$(testsuite_libsframe_encode_encode_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) \
@@ -231,7 +250,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
@@ -423,12 +443,17 @@  INCDIR = $(srcdir)/../include
 # include libctf for swap.h
 AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/../include -I$(srcdir)/../libctf
 AM_CFLAGS = @ac_libsframe_warn_cflags@
-@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)
+@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
 
 # Setup the testing framework
 EXPECT = expect
@@ -550,6 +575,9 @@  clean-noinstLTLIBRARIES:
 libsframe.la: $(libsframe_la_OBJECTS) $(libsframe_la_DEPENDENCIES) $(EXTRA_libsframe_la_DEPENDENCIES) 
 	$(AM_V_CCLD)$(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; \
@@ -610,6 +638,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@
@@ -660,6 +690,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/aclocal.m4 b/libsframe/aclocal.m4
index b0cdd6b184d..1bceeeab5a3 100644
--- a/libsframe/aclocal.m4
+++ b/libsframe/aclocal.m4
@@ -1234,6 +1234,7 @@  m4_include([../config/depstand.m4])
 m4_include([../config/jobserver.m4])
 m4_include([../config/lead-dot.m4])
 m4_include([../config/override.m4])
+m4_include([../config/sframe.m4])
 m4_include([../config/warnings.m4])
 m4_include([../libtool.m4])
 m4_include([../ltoptions.m4])
diff --git a/libsframe/configure.ac b/libsframe/configure.ac
index a6c1d26bbbb..2775fe88b68 100644
--- a/libsframe/configure.ac
+++ b/libsframe/configure.ac
@@ -59,6 +59,13 @@  AM_CONDITIONAL([HAVE_COMPAT_DEJAGNU], [test "x$ac_cv_dejagnu_compat" = "xyes"])
 COMPAT_DEJAGNU=$ac_cv_dejagnu_compat
 AC_SUBST(COMPAT_DEJAGNU)
 
+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_AS_SFRAME
+AM_CONDITIONAL([HAVE_SFRAME_AS], [test "x$ac_cv_have_sframe" = "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
index 00000000000..70bd55ccf35
--- /dev/null
+++ b/libsframe/sframe-backtrace-err.c
@@ -0,0 +1,46 @@ 
+/* 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)
+{
+  return sframe_bt_errlist[ecode];
+}
diff --git a/libsframe/sframe-backtrace.c b/libsframe/sframe-backtrace.c
new file mode 100644
index 00000000000..877bbccfba7
--- /dev/null
+++ b/libsframe/sframe-backtrace.c
@@ -0,0 +1,626 @@ 
+/* 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/>.  */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include "config.h"
+#include <elf.h>
+#include <link.h>
+#include <stdlib.h>
+#include <stdio.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__ ((__format__ (__printf__, (string_index), (first_to_check))))
+
+static int _sframe_unwind_debug;	/* Control for printing out debug info.  */
+static int no_of_entries = 32;
+
+/* 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)
+    {
+      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 (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)
+{
+  char filename[PATH_MAX];
+  pid_t pid;
+  int fd;
+
+  pid = getpid ();
+  snprintf (filename, sizeof filename, "/proc/%d/task/%d/mem", pid, pid);
+  if ((fd = open (filename, O_RDONLY)) == -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: [%14p; memsz:%7lx] flags: 0x%x; \n", i,
+		   (void *) info->dlpi_phdr[i].p_vaddr,
+		   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 = (char *) 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 = (char *) 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);
+
+#ifdef __x86_64__
+  pc = cp->uc_mcontext.gregs[REG_RIP];
+  rsp = cp->uc_mcontext.gregs[REG_RSP];
+  rfp = cp->uc_mcontext.gregs[REG_RBP];
+#else
+#ifdef __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
+#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 == ESFRAME_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))
+    return -1;
+
+  sframe_free_cfi (&sframeinfo);
+
+  return size;
+}