From: Indu Bhagat <indu.bhagat@oracle.com>
To: binutils@sourceware.org
Subject: [PATCH, RFC, V3 7/8] unwinder: generate backtrace using CTF Frame format
Date: Tue, 14 Jun 2022 00:06:51 -0700 [thread overview]
Message-ID: <20220614070652.1608873-8-indu.bhagat@oracle.com> (raw)
In-Reply-To: <20220614070652.1608873-1-indu.bhagat@oracle.com>
From: Weimin Pan <weimin.pan@oracle.com>
[Changes from V2]
- consistent use of terminology around CTF Frame and its components.
[End of changes from V2]
[Changes from V1]
- buildsystem: changes have been made to build libctfbacktrace only when
--gctf-frame support is available in the assembler. These buildsystem
changes are necessary because the CTF Frame based unwinder needs the CTF
Frame unwind info for itself to work.
[End of changes from V1]
A simple unwinder based on CTF Frame format.
PS: libctfframe/configure has NOT been included in the patch. Please
regenerate.
config/ChangeLog:
* ctf-frame.m4: New file.
include/ChangeLog:
* ctf-backtrace-api.h: New file.
ChangeLog:
* libctfframe/Makefile.am: Build backtrace functionality in its
own library. Install libctfbacktrace conditionally.
* libctfframe/Makefile.in: Regenerate.
* libctfframe/aclocal.m4: Regenerate.
* libctfframe/configure: Regenerate. <-- [REMOVED FROM THE
PATCH. PLEASE REGENERATE. ]
* libctfframe/configure.ac: Check if gas supports --gctf-frame
command line option.
* libctfframe/ctf-backtrace-err.c: New file.
* libctfframe/ctf-backtrace.c: New file.
* libctfframe/ttest.c: New file.
---
config/ctf-frame.m4 | 16 +
include/ctf-backtrace-api.h | 57 +++
libctfframe/Makefile.am | 14 +-
libctfframe/Makefile.in | 69 +++-
libctfframe/aclocal.m4 | 1 +
libctfframe/configure.ac | 7 +
libctfframe/ctf-backtrace-err.c | 46 +++
libctfframe/ctf-backtrace.c | 617 ++++++++++++++++++++++++++++++++
libctfframe/ttest.c | 78 ++++
9 files changed, 889 insertions(+), 16 deletions(-)
create mode 100644 config/ctf-frame.m4
create mode 100644 include/ctf-backtrace-api.h
create mode 100644 libctfframe/ctf-backtrace-err.c
create mode 100644 libctfframe/ctf-backtrace.c
create mode 100644 libctfframe/ttest.c
diff --git a/config/ctf-frame.m4 b/config/ctf-frame.m4
new file mode 100644
index 00000000000..100840b805f
--- /dev/null
+++ b/config/ctf-frame.m4
@@ -0,0 +1,16 @@
+# CTF_CHECK_AS_CTF_FRAME
+# ----------------------
+# Check whether the assembler supports generation of CTF Frame
+# unwind information.
+#
+# Defines:
+# ac_cv_have_ctfframe
+
+AC_DEFUN([CTF_CHECK_AS_CTF_FRAME],[
+ ac_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -Wa,--gctf-frame"
+ AC_MSG_CHECKING([for as that supports --gctf-frame])
+ AC_TRY_COMPILE([], [return 0;], [ac_cv_have_ctfframe=yes], [ac_cv_have_ctfframe=no])
+ AC_MSG_RESULT($ac_cv_have_ctfframe)
+ CFLAGS="$ac_save_CFLAGS"
+])
diff --git a/include/ctf-backtrace-api.h b/include/ctf-backtrace-api.h
new file mode 100644
index 00000000000..7b3463d88a3
--- /dev/null
+++ b/include/ctf-backtrace-api.h
@@ -0,0 +1,57 @@
+/* Public API to CTF backtrace.
+
+ Copyright (C) 2022 Free Software Foundation, Inc.
+
+ This file is part of xxxxxx. (FIXME)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _CTF_BACKTRACE_API_H
+#define _CTF_BACKTRACE_API_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+enum ctf_bt_errcode
+{
+ CTF_BT_OK,
+ CTF_BT_ERR_NOCTF,
+ CTF_BT_ERR_PHDR,
+ CTF_BT_ERR_ARG,
+ CTF_BT_ERR_MALLOC,
+ CTF_BT_ERR_REALLOC,
+ CTF_BT_ERR_OPEN,
+ CTF_BT_ERR_READLINK,
+ CTF_BT_ERR_LSEEK,
+ CTF_BT_ERR_READ,
+ CTF_BT_ERR_GETCONTEXT,
+ CTF_BT_ERR_DECODE,
+ CTF_BT_ERR_CFA_OFFSET,
+};
+
+/* Get the backtrace of the calling program by storing return addresses
+ in BUFFER. The SIZE argument specifies the maximum number of addresses
+ that can be stored in the buffer. Return the number of return addresses
+ collected or -1 if there is any error. */
+extern int ctf_backtrace (void **buffer, int size, int *errp);
+
+extern const char *ctf_bt_errmsg (enum ctf_bt_errcode ecode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CTF_BACKTRACE_API_H */
diff --git a/libctfframe/Makefile.am b/libctfframe/Makefile.am
index 712b5b8e773..f745b24dce1 100644
--- a/libctfframe/Makefile.am
+++ b/libctfframe/Makefile.am
@@ -23,8 +23,6 @@ AUTOMAKE_OPTIONS = dejagnu foreign no-texinfo.tex
BASEDIR = $(srcdir)/..
INCDIR = $(srcdir)/../include
-# include libctf for swap.h
-AM_CPPFLAGS = -D_GNU_SOURCE -I$(srcdir) -I$(srcdir)/../include -I$(srcdir)/../libctf
AM_CFLAGS = -std=gnu99 @ac_libctfframe_warn_cflags@ @warn@ @c_warn@ @WARN_PEDANTIC@ @WERROR@
if INSTALL_LIBBFD
@@ -39,6 +37,18 @@ libctfframe_la_SOURCES = ctf-frame.c ctf-frame-dump.c ctf-frame-error.c
libctfframe_la_CPPFLAGS = -D_GNU_SOURCE -I$(srcdir) -I$(srcdir)/../include \
-I$(srcdir)/../libctf
+if HAVE_CTF_FRAME_AS
+ libctfbacktrace_la_SOURCES = ctf-backtrace.c ctf-backtrace-err.c
+ libctfbacktrace_la_CPPFLAGS = -I$(srcdir) -I$(srcdir)/../include
+ libctfbacktrace_la_CFLAGS = -Wa,--gctf-frame
+if INSTALL_LIBBFD
+ lib_LTLIBRARIES += libctfbacktrace.la
+ include_HEADERS += $(INCDIR)/ctf-backtrace-api.h
+else
+ noinst_LTLIBRARIES += libctfbacktrace.la
+endif
+endif
+
# Setup the testing framework
EXPECT = expect
RUNTEST = runtest
diff --git a/libctfframe/Makefile.in b/libctfframe/Makefile.in
index 2fe55ad39d4..cbf014412cd 100644
--- a/libctfframe/Makefile.in
+++ b/libctfframe/Makefile.in
@@ -107,10 +107,14 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
+@HAVE_CTF_FRAME_AS_TRUE@@INSTALL_LIBBFD_TRUE@am__append_1 = libctfbacktrace.la
+@HAVE_CTF_FRAME_AS_TRUE@@INSTALL_LIBBFD_TRUE@am__append_2 = $(INCDIR)/ctf-backtrace-api.h
+@HAVE_CTF_FRAME_AS_TRUE@@INSTALL_LIBBFD_FALSE@am__append_3 = libctfbacktrace.la
subdir = .
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/../bfd/acinclude.m4 \
$(top_srcdir)/../config/acx.m4 \
+ $(top_srcdir)/../config/ctf-frame.m4 \
$(top_srcdir)/../config/depstand.m4 \
$(top_srcdir)/../config/jobserver.m4 \
$(top_srcdir)/../config/lead-dot.m4 \
@@ -159,15 +163,29 @@ am__uninstall_files_from_dir = { \
}
am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"
LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES)
+libctfbacktrace_la_LIBADD =
+am__libctfbacktrace_la_SOURCES_DIST = ctf-backtrace.c \
+ ctf-backtrace-err.c
+@HAVE_CTF_FRAME_AS_TRUE@am_libctfbacktrace_la_OBJECTS = \
+@HAVE_CTF_FRAME_AS_TRUE@ libctfbacktrace_la-ctf-backtrace.lo \
+@HAVE_CTF_FRAME_AS_TRUE@ libctfbacktrace_la-ctf-backtrace-err.lo
+libctfbacktrace_la_OBJECTS = $(am_libctfbacktrace_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libctfbacktrace_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(libctfbacktrace_la_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+@HAVE_CTF_FRAME_AS_TRUE@@INSTALL_LIBBFD_FALSE@am_libctfbacktrace_la_rpath =
+@HAVE_CTF_FRAME_AS_TRUE@@INSTALL_LIBBFD_TRUE@am_libctfbacktrace_la_rpath = \
+@HAVE_CTF_FRAME_AS_TRUE@@INSTALL_LIBBFD_TRUE@ -rpath $(libdir)
libctfframe_la_LIBADD =
am_libctfframe_la_OBJECTS = libctfframe_la-ctf-frame.lo \
libctfframe_la-ctf-frame-dump.lo \
libctfframe_la-ctf-frame-error.lo
libctfframe_la_OBJECTS = $(am_libctfframe_la_OBJECTS)
-AM_V_lt = $(am__v_lt_@AM_V@)
-am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
-am__v_lt_0 = --silent
-am__v_lt_1 =
@INSTALL_LIBBFD_FALSE@am_libctfframe_la_rpath =
@INSTALL_LIBBFD_TRUE@am_libctfframe_la_rpath = -rpath $(libdir)
AM_V_P = $(am__v_P_@AM_V@)
@@ -204,15 +222,16 @@ AM_V_CCLD = $(am__v_CCLD_@AM_V@)
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
-SOURCES = $(libctfframe_la_SOURCES)
-DIST_SOURCES = $(libctfframe_la_SOURCES)
+SOURCES = $(libctfbacktrace_la_SOURCES) $(libctfframe_la_SOURCES)
+DIST_SOURCES = $(am__libctfbacktrace_la_SOURCES_DIST) \
+ $(libctfframe_la_SOURCES)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
-am__include_HEADERS_DIST = $(INCDIR)/ctf-frame.h \
- $(INCDIR)/ctf-frame-api.h
+am__include_HEADERS_DIST = $(INCDIR)/ctf-backtrace-api.h \
+ $(INCDIR)/ctf-frame.h $(INCDIR)/ctf-frame-api.h
HEADERS = $(include_HEADERS)
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \
$(LISP)config.h.in
@@ -385,17 +404,20 @@ ACLOCAL_AMFLAGS = -I .. -I ../config
AUTOMAKE_OPTIONS = dejagnu foreign no-texinfo.tex
BASEDIR = $(srcdir)/..
INCDIR = $(srcdir)/../include
-# include libctf for swap.h
-AM_CPPFLAGS = -D_GNU_SOURCE -I$(srcdir) -I$(srcdir)/../include -I$(srcdir)/../libctf
AM_CFLAGS = -std=gnu99 @ac_libctfframe_warn_cflags@ @warn@ @c_warn@ @WARN_PEDANTIC@ @WERROR@
-@INSTALL_LIBBFD_TRUE@lib_LTLIBRARIES = libctfframe.la
-@INSTALL_LIBBFD_FALSE@include_HEADERS =
-@INSTALL_LIBBFD_TRUE@include_HEADERS = $(INCDIR)/ctf-frame.h $(INCDIR)/ctf-frame-api.h
-@INSTALL_LIBBFD_FALSE@noinst_LTLIBRARIES = libctfframe.la
+@INSTALL_LIBBFD_TRUE@lib_LTLIBRARIES = libctfframe.la $(am__append_1)
+@INSTALL_LIBBFD_FALSE@include_HEADERS = $(am__append_2)
+@INSTALL_LIBBFD_TRUE@include_HEADERS = $(INCDIR)/ctf-frame.h \
+@INSTALL_LIBBFD_TRUE@ $(INCDIR)/ctf-frame-api.h $(am__append_2)
+@INSTALL_LIBBFD_FALSE@noinst_LTLIBRARIES = libctfframe.la \
+@INSTALL_LIBBFD_FALSE@ $(am__append_3)
libctfframe_la_SOURCES = ctf-frame.c ctf-frame-dump.c ctf-frame-error.c
libctfframe_la_CPPFLAGS = -D_GNU_SOURCE -I$(srcdir) -I$(srcdir)/../include \
-I$(srcdir)/../libctf
+@HAVE_CTF_FRAME_AS_TRUE@libctfbacktrace_la_SOURCES = ctf-backtrace.c ctf-backtrace-err.c
+@HAVE_CTF_FRAME_AS_TRUE@libctfbacktrace_la_CPPFLAGS = -I$(srcdir) -I$(srcdir)/../include
+@HAVE_CTF_FRAME_AS_TRUE@libctfbacktrace_la_CFLAGS = -Wa,--gctf-frame
# Setup the testing framework
EXPECT = expect
@@ -516,6 +538,9 @@ clean-noinstLTLIBRARIES:
rm -f $${locs}; \
}
+libctfbacktrace.la: $(libctfbacktrace_la_OBJECTS) $(libctfbacktrace_la_DEPENDENCIES) $(EXTRA_libctfbacktrace_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libctfbacktrace_la_LINK) $(am_libctfbacktrace_la_rpath) $(libctfbacktrace_la_OBJECTS) $(libctfbacktrace_la_LIBADD) $(LIBS)
+
libctfframe.la: $(libctfframe_la_OBJECTS) $(libctfframe_la_DEPENDENCIES) $(EXTRA_libctfframe_la_DEPENDENCIES)
$(AM_V_CCLD)$(LINK) $(am_libctfframe_la_rpath) $(libctfframe_la_OBJECTS) $(libctfframe_la_LIBADD) $(LIBS)
@@ -525,6 +550,8 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctfbacktrace_la-ctf-backtrace-err.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctfbacktrace_la-ctf-backtrace.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctfframe_la-ctf-frame-dump.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctfframe_la-ctf-frame-error.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctfframe_la-ctf-frame.Plo@am__quote@
@@ -550,6 +577,20 @@ distclean-compile:
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+libctfbacktrace_la-ctf-backtrace.lo: ctf-backtrace.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libctfbacktrace_la_CPPFLAGS) $(CPPFLAGS) $(libctfbacktrace_la_CFLAGS) $(CFLAGS) -MT libctfbacktrace_la-ctf-backtrace.lo -MD -MP -MF $(DEPDIR)/libctfbacktrace_la-ctf-backtrace.Tpo -c -o libctfbacktrace_la-ctf-backtrace.lo `test -f 'ctf-backtrace.c' || echo '$(srcdir)/'`ctf-backtrace.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libctfbacktrace_la-ctf-backtrace.Tpo $(DEPDIR)/libctfbacktrace_la-ctf-backtrace.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ctf-backtrace.c' object='libctfbacktrace_la-ctf-backtrace.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libctfbacktrace_la_CPPFLAGS) $(CPPFLAGS) $(libctfbacktrace_la_CFLAGS) $(CFLAGS) -c -o libctfbacktrace_la-ctf-backtrace.lo `test -f 'ctf-backtrace.c' || echo '$(srcdir)/'`ctf-backtrace.c
+
+libctfbacktrace_la-ctf-backtrace-err.lo: ctf-backtrace-err.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libctfbacktrace_la_CPPFLAGS) $(CPPFLAGS) $(libctfbacktrace_la_CFLAGS) $(CFLAGS) -MT libctfbacktrace_la-ctf-backtrace-err.lo -MD -MP -MF $(DEPDIR)/libctfbacktrace_la-ctf-backtrace-err.Tpo -c -o libctfbacktrace_la-ctf-backtrace-err.lo `test -f 'ctf-backtrace-err.c' || echo '$(srcdir)/'`ctf-backtrace-err.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libctfbacktrace_la-ctf-backtrace-err.Tpo $(DEPDIR)/libctfbacktrace_la-ctf-backtrace-err.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ctf-backtrace-err.c' object='libctfbacktrace_la-ctf-backtrace-err.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libctfbacktrace_la_CPPFLAGS) $(CPPFLAGS) $(libctfbacktrace_la_CFLAGS) $(CFLAGS) -c -o libctfbacktrace_la-ctf-backtrace-err.lo `test -f 'ctf-backtrace-err.c' || echo '$(srcdir)/'`ctf-backtrace-err.c
+
libctfframe_la-ctf-frame.lo: ctf-frame.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libctfframe_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libctfframe_la-ctf-frame.lo -MD -MP -MF $(DEPDIR)/libctfframe_la-ctf-frame.Tpo -c -o libctfframe_la-ctf-frame.lo `test -f 'ctf-frame.c' || echo '$(srcdir)/'`ctf-frame.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libctfframe_la-ctf-frame.Tpo $(DEPDIR)/libctfframe_la-ctf-frame.Plo
diff --git a/libctfframe/aclocal.m4 b/libctfframe/aclocal.m4
index 3a0b3426ebc..bda656c049c 100644
--- a/libctfframe/aclocal.m4
+++ b/libctfframe/aclocal.m4
@@ -1229,6 +1229,7 @@ AC_SUBST([am__untar])
m4_include([../bfd/acinclude.m4])
m4_include([../config/acx.m4])
+m4_include([../config/ctf-frame.m4])
m4_include([../config/depstand.m4])
m4_include([../config/jobserver.m4])
m4_include([../config/lead-dot.m4])
diff --git a/libctfframe/configure.ac b/libctfframe/configure.ac
index d5e5db3c416..cd7ed5f7cdb 100644
--- a/libctfframe/configure.ac
+++ b/libctfframe/configure.ac
@@ -57,6 +57,13 @@ ACX_PROG_CC_WARNING_ALMOST_PEDANTIC([-Wno-long-long])
# corrected.
ACX_PROG_CC_WARNINGS_ARE_ERRORS([manual])
+dnl The libctfbacktrace library needs to be built with CTF Frame info.
+dnl If the build assembler is not capable of generate CTF Frame then
+dnl the library is not built.
+
+CTF_CHECK_AS_CTF_FRAME
+AM_CONDITIONAL([HAVE_CTF_FRAME_AS], [test "x$ac_cv_have_ctfframe" = "xyes"])
+
AM_MAINTAINER_MODE
AM_INSTALL_LIBBFD
ACX_PROG_CC_WARNING_OPTS([-Wall], [ac_libctfframe_warn_cflags])
diff --git a/libctfframe/ctf-backtrace-err.c b/libctfframe/ctf-backtrace-err.c
new file mode 100644
index 00000000000..374ceaf3266
--- /dev/null
+++ b/libctfframe/ctf-backtrace-err.c
@@ -0,0 +1,46 @@
+/* ctf-backtrace-err.c - CTF Backtrace Error table.
+
+ Copyright (C) 2022 Free Software Foundation, Inc.
+
+ This file is part of libctfbacktrace.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "ctf-backtrace-api.h"
+
+/* CTF backtrace error messages. */
+static const char *const ctf_bt_errlist[] =
+{
+ "",
+ "File does not contain CTF Frame data",
+ "Iterating shared object reading error",
+ "Failed to malloc memory space",
+ "Failed to realloc memory space",
+ "Failed to open file",
+ "Failed on resolve canonical file name",
+ "Failed to reposition file offset",
+ "Failed to read from a file descriptor",
+ "Failed to get the user context",
+ "Failed to set up decode data",
+ "Illegal CFA offset"
+};
+
+/* ctf_bt_perror -Return the error message associated with the error code. */
+
+const char *
+ctf_bt_errmsg (enum ctf_bt_errcode ecode)
+{
+ return ctf_bt_errlist[ecode];
+}
diff --git a/libctfframe/ctf-backtrace.c b/libctfframe/ctf-backtrace.c
new file mode 100644
index 00000000000..652877e26d0
--- /dev/null
+++ b/libctfframe/ctf-backtrace.c
@@ -0,0 +1,617 @@
+/* ctf-backtrace.c - The CTF Frame unwinder.
+
+ Copyright (C) 2022 Free Software Foundation, Inc.
+
+ This file is part of libctfbacktrace.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include "config.h"
+#include <elf.h>
+#include <link.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <execinfo.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ucontext.h>
+#include <stdarg.h>
+#include "ansidecl.h"
+#include "ctf-frame-api.h"
+#include "ctf-backtrace-api.h"
+
+#ifndef PT_CTF_FRAME
+#define PT_CTF_FRAME 0x6474e554 /* FIXME. */
+#endif
+
+#define _cf_printflike_(string_index,first_to_check) \
+ __attribute__ ((__format__ (__printf__, (string_index), (first_to_check))))
+
+static int _ctf_unwind_debug; /* Control for printing out debug info. */
+static int no_of_entries = 32;
+
+/* CTF decode data for the main module or a DSO. */
+struct ctf_decode_data
+{
+ char *cdd_data; /* CTF decode data. */
+ int cdd_data_size; /* CTF decode data size. */
+ uint64_t cdd_text_vma; /* Text segment's virtual address. */
+ int cdd_text_size; /* Text segment's size. */
+ uint64_t cdd_ctf_vma; /* CTF segment's virtual address. */
+ ctf_frame_decoder_ctx *cdd_ctf_ctx; /* CTF decoder context. */
+};
+
+/* List that holds CTF Frame info for the shared libraries. */
+struct dso_cfi_list
+{
+ int alloced; /* Entries allocated. */
+ int used; /* Entries used. */
+ struct ctf_decode_data *entry; /* DSO's decode data. */
+};
+
+/* Data that's passed through ctf_frame_callback. */
+struct ctf_unwind_info
+{
+ int cui_fd; /* File descriptor. */
+ struct ctf_decode_data cui_ctx; /* The decode data. */
+ struct dso_cfi_list cui_dsos; /* The DSO list. */
+};
+
+static void
+ctf_unwind_init_debug (void)
+{
+ static int inited;
+
+ if (!inited)
+ {
+ _ctf_unwind_debug = getenv ("CTF_UNWIND_DEBUG") != NULL;
+ inited = 1;
+ }
+}
+
+_cf_printflike_ (1, 2)
+static void
+debug_printf (const char *format, ...)
+{
+ if (_ctf_unwind_debug)
+ {
+ va_list args;
+
+ va_start (args, format);
+ __builtin_vprintf (format, args);
+ va_end (args);
+ }
+}
+
+/* ctf_bt_errno - Check if there is error code in ERRP. */
+
+static int
+ctf_bt_errno (int *errp)
+{
+ if (errp == NULL)
+ return 0;
+
+ return (*errp != CTF_BT_OK);
+}
+
+/* ctf_bt_set_errno - Store the specified error code ERROR into ERRP if
+ it is non-NULL. */
+
+static void
+ctf_bt_set_errno (int *errp, int error)
+{
+ if (errp != NULL)
+ *errp = error;
+}
+
+/* ctf_add_dso - Add .ctf_frame info in D_DATA, which is associated with
+ a dynamic shared object, to D_LIST. */
+
+static void
+ctf_add_dso (struct dso_cfi_list *d_list,
+ struct ctf_decode_data d_data,
+ int *errp)
+{
+ if (d_list->alloced == 0)
+ {
+ d_list->entry = malloc (no_of_entries * sizeof (struct ctf_decode_data));
+ if (d_list->entry == NULL)
+ {
+ ctf_bt_set_errno (errp, CTF_BT_ERR_MALLOC);
+ return;
+ }
+ memset (d_list->entry, 0,
+ no_of_entries * sizeof (struct ctf_decode_data));
+ d_list->alloced = no_of_entries;
+ }
+ else if (d_list->used == d_list->alloced)
+ {
+ d_list->entry = realloc (d_list->entry,
+ (d_list->alloced + no_of_entries) *
+ sizeof (struct ctf_decode_data));
+ if (d_list->entry == NULL)
+ {
+ ctf_bt_set_errno (errp, CTF_BT_ERR_REALLOC);
+ return;
+ }
+
+ memset (&d_list->entry[d_list->alloced], 0,
+ no_of_entries * sizeof (struct ctf_decode_data));
+ d_list->alloced += no_of_entries;
+ }
+
+ ctf_bt_set_errno (errp, CTF_BT_OK);
+ d_list->entry[d_list->used++] = d_data;
+}
+
+/* ctf_free_cfi - Free up space allocated for .ctf_frame info for CF. */
+
+static void
+ctf_free_cfi (struct ctf_unwind_info *cf)
+{
+ struct dso_cfi_list *d_list;
+ int i;
+
+ if (cf == NULL)
+ return;
+
+ free (cf->cui_ctx.cdd_data);
+ ctf_frame_decoder_free (&cf->cui_ctx.cdd_ctf_ctx);
+ close (cf->cui_fd);
+
+ d_list = &cf-> cui_dsos;
+ if (d_list == NULL)
+ return;
+
+ for (i = 0; i < d_list->used; ++i)
+ {
+ free (d_list->entry[i].cdd_data);
+ ctf_frame_decoder_free (&d_list->entry[i].cdd_ctf_ctx);
+ }
+}
+
+/* ctf_find_context - Find the decode data that contains ADDR from CF.
+ Return the pointer to the decode data or NULL. */
+
+static struct ctf_decode_data *
+ctf_find_context (struct ctf_unwind_info *cf, uint64_t addr)
+{
+ struct dso_cfi_list *d_list;
+ int i;
+
+ if (cf == NULL)
+ return NULL;
+
+ if (cf->cui_ctx.cdd_text_vma < addr
+ && cf->cui_ctx.cdd_text_vma + cf->cui_ctx.cdd_text_size > addr)
+ return &cf->cui_ctx;
+
+ d_list = &cf->cui_dsos;
+ for (i = 0; i < cf->cui_dsos.used; ++i)
+ {
+ if (d_list->entry[i].cdd_text_vma <= addr &&
+ d_list->entry[i].cdd_text_vma
+ + d_list->entry[i].cdd_text_size >= addr)
+ return &d_list->entry[i];
+ }
+
+ return NULL;
+}
+
+/* ctf_valid_addr - Check if ADDR is valid in CF. The address is considered
+ invalid, with regards to CTF, if it's not in any address range of the main
+ module or any of its DSO's. Return 1 if valid, 0 otherwise. */
+
+static int
+ctf_valid_addr (struct ctf_unwind_info *cf, uint64_t addr)
+{
+ struct ctf_decode_data *cdp;
+
+ if (cf == NULL)
+ return 0;
+
+ cdp = ctf_find_context (cf, addr);
+ return cdp ? 1 : 0;
+}
+
+/* ctf_load_ctx - Call decoder to create and set up the ctf frame info for
+ either the main module or one of the DSOs from CF, based on the input
+ RADDR argument. Return the newly created decode context or NULL. */
+
+static ctf_frame_decoder_ctx *
+ctf_load_ctx (struct ctf_unwind_info *cf, uint64_t raddr)
+{
+ ctf_frame_decoder_ctx *nctx;
+ struct ctf_decode_data *cdp;
+
+ if (cf == NULL)
+ return NULL;
+
+ cdp = ctf_find_context (cf, raddr);
+ if (cdp == NULL)
+ return NULL;
+
+ if (cdp->cdd_ctf_ctx == NULL)
+ {
+ int err;
+ nctx = ctf_frame_decode (cdp->cdd_data, cdp->cdd_data_size, &err);
+ if (nctx == NULL)
+ return NULL;
+ cdp->cdd_ctf_ctx = nctx;
+ return nctx;
+ }
+
+ return NULL;
+}
+
+/* ctf_update_ctx - Check if need to do a decode context switch, based on
+ the input RADDR argument, from CF. A new decode context will be created
+ and set up if it isn't already done so. Return the new decode context in
+ CTX and vma in CFI_VMA. */
+
+static void
+ctf_update_ctx (struct ctf_unwind_info *cf, uint64_t raddr,
+ ctf_frame_decoder_ctx **ctx, uint64_t *cfi_vma)
+{
+ ctf_frame_decoder_ctx *nctx;
+ struct ctf_decode_data *cdp;
+
+ cdp = ctf_find_context (cf, raddr);
+ if (cdp != NULL)
+ {
+ if (cdp->cdd_ctf_ctx == NULL)
+ {
+ int err;
+ nctx = ctf_frame_decode (cdp->cdd_data, cdp->cdd_data_size, &err);
+ if (nctx == NULL)
+ {
+ *ctx = NULL;
+ return;
+ }
+ cdp->cdd_ctf_ctx = nctx;
+ }
+ *ctx = cdp->cdd_ctf_ctx;
+ *cfi_vma = cdp->cdd_ctf_vma;
+ }
+}
+
+/* get_contents - Return contents at ADDR from file descriptor FD. */
+
+static uint64_t
+get_contents (int fd, uint64_t addr, int *errp)
+{
+ uint64_t data;
+ size_t sz;
+
+ ctf_bt_set_errno (errp, CTF_BT_OK);
+ if (lseek (fd, addr, SEEK_SET) == -1)
+ {
+ ctf_bt_set_errno (errp, CTF_BT_ERR_LSEEK);
+ return 0;
+ }
+ sz = read (fd, &data, sizeof (uint64_t));
+ if (sz != sizeof (uint64_t))
+ {
+ ctf_bt_set_errno (errp, CTF_BT_ERR_READ);
+ return 0;
+ }
+
+ return data;
+}
+
+/* ctf_fd_open - Open /proc image associated with the process id and return
+ the file descriptor. */
+
+static int
+ctf_fd_open (int *errp)
+{
+ char filename[PATH_MAX];
+ pid_t pid;
+ int fd;
+
+ pid = getpid ();
+ snprintf (filename, sizeof filename, "/proc/%d/task/%d/mem", pid, pid);
+ if ((fd = open (filename, O_RDONLY)) == -1)
+ {
+ ctf_bt_set_errno (errp, CTF_BT_ERR_OPEN);
+ return -1;
+ }
+
+ return fd;
+}
+
+/* ctf_frame_callback - The callback from dl_iterate_phdr with header info
+ in INFO.
+ Return CTF Frame info for either the main module or a DSO in DATA. */
+
+static int
+ctf_frame_callback (struct dl_phdr_info *info,
+ size_t size ATTRIBUTE_UNUSED,
+ void *data)
+{
+ struct ctf_unwind_info *cf = (struct ctf_unwind_info *) data;
+ int p_type, i, fd, ctf_err;
+ ssize_t len;
+ uint64_t text_vma = 0;
+ int text_size = 0;
+
+ if (data == NULL || info == NULL)
+ return 1;
+
+ debug_printf ("-- name: %s %14p\n", info->dlpi_name, (void *)info->dlpi_addr);
+
+ for (i = 0; i < info->dlpi_phnum; i++)
+ {
+ debug_printf(" %2d: [%14p; memsz:%7lx] flags: 0x%x; \n", i,
+ (void *) info->dlpi_phdr[i].p_vaddr,
+ info->dlpi_phdr[i].p_memsz,
+ info->dlpi_phdr[i].p_flags);
+
+ p_type = info->dlpi_phdr[i].p_type;
+ if (p_type == PT_LOAD && info->dlpi_phdr[i].p_flags & PF_X)
+ {
+ text_vma = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
+ text_size = info->dlpi_phdr[i].p_memsz;
+ continue;
+ }
+ if (p_type != PT_CTF_FRAME)
+ continue;
+
+ if (info->dlpi_name[0] == '\0') /* the main module. */
+ {
+ fd = ctf_fd_open (&ctf_err);
+ if (fd == -1)
+ return 1;
+ if (lseek (fd, info->dlpi_addr + info->dlpi_phdr[i].p_vaddr,
+ SEEK_SET) == -1)
+ {
+ ctf_bt_set_errno (&ctf_err, CTF_BT_ERR_LSEEK);
+ return 1;
+ }
+
+ cf->cui_ctx.cdd_data = (char *) malloc (info->dlpi_phdr[i].p_memsz);
+ if (cf->cui_ctx.cdd_data == NULL)
+ {
+ ctf_bt_set_errno (&ctf_err, CTF_BT_ERR_MALLOC);
+ return 1;
+ }
+
+ len = read (fd, cf->cui_ctx.cdd_data, info->dlpi_phdr[i].p_memsz);
+ if (len == -1 || len != (ssize_t) info->dlpi_phdr[i].p_memsz)
+ {
+ ctf_bt_set_errno (&ctf_err, CTF_BT_ERR_READ);
+ return 1;
+ }
+
+ assert (text_vma);
+ cf->cui_ctx.cdd_data_size = len;
+ cf->cui_ctx.cdd_ctf_vma = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
+ cf->cui_fd = fd;
+ cf->cui_ctx.cdd_text_vma = text_vma;
+ cf->cui_ctx.cdd_text_size = text_size;
+ text_vma = 0;
+ return 0;
+ }
+ else
+ { /* a dynamic shared object. */
+ struct ctf_decode_data dt;
+ memset (&dt, 0, sizeof (struct ctf_decode_data));
+ assert (cf->cui_fd);
+ if (lseek (cf->cui_fd, info->dlpi_addr + info->dlpi_phdr[i].p_vaddr,
+ SEEK_SET) == -1)
+ {
+ ctf_bt_set_errno (&ctf_err, CTF_BT_ERR_LSEEK);
+ return 1;
+ }
+
+ dt.cdd_data = (char *) malloc (info->dlpi_phdr[i].p_memsz);
+ if (dt.cdd_data == NULL)
+ {
+ ctf_bt_set_errno (&ctf_err, CTF_BT_ERR_MALLOC);
+ return 1;
+ }
+
+ len = read (cf->cui_fd, dt.cdd_data, info->dlpi_phdr[i].p_memsz);
+ if (len == -1 || len != (ssize_t) info->dlpi_phdr[i].p_memsz)
+ {
+ ctf_bt_set_errno (&ctf_err, CTF_BT_ERR_READ);
+ return 1;
+ }
+
+ assert (text_vma);
+ dt.cdd_data_size = len;
+ dt.cdd_ctf_vma = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
+ dt.cdd_text_vma = text_vma;
+ dt.cdd_text_size = text_size;
+ text_vma = 0;
+ ctf_add_dso (&cf->cui_dsos, dt, &ctf_err);
+ if (ctf_err != CTF_BT_OK)
+ return 1;
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/* ctf_frame_unwind - Unwind the stack backtrace for CF. If successful,
+ store the return addresses in RA_LST. The RA_SIZE argument specifies
+ the maximum number of return addresses that can be stored in RA_LST
+ and contains the number of the addresses collected. */
+
+static void
+ctf_frame_unwind (struct ctf_unwind_info *cf, void **ra_lst,
+ int *ra_size, int *errp)
+{
+ uint64_t cfa, return_addr, ra_stack_loc, rfp_stack_loc;
+ ctf_frame_decoder_ctx *ctx;
+ int cfa_offset, rfp_offset, errnum, i, count;
+ ctf_frame_row_entry fred, *frep = &fred;
+ uint64_t pc, rfp, rsp, cfi_vma;
+ ucontext_t context, *cp = &context;
+
+ if (cf == NULL || ra_lst == NULL || ra_size == NULL)
+ {
+ ctf_bt_set_errno (errp, CTF_BT_ERR_ARG);
+ return;
+ }
+
+ /* Get the user context for its registers. */
+ if (getcontext (cp) != 0)
+ {
+ ctf_bt_set_errno (errp, CTF_BT_ERR_GETCONTEXT);
+ return;
+ }
+ ctf_bt_set_errno (errp, CTF_BT_OK);
+
+#ifdef __x86_64__
+ pc = cp->uc_mcontext.gregs[REG_RIP];
+ rsp = cp->uc_mcontext.gregs[REG_RSP];
+ rfp = cp->uc_mcontext.gregs[REG_RBP];
+#else
+#ifdef __aarch64__
+#define UNWIND_AARCH64_X29 29 /* 64-bit frame pointer. */
+#define UNWIND_AARCH64_X30 30 /* 64-bit link pointer. */
+ pc = cp->uc_mcontext.pc;
+ rsp = cp->uc_mcontext.sp;
+ rfp = cp->uc_mcontext.regs[UNWIND_AARCH64_X29];
+ uint64_t ra = cp->uc_mcontext.regs[UNWIND_AARCH64_X30];
+#endif
+#endif
+
+ /* Load and set up the decoder. */
+ ctx = ctf_load_ctx (cf, pc);
+ if (ctx == NULL)
+ {
+ ctf_bt_set_errno (errp, CTF_BT_ERR_DECODE);
+ return;
+ }
+ cfi_vma = cf->cui_ctx.cdd_ctf_vma;
+ count = *ra_size;
+
+ for (i = 0; i < count; ++i)
+ {
+ pc -= cfi_vma;
+ errnum = ctf_frame_find_fre (ctx, pc, frep);
+ if (errnum == 0)
+ {
+ cfa_offset = ctf_frame_fre_get_cfa_offset (frep, &errnum);
+ if (errnum == ECTF_FRAME_FREOFFSET_NOPRESENT)
+ {
+ ctf_bt_set_errno (errp, CTF_BT_ERR_CFA_OFFSET);
+ return;
+ }
+
+ cfa = ((frep->fre_info & 0x1) == CTF_FRAME_BASE_REG_SP
+ ? rsp : rfp) + cfa_offset;
+
+#ifdef __x86_64__
+ /* For x86, read the return address from cfa - 8. */
+ ra_stack_loc = cfa - 8;
+ return_addr = get_contents (cf->cui_fd, ra_stack_loc, errp);
+ if (ctf_bt_errno (errp))
+ return;
+#else
+#ifdef __aarch64__
+ int ra_offset = ctf_frame_fre_get_ra_offset (frep, &errnum);
+ if (errnum == 0)
+ {
+ ra_stack_loc = cfa + ra_offset;
+ return_addr = get_contents (cf->cui_fd, ra_stack_loc, errp);
+ if (ctf_bt_errno (errp))
+ return;
+ }
+ else
+ return_addr = ra;
+#endif
+#endif
+
+ /* Validate and add return address to the list. */
+ if (ctf_valid_addr (cf, return_addr) == 0)
+ {
+ i -= 1;
+ goto find_fre_ra_err;
+ }
+ if (i != 0) /* exclude self. */
+ ra_lst[i-1] = (void *)return_addr;
+
+ /* Set up for the next frame. */
+ rfp_offset = ctf_frame_fre_get_fp_offset (frep, &errnum);
+ if (errnum == 0)
+ {
+ rfp_stack_loc = cfa + rfp_offset;
+ rfp = get_contents (cf->cui_fd, rfp_stack_loc, errp);
+ if (ctf_bt_errno (errp))
+ return;
+ }
+ rsp = cfa;
+ pc = return_addr;
+
+ /* Check if need to update the decoder context and vma. */
+ ctf_update_ctx (cf, return_addr, &ctx, &cfi_vma);
+ if (ctx == NULL)
+ {
+ ctf_bt_set_errno (errp, CTF_BT_ERR_DECODE);
+ return;
+ }
+ }
+ else
+ {
+ i -= 1;
+ goto find_fre_ra_err;
+ }
+ }
+
+find_fre_ra_err:
+ *ra_size = i;
+}
+
+/* ctf_backtrace - Main API that user program calls to get a backtrace.
+ The BUFFER argument provides space for the list of the return addresses
+ and the SIZE argument specifies the maximum number of addresses that
+ can be stored in the buffer. Return the number of return addresses
+ collected or -1 if there is any error. */
+
+int
+ctf_backtrace (void **buffer, int size, int *errp)
+{
+ struct ctf_unwind_info ctfinfo;
+
+ ctf_unwind_init_debug ();
+
+ memset (&ctfinfo, 0, sizeof (struct ctf_unwind_info));
+
+ /* find and set up the .ctf_frame sections. */
+ (void) dl_iterate_phdr (ctf_frame_callback, (void *)&ctfinfo);
+ if (ctfinfo.cui_fd == 0)
+ {
+ ctf_bt_set_errno (errp, CTF_BT_ERR_NOCTF);
+ return -1;
+ }
+
+ /* Do the stack unwinding. */
+ ctf_frame_unwind (&ctfinfo, buffer, &size, errp);
+ if (ctf_bt_errno (errp))
+ return -1;
+
+ ctf_free_cfi (&ctfinfo);
+
+ return size;
+}
diff --git a/libctfframe/ttest.c b/libctfframe/ttest.c
new file mode 100644
index 00000000000..c0b8434aad5
--- /dev/null
+++ b/libctfframe/ttest.c
@@ -0,0 +1,78 @@
+#include <execinfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "ctf-backtrace-api.h"
+
+#define BT_BUF_SIZE 100
+
+/* funclist for running "ttest.x 3". */
+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 = ctf_backtrace (buffer, BT_BUF_SIZE, &err);
+ if (nptrs == -1 || nptrs != 6)
+ {
+ printf ("CTF error: %s\n", ctf_bt_errmsg (err));
+ return;
+ }
+
+ /* Get these addresses symbolically. */
+ strings = backtrace_symbols (buffer, nptrs);
+ if (strings == NULL) {
+ perror("backtrace_symbols");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Verify the results. */
+ for (j = 0; j < nptrs; j++)
+ if (!strstr (strings[j], func_list[j]))
+ break;
+
+ free(strings);
+
+ printf ("%s\n", j == nptrs ? "PASS" : "FAIL");
+}
+
+static void /* "static" means don't export the symbol... */
+myfunc2 (void)
+{
+ myfunc3 ();
+}
+
+void
+myfunc (int ncalls)
+{
+ if (ncalls > 1)
+ myfunc (ncalls - 1);
+ else
+ myfunc2 ();
+}
+
+int
+main (int argc, char *argv[])
+{
+ if (argc != 2) {
+ fprintf (stderr, "%s num-calls\n", argv[0]);
+ exit (EXIT_FAILURE);
+ }
+
+ myfunc (atoi(argv[1]));
+ exit (EXIT_SUCCESS);
+}
--
2.31.1
next prev parent reply other threads:[~2022-06-14 7:07 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-06-14 7:06 [PATCH,RFC,V3 0/8] Definition and Implementation of " Indu Bhagat
2022-06-14 7:06 ` [PATCH,RFC,V3 1/8] ctf-frame.h: Add CTF Frame format definition Indu Bhagat
2022-06-14 7:06 ` [PATCH,RFC,V3 2/8] gas: add new command line option --gctf-frame Indu Bhagat
2022-06-14 7:06 ` [PATCH,RFC,V3 3/8] gas: generate .ctf_frame Indu Bhagat
2022-06-14 7:06 ` [PATCH,RFC,V3 4/8] libctfframe: add the CTF Frame library Indu Bhagat
2022-06-14 7:06 ` [PATCH,RFC,V3 5/8] bfd: linker: merge .ctf_frame sections Indu Bhagat
2022-06-14 7:06 ` [PATCH,RFC,V3 6/8] readelf/objdump: support for CTF Frame section Indu Bhagat
2022-06-14 7:06 ` Indu Bhagat [this message]
2022-06-14 7:06 ` [PATCH, RFC, V3 8/8] gdb: sim: buildsystem changes to accommodate libctfframe Indu Bhagat
2022-06-14 20:44 ` [PATCH,RFC,V3 0/8] Definition and Implementation of CTF Frame format Indu Bhagat
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220614070652.1608873-8-indu.bhagat@oracle.com \
--to=indu.bhagat@oracle.com \
--cc=binutils@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).