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
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
new file mode 100644
@@ -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"
+])
new file mode 100644
@@ -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 */
@@ -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
@@ -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
@@ -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])
@@ -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
new file mode 100644
@@ -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];
+}
new file mode 100644
@@ -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;
+}