]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
unwinder: Add SFrame unwinder tests
authorWeimin Pan <weimin.pan@oracle.com>
Tue, 27 Sep 2022 22:24:47 +0000 (15:24 -0700)
committerIndu Bhagat <indu.bhagat@oracle.com>
Sun, 23 Mar 2025 18:42:04 +0000 (11:42 -0700)
[Changes in V4]
  - Addressed Mike's review comments.
    - Be careful with the use of # and dnl in configure.ac
    - Add AC_CANONICAL_TARGET as we check for target.
    - Remove the LC_ALL=C bits.
  - Minor code fixups in the testcases
    - Removed unnecessary unistd.h.
    - use ATTRIBUTE_NOCLONE consistently.
    - Other minor cleanups.
[End of changes in V4]

[Changes in V3]
  - Added two new tests with attributes -f(no-)omit-frame-pointer.
  - Minor adjustments due to buildsystem changes in libsframe.
[End of changes in V3]

[Changes in V2]
  - minor changes in filenames in the testsuite.
[End of changes in V2]

Add tests for backtracing using SFrame section.

ChangeLog:

* libsframe/Makefile.in: Regenerated.
* libsframe/configure: Regenerated.
* libsframe/configure.ac: Check for cross compilation.
* libsframe/testsuite/Makefile.in: Regenerated.
* libsframe/testsuite/config/default.exp: Load
  sframe-lib.exp.
* libsframe/testsuite/libsframe.decode/Makefile.in:
  Regenerated.
* libsframe/testsuite/libsframe.encode/Makefile.in:
  Regenerated.
* libsframe/testsuite/lib/sframe-lib.exp: New file.  Add
  procedures for handling unwinder tests.
* libsframe/testsuite/libsframe.unwind/backtrace.c: New test.
* libsframe/testsuite/libsframe.unwind/backtrace.lk: New test.
* libsframe/testsuite/libsframe.unwind/inline-cmds.c: New test.
* libsframe/testsuite/libsframe.unwind/inline-cmds.lk: New test.
* libsframe/testsuite/libsframe.unwind/inline.c: New test.
* libsframe/testsuite/libsframe.unwind/inline.lk: New test.
* libsframe/testsuite/libsframe.unwind/solib-lib1.c: New test.
* libsframe/testsuite/libsframe.unwind/solib-lib2.c: New test.
* libsframe/testsuite/libsframe.unwind/solib-main.c: New test.
* libsframe/testsuite/libsframe.unwind/solib-main.d: New test.
* libsframe/testsuite/libsframe.unwind/solib.exp: New file.
* libsframe/testsuite/libsframe.unwind/solib-lib1.h: New test.
* libsframe/testsuite/libsframe.unwind/solib-lib2.h: New test.
* libsframe/testsuite/libsframe.unwind/tailcall.c: New test.
* libsframe/testsuite/libsframe.unwind/tailcall.lk: New test.
* libsframe/testsuite/libsframe.unwind/ttest.c: New test.
* libsframe/testsuite/libsframe.unwind/ttest.lk: New test.
* libsframe/testsuite/libsframe.unwind/unwind.exp: New file.
* libsframe/testsuite/libsframe.unwind/backtrace-fp-attr-1.c:
  Likewise.
* libsframe/testsuite/libsframe.unwind/backtrace-fp-attr-1.lk:
  Likewise.
* libsframe/testsuite/libsframe.unwind/backtrace-fp-attr-2.c:
  Likewise.
* libsframe/testsuite/libsframe.unwind/backtrace-fp-attr-2.lk:
  Likewise.

