diff options
author | Weimin Pan <weimin.pan@oracle.com> | 2022-09-27 15:24:47 -0700 |
---|---|---|
committer | Indu Bhagat <indu.bhagat@oracle.com> | 2024-04-25 15:03:50 -0700 |
commit | 171324cb0325c6c54a1d98d3564a4da5ca2dc1a5 (patch) | |
tree | 7fc69957cc74e9b2f60d93246fd32f8efbc308cc | |
parent | 6d551065a7add22cef04864d850400443cdd065b (diff) | |
download | gdb-171324cb0325c6c54a1d98d3564a4da5ca2dc1a5.zip gdb-171324cb0325c6c54a1d98d3564a4da5ca2dc1a5.tar.gz gdb-171324cb0325c6c54a1d98d3564a4da5ca2dc1a5.tar.bz2 |
unwinder: Add SFrame unwinder tests
[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, 1533 insertions, 81 deletions
diff --git a/libsframe/Makefile.in b/libsframe/Makefile.in index 46ea309..03ac628 100644 --- a/libsframe/Makefile.in +++ b/libsframe/Makefile.in @@ -107,6 +107,7 @@ PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ +target_triplet = @target@ @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 @@ -411,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@ @@ -509,8 +511,12 @@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ +target = @target@ target_alias = @target_alias@ +target_cpu = @target_cpu@ target_noncanonical = @target_noncanonical@ +target_os = @target_os@ +target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ @@ -1192,6 +1198,8 @@ site.exp: Makefile $(EXTRA_DEJAGNU_SITE_CONFIG) @echo 'set build_triplet $(build_triplet)' >>site.tmp @echo 'set host_alias "$(host_alias)"' >>site.tmp @echo 'set host_triplet $(host_triplet)' >>site.tmp + @echo 'set target_alias "$(target_alias)"' >>site.tmp + @echo 'set target_triplet $(target_triplet)' >>site.tmp @list='$(EXTRA_DEJAGNU_SITE_CONFIG)'; for f in $$list; do \ echo "## Begin content included from file $$f. Do not modify. ##" \ && cat `test -f "$$f" || echo '$(srcdir)/'`$$f \ diff --git a/libsframe/configure b/libsframe/configure index acfc805..5f4d613 100755 --- a/libsframe/configure +++ b/libsframe/configure @@ -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 @@ -668,14 +669,6 @@ DUMPBIN LD FGREP SED -host_os -host_vendor -host_cpu -host -build_os -build_vendor -build_cpu -build LIBTOOL ac_ct_AR AR @@ -727,6 +720,18 @@ CPPFLAGS LDFLAGS CFLAGS CC +target_os +target_vendor +target_cpu +target +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build target_alias host_alias build_alias @@ -1394,6 +1399,7 @@ Program names: System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] + --target=TARGET configure for building compilers for TARGET [HOST] _ACEOF fi @@ -2302,6 +2308,117 @@ ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if ${ac_cv_build+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if ${ac_cv_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking target system type" >&5 +$as_echo_n "checking target system type... " >&6; } +if ${ac_cv_target+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$target_alias" = x; then + ac_cv_target=$ac_cv_host +else + ac_cv_target=`$SHELL "$ac_aux_dir/config.sub" $target_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $target_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5 +$as_echo "$ac_cv_target" >&6; } +case $ac_cv_target in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical target" "$LINENO" 5;; +esac +target=$ac_cv_target +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_target +shift +target_cpu=$1 +target_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +target_os=$* +IFS=$ac_save_IFS +case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac + + +# The aliases save the names the user supplied, while $host etc. +# will get canonicalized. +test -n "$target_alias" && + test "$program_prefix$program_suffix$program_transform_name" = \ + NONENONEs,x,x, && + program_prefix=${target_alias}- + # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` @@ -5370,77 +5487,6 @@ macro_revision='1.3134' ltmain="$ac_aux_dir/ltmain.sh" -# Make sure we can run config.sub. -$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || - as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 -$as_echo_n "checking build system type... " >&6; } -if ${ac_cv_build+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_build_alias=$build_alias -test "x$ac_build_alias" = x && - ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` -test "x$ac_build_alias" = x && - as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 -ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 -$as_echo "$ac_cv_build" >&6; } -case $ac_cv_build in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; -esac -build=$ac_cv_build -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_build -shift -build_cpu=$1 -build_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -build_os=$* -IFS=$ac_save_IFS -case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 -$as_echo_n "checking host system type... " >&6; } -if ${ac_cv_host+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "x$host_alias" = x; then - ac_cv_host=$ac_cv_build -else - ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 -$as_echo "$ac_cv_host" >&6; } -case $ac_cv_host in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; -esac -host=$ac_cv_host -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_host -shift -host_cpu=$1 -host_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -host_os=$* -IFS=$ac_save_IFS -case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac - - # Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\(["`$\\]\)/\\\1/g' @@ -11519,7 +11565,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11522 "configure" +#line 11568 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11625,7 +11671,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11628 "configure" +#line 11674 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -12420,6 +12466,16 @@ fi +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; } diff --git a/libsframe/configure.ac b/libsframe/configure.ac index c0da671..3059303 100644 --- a/libsframe/configure.ac +++ b/libsframe/configure.ac @@ -20,6 +20,8 @@ dnl AC_INIT([libsframe], BFD_VERSION) AC_CONFIG_SRCDIR(sframe.c) +AC_CANONICAL_TARGET +AC_CANONICAL_HOST AC_USE_SYSTEM_EXTENSIONS AM_INIT_AUTOMAKE AM_SILENT_RULES([yes]) @@ -109,6 +111,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. diff --git a/libsframe/testsuite/config/default.exp b/libsframe/testsuite/config/default.exp index 609f286..b3cc58d 100644 --- a/libsframe/testsuite/config/default.exp +++ b/libsframe/testsuite/config/default.exp @@ -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 index 0000000..d686d27 --- /dev/null +++ b/libsframe/testsuite/lib/sframe-lib.exp @@ -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 index 0000000..ef01e97 --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/backtrace-fp-attr-1.c @@ -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 index 0000000..a26ef7e --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/backtrace-fp-attr-1.lk @@ -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 index 0000000..ee62b20 --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/backtrace-fp-attr-2.c @@ -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 index 0000000..97f49ef --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/backtrace-fp-attr-2.lk @@ -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 index 0000000..3e00b76 --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/backtrace.c @@ -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 index 0000000..fdc78eb --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/backtrace.lk @@ -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 index 0000000..bc15545 --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/inline-cmds.c @@ -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 index 0000000..053b66b --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/inline-cmds.lk @@ -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 index 0000000..fc753fc --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/inline.c @@ -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 index 0000000..88f846b --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/inline.lk @@ -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 index 0000000..16c77ca --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/solib-lib1.c @@ -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 index 0000000..d40eac0 --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/solib-lib1.h @@ -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 index 0000000..9ba3096 --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/solib-lib2.c @@ -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 index 0000000..61b7212 --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/solib-lib2.h @@ -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 index 0000000..d0bd806 --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/solib-main.c @@ -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 index 0000000..483ded5 --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/solib-main.d @@ -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 index 0000000..9ea8ce7 --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/solib.exp @@ -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 index 0000000..fd3add2 --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/tailcall.c @@ -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 index 0000000..3d7ab98 --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/tailcall.lk @@ -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 index 0000000..342ba14 --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/ttest.c @@ -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 index 0000000..80aa224 --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/ttest.lk @@ -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 index 0000000..510b92e --- /dev/null +++ b/libsframe/testsuite/libsframe.unwind/unwind.exp @@ -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] +} |