27 files changed:
libsframe/Makefile.in
libsframe/configure
libsframe/configure.ac
libsframe/testsuite/config/default.exp
libsframe/testsuite/lib/sframe-lib.exp [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/backtrace-fp-attr-1.c [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/backtrace-fp-attr-1.lk [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/backtrace-fp-attr-2.c [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/backtrace-fp-attr-2.lk [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/backtrace.c [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/backtrace.lk [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/inline-cmds.c [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/inline-cmds.lk [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/inline.c [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/inline.lk [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/solib-lib1.c [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/solib-lib1.h [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/solib-lib2.c [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/solib-lib2.h [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/solib-main.c [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/solib-main.d [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/solib.exp [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/tailcall.c [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/tailcall.lk [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/ttest.c [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/ttest.lk [new file with mode: 0644]
libsframe/testsuite/libsframe.unwind/unwind.exp [new file with mode: 0644]

index f9384c1c2666b03d5ed8c49150c2d21ef122535a..9aabca70d59e4d5454dbc04d3e4877d757695d76 100644 (file)
@@ -412,6 +412,7 @@ CFLAGS = @CFLAGS@
 COMPAT_DEJAGNU = @COMPAT_DEJAGNU@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
+CROSS_COMPILE = @CROSS_COMPILE@
 CYGPATH_W = @CYGPATH_W@
 DEFS = @DEFS@
 DEPDIR = @DEPDIR@
index cf84dc7db1b114539c0a687602b3f2d6d7caf42f..fd1dfe9a038a47bbe9b95b7d7f4e122b548c21a7 100755 (executable)
@@ -645,6 +645,7 @@ MAINTAINER_MODE_FALSE
 MAINTAINER_MODE_TRUE
 HAVE_SFRAME_AS_FALSE
 HAVE_SFRAME_AS_TRUE
+CROSS_COMPILE
 HAVE_LD_VERSION_SCRIPT_FALSE
 HAVE_LD_VERSION_SCRIPT_TRUE
 HAVE_SOLARIS_LD_FALSE
@@ -2419,6 +2420,7 @@ test -n "$target_alias" &&
     NONENONEs,x,x, &&
   program_prefix=${target_alias}-
 
+
 # Expand $ac_aux_dir to an absolute path.
 am_aux_dir=`cd "$ac_aux_dir" && pwd`
 
@@ -11565,7 +11567,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11568 "configure"
+#line 11570 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -11671,7 +11673,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11674 "configure"
+#line 11676 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
 
 
 
+is_cross_compiler=
+if test x"${host}" = x"${target}" ; then
+  is_cross_compiler=no
+else
+  is_cross_compiler=yes
+fi
+CROSS_COMPILE=$is_cross_compiler
+
+
+
 
   { $as_echo "$as_me:${as_lineno-$LINENO}: checking if as supports --gsframe" >&5
 $as_echo_n "checking if as supports --gsframe... " >&6; }
index be9fbbec074eb5dca9072294a50f718ebb7e3973..af23e6e4f1ebbd3e8320a107a87589534e29ae90 100644 (file)
@@ -22,6 +22,7 @@ AC_INIT([libsframe], [BFD_VERSION])
 AC_CONFIG_SRCDIR(sframe.c)
 
 AC_CANONICAL_TARGET
+AC_CANONICAL_HOST
 
 AC_USE_SYSTEM_EXTENSIONS
 AM_INIT_AUTOMAKE
@@ -112,6 +113,17 @@ else
 fi
 AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$have_ld_version_script" = "yes")
 
+dnl Determine if we are cross compiling
+
+is_cross_compiler=
+if test x"${host}" = x"${target}" ; then
+  is_cross_compiler=no
+else
+  is_cross_compiler=yes
+fi
+CROSS_COMPILE=$is_cross_compiler
+AC_SUBST([CROSS_COMPILE])
+
 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.
index 3999215acd2e55cd5dcf0e73d824cbe7863684a1..8d77f7103dbcc0dfb5574c154af46b34754711a8 100644 (file)
@@ -52,3 +52,6 @@ if {![info exists CFLAGS]} {
 if {![info exists CFLAGS_FOR_TARGET]} {
     set CFLAGS_FOR_TARGET $CFLAGS
 }
+
+# load the utility procedures
+load_lib sframe-lib.exp
diff --git a/libsframe/testsuite/lib/sframe-lib.exp b/libsframe/testsuite/lib/sframe-lib.exp
new file mode 100644 (file)
index 0000000..d686d27
--- /dev/null
@@ -0,0 +1,180 @@
+# Support routines for libsframe testsuite.
+#   Copyright (C) 2022 Free Software Foundation, Inc.
+#
+# This file is part of the GNU Binutils.
+#
+# This file 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+load_file $srcdir/../../ld/testsuite/lib/ld-lib.exp
+
+set unwind_test_file_name ""
+
+proc run_native_host_cmd { command } {
+    global link_output
+    global ld
+
+    verbose -log "$command"
+    set run_output ""
+    try {
+       set run_output [exec "sh" "-c" "$command" "2>@1"]
+       set status 0
+    } trap CHILDSTATUS {results options} {
+       set status [lindex [dict get $options -errorcode] 2]
+       set run_output $results
+    }
+    regsub "\n$" $run_output "" run_output
+    if { [lindex $status 0] != 0 && [string match "" $run_output] } then {
+       append run_output "child process exited abnormally"
+    }
+
+    if [string match "" $run_output] then {
+       return ""
+    }
+
+    verbose -log "$run_output"
+    return "$run_output"
+}
+
+# Compile and link a C source file for execution on the host.
+proc compile_link_one_host_cc { src output additional_args } {
+    global CC
+    global CFLAGS
+
+    return [run_native_host_cmd "./libtool --quiet --tag=CC --mode=link $CC $CFLAGS $src -o $output $additional_args" ]
+}
+
+proc make_unwind_parallel_path { args } {
+    global objdir
+    set joiner [list "file" "join" $objdir]
+    set joiner [concat $joiner $args]
+    return [eval $joiner]
+}
+
+proc standard_output_file {basename} {
+    global objdir subdir unwind_test_file_name
+
+    set dir [make_unwind_parallel_path outputs $subdir $unwind_test_file_name]
+    file mkdir $dir
+    return [file join $dir $basename]
+}
+
+proc standard_testfile {args} {
+    global unwind_test_file_name
+    global subdir
+    global unwind_test_file_last_vars
+
+    # Outputs.
+    global testfile binfile
+
+    set testfile $unwind_test_file_name
+    set binfile [standard_output_file ${testfile}]
+
+    if {[llength $args] == 0} {
+       set args .c
+    }
+
+    # Unset our previous output variables.
+    # This can help catch hidden bugs.
+    if {[info exists unwind_test_file_last_vars]} {
+       foreach varname $unwind_test_file_last_vars {
+           global $varname
+           catch {unset $varname}
+       }
+    }
+    # 'executable' is often set by tests.
+    set unwind_test_file_last_vars {executable}
+
+    set suffix ""
+    foreach arg $args {
+       set varname srcfile$suffix
+       global $varname
+
+       # Handle an extension.
+       if {$arg == ""} {
+           set arg $testfile.c
+       } else {
+           set first [string range $arg 0 0]
+           if { $first == "." || $first == "-" } {
+               set arg $testfile$arg
+           }
+       }
+
+       set $varname $arg
+       lappend unwind_test_file_last_vars $varname
+
+       if {$suffix == ""} {
+           set suffix 2
+       } else {
+           incr suffix
+       }
+    }
+}
+
+# Build a shared object DEST from SOURCES.
+proc unwind_compile_so {sources dest} {
+    global CFLAGS
+    set obj_options $CFLAGS
+    lappend obj_options "additional_flags=-fPIC -Wa,--gsframe"
+
+    set outdir [file dirname $dest]
+    set objects ""
+    foreach source $sources {
+       set sourcebase [file tail $source]
+       set object ${outdir}/${sourcebase}.o
+
+       if {[target_compile $source $object object \
+                 $obj_options] != ""} {
+           return -1
+       }
+
+       lappend objects $object
+    }
+
+    set link_options "additional_flags=-shared"
+
+    set destbase [file tail $dest]
+    lappend link_options "additional_flags=-Wl,-soname,$destbase"
+
+    if {[target_compile "${objects}" "${dest}" executable $link_options] != ""} {
+       catch "exec rm ${objects}" status
+       return -1
+    }
+    catch "exec rm ${objects}" status
+    return ""
+}
+
+# Build a binary of TYPE from SOURCE at path DEST.
+proc unwind_compile {source dest type options} {
+    set new_options ""
+
+    foreach opt $options {
+       if {[regexp {^shlib=(.*)} $opt dummy_var shlib_name]
+           && $type == "executable"} {
+           lappend source "-Wl,$shlib_name"
+       } else {
+           lappend new_options $opt
+       }
+    }
+    set options $new_options
+
+    verbose "options are $options"
+    verbose "source is $source $dest $type $options"
+
+    lappend options "additional_flags=-rdynamic -Wa,--gsframe ./.libs/libsframebt.a ./.libs/libsframe.a"
+    set result [target_compile $source $dest $type $options]
+
+    return $result
+}
diff --git a/libsframe/testsuite/libsframe.unwind/backtrace-fp-attr-1.c b/libsframe/testsuite/libsframe.unwind/backtrace-fp-attr-1.c
new file mode 100644 (file)
index 0000000..ef01e97
--- /dev/null
@@ -0,0 +1,108 @@
+/* Copyright (C) 2022 Free Software Foundation, Inc.
+
+   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/>.  */
+
+/* This is a revised version of gdb/testsuite/gdb.base/backtrace.c.  */
+
+#ifdef __has_attribute
+# if !__has_attribute (noclone)
+#  define ATTRIBUTE_NOCLONE
+# endif
+#endif
+#ifndef ATTRIBUTE_NOCLONE
+# define ATTRIBUTE_NOCLONE __attribute__((noclone))
+#endif
+
+#include <execinfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sframe-backtrace-api.h"
+
+#define BT_BUF_SIZE 100
+
+#define BT_EXPECTED_NPTRS 5
+/* Expected funclist.  */
+static const char *const func_list[] =
+{
+  "show_bt",
+  "baz",
+  "bar",
+  "foo",
+  "main"
+};
+
+void __attribute__((__noinline__,__optimize__("omit-frame-pointer"))) ATTRIBUTE_NOCLONE
+show_bt ()
+{
+  void *buffer[BT_BUF_SIZE];
+  int j, nptrs, err;
+  char **strings;
+
+  /* Call the unwinder to get an array of return addresses.  */
+  nptrs = sframe_backtrace (buffer, BT_BUF_SIZE, &err);
+  if (err)
+    {
+      printf ("SFrame error: %s (%d)\n", sframe_bt_errmsg (err), nptrs);
+      return;
+    }
+  if (nptrs != BT_EXPECTED_NPTRS)
+    {
+      printf ("Backtace nptrs mismatch: expected = %d, generated = %d \n",
+             BT_EXPECTED_NPTRS, nptrs);
+      return;
+    }
+
+  /* Get these addresses symbolically.  */
+  strings = backtrace_symbols (buffer, nptrs);
+  if (strings == NULL) {
+      printf ("Error in backtrace_symbols");
+      return;
+  }
+
+  /* Verify the results.  */
+  for (j = 0; j < nptrs; j++)
+    if (!strstr (strings[j], func_list[j]))
+      break;
+
+  free(strings);
+
+  printf ("%s: backtrace with omit-frame-pointer attr\n",
+         (j == nptrs) ? "PASS" : "FAIL");
+}
+
+int __attribute__((__noinline__)) ATTRIBUTE_NOCLONE
+baz ()
+{
+  show_bt ();
+  return 0;
+}
+
+int __attribute__((__noinline__)) ATTRIBUTE_NOCLONE
+bar ()
+{
+  return baz ();
+}
+
+int __attribute__((__noinline__)) ATTRIBUTE_NOCLONE
+foo ()
+{
+  return bar ();
+}
+
+int __attribute__((__noinline__)) ATTRIBUTE_NOCLONE
+main ()
+{
+  return foo ();
+}
diff --git a/libsframe/testsuite/libsframe.unwind/backtrace-fp-attr-1.lk b/libsframe/testsuite/libsframe.unwind/backtrace-fp-attr-1.lk
new file mode 100644 (file)
index 0000000..a26ef7e
--- /dev/null
@@ -0,0 +1,4 @@
+# source: backtrace-fp-attr-1.c
+# cflags: -fno-omit-frame-pointer
+# link: on
+PASS: backtrace with omit-frame-pointer attr
diff --git a/libsframe/testsuite/libsframe.unwind/backtrace-fp-attr-2.c b/libsframe/testsuite/libsframe.unwind/backtrace-fp-attr-2.c
new file mode 100644 (file)
index 0000000..ee62b20
--- /dev/null
@@ -0,0 +1,109 @@
+/* Copyright (C) 2022 Free Software Foundation, Inc.
+
+   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/>.  */
+
+/* This is a revised version of gdb/testsuite/gdb.base/backtrace.c.  */
+
+#ifdef __has_attribute
+# if !__has_attribute (noclone)
+#  define ATTRIBUTE_NOCLONE
+# endif
+#endif
+#ifndef ATTRIBUTE_NOCLONE
+# define ATTRIBUTE_NOCLONE __attribute__((noclone))
+#endif
+
+#include <execinfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sframe-backtrace-api.h"
+
+#define BT_BUF_SIZE 100
+
+#define BT_EXPECTED_NPTRS 5
+
+/* Expected funclist.  */
+static const char *const func_list[] =
+{
+  "show_bt",
+  "baz",
+  "bar",
+  "foo",
+  "main"
+};
+
+void __attribute__((__noinline__,__optimize__("no-omit-frame-pointer"))) ATTRIBUTE_NOCLONE
+show_bt ()
+{
+  void *buffer[BT_BUF_SIZE];
+  int j, nptrs, err;
+  char **strings;
+
+  /* Call the unwinder to get an array of return addresses.  */
+  nptrs = sframe_backtrace (buffer, BT_BUF_SIZE, &err);
+  if (err)
+    {
+      printf ("SFrame error: %s (%d)\n", sframe_bt_errmsg (err), nptrs);
+      return;
+    }
+  if (nptrs != BT_EXPECTED_NPTRS)
+    {
+      printf ("Backtace nptrs mismatch: expected = %d, generated = %d \n",
+             BT_EXPECTED_NPTRS, nptrs);
+      return;
+    }
+
+  /* Get these addresses symbolically.  */
+  strings = backtrace_symbols (buffer, nptrs);
+  if (strings == NULL) {
+      printf ("Error in backtrace_symbols");
+      return;
+  }
+
+  /* Verify the results.  */
+  for (j = 0; j < nptrs; j++)
+    if (!strstr (strings[j], func_list[j]))
+      break;
+
+  free(strings);
+
+  printf ("%s: backtrace with no-omit-frame-pointer attr\n",
+         (j == nptrs) ? "PASS" : "FAIL");
+}
+
+int __attribute__((__noinline__)) ATTRIBUTE_NOCLONE
+baz ()
+{
+  show_bt ();
+  return 0;
+}
+
+int __attribute__((__noinline__)) ATTRIBUTE_NOCLONE
+bar ()
+{
+  return baz ();
+}
+
+int __attribute__((__noinline__)) ATTRIBUTE_NOCLONE
+foo ()
+{
+  return bar ();
+}
+
+int __attribute__((__noinline__)) ATTRIBUTE_NOCLONE
+main ()
+{
+  return foo ();
+}
diff --git a/libsframe/testsuite/libsframe.unwind/backtrace-fp-attr-2.lk b/libsframe/testsuite/libsframe.unwind/backtrace-fp-attr-2.lk
new file mode 100644 (file)
index 0000000..97f49ef
--- /dev/null
@@ -0,0 +1,4 @@
+# source: backtrace-fp-attr-2.c
+# cflags: -fomit-frame-pointer
+# link: on
+PASS: backtrace with no-omit-frame-pointer attr
diff --git a/libsframe/testsuite/libsframe.unwind/backtrace.c b/libsframe/testsuite/libsframe.unwind/backtrace.c
new file mode 100644 (file)
index 0000000..3e00b76
--- /dev/null
@@ -0,0 +1,144 @@
+/* Copyright (C) 2022 Free Software Foundation, Inc.
+
+   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/>.  */
+
+/* This is a revised version of gdb/testsuite/gdb.base/backtrace.c.  */
+
+#ifdef __has_attribute
+# if !__has_attribute (noclone)
+#  define ATTRIBUTE_NOCLONE
+# endif
+#endif
+#ifndef ATTRIBUTE_NOCLONE
+# define ATTRIBUTE_NOCLONE __attribute__((noclone))
+#endif
+
+#include <execinfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sframe-backtrace-api.h"
+
+#define BT_BUF_SIZE 100
+
+/* Expected funclist.  */
+static const char *const func_list[] =
+{
+  "show_bt",
+  "baz",
+  "bar",
+  "foo",
+  "main"
+};
+
+void __attribute__((__noinline__)) ATTRIBUTE_NOCLONE
+show_bt ()
+{
+  void *buffer[BT_BUF_SIZE];
+  int j, nptrs, err;
+  char **strings;
+
+  /* Call the unwinder to get an array of return addresses.  */
+  nptrs = sframe_backtrace (buffer, BT_BUF_SIZE, &err);
+  if (nptrs != 5)
+    {
+      printf ("SFrame error: %s (%d)\n", sframe_bt_errmsg (err), nptrs);
+      return;
+    }
+
+  /* Get these addresses symbolically.  */
+  strings = backtrace_symbols (buffer, nptrs);
+  if (strings == NULL) {
+      printf ("Error in backtrace_symbols");
+      return;
+  }
+
+  /* Verify the results.  */
+  for (j = 0; j < nptrs; j++)
+    if (!strstr (strings[j], func_list[j]))
+      break;
+
+  free(strings);
+
+  printf ("%s: backtrace test\n", j == nptrs ? "PASS" : "FAIL");
+}
+
+int __attribute__((__noinline__)) ATTRIBUTE_NOCLONE
+baz ()
+{
+  void *buffer[BT_BUF_SIZE];
+  int nptrs, err;
+
+  /* Call the unwinder to get an array of return addresses.  */
+  nptrs = sframe_backtrace (buffer, BT_BUF_SIZE, &err);
+  if (nptrs == -1)
+    {
+      printf ("SFrame error: %s (%d)\n", sframe_bt_errmsg (err), nptrs);
+      return -1;
+    }
+
+  show_bt ();
+  return 0;
+}
+
+int __attribute__((__noinline__)) ATTRIBUTE_NOCLONE
+bar ()
+{
+  void *buffer[BT_BUF_SIZE];
+  int nptrs, err;
+
+  /* Call the unwinder to get an array of return addresses.  */
+  nptrs = sframe_backtrace (buffer, BT_BUF_SIZE, &err);
+  if (nptrs == -1)
+    {
+      printf ("SFrame error: %s (%d)\n", sframe_bt_errmsg (err), nptrs);
+      return -1;
+    }
+
+  return baz ();
+}
+
+int __attribute__((__noinline__)) ATTRIBUTE_NOCLONE
+foo ()
+{
+  void *buffer[BT_BUF_SIZE];
+  int nptrs, err;
+
+  /* Call the unwinder to get an array of return addresses.  */
+  nptrs = sframe_backtrace (buffer, BT_BUF_SIZE, &err);
+  if (nptrs == -1)
+    {
+      printf ("SFrame error: %s (%d)\n", sframe_bt_errmsg (err), nptrs);
+      return -1;
+    }
+
+  return bar ();
+}
+
+int __attribute__((__noinline__)) ATTRIBUTE_NOCLONE
+main ()
+{
+  void *buffer[BT_BUF_SIZE];
+  int nptrs, err;
+
+  /* Call the unwinder to get an array of return addresses.  */
+  nptrs = sframe_backtrace (buffer, BT_BUF_SIZE, &err);
+  if (nptrs == -1)
+    {
+      printf ("SFrame error: %s (%d)\n", sframe_bt_errmsg (err), nptrs);
+      return -1;
+    }
+
+  return foo ();
+}
diff --git a/libsframe/testsuite/libsframe.unwind/backtrace.lk b/libsframe/testsuite/libsframe.unwind/backtrace.lk
new file mode 100644 (file)
index 0000000..fdc78eb
--- /dev/null
@@ -0,0 +1,3 @@
+# source: backtrace.c
+# link: on
+PASS: backtrace test
diff --git a/libsframe/testsuite/libsframe.unwind/inline-cmds.c b/libsframe/testsuite/libsframe.unwind/inline-cmds.c
new file mode 100644 (file)
index 0000000..bc15545
--- /dev/null
@@ -0,0 +1,107 @@
+/* Copyright (C) 2022 Free Software Foundation, Inc.
+
+   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/>.  */
+
+/* This is only ever run if it is compiled with a new-enough GCC, but
+   we don't want the compilation to fail if compiled by some other
+   compiler.  */
+
+/* This is a revised version of gdb/testsuite/gdb.opt/inline-cmds.c.  */
+
+#ifdef __GNUC__
+#define ATTR __attribute__((always_inline))
+#else
+#define ATTR
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <execinfo.h>
+#include "sframe-backtrace-api.h"
+
+#define BT_BUF_SIZE 10
+
+int x, y;
+volatile int z = 0;
+volatile int result;
+
+void noinline(void);
+
+inline ATTR void outer_inline1(void)
+{
+  noinline ();
+}
+
+inline ATTR void outer_inline2(void)
+{
+  outer_inline1 ();
+}
+
+int main (void)
+{ /* start of main */
+  int val;
+
+  x = 7;
+  y = 8;
+
+  outer_inline2 ();
+
+  return 0;
+}
+
+
+/* funclist for inline-cmds.  */
+const char *const func_list[] =
+{
+  "noinline",
+  "main"
+};
+
+inline ATTR void inlined_fn(void)
+{
+  x += y + z;
+
+  void *buffer[BT_BUF_SIZE];
+  char **strings;
+  /* Call the unwinder to get an array of return addresses.  */
+  int j, err;
+  int nptrs = sframe_backtrace (buffer, BT_BUF_SIZE, &err);
+  if (nptrs == -1 || nptrs != 2)
+    {
+      printf ("SFrame error: %s (%d)\n", sframe_bt_errmsg (err), nptrs);
+      return;
+    }
+
+  /* Get these addresses symbolically.  */
+  strings = backtrace_symbols (buffer, nptrs);
+  if (strings == NULL)
+    {
+      printf ("Error in backtrace_symbols");
+      return;
+    }
+
+  /* Verify the results.  */
+  for (j = 0; j < nptrs; j++)
+    if (!strstr (strings[j], func_list[j]))
+      break;
+
+    free(strings);
+    printf ("%s: inline-cmds test\n", j == nptrs ? "PASS" : "FAIL");
+}
+
+void noinline(void)
+{
+  inlined_fn (); /* inlined */
+}
diff --git a/libsframe/testsuite/libsframe.unwind/inline-cmds.lk b/libsframe/testsuite/libsframe.unwind/inline-cmds.lk
new file mode 100644 (file)
index 0000000..053b66b
--- /dev/null
@@ -0,0 +1,3 @@
+# source: inline-cmds.c
+# link: on
+PASS: inline-cmds test
diff --git a/libsframe/testsuite/libsframe.unwind/inline.c b/libsframe/testsuite/libsframe.unwind/inline.c
new file mode 100644 (file)
index 0000000..fc753fc
--- /dev/null
@@ -0,0 +1,96 @@
+/* Copyright (C) 2022 Free Software Foundation, Inc.
+
+   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/>.  */
+
+/* This is a revised version of gdb/testsuite/gdb.opt/inline-bt.c.  */
+
+#include <execinfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sframe-backtrace-api.h"
+
+#define ATTR __attribute__((always_inline))
+
+#define BT_BUF_SIZE 32
+
+int x, y;
+volatile int z = 0;
+volatile int result;
+
+/* funclist.  */
+const char *const flist[] =
+{
+  "main"
+};
+
+void bar(void)
+{
+  x += y;
+}
+
+inline ATTR int func1(void)
+{
+  bar ();
+  return x * y;
+}
+
+inline ATTR int func2(void)
+{
+  void *buffer[BT_BUF_SIZE];
+  int ok = 0, nptrs, err;
+  char **strings;
+
+  /* Call the unwinder to get an array of return addresses.  */
+  nptrs = sframe_backtrace (buffer, BT_BUF_SIZE, &err);
+  if (nptrs == -1)
+    {
+      printf ("SFrame error: %s\n", sframe_bt_errmsg (err));
+      return -1;
+    }
+
+  /* Get these addresses symbolically.  */
+  strings = backtrace_symbols (buffer, nptrs);
+  if (strings == NULL) {
+      printf ("Error in backtrace_symbols");
+      return -1;
+  }
+
+  /* Verify the results.  */
+  if (nptrs == 1 && strstr (strings[0], flist[0]))
+    ok = 1;
+
+  free(strings);
+
+  printf ("%s: unwind test\n", ok == 1 ? "PASS" : "FAIL");
+
+  return x * func1 ();
+}
+
+int main (void)
+{
+  int val;
+
+  x = 7;
+  y = 8;
+  bar ();
+
+  val = func1 ();
+  result = val;
+
+  val = func2 ();
+  result = val;
+
+  return 0;
+}
diff --git a/libsframe/testsuite/libsframe.unwind/inline.lk b/libsframe/testsuite/libsframe.unwind/inline.lk
new file mode 100644 (file)
index 0000000..88f846b
--- /dev/null
@@ -0,0 +1,3 @@
+# source: inline.c
+# link: on
+PASS: unwind test
diff --git a/libsframe/testsuite/libsframe.unwind/solib-lib1.c b/libsframe/testsuite/libsframe.unwind/solib-lib1.c
new file mode 100644 (file)
index 0000000..16c77ca
--- /dev/null
@@ -0,0 +1,8 @@
+#include "solib-lib1.h"
+
+unsigned int
+adder(unsigned int a, unsigned int b, int (*call)(int))
+{
+  (void)(*call)(a+b);
+  return (a+b);
+}
diff --git a/libsframe/testsuite/libsframe.unwind/solib-lib1.h b/libsframe/testsuite/libsframe.unwind/solib-lib1.h
new file mode 100644 (file)
index 0000000..d40eac0
--- /dev/null
@@ -0,0 +1,3 @@
+#include<stdio.h>
+
+extern unsigned int adder(unsigned int a, unsigned int b, int (*call)(int));
diff --git a/libsframe/testsuite/libsframe.unwind/solib-lib2.c b/libsframe/testsuite/libsframe.unwind/solib-lib2.c
new file mode 100644 (file)
index 0000000..9ba3096
--- /dev/null
@@ -0,0 +1,55 @@
+#include <execinfo.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sframe-backtrace-api.h"
+#include "solib-lib2.h"
+
+#define BT_BUF_SIZE 100
+
+/* funclist for running "ttest.x 3".  */
+static const char *const bt_list[] =
+{
+  "adder2",
+  "bar",
+  "adder",
+  "main"
+};
+
+unsigned int
+adder2 (unsigned int a, unsigned int b, int (*call)(int))
+{
+  void *buffer[BT_BUF_SIZE];
+  int i, nptrs, err;
+  char **strings;
+
+  nptrs = sframe_backtrace (buffer, BT_BUF_SIZE, &err);
+  if (err)
+    {
+      printf ("SFrame error: %s\n", sframe_bt_errmsg (err));
+      return (-1);
+    }
+  if (nptrs != 4)
+    {
+      printf ("sframe_backtrace failed: %d %d\n", nptrs, err);
+      return (-1);
+    }
+
+  strings = backtrace_symbols (buffer, nptrs);
+  if (strings == NULL)
+    {
+       printf ("Error in backtrace_symbols");
+       return (-1);
+    }
+
+  /* Verify the results.  */
+  for (i = 0; i < nptrs; i++)
+    if (!strstr (strings[i], bt_list[i]))
+      break;
+
+  free (strings);
+
+  printf ("%s: unwind solib test\n", i == nptrs ? "PASS" : "FAIL");
+
+  (void)(*call) (a+b);
+  return (a+b);
+}
diff --git a/libsframe/testsuite/libsframe.unwind/solib-lib2.h b/libsframe/testsuite/libsframe.unwind/solib-lib2.h
new file mode 100644 (file)
index 0000000..61b7212
--- /dev/null
@@ -0,0 +1,3 @@
+#include<stdio.h>
+
+extern unsigned int adder2(unsigned int a, unsigned int b, int (*call)(int));
diff --git a/libsframe/testsuite/libsframe.unwind/solib-main.c b/libsframe/testsuite/libsframe.unwind/solib-main.c
new file mode 100644 (file)
index 0000000..d0bd806
--- /dev/null
@@ -0,0 +1,46 @@
+/* Copyright (C) 2022 Free Software Foundation, Inc.
+
+   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 <execinfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "sframe-backtrace-api.h"
+#include "solib-lib1.h"
+#include "solib-lib2.h"
+
+#define BT_BUF_SIZE 100
+
+int foo (int x)
+{
+  return ++x;
+}
+
+int bar (int x)
+{
+  x = adder2 (x, x+1, foo);
+
+  return ++x;
+}
+
+int main (void)
+{
+  unsigned int a = 1;
+  unsigned int b = 2;
+  unsigned int result = 0;
+
+  result = adder (a,b, bar);
+
+  return 0;
+}
diff --git a/libsframe/testsuite/libsframe.unwind/solib-main.d b/libsframe/testsuite/libsframe.unwind/solib-main.d
new file mode 100644 (file)
index 0000000..483ded5
--- /dev/null
@@ -0,0 +1,3 @@
+# source: solib-main.c
+# link: on
+PASS: unwind solib test
diff --git a/libsframe/testsuite/libsframe.unwind/solib.exp b/libsframe/testsuite/libsframe.unwind/solib.exp
new file mode 100644 (file)
index 0000000..9ea8ce7
--- /dev/null
@@ -0,0 +1,70 @@
+# Copyright 2022 Free Software Foundation, Inc.
+
+# 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/>.
+
+# Run the test only if sframebt library exists.
+if [catch "exec ls $objdir/.libs/libsframebt.la" status] then {
+  return;
+}
+
+set experimental ""
+
+# Shared object files.
+set libname1 "solib-lib1"
+set srcfile_lib1 ${srcdir}/${subdir}/${libname1}.c
+set binfile_lib1 ${objdir}/${libname1}.so
+set libname2 "solib-lib2"
+set srcfile_lib2 ${srcdir}/${subdir}/${libname2}.c
+set binfile_lib2 ${objdir}/${libname2}.so
+
+# Binary file.
+set testfile "solib-main"
+set srcfile ${srcdir}/${subdir}/${testfile}.c
+set binfile [standard_output_file ${testfile}]
+set bin_flags [list debug shlib=${binfile_lib1} shlib=${binfile_lib2}]
+
+if { [unwind_compile_so ${srcfile_lib1} ${binfile_lib1}] != ""
+     || [unwind_compile_so ${srcfile_lib2} ${binfile_lib2}] != ""
+     || [unwind_compile ${srcfile} ${binfile} executable $bin_flags] != "" } {
+    untested "failed to compile"
+    return -1
+}
+
+if {[info exists env(LD_LIBRARY_PATH)]} {
+    set old_ld_lib $env(LD_LIBRARY_PATH)
+}
+set env(LD_LIBRARY_PATH) "${objdir}"
+
+set solib_output "${binfile} ${binfile_lib1} ${binfile_lib2}"
+set results [run_host_cmd ${binfile} $solib_output]
+
+set f [open "tmpdir/solib.out" "w"]
+puts $f $results
+close $f
+
+if { [regexp_diff "tmpdir/solib.out" "${srcdir}/${subdir}/${testfile}.d"] } then {
+    fail "$test_name"
+} else {
+    pass "$test_name"
+}
+
+catch "exec rm ${binfile_lib1}" status
+catch "exec rm ${binfile_lib2}" status
+catch "exec rm tmpdir/solib.out" status
+
+if {[info exists old_ld_lib]} {
+    set env(LD_LIBRARY_PATH) $old_ld_lib
+} else {
+    unset env(LD_LIBRARY_PATH)
+}
diff --git a/libsframe/testsuite/libsframe.unwind/tailcall.c b/libsframe/testsuite/libsframe.unwind/tailcall.c
new file mode 100644 (file)
index 0000000..fd3add2
--- /dev/null
@@ -0,0 +1,102 @@
+/* Copyright (C) 2022 Free Software Foundation, Inc.
+
+   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 <execinfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sframe-backtrace-api.h"
+
+#ifdef __has_attribute
+# if !__has_attribute (noclone)
+#  define ATTRIBUTE_NOCLONE
+# endif
+#endif
+#ifndef ATTRIBUTE_NOCLONE
+# define ATTRIBUTE_NOCLONE __attribute__((noclone))
+#endif
+
+#define BT_BUF_SIZE 16
+
+/* funclist for running tailcall.  */
+const char *const func_list[] =
+{
+  "show_bt",
+  "dec",
+  "dec",
+  "main"
+};
+
+void show_bt ()
+{
+  void *buffer[BT_BUF_SIZE];
+  int j, nptrs, err;
+  char **strings;
+
+  /* Call the unwinder to get an array of return addresses.  */
+  nptrs = sframe_backtrace (buffer, BT_BUF_SIZE, &err);
+  if (nptrs == -1 || nptrs != 4)
+    {
+      printf ("SFrame error: %s (%d)\n", sframe_bt_errmsg (err), nptrs);
+      return;
+    }
+
+  /* Get these addresses symbolically.  */
+  strings = backtrace_symbols (buffer, nptrs);
+  if (strings == NULL) {
+      printf ("Error in backtrace_symbols");
+      return;
+  }
+
+  /* Verify the results.  */
+  for (j = 0; j < nptrs; j++)
+    if (!strstr (strings[j], func_list[j]))
+      break;
+
+  free(strings);
+
+  printf ("%s: tailcall test\n", j == nptrs ? "PASS" : "FAIL");
+}
+
+/* An example of tail recursive function.  */
+void __attribute__((__noinline__)) ATTRIBUTE_NOCLONE
+dec (int n)
+{
+  void *buffer[BT_BUF_SIZE];
+  int nptrs, err;
+
+  /* Call the unwinder to get an array of return addresses.  */
+  nptrs = sframe_backtrace (buffer, BT_BUF_SIZE, &err);
+  if (nptrs == -1)
+    {
+      printf ("SFrame error: %s (%d)\n", sframe_bt_errmsg (err), nptrs);
+      return;
+    }
+
+  if (n < 0)
+     return;
+
+  if (n == 2)
+    show_bt ();
+
+  /* The last executed statement is recursive call.  */
+  dec (n-1);
+}
+
+int
+main (void)
+{
+  dec (3);
+}
diff --git a/libsframe/testsuite/libsframe.unwind/tailcall.lk b/libsframe/testsuite/libsframe.unwind/tailcall.lk
new file mode 100644 (file)
index 0000000..3d7ab98
--- /dev/null
@@ -0,0 +1,3 @@
+# source: tailcall.c
+# link: on
+PASS: tailcall test
diff --git a/libsframe/testsuite/libsframe.unwind/ttest.c b/libsframe/testsuite/libsframe.unwind/ttest.c
new file mode 100644 (file)
index 0000000..342ba14
--- /dev/null
@@ -0,0 +1,126 @@
+/* Copyright (C) 2022 Free Software Foundation, Inc.
+
+   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/>.  */
+
+/* This is the revised version of the example in "man backtrace".  */
+
+#include <execinfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sframe-backtrace-api.h"
+
+#ifdef __has_attribute
+# if !__has_attribute (noclone)
+#  define ATTRIBUTE_NOCLONE
+# endif
+#endif
+#ifndef ATTRIBUTE_NOCLONE
+# define ATTRIBUTE_NOCLONE __attribute__((noclone))
+#endif
+
+#define BT_BUF_SIZE 100
+
+/* funclist  */
+static const char *const func_list[] =
+{
+  "myfunc3",
+  "()",
+  "myfunc",
+  "myfunc",
+  "myfunc",
+  "main"
+};
+
+void myfunc3 (void)
+{
+  void *buffer[BT_BUF_SIZE];
+  int j, nptrs, err;
+  char **strings;
+
+  /* Call the unwinder to get an array of return addresses.  */
+  nptrs = sframe_backtrace (buffer, BT_BUF_SIZE, &err);
+  if (nptrs == -1 || nptrs != 6)
+    {
+      printf ("SFrame error: %s (%d)\n", sframe_bt_errmsg (err), nptrs);
+      return;
+    }
+
+  /* Get these addresses symbolically.  */
+  strings = backtrace_symbols (buffer, nptrs);
+  if (strings == NULL) {
+    printf ("Error in backtrace_symbols");
+    return;
+  }
+
+  /* Verify the results.  */
+  for (j = 0; j < nptrs; j++)
+    if (!strstr (strings[j], func_list[j]))
+      break;
+
+  free(strings);
+
+  printf ("%s: unwind test\n", j == nptrs ? "PASS" : "FAIL");
+}
+
+static void __attribute__((__noinline__)) ATTRIBUTE_NOCLONE
+/* "static" means don't export the symbol.  */
+myfunc2 (void)
+{
+  void *buffer[BT_BUF_SIZE];
+  int nptrs, err;
+
+  /* Call the unwinder to get an array of return addresses.  */
+  nptrs = sframe_backtrace (buffer, BT_BUF_SIZE, &err);
+  if (nptrs == -1)
+    {
+      printf ("SFrame error: %s (%d)\n", sframe_bt_errmsg (err), nptrs);
+      return;
+    }
+
+  myfunc3 ();
+}
+
+void __attribute__((__noinline__)) ATTRIBUTE_NOCLONE
+myfunc (int ncalls)
+{
+  void *buffer[BT_BUF_SIZE];
+  int nptrs, err;
+
+  /* Call the unwinder to get an array of return addresses.  */
+  nptrs = sframe_backtrace (buffer, BT_BUF_SIZE, &err);
+  if (nptrs == -1)
+    {
+      printf ("SFrame error: %s (%d)\n", sframe_bt_errmsg (err), nptrs);
+      return;
+    }
+
+  if (ncalls > 1)
+    myfunc (ncalls - 1);
+  else
+    myfunc2 ();
+}
+
+int
+main (int argc, char *argv[])
+{
+  int cnt;
+  if (argc != 2) {
+    cnt = 3;
+  }
+  else
+    cnt = atoi(argv[1]);
+  myfunc (cnt);
+  exit (EXIT_SUCCESS);
+}
diff --git a/libsframe/testsuite/libsframe.unwind/ttest.lk b/libsframe/testsuite/libsframe.unwind/ttest.lk
new file mode 100644 (file)
index 0000000..80aa224
--- /dev/null
@@ -0,0 +1,3 @@
+# source: ttest.c
+# link: on
+PASS: unwind test
diff --git a/libsframe/testsuite/libsframe.unwind/unwind.exp b/libsframe/testsuite/libsframe.unwind/unwind.exp
new file mode 100644 (file)
index 0000000..510b92e
--- /dev/null
@@ -0,0 +1,189 @@
+# Copyright (C) 2022 Free Software Foundation, Inc.
+#
+# This file is part of the GNU Binutils.
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
+#
+
+# Run the tests only if sframebt library exists.
+
+if [catch "exec ls $objdir/.libs/libsframebt.la" status] then {
+  verbose -log "$objdir/.libs/libsframebt.la not found.";
+  verbose -log "Skipping SFrame unwind tests";
+  return;
+}
+
+proc run_unwind_test { name } {
+    global CC
+    global CFLAGS
+    global copyfile env runtests srcdir subdir verbose
+
+    # Append additional flags for unwinder to work properly
+    set unwind_cflags "-Wa,--gsframe -rdynamic "
+
+    if ![runtest_file_p $runtests $name] then {
+       return
+    }
+
+    if [string match "*/*" $name] {
+       set file $name
+       set name [file tail $name]
+    } else {
+       set file "$srcdir/$subdir/$name"
+    }
+
+    set opt_array [slurp_options "${file}.lk"]
+    if { $opt_array == -1 } {
+       perror "error reading options from $file.lk"
+       unresolved $subdir/$name
+       return
+    }
+    set run_ld 0
+    set shared "-shared"
+    set opts(cflags) {}
+    set opts(link) {}
+    set opts(link_flags) {}
+    set opts(nonshared) {}
+    set opts(unwind) {}
+    set opts(name) {}
+    set opts(source) {}
+    set opts(xfail) {}
+
+    foreach i $opt_array {
+       set opt_name [lindex $i 0]
+       set opt_val [lindex $i 1]
+       if { $opt_name == "" } {
+           set in_extra 1
+           continue
+       }
+       if ![info exists opts($opt_name)] {
+           perror "unknown option $opt_name in file $file.lk"
+           unresolved $subdir/$name
+           return
+       }
+
+       set opts($opt_name) [concat $opts($opt_name) $opt_val]
+    }
+
+    if { [llength $opts(unwind)] == 0 } {
+       set opts(unwind) "$file.c"
+    } else {
+       set opts(unwind) "[file dirname $file]/$opts(unwind)"
+    }
+
+    if { [llength $opts(name)] == 0 } {
+       set opts(name) $opts(unwind)
+    }
+
+    if { [llength $opts(cflags)] != 0 } {
+       append unwind_cflags $opts(cflags)
+    }
+
+    if { [llength $opts(link)] != 0
+        || [llength $opts(source)] > 1 } {
+       set run_ld 1
+    }
+
+    if { [llength $opts(nonshared)] != 0 } {
+       set shared ""
+    }
+
+    set testname $opts(name)
+    if { $opts(name) == "" } {
+       set testname "$subdir/$name"
+    }
+
+    # Compile and link the unwind program.
+    set comp_output [compile_link_one_host_cc $opts(unwind) "tmpdir/test_x" "./.libs/libsframebt.la ./.libs/libsframe.la"]
+
+    if { $comp_output != ""} {
+       send_log "compilation of unwind program $opts(unwind) failed with <$comp_output>"
+       perror "compilation of unwind program $opts(unwind) failed"
+       fail $testname
+       return 0
+    }
+
+    # Compile the inputs and posibly link them together.
+
+    set unwind ""
+    if { [llength $opts(source)] > 0 } {
+       set unwind ""
+       if { $run_ld } {
+           set unwind_output "tmpdir/test_x ./.libs/libsframebt.a ./.libs/libsframe.a"
+           # set unwind_output "tmpdir/out.so"
+           # set unwind_flags "-fPIC $shared $opts(link_flags)"
+       } else {
+           set unwind_output "tmpdir/out.o"
+           # set unwind_flags "-fPIC -c"
+       }
+       if [board_info [target_info name] exists cflags] {
+           append unwind_flags " [board_info [target_info name] cflags]"
+       }
+       if [board_info [target_info name] exists ldflags] {
+           append unwind_flags " [board_info [target_info name] ldflags]"
+       }
+       set src {}
+       foreach sfile $opts(source) {
+           if [is_remote host] {
+               lappend src [remote_download host [file join [file dirname $file] $sfile]]
+           } else {
+               lappend src [file join [file dirname $file] $sfile]
+           }
+       }
+
+       set comp_output [run_host_cmd "$CC" "$CFLAGS $unwind_cflags [concat $src] -o $unwind_output"]
+
+       if { $comp_output != ""} {
+           send_log "compilation of SFrame test program [concat $src] failed with <$comp_output>"
+           fail $testname
+           return 0
+       }
+    }
+
+    # Time to setup xfailures.
+    foreach targ $opts(xfail) {
+       if [match_target $targ] {
+           setup_xfail "*-*-*"
+           break
+       }
+    }
+
+    # Invoke the unwind program on the outputs.
+
+    verbose -log "$srcdir"
+    set results [run_host_cmd tmpdir/test_x $unwind_output]
+
+    set f [open "tmpdir/test_x.out" "w"]
+    puts $f $results
+    close $f
+
+    if { [regexp_diff "tmpdir/test_x.out" "${file}.lk"] } then {
+       fail $testname
+       if { $verbose == 2 } then { verbose "output is [file_contents tmpdir/test_x.out]" 2 }
+       return 0
+    }
+
+    pass $testname
+    return 0
+}
+
+set sframe_test_list [lsort [glob -nocomplain $srcdir/$subdir/*.lk]]
+
+foreach sframe_test $sframe_test_list {
+    verbose [file rootname $sframe_test]
+    verbose running unwind test on $sframe_test
+    run_unwind_test [file rootname $sframe_test]
+}