unwContext->regs[13] = winContext->Sp;
unwContext->regs[14] = winContext->Lr;
unwContext->regs[15] = winContext->Pc;
+#elif defined(HOST_ARM64)
+ unwContext->uc_mcontext.pc = winContext->Pc;
+ unwContext->uc_mcontext.sp = winContext->Sp;
+ unwContext->uc_mcontext.regs[29] = winContext->Fp;
+ unwContext->uc_mcontext.regs[30] = winContext->Lr;
+
+ unwContext->uc_mcontext.regs[19] = winContext->X19;
+ unwContext->uc_mcontext.regs[20] = winContext->X20;
+ unwContext->uc_mcontext.regs[21] = winContext->X21;
+ unwContext->uc_mcontext.regs[22] = winContext->X22;
+ unwContext->uc_mcontext.regs[23] = winContext->X23;
+ unwContext->uc_mcontext.regs[24] = winContext->X24;
+ unwContext->uc_mcontext.regs[25] = winContext->X25;
+ unwContext->uc_mcontext.regs[26] = winContext->X26;
+ unwContext->uc_mcontext.regs[27] = winContext->X27;
+ unwContext->uc_mcontext.regs[28] = winContext->X28;
#endif
}
tests/[GL]ia64-test-stack
tests/ia64-test-dyn1
tests/ia64-test-sig
+tests/[GL]x64-test-dwarf-expressions
+tests/x64-unwind-badjmp-signal-frame
tests/*.log
tests/*.trs
- TARGET=mipsel-unknown-linux-gnu
# Currently experiencing build failures here
#- TARGET=powerpc64-linux-gnu
+
+linux-s390x: &linux-s390x
+ os: linux
+ arch: s390x
+ env: TARGET=s390x-linux-gnu
+ script:
+ - ./autogen.sh
+ - ./configure
+ - make -j32
+ - ulimit -c unlimited
+ - make check -j32
+
script:
- ./autogen.sh
- ./configure --target=$TARGET --host=$HOST
- sudo bash -c 'echo core.%p.%p > /proc/sys/kernel/core_pattern'
- ulimit -c unlimited
- if [ $TARGET == 'x86_64-linux-gnu' ]; then make check -j32; fi
+
+jobs:
+ include:
+ - <<: *linux-s390x
endif()
set(PKG_MAJOR "1")
-set(PKG_MINOR "3")
-set(PKG_EXTRA "-rc1")
+set(PKG_MINOR "5")
+set(PKG_EXTRA "-rc2")
configure_file(include/libunwind-common.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/libunwind-common.h)
configure_file(include/libunwind.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/libunwind.h)
configure_file(include/tdep/libunwind_i.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/tdep/libunwind_i.h)
-
if ARCH_SH
include_HEADERS += include/libunwind-sh.h
endif
+if ARCH_S390X
+include_HEADERS += include/libunwind-s390x.h
+endif
if !REMOTE_ONLY
include_HEADERS += include/libunwind.h include/unwind.h
include/tdep-ppc64/jmpbuf.h include/tdep-ppc64/libunwind_i.h \
include/tdep-sh/dwarf-config.h \
include/tdep-sh/jmpbuf.h include/tdep-sh/libunwind_i.h \
+ include/tdep-s390x/dwarf-config.h \
+ include/tdep-s390x/jmpbuf.h include/tdep-s390x/libunwind_i.h \
include/tdep/libunwind_i.h \
include/tdep/jmpbuf.h include/tdep/dwarf-config.h
--*- mode: Outline -*-
+# libunwind
[![Build Status](https://travis-ci.org/libunwind/libunwind.svg?branch=master)](https://travis-ci.org/libunwind/libunwind)
-This is version 1.3 of the unwind library. This library supports
+This is version 1.5 of the unwind library. This library supports
several architecture/operating-system combinations:
- Linux/x86-64: Works well.
- Linux/x86: Works well.
- Linux/ARM: Works well.
- Linux/IA-64: Works well.
- Linux/PARISC: Works well, but C library missing unwind-info.
- HP-UX/IA-64: Mostly works but known to have some serious limitations.
- MIPS: Newly added.
- Linux/AArch64: Works well.
- Linux/PPC64: Newly added.
- Linux/SuperH: Newly added.
- FreeBSD/i386: Works well.
- FreeBSD/x86-64: Newly added (FreeBSD architecture is known as amd64).
- Linux/Tilegx: Newly added (64-bit mode only).
-
-* General Build Instructions
+| System | Architecture | Status |
+| :------ | :----------- | :----- |
+| Linux | x86-64 | ✓ |
+| Linux | x86 | ✓ |
+| Linux | ARM | ✓ |
+| Linux | AArch64 | ✓ |
+| Linux | PPC64 | ✓ |
+| Linux | SuperH | ✓ |
+| Linux | IA-64 | ✓ |
+| Linux | PARISC | Works well, but C library missing unwind-info |
+| Linux | Tilegx | 64-bit mode only |
+| Linux | MIPS | Newly added |
+| HP-UX | IA-64 | Mostly works, but known to have serious limitations |
+| FreeBSD | x86-64 | ✓ |
+| FreeBSD | x86 | ✓ |
+| FreeBSD | AArch64 | ✓ |
+| Solaris | x86-64 | ✓ |
+
+## Libc Requirements
+
+libunwind depends on getcontext(), setcontext() functions which are missing
+from C libraries like musl-libc because they are considered to be "obsolescent"
+API by POSIX document. The following table tries to track current status of
+such dependencies
+
+ - r, requires
+ - p, provides its own implementation
+ - empty, no requirement
+
+| Archtecture | getcontext | setcontext |
+|--------------|------------|------------|
+| aarch64 | p | |
+| arm | p | |
+| hppa | p | p |
+| ia64 | p | r |
+| mips | p | |
+| ppc32 | r | |
+| ppc64 | r | r |
+| s390x | p | p |
+| sh | r | |
+| tilegx | r | r |
+| x86 | p | r |
+| x86_64 | p | p |
+
+## General Build Instructions
In general, this library can be built and installed with the following
commands:
- $ ./autogen.sh # Needed only for building from git. Depends on libtool.
- $ ./configure
- $ make
- $ make install prefix=PREFIX
+ $ ./autogen.sh # Needed only for building from git. Depends on libtool.
+ $ ./configure
+ $ make
+ $ make install prefix=PREFIX
-where PREFIX is the installation prefix. By default, a prefix of
-/usr/local is used, such that libunwind.a is installed in
-/usr/local/lib and unwind.h is installed in /usr/local/include. For
-testing, you may want to use a prefix of /usr/local instead.
+where `PREFIX` is the installation prefix. By default, a prefix of
+`/usr/local` is used, such that `libunwind.a` is installed in
+`/usr/local/lib` and `unwind.h` is installed in `/usr/local/include`. For
+testing, you may want to use a prefix of `/usr/local` instead.
-* Building with Intel compiler
+### Building with Intel compiler
-** Version 8 and later
+#### Version 8 and later
Starting with version 8, the preferred name for the IA-64 Intel
-compiler is "icc" (same name as on x86). Thus, the configure-line
+compiler is `icc` (same name as on x86). Thus, the configure-line
should look like this:
$ ./configure CC=icc CFLAGS="-g -O3 -ip" CXX=icc CCAS=gcc CCASFLAGS=-g \
- LDFLAGS="-L$PWD/src/.libs"
+ LDFLAGS="-L$PWD/src/.libs"
-* Building on HP-UX
+### Building on HP-UX
For the time being, libunwind must be built with GCC on HP-UX.
$ ./configure CFLAGS="-g -O2 -mlp64" CXXFLAGS="-g -O2 -mlp64"
-Caveat: Unwinding of 32-bit (ILP32) binaries is not supported
- at the moment.
+Caveat: Unwinding of 32-bit (ILP32) binaries is not supported at the moment.
-** Workaround for older versions of GCC
+### Workaround for older versions of GCC
-GCC v3.0 and GCC v3.2 ship with a bad version of sys/types.h. The
+GCC v3.0 and GCC v3.2 ship with a bad version of `sys/types.h`. The
workaround is to issue the following commands before running
-"configure":
+`configure`:
$ mkdir $top_dir/include/sys
$ cp /usr/include/sys/types.h $top_dir/include/sys
GCC v3.3.2 or later have been fixed and do not require this
workaround.
-* Building for PowerPC64 / Linux
+### Building for PowerPC64 / Linux
For building for power64 you should use:
- $ ./configure CFLAGS="-g -O2 -m64" CXXFLAGS="-g -O2 -m64"
+ $ ./configure CFLAGS="-g -O2 -m64" CXXFLAGS="-g -O2 -m64"
If your power support altivec registers:
- $ ./configure CFLAGS="-g -O2 -m64 -maltivec" CXXFLAGS="-g -O2 -m64 -maltivec"
+
+ $ ./configure CFLAGS="-g -O2 -m64 -maltivec" CXXFLAGS="-g -O2 -m64 -maltivec"
To check if your processor has support for vector registers (altivec):
+
cat /proc/cpuinfo | grep altivec
+
and should have something like this:
+
cpu : PPC970, altivec supported
If libunwind seems to not work (backtracing failing), try to compile
-it with -O0, without optimizations. There are some compiler problems
+it with `-O0`, without optimizations. There are some compiler problems
depending on the version of your gcc.
-* Building on FreeBSD
-
-General building instructions apply. To build and execute several tests,
-you need libexecinfo library available in ports as devel/libexecinfo.
+### Building on FreeBSD
-Development of the port was done of FreeBSD 8.0-STABLE. The library
-was build with the system compiler that is modified version of gcc 4.2.1,
-as well as the gcc 4.4.3.
+General building instructions apply. To build and execute several tests
+on older versions of FreeBSD, you need libexecinfo library available in
+ports as devel/libexecinfo. This port has been removed as of 2017 and is
+indeed no longer needed.
-* Regression Testing
+## Regression Testing
After building the library, you can run a set of regression tests with:
- $ make check
+ $ make check
-** Expected results on IA-64 Linux
+### Expected results on IA-64 Linux
Unless you have a very recent C library and compiler installed, it is
currently expected to have the following tests fail on IA-64 Linux:
- Gtest-init (should pass starting with glibc-2.3.x/gcc-3.4)
- Ltest-init (should pass starting with glibc-2.3.x/gcc-3.4)
- test-ptrace (should pass starting with glibc-2.3.x/gcc-3.4)
- run-ia64-test-dyn1 (should pass starting with glibc-2.3.x)
+* `Gtest-init` (should pass starting with glibc-2.3.x/gcc-3.4)
+* `Ltest-init` (should pass starting with glibc-2.3.x/gcc-3.4)
+* `test-ptrace` (should pass starting with glibc-2.3.x/gcc-3.4)
+* `run-ia64-test-dyn1` (should pass starting with glibc-2.3.x)
This does not mean that libunwind cannot be used with older compilers
or C libraries, it just means that for certain corner cases, unwinding
will fail. Since they're corner cases, it is not likely for
applications to trigger them.
-Note: If you get lots of errors in Gia64-test-nat and Lia64-test-nat, it's
- almost certainly a sign of an old assembler. The GNU assembler used
- to encode previous-stack-pointer-relative offsets incorrectly.
- This bug was fixed on 21-Sep-2004 so any later assembler will be
- fine.
+Note: If you get lots of errors in `Gia64-test-nat` and `Lia64-test-nat`, it's
+almost certainly a sign of an old assembler. The GNU assembler used
+to encode previous-stack-pointer-relative offsets incorrectly.
+This bug was fixed on 21-Sep-2004 so any later assembler will be
+fine.
-** Expected results on x86 Linux
+### Expected results on x86 Linux
The following tests are expected to fail on x86 Linux:
- test-ptrace
+* `test-ptrace`
-** Expected results on x86-64 Linux
+### Expected results on x86-64 Linux
The following tests are expected to fail on x86-64 Linux:
- run-ptrace-misc (see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=18748
- and http://gcc.gnu.org/bugzilla/show_bug.cgi?id=18749)
+* `run-ptrace-misc` (see <http://gcc.gnu.org/bugzilla/show_bug.cgi?id=18748>
+ and <http://gcc.gnu.org/bugzilla/show_bug.cgi?id=18749>)
-** Expected results on PARISC Linux
+### Expected results on PARISC Linux
Caveat: GCC v3.4 or newer is needed on PA-RISC Linux. Earlier
versions of the compiler failed to generate the exception-handling
-program header (GNU_EH_FRAME) needed for unwinding.
+program header (`GNU_EH_FRAME`) needed for unwinding.
The following tests are expected to fail on x86-64 Linux:
- Gtest-bt (backtrace truncated at kill() due to lack of unwind-info)
- Ltest-bt (likewise)
- Gtest-resume-sig (Gresume.c:my_rt_sigreturn() is wrong somehow)
- Ltest-resume-sig (likewise)
- Gtest-init (likewise)
- Ltest-init (likewise)
- Gtest-dyn1 (no dynamic unwind info support yet)
- Ltest-dyn1 (no dynamic unwind info support yet)
- test-setjmp (longjmp() not implemented yet)
- run-check-namespace (toolchain doesn't support HIDDEN yet)
+* `Gtest-bt` (backtrace truncated at `kill()` due to lack of unwind-info)
+* `Ltest-bt` (likewise)
+* `Gtest-resume-sig` (`Gresume.c:my_rt_sigreturn()` is wrong somehow)
+* `Ltest-resume-sig` (likewise)
+* `Gtest-init` (likewise)
+* `Ltest-init` (likewise)
+* `Gtest-dyn1` (no dynamic unwind info support yet)
+* `Ltest-dyn1` (no dynamic unwind info support yet)
+* `test-setjmp` (`longjmp()` not implemented yet)
+* `run-check-namespace` (toolchain doesn't support `HIDDEN` yet)
-** Expected results on HP-UX
+### Expected results on HP-UX
-"make check" is currently unsupported for HP-UX. You can try to run
+`make check` is currently unsupported for HP-UX. You can try to run
it, but most tests will fail (and some may fail to terminate). The
only test programs that are known to work at this time are:
- tests/bt
- tests/Gperf-simple
- tests/test-proc-info
- tests/test-static-link
- tests/Gtest-init
- tests/Ltest-init
- tests/Gtest-resume-sig
- tests/Ltest-resume-sig
+* `tests/bt`
+* `tests/Gperf-simple`
+* `tests/test-proc-info`
+* `tests/test-static-link`
+* `tests/Gtest-init`
+* `tests/Ltest-init`
+* `tests/Gtest-resume-sig`
+* `tests/Ltest-resume-sig`
+
+### Expected results on PPC64 Linux
-** Expected results on PPC64 Linux
+`make check` should run with no more than 10 out of 24 tests failed.
-"make check" should run with no more than 10 out of 24 tests failed.
+### Expected results on Solaris x86-64
+`make check` is passing 27 out of 33 tests. The following six tests are consistently
+failing:
-* Performance Testing
+* `Gtest-concurrent`
+* `Ltest-concurrent`
+* `Ltest-init-local-signal`
+* `Lrs-race`
+* `test-setjmp`
+* `x64-unwind-badjmp-signal-frame`
+
+## Performance Testing
This distribution includes a few simple performance tests which give
some idea of the basic cost of various libunwind operations. After
building the library, you can run these tests with the following
commands:
- $ cd tests
- $ make perf
-
-* Contacting the Developers
-
-Please direct all questions regarding this library to:
-
- libunwind-devel@nongnu.org
-
-You can do this by sending a mail to libunwind-request@nongnu.org with
-a body of:
-
- subscribe libunwind-devel
+ $ cd tests
+ $ make perf
-or you can subscribe and manage your subscription via the
-web-interface at:
+## Contacting the Developers
- https://savannah.nongnu.org/mail/?group=libunwind
+Please direct all questions regarding this library to <libunwind-devel@nongnu.org>.
-Or interact at the gihub page:
+You can do this by sending an email to <libunwind-request@nongnu.org> with
+a body of "subscribe libunwind-devel", or you can subscribe and manage your
+subscription via the web-interface at <https://savannah.nongnu.org/mail/?group=libunwind>.
- https://github.com/libunwind/libunwind
+You can also interact on our GitHub page: <https://github.com/libunwind/libunwind>.
#!/bin/sh
-test -n "$srcdir" || srcdir=`dirname "${BASH_SOURCE[0]}"`
+test -n "$srcdir" || srcdir=`dirname "$0"`
test -n "$srcdir" || srcdir=.
(
cd "$srcdir" &&
define(pkg_major, 1)
-define(pkg_minor, 3)
+define(pkg_minor, 5)
define(pkg_extra, -rc1)
define(pkg_maintainer, libunwind-devel@nongnu.org)
define(mkvers, $1.$2$3)
dnl Checks for library functions.
AC_CHECK_FUNCS(dl_iterate_phdr dl_phdr_removals_counter dlmodinfo getunwind \
- ttrace mincore)
+ ttrace mincore pipe2)
AC_MSG_CHECKING([if building with AltiVec])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
AS_HELP_STRING([--disable-tests],[Disable tests build]),,
[enable_tests=yes])
+AC_ARG_ENABLE(weak-backtrace,
+ AS_HELP_STRING([--disable-weak-backtrace],[Do not provide the weak 'backtrace' symbol.]),,
+ [enable_weak_backtrace=yes])
+
AC_MSG_CHECKING([if we should build libunwind-setjmp])
AC_MSG_RESULT([$enable_setjmp])
AM_CONDITIONAL(ARCH_PPC64, test x$target_arch = xppc64)
AM_CONDITIONAL(ARCH_SH, test x$target_arch = xsh)
AM_CONDITIONAL(ARCH_TILEGX, test x$target_arch = xtilegx)
+AM_CONDITIONAL(ARCH_S390X, test x$target_arch = xs390x)
AM_CONDITIONAL(OS_LINUX, expr x$target_os : xlinux >/dev/null)
AM_CONDITIONAL(OS_HPUX, expr x$target_os : xhpux >/dev/null)
AM_CONDITIONAL(OS_FREEBSD, expr x$target_os : xfreebsd >/dev/null)
AM_CONDITIONAL(OS_QNX, expr x$target_os : xnto-qnx >/dev/null)
+AM_CONDITIONAL(OS_SOLARIS, expr x$target_os : xsolaris >/dev/null)
AC_MSG_CHECKING([for ELF helper width])
case "${target_arch}" in
(arm|hppa|ppc32|x86|sh) use_elf32=yes; AC_MSG_RESULT([32]);;
-(aarch64|ia64|ppc64|x86_64|tilegx) use_elf64=yes; AC_MSG_RESULT([64]);;
+(aarch64|ia64|ppc64|x86_64|s390x|tilegx) use_elf64=yes; AC_MSG_RESULT([64]);;
(mips) use_elfxx=yes; AC_MSG_RESULT([xx]);;
*) AC_MSG_ERROR([Unknown ELF target: ${target_arch}])
esac
AC_SUBST([LIBLZMA])
AM_CONDITIONAL(HAVE_LZMA, test x$enable_minidebuginfo = xyes)
+LIBZ=
+AC_MSG_CHECKING([whether to support ZLIB-compressed symbol tables])
+AC_ARG_ENABLE(zlibdebuginfo,
+AS_HELP_STRING([--enable-zlibdebuginfo], [Enables support for ZLIB-compressed symbol tables]),, [enable_zlibdebuginfo=auto])
+AC_MSG_RESULT([$enable_zlibdebuginfo])
+if test x$enable_zlibdebuginfo != xno; then
+ AC_CHECK_LIB([z], [uncompress],
+ [LIBZ=-lz
+ AC_DEFINE([HAVE_ZLIB], [1], [Define if you have libz])
+ enable_zlibdebuginfo=yes],
+ [if test x$enable_zlibdebuginfo = xyes; then
+ AC_MSG_FAILURE([libz not found])
+ fi])
+fi
+AC_SUBST([LIBZ])
+AM_CONDITIONAL(HAVE_ZLIB, test x$enable_zlibdebuginfo = xyes)
+
AC_MSG_CHECKING([whether to support UNW_CACHE_PER_THREAD])
AC_ARG_ENABLE([per-thread-cache],
AS_HELP_STRING([--enable-per-thread-cache], [build with support for UNW_CACHE_PER_THREAD (which imposes a hight TLS memory usage) (default: disabled)]))
fi
AC_MSG_RESULT([$intel_compiler])
+AC_MSG_CHECKING([if building on Solaris then define __EXTENSIONS__ macro])
+if $OS_SOLARIS; then
+ CFLAGS="${CFLAGS} -D__EXTENSIONS__"
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
+fi
+
AC_MSG_CHECKING([for QCC compiler])
AS_CASE([$CC], [qcc*|QCC*], [qcc_compiler=yes], [qcc_compiler=no])
AC_MSG_RESULT([$qcc_compiler])
AC_CONFIG_FILES(tests/Makefile tests/check-namespace.sh)
fi
+AM_CONDITIONAL([CONFIG_WEAK_BACKTRACE], [test "x$enable_weak_backtrace" = xyes])
+AM_COND_IF([CONFIG_WEAK_BACKTRACE], [
+ AC_DEFINE([CONFIG_WEAK_BACKTRACE], [1], [Define if the weak 'backtrace' symbol is provided.])
+])
+
AC_CONFIG_FILES(Makefile src/Makefile
include/libunwind-common.h
include/libunwind.h include/tdep/libunwind_i.h)
#define dwarf_eh_h
#include "dwarf.h"
+#include "libunwind_i.h"
/* This header file defines the format of a DWARF exception-header
section (.eh_frame_hdr, pointed to by program-header
unw_word_t ip);
extern void dwarf_put_unwind_info (unw_addr_space_t as,
unw_proc_info_t *pi, void *arg);
-extern int dwarf_eval_expr (struct dwarf_cursor *c, unw_word_t *addr,
+extern int dwarf_eval_expr (struct dwarf_cursor *c, unw_word_t stack_val, unw_word_t *addr,
unw_word_t len, unw_word_t *valp,
int *is_register);
extern int
leaving some slack for future expansion. Changing this value will
require recompiling all users of this library. Stack allocation is
relatively cheap and unwind-state copying is relatively rare, so we
- want to err on making it rather too big than too small. */
+ want to err on making it rather too big than too small.
-#define UNW_TDEP_CURSOR_LEN 512
+ Calculation is regs used (64 + 34) * 2 + 40 (bytes of rest of
+ cursor) + padding
+*/
+
+#define UNW_TDEP_CURSOR_LEN 250
typedef uint64_t unw_word_t;
typedef int64_t unw_sword_t;
unw_tdep_save_loc_t;
-/* On AArch64, we can directly use ucontext_t as the unwind context. */
-typedef ucontext_t unw_tdep_context_t;
+/* On AArch64, we can directly use ucontext_t as the unwind context,
+ * however, the __reserved struct is quite large: tune it down to only
+ * the necessary used fields. */
+
+struct unw_sigcontext
+ {
+ uint64_t fault_address;
+ uint64_t regs[31];
+ uint64_t sp;
+ uint64_t pc;
+ uint64_t pstate;
+ uint8_t __reserved[(66 * 8)] __attribute__((__aligned__(16)));
+};
+
+typedef struct
+ {
+ unsigned long uc_flags;
+ struct ucontext *uc_link;
+ stack_t uc_stack;
+ __sigset_t uc_sigmask;
+ struct unw_sigcontext uc_mcontext;
+ } unw_tdep_context_t;
+
+typedef struct
+ {
+ uint32_t _ctx_magic;
+ uint32_t _ctx_size;
+ uint32_t fpsr;
+ uint32_t fpcr;
+ uint64_t vregs[64];
+ } unw_fpsimd_context_t;
+
+
#include "libunwind-common.h"
#include "libunwind-dynamic.h"
#define unw_tdep_getcontext(uc) (({ \
unw_tdep_context_t *unw_ctx = (uc); \
- register uint64_t *unw_base asm ("x0") = (uint64_t*) unw_ctx->uc_mcontext.regs; \
+ register uint64_t *unw_base __asm__ ("x0") = (uint64_t*) unw_ctx->uc_mcontext.regs; \
__asm__ __volatile__ ( \
"stp x0, x1, [%[base], #0]\n" \
"stp x2, x3, [%[base], #16]\n" \
#define UNW_VERSION_CODE(maj,min) (((maj) << 16) | (min))
#define UNW_VERSION UNW_VERSION_CODE(UNW_VERSION_MAJOR, UNW_VERSION_MINOR)
+#ifdef __sun
+// On SmartOS, gcc fails with the following error:
+//
+// ../include/libunwind-common.h:43:41: error: expected identifier or '(' before numeric constant
+// # define UNW_PREFIX UNW_PASTE(UNW_PASTE(_U,UNW_TARGET),_)
+// ^
+//
+// workaround is to undefine _U explicitly.
+// see https://github.com/libunwind/libunwind/issues/118 for more details.
+//
+#undef _U
+#endif
+
#define UNW_PASTE2(x,y) x##y
#define UNW_PASTE(x,y) UNW_PASTE2(x,y)
#define UNW_OBJ(fn) UNW_PASTE(UNW_PREFIX, fn)
#define unw_set_fpreg UNW_OBJ(set_fpreg)
#define unw_get_save_loc UNW_OBJ(get_save_loc)
#define unw_is_signal_frame UNW_OBJ(is_signal_frame)
-#define unw_handle_signal_frame UNW_OBJ(handle_signal_frame)
#define unw_get_proc_name UNW_OBJ(get_proc_name)
#define unw_set_caching_policy UNW_OBJ(set_caching_policy)
#define unw_set_cache_size UNW_OBJ(set_cache_size)
extern int unw_set_fpreg (unw_cursor_t *, int, unw_fpreg_t);
extern int unw_get_save_loc (unw_cursor_t *, int, unw_save_loc_t *);
extern int unw_is_signal_frame (unw_cursor_t *);
-extern int unw_handle_signal_frame (unw_cursor_t *);
extern int unw_get_proc_name (unw_cursor_t *, char *, size_t, unw_word_t *);
extern const char *unw_strerror (int);
extern int unw_backtrace (void **, int);
UNW_MIPS_R30,
UNW_MIPS_R31,
- UNW_MIPS_PC = 34,
+ UNW_MIPS_PC = 64,
/* FIXME: Other registers! */
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (C) 2002-2004 Hewlett-Packard Co
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+ Modified for s390x by Michael Munday <mike.munday@ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#ifndef LIBUNWIND_H
+#define LIBUNWIND_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <ucontext.h>
+
+#define UNW_TARGET s390x
+#define UNW_TARGET_S390X 1
+
+#define _U_TDEP_QP_TRUE 0 /* see libunwind-dynamic.h */
+
+/* This needs to be big enough to accommodate "struct cursor", while
+ leaving some slack for future expansion. Changing this value will
+ require recompiling all users of this library. Stack allocation is
+ relatively cheap and unwind-state copying is relatively rare, so we
+ want to err on making it rather too big than too small. */
+#define UNW_TDEP_CURSOR_LEN 384
+
+typedef uint64_t unw_word_t;
+typedef int64_t unw_sword_t;
+
+typedef double unw_tdep_fpreg_t;
+
+typedef enum
+ {
+ /* general purpose registers */
+ UNW_S390X_R0,
+ UNW_S390X_R1,
+ UNW_S390X_R2,
+ UNW_S390X_R3,
+ UNW_S390X_R4,
+ UNW_S390X_R5,
+ UNW_S390X_R6,
+ UNW_S390X_R7,
+ UNW_S390X_R8,
+ UNW_S390X_R9,
+ UNW_S390X_R10,
+ UNW_S390X_R11,
+ UNW_S390X_R12,
+ UNW_S390X_R13,
+ UNW_S390X_R14,
+ UNW_S390X_R15,
+
+ /* floating point registers */
+ UNW_S390X_F0,
+ UNW_S390X_F1,
+ UNW_S390X_F2,
+ UNW_S390X_F3,
+ UNW_S390X_F4,
+ UNW_S390X_F5,
+ UNW_S390X_F6,
+ UNW_S390X_F7,
+ UNW_S390X_F8,
+ UNW_S390X_F9,
+ UNW_S390X_F10,
+ UNW_S390X_F11,
+ UNW_S390X_F12,
+ UNW_S390X_F13,
+ UNW_S390X_F14,
+ UNW_S390X_F15,
+
+ /* PSW */
+ UNW_S390X_IP,
+
+ UNW_TDEP_LAST_REG = UNW_S390X_IP,
+
+ /* TODO: access, vector registers */
+
+ /* frame info (read-only) */
+ UNW_S390X_CFA,
+
+ UNW_TDEP_IP = UNW_S390X_IP,
+ UNW_TDEP_SP = UNW_S390X_R15,
+
+ /* TODO: placeholders */
+ UNW_TDEP_EH = UNW_S390X_R0,
+ }
+s390x_regnum_t;
+
+#define UNW_TDEP_NUM_EH_REGS 2 /* XXX Not sure what this means */
+
+typedef struct unw_tdep_save_loc
+ {
+ /* Additional target-dependent info on a save location. */
+ char unused;
+ }
+unw_tdep_save_loc_t;
+
+/* On s390x, we can directly use ucontext_t as the unwind context. */
+typedef ucontext_t unw_tdep_context_t;
+
+typedef struct
+ {
+ /* no s390x-specific auxiliary proc-info */
+ char unused;
+ }
+unw_tdep_proc_info_t;
+
+#include "libunwind-dynamic.h"
+#include "libunwind-common.h"
+
+#define unw_tdep_getcontext UNW_ARCH_OBJ(getcontext)
+#define unw_tdep_is_fpreg UNW_ARCH_OBJ(is_fpreg)
+
+extern int unw_tdep_getcontext (unw_tdep_context_t *);
+extern int unw_tdep_is_fpreg (int);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* LIBUNWIND_H */
# include "libunwind-x86_64.h"
#elif defined __tilegx__
# include "libunwind-tilegx.h"
+#elif defined __s390x__
+# include "libunwind-s390x.h"
#else
# error "Unsupported arch"
#endif
}
# define fetch_and_add1(_ptr) AO_fetch_and_add1(_ptr)
# define fetch_and_add(_ptr, value) AO_fetch_and_add(_ptr, value)
+# define atomic_read(ptr) (AO_load(ptr))
/* GCC 3.2.0 on HP-UX crashes on cmpxchg_ptr() */
# if !(defined(__hpux) && __GNUC__ == 3 && __GNUC_MINOR__ == 2)
# define HAVE_CMPXCHG
}
# define fetch_and_add1(_ptr) __sync_fetch_and_add(_ptr, 1)
# define fetch_and_add(_ptr, value) __sync_fetch_and_add(_ptr, value)
+# define atomic_read(ptr) (__atomic_load_n(ptr,__ATOMIC_RELAXED))
# define HAVE_CMPXCHG
# define HAVE_FETCH_AND_ADD
#endif
+
+#ifndef atomic_read
#define atomic_read(ptr) (*(ptr))
+#endif
#define UNWI_OBJ(fn) UNW_PASTE(UNW_PREFIX,UNW_PASTE(I,fn))
#define UNWI_ARCH_OBJ(fn) UNW_PASTE(UNW_PASTE(UNW_PASTE(_UI,UNW_TARGET),_), fn)
#define UNW_ALIGN(x,a) (((x)+(a)-1UL)&~((a)-1UL))
#endif /* libunwind_i_h */
-
/* Return TRUE if the ADDR_SPACE uses big-endian byte-order. */
#define dwarf_is_big_endian(addr_space) ((addr_space)->big_endian)
-/* Return the size of an address, for DWARF purposes. */
-#define dwarf_addr_size(addr_space) ((addr_space)->addr_size)
-
/* Convert a pointer to a dwarf_cursor structure to a pointer to
unw_cursor_t. */
#define dwarf_to_cursor(c) ((unw_cursor_t *) (c))
0, c->as_arg);
else if (c->as->abi == UNW_MIPS_ABI_O32)
return read_s32 (c, DWARF_GET_LOC (loc), val);
+ else if (c->as->abi == UNW_MIPS_ABI_N32) {
+ if (tdep_big_endian(c->as))
+ return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc) + 4, val,
+ 0, c->as_arg);
+ else
+ return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc), val,
+ 0, c->as_arg);
+ }
else
return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc), val,
0, c->as_arg);
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (c) 2003, 2005 Hewlett-Packard Development Company, L.P.
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+ Modified for x86_64 by Max Asbock <masbock@us.ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+/* copy of include/tdep-x86/dwarf-config.h, modified slightly for x86-64
+ some consolidation is possible here */
+
+#ifndef dwarf_config_h
+#define dwarf_config_h
+
+/* derived from DWARF register mappings in Z ELF ABI */
+#define DWARF_NUM_PRESERVED_REGS 66
+#define DWARF_REGNUM_MAP_LENGTH DWARF_NUM_PRESERVED_REGS
+
+/* Return TRUE if the ADDR_SPACE uses big-endian byte-order. */
+#define dwarf_is_big_endian(addr_space) 1
+
+/* Convert a pointer to a dwarf_cursor structure to a pointer to
+ unw_cursor_t. */
+#define dwarf_to_cursor(c) ((unw_cursor_t *) (c))
+
+typedef struct dwarf_loc
+ {
+ unw_word_t val;
+ unw_word_t type; /* see S390X_LOC_TYPE_* macros. */
+ }
+dwarf_loc_t;
+
+#endif /* dwarf_config_h */
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (C) 2004 Hewlett-Packard Co
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#if defined __linux__
+
+/* Use glibc's jump-buffer indices; NPTL peeks at SP: */
+
+#define JB_SP 9 // __gregs[9]
+#define JB_RP 8 // __gregs[8]
+#define JB_MASK_SAVED 18 // __mask_was_saved
+#define JB_MASK 19 // __saved_mask
+
+#endif
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (C) 2002-2005 Hewlett-Packard Co
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+ Modified for x86_64 by Max Asbock <masbock@us.ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#ifndef S390X_LIBUNWIND_I_H
+#define S390X_LIBUNWIND_I_H
+
+/* Target-dependent definitions that are internal to libunwind but need
+ to be shared with target-independent code. */
+
+#include <stdlib.h>
+#include <libunwind.h>
+
+#include "elf64.h"
+#include "mempool.h"
+#include "dwarf.h"
+
+struct unw_addr_space
+ {
+ struct unw_accessors acc;
+ unw_caching_policy_t caching_policy;
+#ifdef HAVE_ATOMIC_OPS_H
+ AO_t cache_generation;
+#else
+ uint32_t cache_generation;
+#endif
+ unw_word_t dyn_generation; /* see dyn-common.h */
+ unw_word_t dyn_info_list_addr; /* (cached) dyn_info_list_addr */
+ struct dwarf_rs_cache global_cache;
+ struct unw_debug_frame_list *debug_frames;
+ };
+
+struct cursor
+ {
+ struct dwarf_cursor dwarf; /* must be first */
+
+ /* Format of sigcontext structure and address at which it is
+ stored: */
+ enum
+ {
+ S390X_SCF_NONE = 0, /* no signal frame encountered */
+ S390X_SCF_LINUX_SIGFRAME = 1, /* Linux struct sigcontext */
+ S390X_SCF_LINUX_RT_SIGFRAME = 2, /* Linux ucontext_t */
+ }
+ sigcontext_format;
+ unw_word_t sigcontext_addr;
+ unw_word_t sigcontext_sp;
+ unw_word_t sigcontext_pc;
+ int validate;
+ ucontext_t *uc;
+ };
+
+static inline ucontext_t *
+dwarf_get_uc(const struct dwarf_cursor *cursor)
+{
+ const struct cursor *c = (struct cursor *) cursor->as_arg;
+ return c->uc;
+}
+
+#define DWARF_GET_LOC(l) ((l).val)
+# define DWARF_LOC_TYPE_MEM (0 << 0)
+# define DWARF_LOC_TYPE_FP (1 << 0)
+# define DWARF_LOC_TYPE_REG (1 << 1)
+# define DWARF_LOC_TYPE_VAL (1 << 2)
+
+# define DWARF_IS_REG_LOC(l) (((l).type & DWARF_LOC_TYPE_REG) != 0)
+# define DWARF_IS_FP_LOC(l) (((l).type & DWARF_LOC_TYPE_FP) != 0)
+# define DWARF_IS_MEM_LOC(l) ((l).type == DWARF_LOC_TYPE_MEM)
+# define DWARF_IS_VAL_LOC(l) (((l).type & DWARF_LOC_TYPE_VAL) != 0)
+
+# define DWARF_LOC(r, t) ((dwarf_loc_t) { .val = (r), .type = (t) })
+# define DWARF_VAL_LOC(c,v) DWARF_LOC ((v), DWARF_LOC_TYPE_VAL)
+# define DWARF_MEM_LOC(c,m) DWARF_LOC ((m), DWARF_LOC_TYPE_MEM)
+
+#ifdef UNW_LOCAL_ONLY
+# define DWARF_NULL_LOC DWARF_LOC (0, 0)
+# define DWARF_IS_NULL_LOC(l) (DWARF_GET_LOC (l) == 0)
+# define DWARF_REG_LOC(c,r) (DWARF_LOC((unw_word_t) \
+ tdep_uc_addr(dwarf_get_uc(c), (r)), 0))
+# define DWARF_FPREG_LOC(c,r) (DWARF_LOC((unw_word_t) \
+ tdep_uc_addr(dwarf_get_uc(c), (r)), 0))
+
+#else /* !UNW_LOCAL_ONLY */
+
+# define DWARF_NULL_LOC DWARF_LOC (0, 0)
+# define DWARF_IS_NULL_LOC(l) \
+ ({ dwarf_loc_t _l = (l); _l.val == 0 && _l.type == 0; })
+# define DWARF_REG_LOC(c,r) DWARF_LOC((r), DWARF_LOC_TYPE_REG)
+# define DWARF_FPREG_LOC(c,r) DWARF_LOC((r), (DWARF_LOC_TYPE_REG \
+ | DWARF_LOC_TYPE_FP))
+
+#endif /* !UNW_LOCAL_ONLY */
+
+static inline int
+dwarf_getfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t *val)
+{
+ assert(sizeof(unw_fpreg_t) == sizeof(unw_word_t));
+
+ if (DWARF_IS_NULL_LOC (loc))
+ return -UNW_EBADREG;
+
+ if (DWARF_IS_FP_LOC (loc))
+ return (*c->as->acc.access_fpreg) (c->as, DWARF_GET_LOC (loc), val,
+ 0, c->as_arg);
+ /* FPRs may be saved in GPRs */
+ if (DWARF_IS_REG_LOC (loc))
+ return (*c->as->acc.access_reg) (c->as, DWARF_GET_LOC (loc), (unw_word_t*)val,
+ 0, c->as_arg);
+ if (DWARF_IS_MEM_LOC (loc))
+ return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc), (unw_word_t*)val,
+ 0, c->as_arg);
+ assert(DWARF_IS_VAL_LOC (loc));
+ *val = *(unw_fpreg_t*) DWARF_GET_LOC (loc);
+ return 0;
+}
+
+static inline int
+dwarf_putfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t val)
+{
+ assert(sizeof(unw_fpreg_t) == sizeof(unw_word_t));
+ assert(!DWARF_IS_VAL_LOC (loc));
+
+ if (DWARF_IS_NULL_LOC (loc))
+ return -UNW_EBADREG;
+
+ if (DWARF_IS_FP_LOC (loc))
+ return (*c->as->acc.access_fpreg) (c->as, DWARF_GET_LOC (loc), &val,
+ 1, c->as_arg);
+ /* FPRs may be saved in GPRs */
+ if (DWARF_IS_REG_LOC (loc))
+ return (*c->as->acc.access_reg) (c->as, DWARF_GET_LOC (loc), (unw_word_t*) &val,
+ 1, c->as_arg);
+
+ assert(DWARF_IS_MEM_LOC (loc));
+ return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc), (unw_word_t*) &val,
+ 1, c->as_arg);
+}
+
+static inline int
+dwarf_get (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t *val)
+{
+ assert(sizeof(unw_fpreg_t) == sizeof(unw_word_t));
+
+ if (DWARF_IS_NULL_LOC (loc))
+ return -UNW_EBADREG;
+
+ if (DWARF_IS_REG_LOC (loc))
+ return (*c->as->acc.access_reg) (c->as, DWARF_GET_LOC (loc), val,
+ 0, c->as_arg);
+ if (DWARF_IS_MEM_LOC (loc))
+ return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc), val,
+ 0, c->as_arg);
+ /* GPRs may be saved in FPRs */
+ if (DWARF_IS_FP_LOC (loc))
+ return (*c->as->acc.access_fpreg) (c->as, DWARF_GET_LOC (loc), (unw_fpreg_t*)val,
+ 0, c->as_arg);
+ assert(DWARF_IS_VAL_LOC (loc));
+ *val = DWARF_GET_LOC (loc);
+ return 0;
+}
+
+static inline int
+dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val)
+{
+ assert(sizeof(unw_fpreg_t) == sizeof(unw_word_t));
+ assert(!DWARF_IS_VAL_LOC (loc));
+
+ if (DWARF_IS_NULL_LOC (loc))
+ return -UNW_EBADREG;
+
+ if (DWARF_IS_REG_LOC (loc))
+ return (*c->as->acc.access_reg) (c->as, DWARF_GET_LOC (loc), &val,
+ 1, c->as_arg);
+ /* GPRs may be saved in FPRs */
+ if (DWARF_IS_FP_LOC (loc))
+ return (*c->as->acc.access_fpreg) (c->as, DWARF_GET_LOC (loc), (unw_fpreg_t*) &val,
+ 1, c->as_arg);
+
+ assert(DWARF_IS_MEM_LOC (loc));
+ return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc), &val,
+ 1, c->as_arg);
+}
+
+#define tdep_getcontext_trace unw_getcontext
+#define tdep_init_done UNW_OBJ(init_done)
+#define tdep_init_mem_validate UNW_OBJ(init_mem_validate)
+#define tdep_init UNW_OBJ(init)
+/* Platforms that support UNW_INFO_FORMAT_TABLE need to define
+ tdep_search_unwind_table. */
+#define tdep_search_unwind_table dwarf_search_unwind_table
+#define tdep_find_unwind_table dwarf_find_unwind_table
+#define tdep_get_elf_image UNW_ARCH_OBJ(get_elf_image)
+#define tdep_get_exe_image_path UNW_ARCH_OBJ(get_exe_image_path)
+#define tdep_access_reg UNW_OBJ(access_reg)
+#define tdep_access_fpreg UNW_OBJ(access_fpreg)
+#define tdep_fetch_frame(c,ip,n) do {} while(0)
+#define tdep_cache_frame(c) 0
+#define tdep_reuse_frame(c,rs) do {} while(0)
+#define tdep_stash_frame(cs,rs) do {} while(0)
+#define tdep_trace(cur,addr,n) (-UNW_ENOINFO)
+#define tdep_uc_addr UNW_OBJ(uc_addr)
+
+#ifdef UNW_LOCAL_ONLY
+# define tdep_find_proc_info(c,ip,n) \
+ dwarf_find_proc_info((c)->as, (ip), &(c)->pi, (n), \
+ (c)->as_arg)
+# define tdep_put_unwind_info(as,pi,arg) \
+ dwarf_put_unwind_info((as), (pi), (arg))
+#else
+# define tdep_find_proc_info(c,ip,n) \
+ (*(c)->as->acc.find_proc_info)((c)->as, (ip), &(c)->pi, (n), \
+ (c)->as_arg)
+# define tdep_put_unwind_info(as,pi,arg) \
+ (*(as)->acc.put_unwind_info)((as), (pi), (arg))
+#endif
+
+#define tdep_get_as(c) ((c)->dwarf.as)
+#define tdep_get_as_arg(c) ((c)->dwarf.as_arg)
+#define tdep_get_ip(c) ((c)->dwarf.ip)
+#define tdep_big_endian(as) 1
+
+extern int tdep_init_done;
+
+extern void tdep_init (void);
+extern void tdep_init_mem_validate (void);
+extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip,
+ unw_dyn_info_t *di, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+extern void *tdep_uc_addr (unw_tdep_context_t *uc, int reg);
+extern int tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip,
+ unsigned long *segbase, unsigned long *mapoff,
+ char *path, size_t pathlen);
+extern void tdep_get_exe_image_path (char *path);
+extern int tdep_access_reg (struct cursor *c, unw_regnum_t reg,
+ unw_word_t *valp, int write);
+extern int tdep_access_fpreg (struct cursor *c, unw_regnum_t reg,
+ unw_fpreg_t *valp, int write);
+
+#endif /* S390X_LIBUNWIND_I_H */
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-#if defined __linux__
+#if defined __linux__ || defined __sun
-/* Use glibc's jump-buffer indices; NPTL peeks at SP: */
+/* Use glibc's jump-buffer indices; NPTL peeks at SP:
+ https://sourceware.org/git/gitweb.cgi?p=glibc.git;a=blob;f=sysdeps/x86_64/jmpbuf-offsets.h;h=ea94a1f90554deecceaf995ca5ee485ae8bffab7;hb=HEAD */
#define JB_SP 6
#define JB_RP 7
X86_64_SCF_LINUX_RT_SIGFRAME, /* Linux ucontext_t */
X86_64_SCF_FREEBSD_SIGFRAME, /* FreeBSD signal frame */
X86_64_SCF_FREEBSD_SYSCALL, /* FreeBSD syscall */
+ X86_64_SCF_SOLARIS_SIGFRAME, /* illumos/Solaris signal frame */
}
sigcontext_format;
unw_word_t sigcontext_addr;
#define tdep_get_ip(c) ((c)->dwarf.ip)
#define tdep_big_endian(as) 0
+#ifdef HAVE_ATOMIC_OPS_H
+extern AO_t tdep_init_done;
+#else
extern int tdep_init_done;
+#endif
extern void tdep_init (void);
extern void tdep_init_mem_validate (void);
# include "tdep-x86_64/libunwind_i.h"
#elif defined __tilegx__
# include "tdep-tilegx/libunwind_i.h"
+#elif defined __s390x__
+# include "tdep-s390x/libunwind_i.h"
#else
# error "Unsupported arch"
#endif
--- /dev/null
+v1.5-rc2
+https://github.com/libunwind/libunwind/releases/tag/v1.5-rc2
add_definitions(-DPACKAGE_BUGREPORT="")
add_definitions(-D_GNU_SOURCE)
-# Ensure that the remote and local unwind code can reside in the same binary without name clashing
-add_definitions("-Ddwarf_search_unwind_table_int=UNW_OBJ(dwarf_search_unwind_table_int)")
# Disable warning due to incorrect format specifier in debugging printf via the Debug macro
add_compile_options(-Wno-format -Wno-format-security)
else()
add_compile_options(-Wno-unused-value)
add_compile_options(-Wno-unused-result)
+ add_compile_options(-Wno-implicit-function-declaration)
+ add_compile_options(-Wno-incompatible-pointer-types)
endif()
if(CLR_CMAKE_HOST_ARCH_ARM)
# Disable warning due to labs function called on unsigned argument
add_compile_options(-Wno-absolute-value)
endif()
+ # Issue https://github.com/libunwind/libunwind/issues/176
+ if (CLR_CMAKE_HOST_ALPINE_LINUX)
+ add_definitions("-D__sigset_t=sigset_t")
+ endif()
# We compile code with -std=c99 and the asm keyword is not recognized as it is a gnu extension
add_definitions(-Dasm=__asm__)
elseif(CLR_CMAKE_HOST_ARCH_I386)
# Nothing
)
+SET(libunwind_la_SOURCES_os_solaris
+ os-solaris.c
+)
+
+SET(libunwind_la_SOURCES_os_solaris_local
+# Nothing
+)
+
if(CLR_CMAKE_HOST_LINUX)
SET(libunwind_la_SOURCES_os ${libunwind_la_SOURCES_os_linux})
SET(libunwind_la_SOURCES_os_local ${libunwind_la_SOURCES_os_linux_local})
SET(libunwind_la_SOURCES_arm_os arm/Gos-freebsd.c)
SET(libunwind_la_SOURCES_arm_os_local arm/Los-freebsd.c)
list(APPEND libunwind_coredump_la_SOURCES coredump/_UCD_access_reg_freebsd.c)
+elseif(CLR_CMAKE_HOST_SUNOS)
+ SET(libunwind_la_SOURCES_os ${libunwind_la_SOURCES_os_solaris})
+ SET(libunwind_la_SOURCES_os_local ${libunwind_la_SOURCES_os_solaris_local})
+ SET(libunwind_la_SOURCES_x86_64_os x86_64/Gos-solaris.c)
+ SET(libunwind_la_SOURCES_x86_64_os_local x86_64/Los-solaris.c)
endif()
# List of arch-independent files needed by both local-only and generic
coredump/_UPT_resume.c
libunwind_coredump_la_LDFLAGS = $(COMMON_SO_LDFLAGS) \
-version-info $(COREDUMP_SO_VERSION)
-libunwind_coredump_la_LIBADD = $(LIBLZMA)
+libunwind_coredump_la_LIBADD = $(LIBLZMA) $(LIBZ)
noinst_HEADERS += coredump/_UCD_internal.h coredump/_UCD_lib.h
### libunwind-setjmp:
libunwind_la_SOURCES_os_qnx = os-qnx.c
+libunwind_la_SOURCES_os_solaris = os-solaris.c
+
libunwind_dwarf_common_la_SOURCES = dwarf/global.c
libunwind_dwarf_local_la_SOURCES = \
libunwind_elf32_la_SOURCES = elf32.c
libunwind_elf64_la_SOURCES = elf64.c
libunwind_elfxx_la_SOURCES = elfxx.c
-libunwind_elf32_la_LIBADD = $(LIBLZMA)
-libunwind_elf64_la_LIBADD = $(LIBLZMA)
-libunwind_elfxx_la_LIBADD = $(LIBLZMA)
+libunwind_elf32_la_LIBADD = $(LIBLZMA) $(LIBZ)
+libunwind_elf64_la_LIBADD = $(LIBLZMA) $(LIBZ)
+libunwind_elfxx_la_LIBADD = $(LIBLZMA) $(LIBZ)
noinst_LTLIBRARIES += $(LIBUNWIND_ELF)
libunwind_la_LIBADD += $(LIBUNWIND_ELF)
sh/Gglobal.c sh/Ginit.c sh/Ginit_local.c sh/Ginit_remote.c \
sh/Gis_signal_frame.c sh/Gregs.c sh/Gresume.c sh/Gstep.c
+# The list of files that go both into libunwind and libunwind-s390x:
+noinst_HEADERS += s390x/init.h s390x/unwind_i.h
+libunwind_la_SOURCES_s390x_common = $(libunwind_la_SOURCES_common) \
+ s390x/is_fpreg.c s390x/regname.c
+
+# The list of files that go into libunwind:
+libunwind_la_SOURCES_s390x = $(libunwind_la_SOURCES_s390x_common) \
+ $(libunwind_la_SOURCES_local) \
+ s390x/Lapply_reg_state.c s390x/Lreg_states_iterate.c \
+ s390x/Lcreate_addr_space.c s390x/Lget_save_loc.c s390x/Lglobal.c \
+ s390x/Linit.c s390x/Linit_local.c s390x/Linit_remote.c \
+ s390x/Lget_proc_info.c s390x/Lregs.c s390x/Lresume.c \
+ s390x/Lis_signal_frame.c s390x/Lstep.c \
+ s390x/getcontext.S s390x/setcontext.S
+
+# The list of files that go into libunwind-s390x:
+libunwind_s390x_la_SOURCES_s390x = $(libunwind_la_SOURCES_s390x_common) \
+ $(libunwind_la_SOURCES_generic) \
+ s390x/Gapply_reg_state.c s390x/Greg_states_iterate.c \
+ s390x/Gcreate_addr_space.c s390x/Gget_save_loc.c s390x/Gglobal.c \
+ s390x/Ginit.c s390x/Ginit_local.c s390x/Ginit_remote.c \
+ s390x/Gget_proc_info.c s390x/Gregs.c s390x/Gresume.c \
+ s390x/Gis_signal_frame.c s390x/Gstep.c
+
if REMOTE_ONLY
install-exec-hook:
# Nothing to do here....
libunwind_coredump_la_SOURCES += coredump/_UCD_access_reg_freebsd.c
endif
+if OS_SOLARIS
+ libunwind_la_SOURCES_os = $(libunwind_la_SOURCES_os_solaris)
+ libunwind_la_SOURCES_x86_64_os = x86_64/Gos-solaris.c
+ libunwind_la_SOURCES_x86_64_os_local = x86_64/Los-solaris.c
+endif
+
if OS_QNX
libunwind_la_SOURCES_os = $(libunwind_la_SOURCES_os_qnx)
libunwind_la_SOURCES_os_local = $(libunwind_la_SOURCES_os_qnx_local)
libunwind_sh_la_LIBADD += libunwind.la -lc
endif
libunwind_setjmp_la_SOURCES += sh/siglongjmp.S
+else
+if ARCH_S390X
+ lib_LTLIBRARIES += libunwind-s390x.la
+ libunwind_la_SOURCES = $(libunwind_la_SOURCES_s390x)
+ libunwind_s390x_la_SOURCES = $(libunwind_s390x_la_SOURCES_s390x)
+ libunwind_s390x_la_LDFLAGS = $(COMMON_SO_LDFLAGS) -version-info $(SOVERSION)
+ libunwind_s390x_la_LIBADD = libunwind-dwarf-generic.la
+ libunwind_s390x_la_LIBADD += libunwind-elf64.la
+if !REMOTE_ONLY
+ libunwind_s390x_la_LIBADD += libunwind.la -lc
+endif
+endif # ARCH_S390X
endif # ARCH_SH
endif # ARCH_PPC64
endif # ARCH_PPC32
libunwind_la_LDFLAGS = $(COMMON_SO_LDFLAGS) -XCClinker -nostdlib \
$(LDFLAGS_STATIC_LIBCXA) -version-info $(SOVERSION)
libunwind_la_LIBADD += -lc $(LIBCRTS)
-libunwind_la_LIBADD += $(LIBLZMA)
+libunwind_la_LIBADD += $(LIBLZMA) $(LIBZ)
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/include/tdep-$(arch) -I.
AM_CCASFLAGS = $(AM_CPPFLAGS)
$(libunwind_la_SOURCES_os_linux) \
$(libunwind_la_SOURCES_os_hpux) \
$(libunwind_la_SOURCES_os_qnx) \
+ $(libunwind_la_SOURCES_os_solaris) \
$(libunwind_la_SOURCES_common) \
$(libunwind_la_SOURCES_local) \
$(libunwind_la_SOURCES_generic) \
unw_addr_space_t unw_local_addr_space = &local_addr_space;
static inline void *
-uc_addr (ucontext_t *uc, int reg)
+uc_addr (unw_tdep_context_t *uc, int reg)
{
if (reg >= UNW_AARCH64_X0 && reg < UNW_AARCH64_V0)
return &uc->uc_mcontext.regs[reg];
# ifdef UNW_LOCAL_ONLY
HIDDEN void *
-tdep_uc_addr (ucontext_t *uc, int reg)
+tdep_uc_addr (unw_tdep_context_t *uc, int reg)
{
return uc_addr (uc, reg);
}
# endif /* UNW_LOCAL_ONLY */
-HIDDEN unw_dyn_info_list_t _U_dyn_info_list;
-
-/* XXX fix me: there is currently no way to locate the dyn-info list
- by a remote unwinder. On ia64, this is done via a special
- unwind-table entry. Perhaps something similar can be done with
- DWARF2 unwind info. */
-
static void
put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
{
get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
void *arg)
{
- *dyn_info_list_addr = (unw_word_t) &_U_dyn_info_list;
+#ifndef UNW_LOCAL_ONLY
+# pragma weak _U_dyn_info_list_addr
+ if (!_U_dyn_info_list_addr)
+ return -UNW_ENOINFO;
+#endif
+ // Access the `_U_dyn_info_list` from `LOCAL_ONLY` library, i.e. libunwind.so.
+ *dyn_info_list_addr = _U_dyn_info_list_addr ();
return 0;
}
void *arg)
{
unw_word_t *addr;
- ucontext_t *uc = arg;
+ unw_tdep_context_t *uc = arg;
if (unw_is_fpreg (reg))
goto badreg;
access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
int write, void *arg)
{
- ucontext_t *uc = arg;
+ unw_tdep_context_t *uc = arg;
unw_fpreg_t *addr;
if (!unw_is_fpreg (reg))
}
int
-unw_init_local2 (unw_cursor_t *cursor, ucontext_t *uc, int flag)
+unw_init_local2 (unw_cursor_t *cursor, unw_tdep_context_t *uc, int flag)
{
if (!flag)
{
char x[sizeof(regs)];
};
- asm volatile (
+ __asm__ __volatile__ (
"mov x4, %0\n"
"mov x5, %1\n"
"ldp x0, x1, [x4]\n"
sc->pc = uc->uc_mcontext.pc;
sc->pstate = uc->uc_mcontext.pstate;
- asm volatile (
+ __asm__ __volatile__ (
"mov sp, %0\n"
"ret %1\n"
: : "r" (c->sigcontext_sp), "r" (c->sigcontext_pc)
} while (0)
#endif
-#define GET_FPCTX(uc) ((struct fpsimd_context *)(&uc->uc_mcontext.__reserved))
+#define GET_FPCTX(uc) ((unw_fpsimd_context_t *)(&uc->uc_mcontext.__reserved))
#endif /* unwind_i_h */
arm_exidx_decode (const uint8_t *buf, uint8_t len, struct dwarf_cursor *c)
{
#define READ_OP() *buf++
- assert(buf != NULL);
- assert(len > 0);
const uint8_t *end = buf + len;
int ret;
struct arm_exbuf_data edata;
+ assert(buf != NULL);
+ assert(len > 0);
+
while (buf < end)
{
uint8_t op = READ_OP ();
# endif /* UNW_LOCAL_ONLY */
-HIDDEN unw_dyn_info_list_t _U_dyn_info_list;
-
-/* XXX fix me: there is currently no way to locate the dyn-info list
- by a remote unwinder. On ia64, this is done via a special
- unwind-table entry. Perhaps something similar can be done with
- DWARF2 unwind info. */
-
static int
get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
void *arg)
{
- *dyn_info_list_addr = (unw_word_t) &_U_dyn_info_list;
+#ifndef UNW_LOCAL_ONLY
+# pragma weak _U_dyn_info_list_addr
+ if (!_U_dyn_info_list_addr)
+ return -UNW_ENOINFO;
+#endif
+ // Access the `_U_dyn_info_list` from `LOCAL_ONLY` library, i.e. libunwind.so.
+ *dyn_info_list_addr = _U_dyn_info_list_addr ();
return 0;
}
char x[sizeof(regs)];
};
- asm __volatile__ (
+ __asm__ __volatile__ (
"ldmia %0, {r4-r12, lr}\n"
"mov sp, r12\n"
"bx lr\n"
/* Set the SP and the PC in order to continue execution at the modified
trampoline which restores the signal mask and the registers. */
- asm __volatile__ (
+ __asm__ __volatile__ (
"mov sp, %0\n"
"bx %1\n"
: : "r" (c->sigcontext_sp), "r" (c->sigcontext_pc)
c->dwarf.loc[UNW_ARM_R15] = DWARF_NULL_LOC;
unw_word_t ip = c->dwarf.ip;
if (c->dwarf.use_prev_instr)
- --ip;
+ /* The least bit denotes thumb/arm mode, clear it. */
+ ip = (ip & ~(unw_word_t)0x1) - 1;
/* check dynamic info first --- it overrides everything else */
ret = unwi_find_dynamic_proc_info (c->dwarf.as, ip, &c->dwarf.pi, 1,
else if (unlikely (ret == -UNW_ESTOPUNWIND))
return ret;
- if (ret < 0 && ret != -UNW_ENOINFO)
- {
- Debug (2, "returning %d\n", ret);
- return ret;
- }
+ if (ret < 0 && ret != -UNW_ENOINFO)
+ {
+ Debug (2, "returning %d\n", ret);
+ return ret;
+ }
}
#endif /* CONFIG_DEBUG_FRAME */
/* Next, try extbl-based unwinding. */
if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
{
+ Debug (13, "%s(ret=%d), trying extbl\n",
+ UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) ? "dwarf_step() failed " : "",
+ ret);
ret = arm_exidx_step (c);
if (ret > 0)
return 1;
{
if (UNW_TRY_METHOD(UNW_ARM_METHOD_FRAME))
{
- Debug (13, "dwarf_step() failed (ret=%d), trying frame-chain\n", ret);
+ Debug (13, "%s%s%s%s(ret=%d), trying frame-chain\n",
+ UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) ? "dwarf_step() " : "",
+ (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) && UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX)) ? "and " : "",
+ UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX) ? "arm_exidx_step() " : "",
+ (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) || UNW_TRY_METHOD(UNW_ARM_METHOD_EXIDX)) ? "failed " : "",
+ ret);
ret = UNW_ESUCCESS;
/* DWARF unwinding failed, try to follow APCS/optimized APCS frame chain */
unw_word_t instr, i;
return -UNW_EINVAL;
}
}
+#elif defined(UNW_TARGET_AARCH64)
+ if (regnum >= UNW_AARCH64_X0 && regnum < UNW_AARCH64_X30) {
+ *valp = ui->prstatus->pr_reg.x[regnum];
+ } else {
+ switch (regnum) {
+ case UNW_AARCH64_SP:
+ *valp = ui->prstatus->pr_reg.sp;
+ break;
+ case UNW_AARCH64_X30:
+ *valp = ui->prstatus->pr_reg.lr;
+ break;
+ case UNW_AARCH64_PC:
+ *valp = ui->prstatus->pr_reg.elr;
+ break;
+ default:
+ Debug(0, "bad regnum:%d\n", regnum);
+ return -UNW_EINVAL;
+ }
+ }
+
#else
#error Port me
#endif
#elif defined(UNW_TARGET_TILEGX)
if (regnum > UNW_TILEGX_CFA)
goto badreg;
+#elif defined(UNW_TARGET_S390X)
+ if (regnum > UNW_S390X_R15)
+ goto badreg;
#else
#if defined(UNW_TARGET_MIPS)
static const uint8_t remap_regs[] =
cur->p_flags
);
if (cur->p_filesz < cur->p_memsz)
- Debug(2, " partial");
+ {
+ Debug(2, " partial");
+ }
if (cur->p_flags & PF_X)
- Debug(2, " executable");
+ {
+ Debug(2, " executable");
+ }
}
Debug(2, "\n");
i++;
phdr->backing_filesize = (uoff_t)statbuf.st_size;
if (phdr->p_flags != (PF_X | PF_R))
- Debug(1, "Note: phdr[%u] is not r-x: flags are 0x%x\n", phdr_no, phdr->p_flags);
+ {
+ Debug(1, "Note: phdr[%u] is not r-x: flags are 0x%x\n",
+ phdr_no, phdr->p_flags);
+ }
if (phdr->backing_filesize > phdr->p_memsz)
{
unsigned long segbase, mapoff;
int ret;
+ /* We're about to map an elf image. If there is an elf image currently mapped,
+ then make sure to unmap it. */
+ invalidate_edi(&ui->edi);
+
/* Used to be tdep_get_elf_image() in ptrace unwinding code */
coredump_phdr_t *cphdr = _UCD_get_elf_image(ui, ip);
if (!cphdr)
#else
+/* XXX fix me: there is currently no way to locate the dyn-info list
+ by a remote unwinder. On ia64, this is done via a special
+ unwind-table entry. Perhaps something similar can be done with
+ DWARF2 unwind info. */
+
static inline int
get_list_addr (unw_addr_space_t as, unw_word_t *dil_addr, void *arg,
int *countp)
[DW_OP_const4s] = OPND1 (VAL32),
[DW_OP_const8u] = OPND1 (VAL64),
[DW_OP_const8s] = OPND1 (VAL64),
+ [DW_OP_constu] = OPND1 (ULEB128),
+ [DW_OP_consts] = OPND1 (SLEB128),
[DW_OP_pick] = OPND1 (VAL8),
[DW_OP_plus_uconst] = OPND1 (ULEB128),
[DW_OP_skip] = OPND1 (VAL16),
}
HIDDEN int
-dwarf_eval_expr (struct dwarf_cursor *c, unw_word_t *addr, unw_word_t len,
- unw_word_t *valp, int *is_register)
+dwarf_eval_expr (struct dwarf_cursor *c, unw_word_t stack_val, unw_word_t *addr,
+ unw_word_t len, unw_word_t *valp, int *is_register)
{
unw_word_t operand1 = 0, operand2 = 0, tmp1, tmp2 = 0, tmp3, end_addr;
uint8_t opcode, operands_signature, u8;
end_addr = *addr + len;
*is_register = 0;
- Debug (14, "len=%lu, pushing cfa=0x%lx\n",
- (unsigned long) len, (unsigned long) c->cfa);
+ Debug (14, "len=%lu, pushing initial value=0x%lx\n",
+ (unsigned long) len, (unsigned long) stack_val);
- push (c->cfa); /* push current CFA as required by DWARF spec */
+ /* The DWARF standard requires the current CFA to be pushed onto the stack */
+ /* before evaluating DW_CFA_expression and DW_CFA_val_expression programs. */
+ /* DW_CFA_def_cfa_expressions do not take an initial value, but we push on */
+ /* a dummy value to keep this logic consistent. */
+ push (stack_val);
while (*addr < end_addr)
{
#include "dwarf-eh.h"
#include "libunwind_i.h"
+#ifdef HAVE_ZLIB
+#include <zlib.h>
+#endif /* HAVE_ZLIB */
+
struct table_entry
{
int32_t start_ip_offset;
#include "os-linux.h"
#endif
+#ifndef __clang__
static ALIAS(dwarf_search_unwind_table) int
dwarf_search_unwind_table_int (unw_addr_space_t as,
unw_word_t ip,
unw_dyn_info_t *di,
unw_proc_info_t *pi,
int need_unwind_info, void *arg);
+#else
+#define dwarf_search_unwind_table_int dwarf_search_unwind_table
+#endif
+
static int
linear_search (unw_addr_space_t as, unw_word_t ip,
unw_word_t eh_frame_start, unw_word_t eh_frame_end,
return 1;
}
- *bufsize = shdr->sh_size;
- *buf = malloc (*bufsize);
-
- memcpy(*buf, shdr->sh_offset + ei.image, *bufsize);
+ if (shdr->sh_flags & SHF_COMPRESSED)
+ {
+ unsigned long destSize;
+ Elf_W (Chdr) *chdr = (shdr->sh_offset + ei.image);
+#ifdef HAVE_ZLIB
+ if (chdr->ch_type == ELFCOMPRESS_ZLIB)
+ {
+ *bufsize = destSize = chdr->ch_size;
+ GET_MEMORY(*buf, *bufsize);
+ ret = uncompress((unsigned char *)*buf, &destSize,
+ shdr->sh_offset + ei.image + sizeof(*chdr),
+ shdr->sh_size - sizeof(*chdr));
+ if (ret != Z_OK)
+ {
+ Debug (2, "failed to decompress zlib .debug_frame, skipping\n");
+ munmap(*buf, *bufsize);
+ munmap(ei.image, ei.size);
+ return 1;
+ }
+
+ Debug (4, "read %zd->%zd bytes of .debug_frame from offset %zd\n",
+ shdr->sh_size, *bufsize, shdr->sh_offset);
+ }
+ else
+#endif /* HAVE_ZLIB */
+ {
+ Debug (2, "unknown compression type %d, skipping\n",
+ chdr->ch_type);
+ munmap(ei.image, ei.size);
+ return 1;
+ }
+ }
+ else
+ {
+ *bufsize = shdr->sh_size;
+ GET_MEMORY(*buf, *bufsize);
- Debug (4, "read %zd bytes of .debug_frame from offset %zd\n",
- *bufsize, shdr->sh_offset);
+ memcpy(*buf, shdr->sh_offset + ei.image, *bufsize);
+ Debug (4, "read %zd bytes of .debug_frame from offset %zd\n",
+ *bufsize, shdr->sh_offset);
+ }
munmap(ei.image, ei.size);
return 0;
}
if (!err)
{
- fdesc = malloc (sizeof (struct unw_debug_frame_list));
+ GET_MEMORY(fdesc, sizeof (struct unw_debug_frame_list));
fdesc->start = start;
fdesc->end = end;
return fdesc;
}
-struct debug_frame_tab
- {
- struct table_entry *tab;
- uint32_t length;
- uint32_t size;
- };
-
-static void
-debug_frame_tab_append (struct debug_frame_tab *tab,
- unw_word_t fde_offset, unw_word_t start_ip)
+static size_t
+debug_frame_index_make (struct unw_debug_frame_list *fdesc)
{
- unsigned int length = tab->length;
+ unw_accessors_t *a = unw_get_accessors_int (unw_local_addr_space);
+ char *buf = fdesc->debug_frame;
+ size_t bufsize = fdesc->debug_frame_size;
+ unw_word_t addr = (unw_word_t) (uintptr_t) buf;
+ size_t count = 0;
- if (length == tab->size)
+ while (addr < (unw_word_t) (uintptr_t) (buf + bufsize))
{
- tab->size *= 2;
- tab->tab = realloc (tab->tab, sizeof (struct table_entry) * tab->size);
- }
+ unw_word_t item_start = addr, item_end = 0;
+ uint32_t u32val = 0;
+ uint64_t cie_id = 0;
+ uint64_t id_for_cie;
+
+ dwarf_readu32 (unw_local_addr_space, a, &addr, &u32val, NULL);
+
+ if (u32val == 0)
+ break;
+
+ if (u32val != 0xffffffff)
+ {
+ uint32_t cie_id32 = 0;
+
+ item_end = addr + u32val;
+ dwarf_readu32 (unw_local_addr_space, a, &addr, &cie_id32, NULL);
+ cie_id = cie_id32;
+ id_for_cie = 0xffffffff;
+ }
+ else
+ {
+ uint64_t u64val = 0;
+
+ /* Extended length. */
+ dwarf_readu64 (unw_local_addr_space, a, &addr, &u64val, NULL);
+ item_end = addr + u64val;
+
+ dwarf_readu64 (unw_local_addr_space, a, &addr, &cie_id, NULL);
+ id_for_cie = 0xffffffffffffffffull;
+ }
+
+ /*Debug (1, "CIE/FDE id = %.8x\n", (int) cie_id);*/
+
+ if (cie_id == id_for_cie)
+ {
+ ;
+ /*Debug (1, "Found CIE at %.8x.\n", item_start);*/
+ }
+ else
+ {
+ unw_word_t fde_addr = item_start;
+ unw_proc_info_t this_pi;
+ int err;
+
+ /*Debug (1, "Found FDE at %.8x\n", item_start);*/
+
+ err = dwarf_extract_proc_info_from_fde (unw_local_addr_space,
+ a, &fde_addr,
+ &this_pi,
+ (uintptr_t) buf, 0, 1,
+ NULL);
+
+ if (!err)
+ {
+ Debug (15, "start_ip = %lx, end_ip = %lx\n",
+ (long) this_pi.start_ip, (long) this_pi.end_ip);
+
+ if (fdesc->index)
+ {
+ struct table_entry *e = &fdesc->index[count];
+
+ e->fde_offset = item_start - (unw_word_t) (uintptr_t) buf;
+ e->start_ip_offset = this_pi.start_ip;
+ }
- tab->tab[length].fde_offset = fde_offset;
- tab->tab[length].start_ip_offset = start_ip;
+ count++;
+ }
+ /*else
+ Debug (1, "FDE parse failed\n");*/
+ }
- tab->length = length + 1;
+ addr = item_end;
+ }
+ return count;
}
static void
-debug_frame_tab_shrink (struct debug_frame_tab *tab)
+debug_frame_index_sort (struct unw_debug_frame_list *fdesc)
{
- if (tab->size > tab->length)
+ size_t i, j, k, n = fdesc->index_size / sizeof (*fdesc->index);
+ struct table_entry *a = fdesc->index;
+ struct table_entry t;
+
+ /* Use a simple Shell sort as it relatively fast and
+ * does not require additional memory. */
+
+ for (k = n / 2; k > 0; k /= 2)
{
- tab->tab = realloc (tab->tab, sizeof (struct table_entry) * tab->length);
- tab->size = tab->length;
- }
-}
+ for (i = k; i < n; i++)
+ {
+ t = a[i];
-static int
-debug_frame_tab_compare (const void *a, const void *b)
-{
- const struct table_entry *fa = a, *fb = b;
+ for (j = i; j >= k; j -= k)
+ {
+ if (t.start_ip_offset >= a[j - k].start_ip_offset)
+ break;
- if (fa->start_ip_offset > fb->start_ip_offset)
- return 1;
- else if (fa->start_ip_offset < fb->start_ip_offset)
- return -1;
- else
- return 0;
+ a[j] = a[j - k];
+ }
+
+ a[j] = t;
+ }
+ }
}
-HIDDEN int
+int
dwarf_find_debug_frame (int found, unw_dyn_info_t *di_debug, unw_word_t ip,
unw_word_t segbase, const char* obj_name,
unw_word_t start, unw_word_t end)
{
- unw_dyn_info_t *di;
- struct unw_debug_frame_list *fdesc = 0;
- unw_accessors_t *a;
- unw_word_t addr;
+ unw_dyn_info_t *di = di_debug;
+ struct unw_debug_frame_list *fdesc;
Debug (15, "Trying to find .debug_frame for %s\n", obj_name);
- di = di_debug;
fdesc = locate_debug_info (unw_local_addr_space, ip, obj_name, start, end);
Debug (15, "couldn't load .debug_frame\n");
return found;
}
- else
+
+ Debug (15, "loaded .debug_frame\n");
+
+ if (fdesc->debug_frame_size == 0)
{
- char *buf;
- size_t bufsize;
- unw_word_t item_start, item_end = 0;
- uint32_t u32val = 0;
- uint64_t cie_id = 0;
- struct debug_frame_tab tab;
+ Debug (15, "zero-length .debug_frame\n");
+ return found;
+ }
+
+ /* Now create a binary-search table, if it does not already exist. */
+
+ if (!fdesc->index)
+ {
+ /* Find all FDE entries in debug_frame, and make into a sorted
+ index. First determine an index element count. */
- Debug (15, "loaded .debug_frame\n");
+ size_t count = debug_frame_index_make (fdesc);
- buf = fdesc->debug_frame;
- bufsize = fdesc->debug_frame_size;
+ if (!count)
+ {
+ Debug (15, "no CIE/FDE found in .debug_frame\n");
+ return found;
+ }
- if (bufsize == 0)
- {
- Debug (15, "zero-length .debug_frame\n");
- return found;
- }
+ fdesc->index_size = count * sizeof (*fdesc->index);
+ GET_MEMORY (fdesc->index, fdesc->index_size);
- /* Now create a binary-search table, if it does not already exist. */
if (!fdesc->index)
- {
- addr = (unw_word_t) (uintptr_t) buf;
-
- a = unw_get_accessors_int (unw_local_addr_space);
-
- /* Find all FDE entries in debug_frame, and make into a sorted
- index. */
-
- tab.length = 0;
- tab.size = 16;
- tab.tab = calloc (tab.size, sizeof (struct table_entry));
-
- while (addr < (unw_word_t) (uintptr_t) (buf + bufsize))
- {
- uint64_t id_for_cie;
- item_start = addr;
-
- dwarf_readu32 (unw_local_addr_space, a, &addr, &u32val, NULL);
-
- if (u32val == 0)
- break;
- else if (u32val != 0xffffffff)
- {
- uint32_t cie_id32 = 0;
- item_end = addr + u32val;
- dwarf_readu32 (unw_local_addr_space, a, &addr, &cie_id32,
- NULL);
- cie_id = cie_id32;
- id_for_cie = 0xffffffff;
- }
- else
- {
- uint64_t u64val = 0;
- /* Extended length. */
- dwarf_readu64 (unw_local_addr_space, a, &addr, &u64val, NULL);
- item_end = addr + u64val;
-
- dwarf_readu64 (unw_local_addr_space, a, &addr, &cie_id, NULL);
- id_for_cie = 0xffffffffffffffffull;
- }
-
- /*Debug (1, "CIE/FDE id = %.8x\n", (int) cie_id);*/
-
- if (cie_id == id_for_cie)
- ;
- /*Debug (1, "Found CIE at %.8x.\n", item_start);*/
- else
- {
- unw_word_t fde_addr = item_start;
- unw_proc_info_t this_pi;
- int err;
-
- /*Debug (1, "Found FDE at %.8x\n", item_start);*/
-
- err = dwarf_extract_proc_info_from_fde (unw_local_addr_space,
- a, &fde_addr,
- &this_pi,
- (uintptr_t) buf, 0, 1,
- NULL);
- if (err == 0)
- {
- Debug (15, "start_ip = %lx, end_ip = %lx\n",
- (long) this_pi.start_ip, (long) this_pi.end_ip);
- debug_frame_tab_append (&tab,
- item_start - (unw_word_t) (uintptr_t) buf,
- this_pi.start_ip);
- }
- /*else
- Debug (1, "FDE parse failed\n");*/
- }
-
- addr = item_end;
- }
-
- debug_frame_tab_shrink (&tab);
- qsort (tab.tab, tab.length, sizeof (struct table_entry),
- debug_frame_tab_compare);
- /* for (i = 0; i < tab.length; i++)
- {
- fprintf (stderr, "ip %x, fde offset %x\n",
- (int) tab.tab[i].start_ip_offset,
- (int) tab.tab[i].fde_offset);
- }*/
- fdesc->index = tab.tab;
- fdesc->index_size = tab.length;
- }
-
- di->format = UNW_INFO_FORMAT_TABLE;
- di->start_ip = fdesc->start;
- di->end_ip = fdesc->end;
- di->u.ti.name_ptr = (unw_word_t) (uintptr_t) obj_name;
- di->u.ti.table_data = (unw_word_t *) fdesc;
- di->u.ti.table_len = sizeof (*fdesc) / sizeof (unw_word_t);
- di->u.ti.segbase = segbase;
-
- found = 1;
- Debug (15, "found debug_frame table `%s': segbase=0x%lx, len=%lu, "
- "gp=0x%lx, table_data=0x%lx\n",
- (char *) (uintptr_t) di->u.ti.name_ptr,
- (long) di->u.ti.segbase, (long) di->u.ti.table_len,
- (long) di->gp, (long) di->u.ti.table_data);
+ {
+ Debug (15, "couldn't allocate a frame index table\n");
+ fdesc->index_size = 0;
+ return found;
+ }
+
+ /* Then fill and sort the index. */
+
+ debug_frame_index_make (fdesc);
+ debug_frame_index_sort (fdesc);
+
+ /*for (i = 0; i < count; i++)
+ {
+ const struct table_entry *e = &fdesc->index[i];
+
+ Debug (15, "ip %x, FDE offset %x\n",
+ e->start_ip_offset, e->fde_offset);
+ }*/
}
+
+ di->format = UNW_INFO_FORMAT_TABLE;
+ di->start_ip = fdesc->start;
+ di->end_ip = fdesc->end;
+ di->u.ti.name_ptr = (unw_word_t) (uintptr_t) obj_name;
+ di->u.ti.table_data = (unw_word_t *) fdesc;
+ di->u.ti.table_len = sizeof (*fdesc) / sizeof (unw_word_t);
+ di->u.ti.segbase = segbase;
+
+ found = 1;
+ Debug (15, "found debug_frame table `%s': segbase=0x%lx, len=%lu, "
+ "gp=0x%lx, table_data=0x%lx\n",
+ (char *) (uintptr_t) di->u.ti.name_ptr,
+ (long) di->u.ti.segbase, (long) di->u.ti.table_len,
+ (long) di->gp, (long) di->u.ti.table_data);
+
return found;
}
}
else if (phdr->p_type == PT_GNU_EH_FRAME)
p_eh_hdr = phdr;
+#if defined __sun
+ else if (phdr->p_type == PT_SUNW_UNWIND)
+ p_eh_hdr = phdr;
+#endif
else if (phdr->p_type == PT_DYNAMIC)
p_dynamic = phdr;
}
/* If there is no search table or it has an unsupported
encoding, fall back on linear search. */
if (hdr->table_enc == DW_EH_PE_omit)
- Debug (4, "table `%s' lacks search table; doing linear search\n",
- info->dlpi_name);
+ {
+ Debug (4, "table `%s' lacks search table; doing linear search\n",
+ info->dlpi_name);
+ }
else
- Debug (4, "table `%s' has encoding 0x%x; doing linear search\n",
- info->dlpi_name, hdr->table_enc);
+ {
+ Debug (4, "table `%s' has encoding 0x%x; doing linear search\n",
+ info->dlpi_name, hdr->table_enc);
+ }
eh_frame_end = max_load_addr; /* XXX can we do better? */
endianness is the target one. */
as = unw_local_addr_space;
table = fdesc->index;
- table_len = fdesc->index_size * sizeof (struct table_entry);
+ table_len = fdesc->index_size;
debug_frame_base = (uintptr_t) fdesc->debug_frame;
#endif
}
break;
case PT_GNU_EH_FRAME:
+#if defined __sun
+ case PT_SUNW_UNWIND:
+#endif
peh_hdr = phdr + i;
break;
continue, and it's important we get this right, as 'ip' could be
right at the function entry and hence FDE edge, or at instruction
that manipulates CFA (push/pop). */
+
if (c->use_prev_instr)
- --ip;
+ {
+#if defined(__arm__)
+ /* On arm, the least bit denotes thumb/arm mode, clear it. */
+ ip &= ~(unw_word_t)0x1;
+#endif
+ --ip;
+ }
memset (&c->pi, 0, sizeof (c->pi));
}
static inline int
-eval_location_expr (struct dwarf_cursor *c, unw_addr_space_t as,
+eval_location_expr (struct dwarf_cursor *c, unw_word_t stack_val, unw_addr_space_t as,
unw_accessors_t *a, unw_word_t addr,
dwarf_loc_t *locp, void *arg)
{
return ret;
/* evaluate the expression: */
- if ((ret = dwarf_eval_expr (c, &addr, len, &val, &is_register)) < 0)
+ if ((ret = dwarf_eval_expr (c, stack_val, &addr, len, &val, &is_register)) < 0)
return ret;
if (is_register)
assert (rs->reg.where[DWARF_CFA_REG_COLUMN] == DWARF_WHERE_EXPR);
addr = rs->reg.val[DWARF_CFA_REG_COLUMN];
- if ((ret = eval_location_expr (c, as, a, addr, &cfa_loc, arg)) < 0)
+ /* The dwarf standard doesn't specify an initial value to be pushed on */
+ /* the stack before DW_CFA_def_cfa_expression evaluation. We push on a */
+ /* dummy value (0) to keep the eval_location_expr function consistent. */
+ if ((ret = eval_location_expr (c, 0, as, a, addr, &cfa_loc, arg)) < 0)
return ret;
/* the returned location better be a memory location... */
if (DWARF_IS_REG_LOC (cfa_loc))
break;
case DWARF_WHERE_REG:
- new_loc[i] = DWARF_REG_LOC (c, dwarf_to_unw_regnum (rs->reg.val[i]));
+#ifdef __s390x__
+ /* GPRs can be saved in FPRs on s390x */
+ if (unw_is_fpreg (dwarf_to_unw_regnum (rs->reg.val[i])))
+ {
+ new_loc[i] = DWARF_FPREG_LOC (c, dwarf_to_unw_regnum (rs->reg.val[i]));
+ break;
+ }
+#endif
+ new_loc[i] = new_loc[rs->reg.val[i]];
break;
case DWARF_WHERE_EXPR:
addr = rs->reg.val[i];
- if ((ret = eval_location_expr (c, as, a, addr, new_loc + i, arg)) < 0)
+ /* The dwarf standard requires the current CFA to be pushed on the */
+ /* stack before DW_CFA_expression evaluation. */
+ if ((ret = eval_location_expr (c, cfa, as, a, addr, new_loc + i, arg)) < 0)
return ret;
break;
case DWARF_WHERE_VAL_EXPR:
addr = rs->reg.val[i];
- if ((ret = eval_location_expr (c, as, a, addr, new_loc + i, arg)) < 0)
+ /* The dwarf standard requires the current CFA to be pushed on the */
+ /* stack before DW_CFA_val_expression evaluation. */
+ if ((ret = eval_location_expr (c, cfa, as, a, addr, new_loc + i, arg)) < 0)
return ret;
new_loc[i] = DWARF_VAL_LOC (c, DWARF_GET_LOC (new_loc[i]));
break;
unsigned short index = -1;
if (cache)
{
- put_rs_cache (c->as, cache, &saved_mask);
if (rs)
{
index = rs - cache->buckets;
cache->links[c->prev_rs].hint = index + 1;
c->prev_rs = index;
}
+ put_rs_cache (c->as, cache, &saved_mask);
}
if (ret < 0)
return ret;
#include <stdio.h>
#include <sys/param.h>
+#include <limits.h>
#ifdef HAVE_LZMA
#include <lzma.h>
# endif /* UNW_LOCAL_ONLY */
-HIDDEN unw_dyn_info_list_t _U_dyn_info_list;
-
-/* XXX fix me: there is currently no way to locate the dyn-info list
- by a remote unwinder. On ia64, this is done via a special
- unwind-table entry. Perhaps something similar can be done with
- DWARF2 unwind info. */
-
static void
put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
{
get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
void *arg)
{
- *dyn_info_list_addr = (unw_word_t) &_U_dyn_info_list;
+#ifndef UNW_LOCAL_ONLY
+# pragma weak _U_dyn_info_list_addr
+ if (!_U_dyn_info_list_addr)
+ return -UNW_ENOINFO;
+#endif
+ // Access the `_U_dyn_info_list` from `LOCAL_ONLY` library, i.e. libunwind.so.
+ *dyn_info_list_addr = _U_dyn_info_list_addr ();
return 0;
}
if (!_U_dyn_info_list_addr)
return -UNW_ENOINFO;
#endif
+ // Access the `_U_dyn_info_list` from `LOCAL_ONLY` library, i.e. libunwind.so.
*dyn_info_list_addr = _U_dyn_info_list_addr ();
return 0;
}
return -UNW_ENOINFO;
#endif
+ // Access the `_U_dyn_info_list` from `LOCAL_ONLY` library, i.e. libunwind.so.
list = (unw_dyn_info_list_t *) (uintptr_t) _U_dyn_info_list_addr ();
for (di = list->first; di; di = di->next)
if (ip >= di->start_ip && ip < di->end_ip)
ip = tdep_get_ip (c);
#if !defined(__ia64__)
if (c->dwarf.use_prev_instr)
- --ip;
+ {
+#if defined(__arm__)
+ /* On arm, the least bit denotes thumb/arm mode, clear it. */
+ ip &= ~(unw_word_t)0x1;
+#endif
+ --ip;
+ }
+
+
#endif
error = get_proc_name (tdep_get_as (c), ip, buf, buf_len, offp,
tdep_get_as_arg (c));
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-#ifndef UNW_REMOTE_ONLY
-
+#if !defined(UNW_REMOTE_ONLY) && !defined(UNW_LOCAL_ONLY)
#define UNW_LOCAL_ONLY
+
#include <libunwind.h>
#include <libunwind_i.h>
#include <string.h>
return n;
}
+#ifdef CONFIG_WEAK_BACKTRACE
extern int backtrace (void **buffer, int size)
WEAK ALIAS(unw_backtrace);
+#endif
#endif /* !UNW_REMOTE_ONLY */
{
#if !UNW_TARGET_IA64
struct unw_debug_frame_list *w = as->debug_frames;
-#endif
- /* clear dyn_info_list_addr cache: */
- as->dyn_info_list_addr = 0;
-
-#if !UNW_TARGET_IA64
- for (; w; w = w->next)
+ while (w)
{
+ struct unw_debug_frame_list *n = w->next;
+
if (w->index)
- free (w->index);
- free (w->debug_frame);
+ munmap (w->index, w->index_size);
+
+ munmap (w->debug_frame, w->debug_frame_size);
+ munmap (w, sizeof (*w));
+ w = n;
}
as->debug_frames = NULL;
#endif
+ /* clear dyn_info_list_addr cache: */
+ as->dyn_info_list_addr = 0;
+
/* This lets us flush caches lazily. The implementation currently
ignores the flush range arguments (lo-hi). This is OK because
unw_flush_cache() is allowed to flush more than the requested
as->big_endian = (byte_order == __BIG_ENDIAN);
/* FIXME! There is no way to specify the ABI. */
+#if _MIPS_SIM == _ABIO32
as->abi = UNW_MIPS_ABI_O32;
- as->addr_size = 4;
+#elif _MIPS_SIM == _ABIN32
+ as->abi = UNW_MIPS_ABI_N32;
+#elif _MIPS_SIM == _ABI64
+ as->abi = UNW_MIPS_ABI_N64;
+#else
+# error Unsupported ABI
+#endif
return as;
#endif
struct cursor *c = (struct cursor *) cursor;
int ret;
- /* We can only unwind using Dwarf into on MIPS: return failure code
- if it's not present. */
ret = dwarf_make_proc_info (&c->dwarf);
- if (ret < 0)
- return ret;
+ if (ret < 0) {
+ /* Construct a dummy proc info if Dwarf failed */
+ memset (pi, 0, sizeof (*pi));
+ pi->start_ip = c->dwarf.ip;
+ pi->end_ip = c->dwarf.ip + 4;
+ return 0;
+ }
*pi = c->dwarf.pi;
return 0;
# endif /* UNW_LOCAL_ONLY */
-HIDDEN unw_dyn_info_list_t _U_dyn_info_list;
-
-/* XXX fix me: there is currently no way to locate the dyn-info list
- by a remote unwinder. On ia64, this is done via a special
- unwind-table entry. Perhaps something similar can be done with
- DWARF2 unwind info. */
-
static void
put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
{
get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
void *arg)
{
- *dyn_info_list_addr = (unw_word_t) (intptr_t) &_U_dyn_info_list;
+#ifndef UNW_LOCAL_ONLY
+# pragma weak _U_dyn_info_list_addr
+ if (!_U_dyn_info_list_addr)
+ return -UNW_ENOINFO;
+#endif
+ // Access the `_U_dyn_info_list` from `LOCAL_ONLY` library, i.e. libunwind.so.
+ *dyn_info_list_addr = _U_dyn_info_list_addr ();
return 0;
}
case UNW_MIPS_R26:
case UNW_MIPS_R27:
case UNW_MIPS_R28:
- case UNW_MIPS_R29:
+
case UNW_MIPS_R30:
case UNW_MIPS_R31:
loc = c->dwarf.loc[reg - UNW_MIPS_R0];
loc = c->dwarf.loc[reg];
break;
+ case UNW_MIPS_R29:
case UNW_MIPS_CFA:
if (write)
return -UNW_EREADONLYREG;
return 1;
}
+
+
+static inline
+int is_valid_fp_val(unw_word_t cfa_val, unw_word_t fp_val)
+{
+ return fp_val > 0 && cfa_val > 0 && fp_val >cfa_val && (fp_val - cfa_val < 0x4000);
+}
+
+static int _step_n64(struct cursor *c)
+{
+ #define FP_REG UNW_MIPS_R30
+ #define SP_REG UNW_MIPS_R29
+ #define RA_REG UNW_MIPS_R31
+
+ //TODO:handle plt entry
+ int ret;
+ unw_word_t current_fp_val = 0;
+ unw_word_t current_ra_val = 0;
+ unw_word_t current_sp_val = 0;
+ struct dwarf_loc up_fp_loc = DWARF_NULL_LOC;
+ struct dwarf_loc up_ra_loc = DWARF_NULL_LOC;
+
+ ret = dwarf_get (&c->dwarf, c->dwarf.loc[SP_REG], ¤t_sp_val);
+ if (ret < 0)
+ {
+ Debug (2, "returning %d [SP=0x%lx]\n", ret,
+ DWARF_GET_LOC (c->dwarf.loc[FP_REG]));
+ return ret;
+ }
+ ret = dwarf_get (&c->dwarf, c->dwarf.loc[FP_REG], ¤t_fp_val);
+ if (ret < 0)
+ {
+ Debug (2, "returning %d [FP=0x%lx]\n", ret,
+ DWARF_GET_LOC (c->dwarf.loc[FP_REG]));
+ return ret;
+ }
+ ret = dwarf_get (&c->dwarf, c->dwarf.loc[RA_REG], ¤t_ra_val);
+ if (ret < 0)
+ {
+ Debug (2, "returning %d [RA=0x%lx]\n", ret,
+ DWARF_GET_LOC (c->dwarf.loc[RA_REG]));
+ return ret;
+ }
+
+ Debug(2, "BEGIN GUESSING WITH SP:%p FP:%p CFA:%p at %p, RA:%p\n",
+ current_sp_val, current_fp_val, c->dwarf.cfa,
+ c->dwarf.ip, current_ra_val
+ );
+
+ if (current_fp_val == current_sp_val) {
+ // Don't adjust FP
+ up_fp_loc = c->dwarf.loc[FP_REG];
+ up_ra_loc = c->dwarf.loc[RA_REG];
+ } else if (is_valid_fp_val(c->dwarf.cfa, current_fp_val)) {
+ /* Heuristic to determine incorrect guess. For FP to be a
+ valid frame it needs to be above current CFA, but don't
+ let it go more than a little. Note that we can't deduce
+ anything about new FP (fp1) since it may not be a frame
+ pointer in the frame above. Just check we get the value. */
+ up_fp_loc = DWARF_MEM_LOC (c, current_fp_val+16);
+ up_ra_loc = DWARF_MEM_LOC (c, current_fp_val+24);
+ unw_word_t up_fp_val = 0;
+ ret = dwarf_get (&c->dwarf, up_fp_loc, &up_fp_val);
+ if (ret > 0 && is_valid_fp_val(current_fp_val, up_fp_val)) {
+ c->dwarf.loc[FP_REG] = up_fp_loc;
+ }
+ }
+
+ if (DWARF_IS_NULL_LOC (up_fp_loc))
+ {
+ ret = 0;
+ Debug (2, "NULL %%fp loc, returning %d\n", ret);
+ return ret;
+ }
+
+ c->dwarf.loc[UNW_MIPS_PC] = c->dwarf.loc[RA_REG];
+ c->dwarf.loc[RA_REG] = up_ra_loc;
+ c->dwarf.loc[SP_REG] = up_fp_loc;
+ c->dwarf.loc[FP_REG] = up_fp_loc;
+ c->dwarf.use_prev_instr = 1;
+
+ if (c->dwarf.ip == current_ra_val && current_fp_val == current_sp_val) {
+ // Backtrace stopped: frame did not save the PC
+ c->dwarf.ip = 0;
+ } else {
+ c->dwarf.ip = current_ra_val;
+ }
+ return (c->dwarf.ip == 0) ? 0 : 1;
+}
+
int
unw_step (unw_cursor_t *cursor)
{
if (unlikely (ret == -UNW_ESTOPUNWIND))
return ret;
- /* Dwarf unwinding didn't work, stop. */
+#if _MIPS_SIM == _ABI64
if (unlikely (ret < 0))
- return 0;
-
+ {
+ return _step_n64(c);
+ }
+#endif
return (c->dwarf.ip == 0) ? 0 : 1;
}
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (C) 2003-2005 Hewlett-Packard Co
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include <limits.h>
+#include <stdio.h>
+
+#include "libunwind_i.h"
+#include "os-linux.h" // using linux header for map_iterator implementation
+
+int
+tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip,
+ unsigned long *segbase, unsigned long *mapoff,
+ char *path, size_t pathlen)
+{
+ struct map_iterator mi;
+ int found = 0, rc;
+ unsigned long hi;
+
+ if (maps_init (&mi, pid) < 0)
+ return -1;
+
+ while (maps_next (&mi, segbase, &hi, mapoff))
+ if (ip >= *segbase && ip < hi)
+ {
+ found = 1;
+ break;
+ }
+
+ if (!found)
+ {
+ maps_close (&mi);
+ return -1;
+ }
+ if (path)
+ {
+ strncpy(path, mi.path, pathlen);
+ }
+ rc = elf_map_image (ei, mi.path);
+ maps_close (&mi);
+ return rc;
+}
+
+#ifndef UNW_REMOTE_ONLY
+
+void
+tdep_get_exe_image_path (char *path)
+{
+ strcpy(path, getexecname());
+}
+
+#endif /* !UNW_REMOTE_ONLY */
# endif /* UNW_LOCAL_ONLY */
-HIDDEN unw_dyn_info_list_t _U_dyn_info_list;
-
-
static void
put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
{
get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
void *arg)
{
- *dyn_info_list_addr = (unw_word_t) &_U_dyn_info_list;
+#ifndef UNW_LOCAL_ONLY
+# pragma weak _U_dyn_info_list_addr
+ if (!_U_dyn_info_list_addr)
+ return -UNW_ENOINFO;
+#endif
+ // Access the `_U_dyn_info_list` from `LOCAL_ONLY` library, i.e. libunwind.so.
+ *dyn_info_list_addr = _U_dyn_info_list_addr ();
return 0;
}
# endif /* UNW_LOCAL_ONLY */
-HIDDEN unw_dyn_info_list_t _U_dyn_info_list;
-
-
static void
put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
{
get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
void *arg)
{
- *dyn_info_list_addr = (unw_word_t) &_U_dyn_info_list;
+#ifndef UNW_LOCAL_ONLY
+# pragma weak _U_dyn_info_list_addr
+ if (!_U_dyn_info_list_addr)
+ return -UNW_ENOINFO;
+#endif
+ // Access the `_U_dyn_info_list` from `LOCAL_ONLY` library, i.e. libunwind.so.
+ *dyn_info_list_addr = _U_dyn_info_list_addr ();
return 0;
}
#elif defined(__arm__)
if ((unsigned) reg < UNW_ARM_F0 || (unsigned) reg > UNW_ARM_F7)
return -UNW_EBADREG;
+#elif defined(__aarch64__)
+ if ((unsigned) reg < UNW_AARCH64_V0 || (unsigned) reg > UNW_AARCH64_V31)
+ return -UNW_EBADREG;
#else
#error Fix me
#endif
memcpy(&fpreg.fpr_acc[reg], val, sizeof(unw_fpreg_t));
#elif defined(__arm__)
memcpy(&fpreg.fpr[reg], val, sizeof(unw_fpreg_t));
+#elif defined(__aarch64__)
+ memcpy(&fpreg.fp_q[reg], val, sizeof(unw_fpreg_t));
#else
#error Fix me
#endif
memcpy(val, &fpreg.fpr_acc[reg], sizeof(unw_fpreg_t));
#elif defined(__arm__)
memcpy(val, &fpreg.fpr[reg], sizeof(unw_fpreg_t));
+#elif defined(__aarch64__)
+ memcpy(val, &fpreg.fp_q[reg], sizeof(unw_fpreg_t));
#else
#error Fix me
#endif
#else
+/* XXX fix me: there is currently no way to locate the dyn-info list
+ by a remote unwinder. On ia64, this is done via a special
+ unwind-table entry. Perhaps something similar can be done with
+ DWARF2 unwind info. */
+
static inline int
get_list_addr (unw_addr_space_t as, unw_word_t *dil_addr, void *arg,
int *countp)
[UNW_TILEGX_R54] = 0x1b0,
[UNW_TILEGX_R55] = 0x1b8,
[UNW_TILEGX_PC] = 0x1a0
+#elif defined(UNW_TARGET_S390X)
+ [UNW_S390X_R0] = 0x10,
+ [UNW_S390X_R1] = 0x18,
+ [UNW_S390X_R2] = 0x20,
+ [UNW_S390X_R3] = 0x28,
+ [UNW_S390X_R4] = 0x30,
+ [UNW_S390X_R5] = 0x38,
+ [UNW_S390X_R6] = 0x40,
+ [UNW_S390X_R7] = 0x48,
+ [UNW_S390X_R8] = 0x50,
+ [UNW_S390X_R9] = 0x58,
+ [UNW_S390X_R10] = 0x60,
+ [UNW_S390X_R11] = 0x68,
+ [UNW_S390X_R12] = 0x70,
+ [UNW_S390X_R13] = 0x78,
+ [UNW_S390X_R14] = 0x80,
+ [UNW_S390X_R15] = 0x88,
+ [UNW_S390X_F0] = 0xe0,
+ [UNW_S390X_F1] = 0xe8,
+ [UNW_S390X_F2] = 0xf0,
+ [UNW_S390X_F3] = 0xf8,
+ [UNW_S390X_F4] = 0x100,
+ [UNW_S390X_F5] = 0x108,
+ [UNW_S390X_F6] = 0x110,
+ [UNW_S390X_F7] = 0x118,
+ [UNW_S390X_F8] = 0x120,
+ [UNW_S390X_F9] = 0x128,
+ [UNW_S390X_F10] = 0x130,
+ [UNW_S390X_F11] = 0x138,
+ [UNW_S390X_F12] = 0x140,
+ [UNW_S390X_F13] = 0x148,
+ [UNW_S390X_F14] = 0x150,
+ [UNW_S390X_F15] = 0x150,
+ [UNW_S390X_IP] = 0x08
#else
# error Fix me.
#endif
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (c) 2002-2003 Hewlett-Packard Development Company, L.P.
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+ Modified for x86_64 by Max Asbock <masbock@us.ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include "unwind_i.h"
+
+int
+unw_apply_reg_state (unw_cursor_t *cursor,
+ void *reg_states_data)
+{
+ struct cursor *c = (struct cursor *) cursor;
+
+ return dwarf_apply_reg_state (&c->dwarf, (dwarf_reg_state_t *)reg_states_data);
+}
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (C) 2003 Hewlett-Packard Co
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+ Modified for x86_64 by Max Asbock <masbock@us.ibm.com>
+ Copyright (C) 2012 Tommi Rantala <tt.rantala@gmail.com>
+ Modified for s390x by Michael Munday <mike.munday@ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include <stdlib.h>
+
+#include "unwind_i.h"
+
+#if defined(_BIG_ENDIAN) && !defined(__BIG_ENDIAN)
+#define __BIG_ENDIAN _BIG_ENDIAN
+#endif
+
+unw_addr_space_t
+unw_create_addr_space (unw_accessors_t *a, int byte_order)
+{
+#ifdef UNW_LOCAL_ONLY
+ return NULL;
+#else
+ unw_addr_space_t as;
+
+ /*
+ * s390x supports only big-endian.
+ */
+ if (byte_order != 0 && byte_order != __BIG_ENDIAN)
+ return NULL;
+
+ as = malloc (sizeof (*as));
+ if (!as)
+ return NULL;
+
+ memset (as, 0, sizeof (*as));
+
+ as->acc = *a;
+
+ return as;
+#endif
+}
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (c) 2002-2003 Hewlett-Packard Development Company, L.P.
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+ Modified for x86_64 by Max Asbock <masbock@us.ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include "unwind_i.h"
+
+int
+unw_get_proc_info (unw_cursor_t *cursor, unw_proc_info_t *pi)
+{
+ struct cursor *c = (struct cursor *) cursor;
+
+ if (dwarf_make_proc_info (&c->dwarf) < 0)
+ {
+ /* On x86-64, some key routines such as _start() and _dl_start()
+ are missing DWARF unwind info. We don't want to fail in that
+ case, because those frames are uninteresting and just mark
+ the end of the frame-chain anyhow. */
+ memset (pi, 0, sizeof (*pi));
+ pi->start_ip = c->dwarf.ip;
+ pi->end_ip = c->dwarf.ip + 1;
+ return 0;
+ }
+ *pi = c->dwarf.pi;
+ return 0;
+}
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (C) 2004 Hewlett-Packard Co
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+ Modified for x86_64 by Max Asbock <masbock@us.ibm.com>
+ Modified for s390x by Michael Munday <mike.munday@ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include "unwind_i.h"
+
+int
+unw_get_save_loc (unw_cursor_t *cursor, int reg, unw_save_loc_t *sloc)
+{
+ struct cursor *c = (struct cursor *) cursor;
+ dwarf_loc_t loc;
+
+ loc = DWARF_NULL_LOC; /* default to "not saved" */
+
+ switch (reg)
+ {
+ case UNW_S390X_R6:
+ case UNW_S390X_R7:
+ case UNW_S390X_R8:
+ case UNW_S390X_R9:
+ case UNW_S390X_R10:
+ case UNW_S390X_R11:
+ case UNW_S390X_R12:
+ case UNW_S390X_R13:
+ case UNW_S390X_R15:
+ case UNW_S390X_F8:
+ case UNW_S390X_F9:
+ case UNW_S390X_F10:
+ case UNW_S390X_F11:
+ case UNW_S390X_F12:
+ case UNW_S390X_F13:
+ case UNW_S390X_F14:
+ case UNW_S390X_F15:
+ loc = c->dwarf.loc[reg];
+ break;
+
+ default:
+ break;
+ }
+
+ memset (sloc, 0, sizeof (*sloc));
+
+ if (DWARF_IS_NULL_LOC (loc))
+ {
+ sloc->type = UNW_SLT_NONE;
+ return 0;
+ }
+
+#if !defined(UNW_LOCAL_ONLY)
+ if (DWARF_IS_REG_LOC (loc))
+ {
+ sloc->type = UNW_SLT_REG;
+ sloc->u.regnum = DWARF_GET_LOC (loc);
+ }
+ else
+#endif
+ {
+ sloc->type = UNW_SLT_MEMORY;
+ sloc->u.addr = DWARF_GET_LOC (loc);
+ }
+ return 0;
+}
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (c) 2003, 2005 Hewlett-Packard Development Company, L.P.
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+ Modified for x86_64 by Max Asbock <masbock@us.ibm.com>
+ Modified for s390x by Michael Munday <mike.munday@ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include "config.h"
+#include "unwind_i.h"
+#include "dwarf_i.h"
+
+HIDDEN define_lock (s390x_lock);
+HIDDEN int tdep_init_done;
+
+/* The API register numbers are exactly the same as the .eh_frame
+ registers, for now at least. */
+HIDDEN const uint8_t dwarf_to_unw_regnum_map[DWARF_NUM_PRESERVED_REGS] =
+ {
+ UNW_S390X_R0,
+ UNW_S390X_R1,
+ UNW_S390X_R2,
+ UNW_S390X_R3,
+ UNW_S390X_R4,
+ UNW_S390X_R5,
+ UNW_S390X_R6,
+ UNW_S390X_R7,
+ UNW_S390X_R8,
+ UNW_S390X_R9,
+ UNW_S390X_R10,
+ UNW_S390X_R11,
+ UNW_S390X_R12,
+ UNW_S390X_R13,
+ UNW_S390X_R14,
+ UNW_S390X_R15,
+
+ UNW_S390X_F0,
+ UNW_S390X_F2,
+ UNW_S390X_F4,
+ UNW_S390X_F6,
+ UNW_S390X_F1,
+ UNW_S390X_F3,
+ UNW_S390X_F5,
+ UNW_S390X_F7,
+ UNW_S390X_F8,
+ UNW_S390X_F10,
+ UNW_S390X_F12,
+ UNW_S390X_F14,
+ UNW_S390X_F9,
+ UNW_S390X_F11,
+ UNW_S390X_F13,
+ UNW_S390X_F15,
+ };
+
+HIDDEN void
+tdep_init (void)
+{
+ intrmask_t saved_mask;
+
+ sigfillset (&unwi_full_mask);
+
+ lock_acquire (&s390x_lock, saved_mask);
+ {
+ if (tdep_init_done)
+ /* another thread else beat us to it... */
+ goto out;
+
+ mi_init ();
+
+ dwarf_init ();
+
+ tdep_init_mem_validate ();
+
+#ifndef UNW_REMOTE_ONLY
+ s390x_local_addr_space_init ();
+#endif
+ tdep_init_done = 1; /* signal that we're initialized... */
+ }
+ out:
+ lock_release (&s390x_lock, saved_mask);
+}
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (C) 2002 Hewlett-Packard Co
+ Copyright (C) 2007 David Mosberger-Tang
+ Contributed by David Mosberger-Tang <dmosberger@gmail.com>
+
+ Modified for x86_64 by Max Asbock <masbock@us.ibm.com>
+ Modified for s390x by Michael Munday <mike.munday@ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+
+#include "unwind_i.h"
+
+#ifdef UNW_REMOTE_ONLY
+
+/* unw_local_addr_space is a NULL pointer in this case. */
+unw_addr_space_t unw_local_addr_space;
+
+#else /* !UNW_REMOTE_ONLY */
+
+static struct unw_addr_space local_addr_space;
+
+unw_addr_space_t unw_local_addr_space = &local_addr_space;
+
+static inline void *
+uc_addr (ucontext_t *uc, int reg)
+{
+ if (reg >= UNW_S390X_R0 && reg <= UNW_S390X_R15)
+ return &uc->uc_mcontext.gregs[reg - UNW_S390X_R0];
+ if (reg >= UNW_S390X_F0 && reg <= UNW_S390X_F15)
+ return &uc->uc_mcontext.fpregs.fprs[reg - UNW_S390X_F0];
+ if (reg == UNW_S390X_IP)
+ return &uc->uc_mcontext.psw.addr;
+
+ return NULL;
+}
+
+# ifdef UNW_LOCAL_ONLY
+
+HIDDEN void *
+tdep_uc_addr (ucontext_t *uc, int reg)
+{
+ return uc_addr (uc, reg);
+}
+
+# endif /* UNW_LOCAL_ONLY */
+
+static void
+put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
+{
+ /* it's a no-op */
+}
+
+static int
+get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
+ void *arg)
+{
+#ifndef UNW_LOCAL_ONLY
+# pragma weak _U_dyn_info_list_addr
+ if (!_U_dyn_info_list_addr)
+ return -UNW_ENOINFO;
+#endif
+ // Access the `_U_dyn_info_list` from `LOCAL_ONLY` library, i.e. libunwind.so.
+ *dyn_info_list_addr = _U_dyn_info_list_addr ();
+ return 0;
+}
+
+#define PAGE_SIZE 4096
+#define PAGE_START(a) ((a) & ~(PAGE_SIZE-1))
+
+static int mem_validate_pipe[2] = {-1, -1};
+
+static inline void
+open_pipe (void)
+{
+ /* ignore errors for closing invalid fd's */
+ close (mem_validate_pipe[0]);
+ close (mem_validate_pipe[1]);
+
+ pipe2 (mem_validate_pipe, O_CLOEXEC | O_NONBLOCK);
+}
+
+ALWAYS_INLINE
+static int
+write_validate (void *addr)
+{
+ int ret = -1;
+ ssize_t bytes = 0;
+
+ do
+ {
+ char buf;
+ bytes = read (mem_validate_pipe[0], &buf, 1);
+ }
+ while ( errno == EINTR );
+
+ int valid_read = (bytes > 0 || errno == EAGAIN || errno == EWOULDBLOCK);
+ if (!valid_read)
+ {
+ // re-open closed pipe
+ open_pipe ();
+ }
+
+ do
+ {
+ /* use syscall insteadof write() so that ASAN does not complain */
+ ret = syscall (SYS_write, mem_validate_pipe[1], addr, 1);
+ }
+ while ( errno == EINTR );
+
+ return ret;
+}
+
+static int (*mem_validate_func) (void *addr, size_t len);
+static int msync_validate (void *addr, size_t len)
+{
+ if (msync (addr, len, MS_ASYNC) != 0)
+ {
+ return -1;
+ }
+
+ return write_validate (addr);
+}
+
+#ifdef HAVE_MINCORE
+static int mincore_validate (void *addr, size_t len)
+{
+ unsigned char mvec[2]; /* Unaligned access may cross page boundary */
+ size_t i;
+
+ /* mincore could fail with EAGAIN but we conservatively return -1
+ instead of looping. */
+ if (mincore (addr, len, mvec) != 0)
+ {
+ return -1;
+ }
+
+ for (i = 0; i < (len + PAGE_SIZE - 1) / PAGE_SIZE; i++)
+ {
+ if (!(mvec[i] & 1)) return -1;
+ }
+
+ return write_validate (addr);
+}
+#endif
+
+/* Initialise memory validation method. On linux kernels <2.6.21,
+ mincore() returns incorrect value for MAP_PRIVATE mappings,
+ such as stacks. If mincore() was available at compile time,
+ check if we can actually use it. If not, use msync() instead. */
+HIDDEN void
+tdep_init_mem_validate (void)
+{
+ open_pipe ();
+
+#ifdef HAVE_MINCORE
+ unsigned char present = 1;
+ unw_word_t addr = PAGE_START((unw_word_t)&present);
+ unsigned char mvec[1];
+ int ret;
+ while ((ret = mincore ((void*)addr, PAGE_SIZE, mvec)) == -1 &&
+ errno == EAGAIN) {}
+ if (ret == 0 && (mvec[0] & 1))
+ {
+ Debug(1, "using mincore to validate memory\n");
+ mem_validate_func = mincore_validate;
+ }
+ else
+#endif
+ {
+ Debug(1, "using msync to validate memory\n");
+ mem_validate_func = msync_validate;
+ }
+}
+
+/* Cache of already validated addresses */
+#define NLGA 4
+static unw_word_t last_good_addr[NLGA];
+static int lga_victim;
+
+static int
+validate_mem (unw_word_t addr)
+{
+ int i, victim;
+ size_t len;
+
+ if (PAGE_START(addr + sizeof (unw_word_t) - 1) == PAGE_START(addr))
+ len = PAGE_SIZE;
+ else
+ len = PAGE_SIZE * 2;
+
+ addr = PAGE_START(addr);
+
+ if (addr == 0)
+ return -1;
+
+ for (i = 0; i < NLGA; i++)
+ {
+ if (last_good_addr[i] && (addr == last_good_addr[i]))
+ return 0;
+ }
+
+ if (mem_validate_func ((void *) addr, len) == -1)
+ return -1;
+
+ victim = lga_victim;
+ for (i = 0; i < NLGA; i++) {
+ if (!last_good_addr[victim]) {
+ last_good_addr[victim++] = addr;
+ return 0;
+ }
+ victim = (victim + 1) % NLGA;
+ }
+
+ /* All slots full. Evict the victim. */
+ last_good_addr[victim] = addr;
+ victim = (victim + 1) % NLGA;
+ lga_victim = victim;
+
+ return 0;
+}
+
+static int
+access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
+ void *arg)
+{
+ if (unlikely (write))
+ {
+ Debug (16, "mem[%016lx] <- %lx\n", addr, *val);
+ *(unw_word_t *) addr = *val;
+ }
+ else
+ {
+ /* validate address */
+ const struct cursor *c = (const struct cursor *)arg;
+ if (likely (c != NULL) && unlikely (c->validate)
+ && unlikely (validate_mem (addr))) {
+ Debug (16, "mem[%016lx] -> invalid\n", addr);
+ return -1;
+ }
+ *val = *(unw_word_t *) addr;
+ Debug (16, "mem[%016lx] -> %lx\n", addr, *val);
+ }
+ return 0;
+}
+
+static int
+access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
+ void *arg)
+{
+ unw_word_t *addr;
+ ucontext_t *uc = ((struct cursor *)arg)->uc;
+
+ if (unw_is_fpreg (reg))
+ goto badreg;
+
+ if (!(addr = uc_addr (uc, reg)))
+ goto badreg;
+
+ if (write)
+ {
+ *(unw_word_t *) addr = *val;
+ Debug (12, "%s <- 0x%016lx\n", unw_regname (reg), *val);
+ }
+ else
+ {
+ *val = *(unw_word_t *) addr;
+ Debug (12, "%s -> 0x%016lx\n", unw_regname (reg), *val);
+ }
+ return 0;
+
+ badreg:
+ Debug (1, "bad register number %u\n", reg);
+ return -UNW_EBADREG;
+}
+
+static int
+access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
+ int write, void *arg)
+{
+ ucontext_t *uc = ((struct cursor *)arg)->uc;
+ unw_fpreg_t *addr;
+
+ if (!unw_is_fpreg (reg))
+ goto badreg;
+
+ if (!(addr = uc_addr (uc, reg)))
+ goto badreg;
+
+ if (write)
+ {
+ Debug (12, "%s <- %08lx.%08lx.%08lx\n", unw_regname (reg),
+ ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
+ *(unw_fpreg_t *) addr = *val;
+ }
+ else
+ {
+ *val = *(unw_fpreg_t *) addr;
+ Debug (12, "%s -> %08lx.%08lx.%08lx\n", unw_regname (reg),
+ ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]);
+ }
+ return 0;
+
+ badreg:
+ Debug (1, "bad register number %u\n", reg);
+ /* attempt to access a non-preserved register */
+ return -UNW_EBADREG;
+}
+
+static int
+get_static_proc_name (unw_addr_space_t as, unw_word_t ip,
+ char *buf, size_t buf_len, unw_word_t *offp,
+ void *arg)
+{
+ return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
+}
+
+HIDDEN void
+s390x_local_addr_space_init (void)
+{
+ memset (&local_addr_space, 0, sizeof (local_addr_space));
+ local_addr_space.caching_policy = UNW_CACHE_GLOBAL;
+ local_addr_space.acc.find_proc_info = dwarf_find_proc_info;
+ local_addr_space.acc.put_unwind_info = put_unwind_info;
+ local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
+ local_addr_space.acc.access_mem = access_mem;
+ local_addr_space.acc.access_reg = access_reg;
+ local_addr_space.acc.access_fpreg = access_fpreg;
+ local_addr_space.acc.resume = s390x_local_resume;
+ local_addr_space.acc.get_proc_name = get_static_proc_name;
+ unw_flush_cache (&local_addr_space, 0, 0);
+
+ memset (last_good_addr, 0, sizeof (unw_word_t) * NLGA);
+ lga_victim = 0;
+}
+
+#endif /* !UNW_REMOTE_ONLY */
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (c) 2002-2003 Hewlett-Packard Development Company, L.P.
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+ Modified for x86_64 by Max Asbock <masbock@us.ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include "unwind_i.h"
+#include "init.h"
+
+#ifdef UNW_REMOTE_ONLY
+
+int
+unw_init_local (unw_cursor_t *cursor, ucontext_t *uc)
+{
+ return -UNW_EINVAL;
+}
+
+#else /* !UNW_REMOTE_ONLY */
+
+static int
+unw_init_local_common (unw_cursor_t *cursor, ucontext_t *uc, unsigned use_prev_instr)
+{
+ struct cursor *c = (struct cursor *) cursor;
+
+ if (unlikely (!tdep_init_done))
+ tdep_init ();
+
+ Debug (1, "(cursor=%p)\n", c);
+
+ c->dwarf.as = unw_local_addr_space;
+ c->dwarf.as_arg = c;
+ c->uc = uc;
+ c->validate = 0;
+ return common_init (c, use_prev_instr);
+}
+
+int
+unw_init_local (unw_cursor_t *cursor, ucontext_t *uc)
+{
+ return unw_init_local_common(cursor, uc, 1);
+}
+
+int
+unw_init_local2 (unw_cursor_t *cursor, ucontext_t *uc, int flag)
+{
+ if (!flag)
+ {
+ return unw_init_local_common(cursor, uc, 1);
+ }
+ else if (flag == UNW_INIT_SIGNAL_FRAME)
+ {
+ return unw_init_local_common(cursor, uc, 0);
+ }
+ else
+ {
+ return -UNW_EINVAL;
+ }
+}
+
+#endif /* !UNW_REMOTE_ONLY */
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (c) 2003 Hewlett-Packard Development Company, L.P.
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+ Modified for x86_64 by Max Asbock <masbock@us.ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include "init.h"
+#include "unwind_i.h"
+
+int
+unw_init_remote (unw_cursor_t *cursor, unw_addr_space_t as, void *as_arg)
+{
+#ifdef UNW_LOCAL_ONLY
+ return -UNW_EINVAL;
+#else /* !UNW_LOCAL_ONLY */
+ struct cursor *c = (struct cursor *) cursor;
+
+ if (!tdep_init_done)
+ tdep_init ();
+
+ Debug (1, "(cursor=%p)\n", c);
+
+ c->dwarf.as = as;
+ if (as == unw_local_addr_space)
+ {
+ c->dwarf.as_arg = c;
+ c->uc = as_arg;
+ }
+ else
+ {
+ c->dwarf.as_arg = as_arg;
+ c->uc = NULL;
+ }
+ return common_init (c, 0);
+#endif /* !UNW_LOCAL_ONLY */
+}
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (C) 2012 Tommi Rantala <tt.rantala@gmail.com>
+ Copyright (C) 2013 Linaro Limited
+ Copyright (C) 2017 IBM
+
+ Modified for s390x by Michael Munday <mike.munday@ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include "unwind_i.h"
+
+/* The restorer stub will be a system call:
+ - rt_sigreturn: svc 173 (0x0aad)
+ - sigreturn: svc 119 (0x0a77)
+*/
+
+int
+unw_is_signal_frame (unw_cursor_t *cursor)
+{
+#ifdef __linux__
+ struct cursor *c = (struct cursor *) cursor;
+ unw_word_t w0, ip;
+ unw_addr_space_t as;
+ unw_accessors_t *a;
+ void *arg;
+ int ret, shift = 48;
+
+ as = c->dwarf.as;
+ a = unw_get_accessors (as);
+ arg = c->dwarf.as_arg;
+
+ /* Align the instruction pointer to 8 bytes so that we guarantee
+ an 8 byte read from it won't cross a page boundary.
+ Instructions on s390x are 2 byte aligned. */
+ ip = c->dwarf.ip & ~7;
+ shift -= (c->dwarf.ip - ip) * 8;
+
+ ret = (*a->access_mem) (as, ip, &w0, 0, arg);
+ if (ret < 0)
+ return ret;
+
+ /* extract first 2 bytes of the next instruction */
+ w0 = (w0 >> shift) & 0xffff;
+
+ /* sigreturn */
+ if (w0 == 0x0a77)
+ return 1;
+
+ /* rt_sigreturn */
+ if (w0 == 0x0aad)
+ return 2;
+
+ return 0;
+
+#else
+ return -UNW_ENOINFO;
+#endif
+}
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (c) 2002-2003 Hewlett-Packard Development Company, L.P.
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+ Modified for x86_64 by Max Asbock <masbock@us.ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include "unwind_i.h"
+
+int
+unw_reg_states_iterate (unw_cursor_t *cursor,
+ unw_reg_states_callback cb, void *token)
+{
+ struct cursor *c = (struct cursor *) cursor;
+
+ return dwarf_reg_states_iterate (&c->dwarf, cb, token);
+}
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (c) 2002-2004 Hewlett-Packard Development Company, L.P.
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+ Modified for x86_64 by Max Asbock <masbock@us.ibm.com>
+ Modified for s390x by Michael Munday <mike.munday@ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include "unwind_i.h"
+
+HIDDEN int
+tdep_access_reg (struct cursor *c, unw_regnum_t reg, unw_word_t *valp,
+ int write)
+{
+ dwarf_loc_t loc = DWARF_NULL_LOC;
+
+ switch (reg)
+ {
+ case UNW_S390X_CFA:
+ if (write)
+ return -UNW_EREADONLYREG;
+ *valp = c->dwarf.cfa;
+ return 0;
+
+ case UNW_S390X_R0:
+ case UNW_S390X_R1:
+ case UNW_S390X_R2:
+ case UNW_S390X_R3:
+ case UNW_S390X_R4:
+ case UNW_S390X_R5:
+ case UNW_S390X_R6:
+ case UNW_S390X_R7:
+ case UNW_S390X_R8:
+ case UNW_S390X_R9:
+ case UNW_S390X_R10:
+ case UNW_S390X_R11:
+ case UNW_S390X_R12:
+ case UNW_S390X_R13:
+ case UNW_S390X_R14:
+ case UNW_S390X_IP:
+ loc = c->dwarf.loc[reg];
+ break;
+
+ case UNW_S390X_R15:
+ if (write)
+ return -UNW_EREADONLYREG;
+ loc = c->dwarf.loc[reg];
+ break;
+
+ default:
+ Debug (1, "bad register number %u\n", reg);
+ return -UNW_EBADREG;
+ }
+
+ if (write)
+ return dwarf_put (&c->dwarf, loc, *valp);
+ else
+ return dwarf_get (&c->dwarf, loc, valp);
+}
+
+HIDDEN int
+tdep_access_fpreg (struct cursor *c, unw_regnum_t reg, unw_fpreg_t *valp,
+ int write)
+{
+ dwarf_loc_t loc = DWARF_NULL_LOC;
+
+ switch (reg)
+ {
+ case UNW_S390X_F0:
+ case UNW_S390X_F1:
+ case UNW_S390X_F2:
+ case UNW_S390X_F3:
+ case UNW_S390X_F4:
+ case UNW_S390X_F5:
+ case UNW_S390X_F6:
+ case UNW_S390X_F7:
+ case UNW_S390X_F8:
+ case UNW_S390X_F9:
+ case UNW_S390X_F10:
+ case UNW_S390X_F11:
+ case UNW_S390X_F12:
+ case UNW_S390X_F13:
+ case UNW_S390X_F14:
+ case UNW_S390X_F15:
+ loc = c->dwarf.loc[reg];
+ break;
+ default:
+ Debug (1, "bad register number %u\n", reg);
+ return -UNW_EBADREG;
+ }
+
+ if (write)
+ return dwarf_putfp (&c->dwarf, loc, *valp);
+ else
+ return dwarf_getfp (&c->dwarf, loc, valp);
+}
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (c) 2002-2004 Hewlett-Packard Development Company, L.P.
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+ Modified for x86_64 by Max Asbock <masbock@us.ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include <stdlib.h>
+
+#include "unwind_i.h"
+
+#ifndef UNW_REMOTE_ONLY
+
+HIDDEN inline int
+s390x_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg)
+{
+ struct cursor *c = (struct cursor *) cursor;
+ ucontext_t uc = *c->uc;
+ ucontext_t *rt = NULL;
+ struct sigcontext *sc = NULL;
+ int i;
+ unw_word_t sp, ip;
+ uc.uc_mcontext.psw.addr = c->dwarf.ip;
+
+ /* Ensure c->pi is up-to-date. On x86-64, it's relatively common to
+ be missing DWARF unwind info. We don't want to fail in that
+ case, because the frame-chain still would let us do a backtrace
+ at least. */
+ dwarf_make_proc_info (&c->dwarf);
+
+ switch (c->sigcontext_format)
+ {
+ case S390X_SCF_NONE:
+ Debug (8, "resuming at ip=%llx via setcontext()\n",
+ (unsigned long long) c->dwarf.ip);
+ setcontext (&uc);
+ abort(); /* unreachable */
+ case S390X_SCF_LINUX_SIGFRAME:
+ Debug (8, "resuming at ip=%llx via signal trampoline\n",
+ (unsigned long long) c->dwarf.ip);
+ sc = (struct sigcontext*)c->sigcontext_addr;
+ for (i = UNW_S390X_R0; i <= UNW_S390X_R15; ++i)
+ sc->sregs->regs.gprs[i-UNW_S390X_R0] = uc.uc_mcontext.gregs[i-UNW_S390X_R0];
+ for (i = UNW_S390X_F0; i <= UNW_S390X_F15; ++i)
+ sc->sregs->fpregs.fprs[i-UNW_S390X_F0] = uc.uc_mcontext.fpregs.fprs[i-UNW_S390X_F0].d;
+ sc->sregs->regs.psw.addr = uc.uc_mcontext.psw.addr;
+
+ sp = c->sigcontext_sp;
+ ip = c->sigcontext_pc;
+ __asm__ __volatile__ (
+ "lgr 15, %[sp]\n"
+ "br %[ip]\n"
+ : : [sp] "r" (sp), [ip] "r" (ip)
+ );
+ abort(); /* unreachable */
+ case S390X_SCF_LINUX_RT_SIGFRAME:
+ Debug (8, "resuming at ip=%llx via signal trampoline\n",
+ (unsigned long long) c->dwarf.ip);
+ rt = (ucontext_t*)c->sigcontext_addr;
+ for (i = UNW_S390X_R0; i <= UNW_S390X_R15; ++i)
+ rt->uc_mcontext.gregs[i-UNW_S390X_R0] = uc.uc_mcontext.gregs[i-UNW_S390X_R0];
+ for (i = UNW_S390X_F0; i <= UNW_S390X_F15; ++i)
+ rt->uc_mcontext.fpregs.fprs[i-UNW_S390X_F0] = uc.uc_mcontext.fpregs.fprs[i-UNW_S390X_F0];
+ rt->uc_mcontext.psw.addr = uc.uc_mcontext.psw.addr;
+
+ sp = c->sigcontext_sp;
+ ip = c->sigcontext_pc;
+ __asm__ __volatile__ (
+ "lgr 15, %[sp]\n"
+ "br %[ip]\n"
+ : : [sp] "r" (sp), [ip] "r" (ip)
+ );
+ abort(); /* unreachable */
+ }
+ return -UNW_EINVAL;
+}
+
+#endif /* !UNW_REMOTE_ONLY */
+
+/* This routine is responsible for copying the register values in
+ cursor C and establishing them as the current machine state. */
+
+static inline int
+establish_machine_state (struct cursor *c)
+{
+ int (*access_reg) (unw_addr_space_t, unw_regnum_t, unw_word_t *,
+ int write, void *);
+ int (*access_fpreg) (unw_addr_space_t, unw_regnum_t, unw_fpreg_t *,
+ int write, void *);
+ unw_addr_space_t as = c->dwarf.as;
+ void *arg = c->dwarf.as_arg;
+ unw_fpreg_t fpval;
+ unw_word_t val;
+ int reg;
+
+ access_reg = as->acc.access_reg;
+ access_fpreg = as->acc.access_fpreg;
+
+ Debug (8, "copying out cursor state\n");
+
+ for (reg = 0; reg <= UNW_REG_LAST; ++reg)
+ {
+ Debug (16, "copying %s %d\n", unw_regname (reg), reg);
+ if (unw_is_fpreg (reg))
+ {
+ if (tdep_access_fpreg (c, reg, &fpval, 0) >= 0)
+ (*access_fpreg) (as, reg, &fpval, 1, arg);
+ }
+ else
+ {
+ if (tdep_access_reg (c, reg, &val, 0) >= 0)
+ (*access_reg) (as, reg, &val, 1, arg);
+ }
+ }
+
+ if (c->dwarf.args_size)
+ {
+ if (tdep_access_reg (c, UNW_S390X_R15, &val, 0) >= 0)
+ {
+ val += c->dwarf.args_size;
+ (*access_reg) (as, UNW_S390X_R15, &val, 1, arg);
+ }
+ }
+ return 0;
+}
+
+int
+unw_resume (unw_cursor_t *cursor)
+{
+ struct cursor *c = (struct cursor *) cursor;
+ int ret;
+
+ Debug (1, "(cursor=%p)\n", c);
+
+ if ((ret = establish_machine_state (c)) < 0)
+ return ret;
+
+ return (*c->dwarf.as->acc.resume) (c->dwarf.as, (unw_cursor_t *) c,
+ c->dwarf.as_arg);
+}
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (C) 2002-2004 Hewlett-Packard Co
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+ Modified for x86_64 by Max Asbock <masbock@us.ibm.com>
+ Modified for s390x by Michael Munday <mike.munday@ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include "unwind_i.h"
+#include <signal.h>
+
+static int
+s390x_handle_signal_frame (unw_cursor_t *cursor)
+{
+ struct cursor *c = (struct cursor *) cursor;
+ int ret, i;
+ unw_word_t sc_addr, sp, *gprs, *fprs, *psw;
+
+ ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_S390X_R15], &sp);
+ if (ret < 0)
+ return ret;
+
+ /* Save the SP and PC to be able to return execution at this point
+ later in time (unw_resume). */
+ c->sigcontext_sp = sp;
+ c->sigcontext_pc = c->dwarf.ip;
+ switch (c->sigcontext_format)
+ {
+ case S390X_SCF_LINUX_SIGFRAME: /* sigreturn */
+ sc_addr = sp + 160;
+ gprs = ((struct sigcontext*)sc_addr)->sregs->regs.gprs;
+ fprs = (unw_word_t*)((struct sigcontext*)sc_addr)->sregs->fpregs.fprs;
+ psw = &((struct sigcontext*)sc_addr)->sregs->regs.psw.addr;
+ break;
+ case S390X_SCF_LINUX_RT_SIGFRAME: /* rt_sigreturn */
+ sc_addr = sp + sizeof(siginfo_t) + 8 + 160;
+ gprs = ((ucontext_t*)sc_addr)->uc_mcontext.gregs;
+ fprs = (unw_word_t*)((ucontext_t*)sc_addr)->uc_mcontext.fpregs.fprs;
+ psw = &((ucontext_t*)sc_addr)->uc_mcontext.psw.addr;
+ break;
+ default:
+ return -UNW_EUNSPEC;
+ }
+
+ c->sigcontext_addr = sc_addr;
+
+ /* Update the dwarf cursor.
+ Set the location of the registers to the corresponding addresses of the
+ uc_mcontext / sigcontext structure contents. */
+ for (i = UNW_S390X_R0; i <= UNW_S390X_R15; ++i)
+ c->dwarf.loc[i] = DWARF_MEM_LOC (c, (unw_word_t) &gprs[i-UNW_S390X_R0]);
+ for (i = UNW_S390X_F0; i <= UNW_S390X_F15; ++i)
+ c->dwarf.loc[i] = DWARF_MEM_LOC (c, (unw_word_t) &fprs[i-UNW_S390X_F0]);
+
+ c->dwarf.loc[UNW_S390X_IP] = DWARF_MEM_LOC (c, (unw_word_t) psw);
+
+ /* Set SP/CFA and PC/IP.
+ Normally the default CFA on s390x is r15+160. We do not add that offset
+ here because dwarf_step will add the offset. */
+ dwarf_get (&c->dwarf, c->dwarf.loc[UNW_S390X_R15], &c->dwarf.cfa);
+ dwarf_get (&c->dwarf, c->dwarf.loc[UNW_S390X_IP], &c->dwarf.ip);
+
+ c->dwarf.pi_valid = 0;
+ c->dwarf.use_prev_instr = 0;
+
+ return 1;
+}
+
+int
+unw_step (unw_cursor_t *cursor)
+{
+ struct cursor *c = (struct cursor *) cursor;
+ int ret = 0, val = c->validate, sig;
+
+#if CONSERVATIVE_CHECKS
+ c->validate = 1;
+#endif
+
+ Debug (1, "(cursor=%p, ip=0x%016lx, cfa=0x%016lx)\n",
+ c, c->dwarf.ip, c->dwarf.cfa);
+
+ /* Try DWARF-based unwinding... */
+ c->sigcontext_format = S390X_SCF_NONE;
+ ret = dwarf_step (&c->dwarf);
+
+#if CONSERVATIVE_CHECKS
+ c->validate = val;
+#endif
+
+ if (unlikely (ret == -UNW_ENOINFO))
+ {
+ /* GCC doesn't currently emit debug information for signal
+ trampolines on s390x so we check for them explicitly.
+
+ If there isn't debug information available we could also
+ try using the backchain (if available).
+
+ Other platforms also detect PLT entries here. That's
+ tricky to do reliably on s390x so I've left it out for
+ now. */
+
+ /* Memory accesses here are quite likely to be unsafe. */
+ c->validate = 1;
+
+ /* Check if this is a signal frame. */
+ sig = unw_is_signal_frame (cursor);
+ if (sig > 0)
+ {
+ c->sigcontext_format = sig;
+ ret = s390x_handle_signal_frame (cursor);
+ }
+ else
+ {
+ c->dwarf.ip = 0;
+ ret = 0;
+ }
+
+ c->validate = val;
+ return ret;
+ }
+
+ if (unlikely (ret > 0 && c->dwarf.ip == 0))
+ return 0;
+
+ return ret;
+}
--- /dev/null
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
+#include "Gapply_reg_state.c"
+#endif
--- /dev/null
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
+#include "Gcreate_addr_space.c"
+#endif
--- /dev/null
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
+#include "Gget_proc_info.c"
+#endif
--- /dev/null
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
+#include "Gget_save_loc.c"
+#endif
--- /dev/null
+#define UNW_LOCAL_ONLY
+#include "config.h"
+#include <libunwind.h>
+#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
+#include "Gglobal.c"
+#endif
--- /dev/null
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
+#include "Ginit.c"
+#endif
--- /dev/null
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
+#include "Ginit_local.c"
+#endif
--- /dev/null
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
+#include "Ginit_remote.c"
+#endif
--- /dev/null
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
+#include "Gis_signal_frame.c"
+#endif
--- /dev/null
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
+#include "Greg_states_iterate.c"
+#endif
--- /dev/null
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
+#include "Gregs.c"
+#endif
--- /dev/null
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
+#include "Gresume.c"
+#endif
--- /dev/null
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
+#include "Gstep.c"
+#endif
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (C) 2008 Google, Inc
+ Contributed by Paul Pluzhnikov <ppluzhnikov@google.com>
+ Copyright (C) 2010 Konstantin Belousov <kib@freebsd.org>
+ Copyright (C) 2017 IBM
+
+ Modified for s390x by Michael Munday <mike.munday@ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+// int _Us390x_getcontext (unw_tdep_context_t *ucp)
+
+ .global _Us390x_getcontext
+ .type _Us390x_getcontext, @function
+_Us390x_getcontext:
+ .cfi_startproc
+
+ // Save the minimal set of registers required to restore the
+ // context. Generally speaking this is just the preserved
+ // registers but we've also saved the parameter registers
+ // so that return values can be modified too.
+
+ // save PSW address
+ // (not strictly needed but makes other code simpler)
+ stg %r14,0x30(%r2)
+
+ // floating point parameters (not strictly needed)
+ std %f0,0x100(%r2)
+ std %f2,0x110(%r2)
+ std %f4,0x120(%r2)
+ std %f6,0x130(%r2)
+
+ // floating point preserved registers
+ stfpc 0xf8(%r2)
+ std %f8,0x140(%r2)
+ std %f9,0x148(%r2)
+ std %f10,0x150(%r2)
+ std %f11,0x158(%r2)
+ std %f12,0x160(%r2)
+ std %f13,0x168(%r2)
+ std %f14,0x170(%r2)
+ std %f15,0x178(%r2)
+
+ // preserved registers and parameters
+ lgr %r1,%r2
+ lghi %r2,0
+ stmg %r2,%r15,0x48(%r1)
+
+ br %r14
+
+ .cfi_endproc
+ .size _Us390x_getcontext, . - _Us390x_getcontext
+
+ // We do not need executable stack.
+ .section .note.GNU-stack,"",@progbits
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (C) 2002 Hewlett-Packard Co
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+ Modified for x86_64 by Max Asbock <masbock@us.ibm.com>
+ Modified for s390x by Michael Munday <mike.munday@ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include "unwind_i.h"
+
+static inline int
+common_init (struct cursor *c, unsigned use_prev_instr)
+{
+ int ret;
+ int i;
+
+ for (i = UNW_S390X_R0; i <= UNW_S390X_R15; ++i) {
+ c->dwarf.loc[i] = DWARF_REG_LOC(&c->dwarf, i);
+ }
+ for (i = UNW_S390X_F0; i <= UNW_S390X_F15; ++i) {
+ c->dwarf.loc[i] = DWARF_FPREG_LOC(&c->dwarf, i);
+ }
+ /* IP isn't a real register, it is encoded in the PSW */
+ c->dwarf.loc[UNW_S390X_IP] = DWARF_REG_LOC(&c->dwarf, UNW_S390X_IP);
+
+ ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_S390X_IP], &c->dwarf.ip);
+ if (ret < 0)
+ return ret;
+
+ /* Normally the CFA offset on s390x is biased, however this is taken
+ into account by the CFA offset in dwarf_step, so here we just mark
+ make it equal to the stack pointer. */
+ ret = dwarf_get (&c->dwarf, DWARF_REG_LOC (&c->dwarf, UNW_S390X_R15),
+ &c->dwarf.cfa);
+ if (ret < 0)
+ return ret;
+
+ c->sigcontext_format = S390X_SCF_NONE;
+ c->sigcontext_addr = 0;
+
+ c->dwarf.args_size = 0;
+ c->dwarf.stash_frames = 0;
+ c->dwarf.use_prev_instr = use_prev_instr;
+ c->dwarf.pi_valid = 0;
+ c->dwarf.pi_is_dynamic = 0;
+ c->dwarf.hint = 0;
+ c->dwarf.prev_rs = 0;
+ c->dwarf.eh_valid_mask = 0;
+
+ return 0;
+}
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (c) 2004-2005 Hewlett-Packard Development Company, L.P.
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+ Modified for x86_64 by Max Asbock <masbock@us.ibm.com>
+ Modified for s390x by Michael Munday <mike.munday@ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include "libunwind_i.h"
+
+int
+unw_is_fpreg (int regnum)
+{
+ /* vector registers? */
+ return regnum >= UNW_S390X_F0 && regnum <= UNW_S390X_F15;
+}
--- /dev/null
+/* libunwind - a platform-independent unwind library
+
+ Contributed by Max Asbock <masbock@us.ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include "unwind_i.h"
+
+static const char *regname[] =
+ {
+ [UNW_S390X_R0]="R0",
+ [UNW_S390X_R1]="R1",
+ [UNW_S390X_R2]="R2",
+ [UNW_S390X_R3]="R3",
+ [UNW_S390X_R4]="R4",
+ [UNW_S390X_R5]="R5",
+ [UNW_S390X_R6]="R6",
+ [UNW_S390X_R7]="R7",
+ [UNW_S390X_R8]="R8",
+ [UNW_S390X_R9]="R9",
+ [UNW_S390X_R10]="R10",
+ [UNW_S390X_R11]="R11",
+ [UNW_S390X_R12]="R12",
+ [UNW_S390X_R13]="R13",
+ [UNW_S390X_R14]="R14",
+ [UNW_S390X_R15]="R15",
+
+ [UNW_S390X_IP]="IP"
+ };
+
+const char *
+unw_regname (unw_regnum_t reg)
+{
+ if (reg < (unw_regnum_t) ARRAY_SIZE (regname))
+ return regname[reg];
+ else
+ return "???";
+}
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (C) 2008 Google, Inc
+ Contributed by Paul Pluzhnikov <ppluzhnikov@google.com>
+ Copyright (C) 2010 Konstantin Belousov <kib@freebsd.org>
+ Copyright (C) 2017 IBM
+
+ Modified for s390x by Michael Munday <mike.munday@ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+// int _Us390x_setcontext (const ucontext_t *ucp)
+
+ .global _Us390x_setcontext
+ .type _Us390x_setcontext, @function
+_Us390x_setcontext:
+ .cfi_startproc
+
+ // Must only restore registers saved by getcontext, other fields
+ // in the ucontext_t might be uninitialised.
+
+ // Stop this function being unwound. We are clobbering callee-save
+ // registers in this function so unwinding it is unsafe.
+ // Ideally we'd save callee-save registers, update the CFI for them
+ // and then switch to the new CFI once the context switch is
+ // complete.
+ .cfi_undefined %r14
+
+ // floating point parameters
+ ld %f0,0x100(%r2)
+ ld %f2,0x110(%r2)
+ ld %f4,0x120(%r2)
+ ld %f6,0x130(%r2)
+
+ // floating point preserved registers
+ lfpc 0xf8(%r2)
+ ld %f8,0x140(%r2)
+ ld %f9,0x148(%r2)
+ ld %f10,0x150(%r2)
+ ld %f11,0x158(%r2)
+ ld %f12,0x160(%r2)
+ ld %f13,0x168(%r2)
+ ld %f14,0x170(%r2)
+ ld %f15,0x178(%r2)
+
+ // preserved registers and parameters
+ lgr %r1,%r2
+ lmg %r2,%r15,0x48(%r1)
+
+ // restore PSW address
+ lg %r1,0x30(%r1)
+ br %r1
+
+ .cfi_endproc
+ .size _Us390x_setcontext, . - _Us390x_setcontext
+
+ // We do not need executable stack.
+ .section .note.GNU-stack,"",@progbits
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (C) 2002, 2005 Hewlett-Packard Co
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+ Modified for x86_64 by Max Asbock <masbock@us.ibm.com>
+ Modified for s390x by Michael Munday <mike.munday@ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#ifndef unwind_i_h
+#define unwind_i_h
+
+#include <stdint.h>
+
+#include <libunwind-s390x.h>
+
+#include "libunwind_i.h"
+#include <sys/ucontext.h>
+
+#define s390x_lock UNW_OBJ(lock)
+#define s390x_local_resume UNW_OBJ(local_resume)
+#define s390x_local_addr_space_init UNW_OBJ(local_addr_space_init)
+#define setcontext UNW_ARCH_OBJ(setcontext)
+
+extern void s390x_local_addr_space_init (void);
+extern int s390x_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg);
+extern int setcontext (const ucontext_t *ucp);
+
+#endif /* unwind_i_h */
#include "jmpbuf.h"
#include "setjmp_i.h"
-#if !defined(_NSIG) && defined(_SIG_MAXSIG)
-# define _NSIG (_SIG_MAXSIG - 1)
+#if !defined(_NSIG)
+# if defined(_SIG_MAXSIG)
+# define _NSIG (_SIG_MAXSIG - 1)
+# elif defined(NSIG)
+# define _NSIG NSIG
+# endif
#endif
#if defined(__GLIBC__)
if (!resume_restores_sigmask (&c, wp) && wp[JB_MASK_SAVED])
{
/* sigmask was saved */
-#if defined(__linux__)
+#if defined(__linux__) || defined(__sun)
if (UNW_NUM_EH_REGS < 4 || _NSIG > 16 * sizeof (unw_word_t))
/* signal mask doesn't fit into EH arguments and we can't
put it on the stack without overwriting something
# endif /* UNW_LOCAL_ONLY */
-HIDDEN unw_dyn_info_list_t _U_dyn_info_list;
-
-/* XXX fix me: there is currently no way to locate the dyn-info list
- by a remote unwinder. On ia64, this is done via a special
- unwind-table entry. Perhaps something similar can be done with
- DWARF2 unwind info. */
-
static void
put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
{
get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
void *arg)
{
- *dyn_info_list_addr = (unw_word_t) &_U_dyn_info_list;
+#ifndef UNW_LOCAL_ONLY
+# pragma weak _U_dyn_info_list_addr
+ if (!_U_dyn_info_list_addr)
+ return -UNW_ENOINFO;
+#endif
+ // Access the `_U_dyn_info_list` from `LOCAL_ONLY` library, i.e. libunwind.so.
+ *dyn_info_list_addr = _U_dyn_info_list_addr ();
return 0;
}
#else /* !UNW_REMOTE_ONLY */
static int
-unw_init_local (unw_cursor_t *cursor, unw_context_t *uc, unsigned use_prev_instr)
+unw_init_local_common (unw_cursor_t *cursor, unw_context_t *uc, unsigned use_prev_instr)
{
struct cursor *c = (struct cursor *) cursor;
char x[sizeof(regs)];
};
- asm volatile (
+ __asm__ __volatile__ (
"mov.l @%0+, r8\n"
"mov.l @%0+, r9\n"
"mov.l @%0+, r10\n"
/* Set the SP and the PC in order to continue execution at the modified
trampoline which restores the signal mask and the registers. */
- asm __volatile__ (
+ __asm__ __volatile__ (
"mov %0, r15\n"
"lds %1, pr\n"
"rts\n"
# endif /* UNW_LOCAL_ONLY */
-HIDDEN unw_dyn_info_list_t _U_dyn_info_list;
-
-/* XXX fix me: there is currently no way to locate the dyn-info list
- by a remote unwinder. On ia64, this is done via a special
- unwind-table entry. Perhaps something similar can be done with
- DWARF2 unwind info. */
-
static void
put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
{
get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
void *arg)
{
- *dyn_info_list_addr = (unw_word_t) (intptr_t) &_U_dyn_info_list;
+#ifndef UNW_LOCAL_ONLY
+# pragma weak _U_dyn_info_list_addr
+ if (!_U_dyn_info_list_addr)
+ return -UNW_ENOINFO;
+#endif
+ // Access the `_U_dyn_info_list` from `LOCAL_ONLY` library, i.e. libunwind.so.
+ *dyn_info_list_addr = _U_dyn_info_list_addr ();
return 0;
}
Description: libunwind base library
Version: @VERSION@
Libs: -L${libdir} -lunwind
-Libs.private: @LIBLZMA@
+Libs.private: @LIBLZMA@ @LIBZ@
Cflags: -I${includedir}
# endif /* UNW_LOCAL_ONLY */
-HIDDEN unw_dyn_info_list_t _U_dyn_info_list;
-
-/* XXX fix me: there is currently no way to locate the dyn-info list
- by a remote unwinder. On ia64, this is done via a special
- unwind-table entry. Perhaps something similar can be done with
- DWARF2 unwind info. */
-
static void
put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
{
get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
void *arg)
{
- *dyn_info_list_addr = (unw_word_t) &_U_dyn_info_list;
+#ifndef UNW_LOCAL_ONLY
+# pragma weak _U_dyn_info_list_addr
+ if (!_U_dyn_info_list_addr)
+ return -UNW_ENOINFO;
+#endif
+ // Access the `_U_dyn_info_list` from `LOCAL_ONLY` library, i.e. libunwind.so.
+ *dyn_info_list_addr = _U_dyn_info_list_addr ();
return 0;
}
case UNW_X86_64_R13: loc = c->dwarf.loc[R13]; break;
case UNW_X86_64_R14: loc = c->dwarf.loc[R14]; break;
case UNW_X86_64_R15: loc = c->dwarf.loc[R15]; break;
+ case UNW_X86_64_RIP: loc = c->dwarf.loc[RIP]; break;
default:
break;
#include "dwarf_i.h"
HIDDEN define_lock (x86_64_lock);
-HIDDEN int tdep_init_done;
+#ifdef HAVE_ATOMIC_OPS_H
+ HIDDEN AO_t tdep_init_done;
+#else
+ HIDDEN int tdep_init_done;
+#endif
/* See comments for svr4_dbx_register_map[] in gcc/config/i386/i386.c. */
tdep_init (void)
{
intrmask_t saved_mask;
+ intrmask_t full_mask;
+ sigfillset (&full_mask);
- sigfillset (&unwi_full_mask);
-
- lock_acquire (&x86_64_lock, saved_mask);
+ SIGPROCMASK (SIG_SETMASK, &full_mask, &saved_mask);
+ mutex_lock (&x86_64_lock);
{
- if (tdep_init_done)
+ if (atomic_read(&tdep_init_done))
/* another thread else beat us to it... */
goto out;
+ sigfillset (&unwi_full_mask);
mi_init ();
dwarf_init ();
#ifndef UNW_REMOTE_ONLY
x86_64_local_addr_space_init ();
#endif
- tdep_init_done = 1; /* signal that we're initialized... */
+ fetch_and_add1(&tdep_init_done); /* signal that we're initialized... */
}
out:
- lock_release (&x86_64_lock, saved_mask);
+ mutex_unlock(&x86_64_lock);
+ SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL);
}
unw_addr_space_t unw_local_addr_space = &local_addr_space;
-HIDDEN unw_dyn_info_list_t _U_dyn_info_list;
-
-/* XXX fix me: there is currently no way to locate the dyn-info list
- by a remote unwinder. On ia64, this is done via a special
- unwind-table entry. Perhaps something similar can be done with
- DWARF2 unwind info. */
-
static void
put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
{
get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
void *arg)
{
- *dyn_info_list_addr = (unw_word_t) &_U_dyn_info_list;
+#ifndef UNW_LOCAL_ONLY
+# pragma weak _U_dyn_info_list_addr
+ if (!_U_dyn_info_list_addr)
+ return -UNW_ENOINFO;
+#endif
+ // Access the `_U_dyn_info_list` from `LOCAL_ONLY` library, i.e. libunwind.so.
+ *dyn_info_list_addr = _U_dyn_info_list_addr ();
return 0;
}
static int mem_validate_pipe[2] = {-1, -1};
+#ifdef HAVE_PIPE2
+static inline void
+do_pipe2 (int pipefd[2])
+{
+ pipe2 (pipefd, O_CLOEXEC | O_NONBLOCK);
+}
+#else
+static inline void
+set_pipe_flags (int fd)
+{
+ int fd_flags = fcntl (fd, F_GETFD, 0);
+ int status_flags = fcntl (fd, F_GETFL, 0);
+
+ fd_flags |= FD_CLOEXEC;
+ fcntl (fd, F_SETFD, fd_flags);
+
+ status_flags |= O_NONBLOCK;
+ fcntl (fd, F_SETFL, status_flags);
+}
+
+static inline void
+do_pipe2 (int pipefd[2])
+{
+ pipe (pipefd);
+ set_pipe_flags(pipefd[0]);
+ set_pipe_flags(pipefd[1]);
+}
+#endif
+
static inline void
open_pipe (void)
{
- /* ignore errors for closing invalid fd's */
- close (mem_validate_pipe[0]);
- close (mem_validate_pipe[1]);
+ if (mem_validate_pipe[0] != -1)
+ close (mem_validate_pipe[0]);
+ if (mem_validate_pipe[1] != -1)
+ close (mem_validate_pipe[1]);
- pipe2 (mem_validate_pipe, O_CLOEXEC | O_NONBLOCK);
+ do_pipe2 (mem_validate_pipe);
}
ALWAYS_INLINE
static int mincore_validate (void *addr, size_t len)
{
unsigned char mvec[2]; /* Unaligned access may cross page boundary */
- size_t i;
/* mincore could fail with EAGAIN but we conservatively return -1
instead of looping. */
- if (mincore (addr, len, mvec) != 0)
+ if (mincore (addr, len, (char *)mvec) != 0)
{
return -1;
}
- for (i = 0; i < (len + PAGE_SIZE - 1) / PAGE_SIZE; i++)
- {
- if (!(mvec[i] & 1)) return -1;
- }
-
return write_validate (addr);
}
#endif
unw_word_t addr = PAGE_START((unw_word_t)&present);
unsigned char mvec[1];
int ret;
- while ((ret = mincore ((void*)addr, PAGE_SIZE, mvec)) == -1 &&
+ while ((ret = mincore ((void*)addr, PAGE_SIZE, (char *)mvec)) == -1 &&
errno == EAGAIN) {}
- if (ret == 0 && (mvec[0] & 1))
+ if (ret == 0)
{
Debug(1, "using mincore to validate memory\n");
mem_validate_func = mincore_validate;
/* Cache of already validated addresses */
#define NLGA 4
-static unw_word_t last_good_addr[NLGA];
-static int lga_victim;
+#if defined(HAVE___THREAD) && HAVE___THREAD
+// thread-local variant
+static __thread unw_word_t last_good_addr[NLGA];
+static __thread int lga_victim;
static int
-validate_mem (unw_word_t addr)
+is_cached_valid_mem(unw_word_t addr)
+{
+ int i;
+ for (i = 0; i < NLGA; i++)
+ {
+ if (addr == last_good_addr[i])
+ return 1;
+ }
+ return 0;
+}
+
+static void
+cache_valid_mem(unw_word_t addr)
+{
+ int i, victim;
+ victim = lga_victim;
+ for (i = 0; i < NLGA; i++) {
+ if (last_good_addr[victim] == 0) {
+ last_good_addr[victim] = addr;
+ return;
+ }
+ victim = (victim + 1) % NLGA;
+ }
+
+ /* All slots full. Evict the victim. */
+ last_good_addr[victim] = addr;
+ victim = (victim + 1) % NLGA;
+ lga_victim = victim;
+}
+
+#elif HAVE_ATOMIC_OPS_H
+// global, thread safe variant
+static AO_T last_good_addr[NLGA];
+static AO_T lga_victim;
+
+static int
+is_cached_valid_mem(unw_word_t addr)
+{
+ int i;
+ for (i = 0; i < NLGA; i++)
+ {
+ if (addr == AO_load(&last_good_addr[i]))
+ return 1;
+ }
+ return 0;
+}
+
+static void
+cache_valid_mem(unw_word_t addr)
{
int i, victim;
+ victim = AO_load(&lga_victim);
+ for (i = 0; i < NLGA; i++) {
+ if (AO_compare_and_swap(&last_good_addr[victim], 0, addr)) {
+ return;
+ }
+ victim = (victim + 1) % NLGA;
+ }
+
+ /* All slots full. Evict the victim. */
+ AO_store(&last_good_addr[victim], addr);
+ victim = (victim + 1) % NLGA;
+ AO_store(&lga_victim, victim);
+}
+#else
+// disabled, no cache
+static int
+is_cached_valid_mem(unw_word_t addr UNUSED)
+{
+ return 0;
+}
+
+static void
+cache_valid_mem(unw_word_t addr UNUSED)
+{
+}
+#endif
+
+static int
+validate_mem (unw_word_t addr)
+{
size_t len;
if (PAGE_START(addr + sizeof (unw_word_t) - 1) == PAGE_START(addr))
if (addr == 0)
return -1;
- for (i = 0; i < NLGA; i++)
- {
- if (last_good_addr[i] && (addr == last_good_addr[i]))
- return 0;
- }
+ if (is_cached_valid_mem(addr))
+ return 0;
if (mem_validate_func ((void *) addr, len) == -1)
return -1;
- victim = lga_victim;
- for (i = 0; i < NLGA; i++) {
- if (!last_good_addr[victim]) {
- last_good_addr[victim++] = addr;
- return 0;
- }
- victim = (victim + 1) % NLGA;
- }
-
- /* All slots full. Evict the victim. */
- last_good_addr[victim] = addr;
- victim = (victim + 1) % NLGA;
- lga_victim = victim;
+ cache_valid_mem(addr);
return 0;
}
local_addr_space.acc.resume = x86_64_local_resume;
local_addr_space.acc.get_proc_name = get_static_proc_name;
unw_flush_cache (&local_addr_space, 0, 0);
-
- memset (last_good_addr, 0, sizeof (unw_word_t) * NLGA);
- lga_victim = 0;
}
#endif /* !UNW_REMOTE_ONLY */
{
struct cursor *c = (struct cursor *) cursor;
- if (unlikely (!tdep_init_done))
+ if (unlikely (!atomic_read(&tdep_init_done)))
tdep_init ();
Debug (1, "(cursor=%p)\n", c);
#else /* !UNW_LOCAL_ONLY */
struct cursor *c = (struct cursor *) cursor;
- if (!tdep_init_done)
+ if (!atomic_read(&tdep_init_done))
tdep_init ();
Debug (1, "(cursor=%p)\n", c);
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (C) 2002-2003 Hewlett-Packard Co
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+ Modified for x86_64 by Max Asbock <masbock@us.ibm.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include "unwind_i.h"
+#include "ucontext_i.h"
+
+#include <sys/syscall.h>
+
+struct sigframe {
+ uint64_t signo;
+ uint64_t sip;
+};
+
+int
+unw_is_signal_frame (unw_cursor_t *cursor)
+{
+ struct cursor *c = (struct cursor *) cursor;
+
+ c->sigcontext_format = (c->dwarf.ip == (unw_word_t)-1) ?
+ X86_64_SCF_SOLARIS_SIGFRAME : X86_64_SCF_NONE;
+
+ return (c->sigcontext_format);
+}
+
+HIDDEN int
+x86_64_handle_signal_frame (unw_cursor_t *cursor)
+{
+ struct cursor *c = (struct cursor *) cursor;
+ unw_word_t ucontext = c->dwarf.cfa + sizeof (struct sigframe);
+
+ if (c->sigcontext_format != X86_64_SCF_SOLARIS_SIGFRAME)
+ return -UNW_EBADFRAME;
+
+ c->sigcontext_addr = c->dwarf.cfa;
+
+ Debug(1, "signal frame cfa = %lx ucontext = %lx\n",
+ (uint64_t)c->dwarf.cfa, (uint64_t)ucontext);
+
+ struct dwarf_loc rsp_loc = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RSP, 0);
+ int ret = dwarf_get (&c->dwarf, rsp_loc, &c->dwarf.cfa);
+
+ if (ret < 0)
+ {
+ Debug (2, "return %d\n", ret);
+ return ret;
+ }
+
+ c->dwarf.loc[RAX] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RAX, 0);
+ c->dwarf.loc[RDX] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RDX, 0);
+ c->dwarf.loc[RCX] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RCX, 0);
+ c->dwarf.loc[RBX] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RBX, 0);
+ c->dwarf.loc[RSI] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RSI, 0);
+ c->dwarf.loc[RDI] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RDI, 0);
+ c->dwarf.loc[RBP] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RBP, 0);
+ c->dwarf.loc[RSP] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RSP, 0);
+ c->dwarf.loc[ R8] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_R8, 0);
+ c->dwarf.loc[ R9] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_R9, 0);
+ c->dwarf.loc[R10] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_R10, 0);
+ c->dwarf.loc[R11] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_R11, 0);
+ c->dwarf.loc[R12] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_R12, 0);
+ c->dwarf.loc[R13] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_R13, 0);
+ c->dwarf.loc[R14] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_R14, 0);
+ c->dwarf.loc[R15] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_R15, 0);
+ c->dwarf.loc[RIP] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RIP, 0);
+
+ c->dwarf.use_prev_instr = 1;
+ return 0;
+}
+
+#ifndef UNW_REMOTE_ONLY
+HIDDEN void *
+x86_64_r_uc_addr (ucontext_t *uc, int reg)
+{
+ /* NOTE: common_init() in init.h inlines these for fast path access. */
+ void *addr;
+
+ switch (reg)
+ {
+ case UNW_X86_64_R8: addr = &uc->uc_mcontext.gregs[REG_R8]; break;
+ case UNW_X86_64_R9: addr = &uc->uc_mcontext.gregs[REG_R9]; break;
+ case UNW_X86_64_R10: addr = &uc->uc_mcontext.gregs[REG_R10]; break;
+ case UNW_X86_64_R11: addr = &uc->uc_mcontext.gregs[REG_R11]; break;
+ case UNW_X86_64_R12: addr = &uc->uc_mcontext.gregs[REG_R12]; break;
+ case UNW_X86_64_R13: addr = &uc->uc_mcontext.gregs[REG_R13]; break;
+ case UNW_X86_64_R14: addr = &uc->uc_mcontext.gregs[REG_R14]; break;
+ case UNW_X86_64_R15: addr = &uc->uc_mcontext.gregs[REG_R15]; break;
+ case UNW_X86_64_RDI: addr = &uc->uc_mcontext.gregs[REG_RDI]; break;
+ case UNW_X86_64_RSI: addr = &uc->uc_mcontext.gregs[REG_RSI]; break;
+ case UNW_X86_64_RBP: addr = &uc->uc_mcontext.gregs[REG_RBP]; break;
+ case UNW_X86_64_RBX: addr = &uc->uc_mcontext.gregs[REG_RBX]; break;
+ case UNW_X86_64_RDX: addr = &uc->uc_mcontext.gregs[REG_RDX]; break;
+ case UNW_X86_64_RAX: addr = &uc->uc_mcontext.gregs[REG_RAX]; break;
+ case UNW_X86_64_RCX: addr = &uc->uc_mcontext.gregs[REG_RCX]; break;
+ case UNW_X86_64_RSP: addr = &uc->uc_mcontext.gregs[REG_RSP]; break;
+ case UNW_X86_64_RIP: addr = &uc->uc_mcontext.gregs[REG_RIP]; break;
+
+ default:
+ addr = NULL;
+ }
+ return addr;
+}
+
+HIDDEN NORETURN void
+x86_64_sigreturn (unw_cursor_t *cursor)
+{
+ abort();
+}
+
+#endif
}
else if (f->frame_type == UNW_X86_64_FRAME_ALIGNED) {
- Debug (4, " aligned frame, offset %li\n", f->cfa_reg_offset);
+ Debug (4, " aligned frame, offset %i\n", f->cfa_reg_offset);
}
-
/* PLT and guessed RBP-walked frames are handled in unw_step(). */
- else
+ else {
Debug (4, " unusual frame\n");
+ }
}
via CALLQ. Try this for all non-signal trampoline
code. */
+ unw_word_t invalid_prev_rip = 0;
unw_word_t prev_ip = c->dwarf.ip, prev_cfa = c->dwarf.cfa;
struct dwarf_loc rbp_loc, rsp_loc, rip_loc;
return ret;
}
- if (!rbp)
+ unw_word_t not_used;
+ invalid_prev_rip = dwarf_get(&c->dwarf, DWARF_MEM_LOC(c->dwarf, prev_ip), ¬_used);
+
+ if (!rbp && invalid_prev_rip == 0)
{
/* Looks like we may have reached the end of the call-chain. */
rbp_loc = DWARF_NULL_LOC;
}
else
{
- unw_word_t rbp1 = 0;
- rbp_loc = DWARF_LOC(rbp, 0);
- rsp_loc = DWARF_NULL_LOC;
- rip_loc = DWARF_LOC (rbp + 8, 0);
- ret = dwarf_get (&c->dwarf, rbp_loc, &rbp1);
- Debug (1, "[RBP=0x%lx] = 0x%lx (cfa = 0x%lx) -> 0x%lx\n",
- (unsigned long) DWARF_GET_LOC (c->dwarf.loc[RBP]),
- rbp, c->dwarf.cfa, rbp1);
-
- /* Heuristic to determine incorrect guess. For RBP to be a
- valid frame it needs to be above current CFA, but don't
- let it go more than a little. Note that we can't deduce
- anything about new RBP (rbp1) since it may not be a frame
- pointer in the frame above. Just check we get the value. */
- if (ret < 0
- || rbp < c->dwarf.cfa
- || (rbp - c->dwarf.cfa) > 0x4000)
+ /*
+ * Check if previous RIP was invalid
+ * This could happen if a bad function pointer was
+ * followed and so the stack wasn't updated by the
+ * preamble
+ */
+ int rip_fixup_success = 0;
+ if (invalid_prev_rip != 0)
{
- rip_loc = DWARF_NULL_LOC;
- rbp_loc = DWARF_NULL_LOC;
- }
+ Debug (2, "Previous RIP 0x%lx was invalid, attempting fixup\n", prev_ip);
+ unw_word_t rsp;
+ ret = dwarf_get (&c->dwarf, c->dwarf.loc[RSP], &rsp);
- c->frame_info.frame_type = UNW_X86_64_FRAME_GUESSED;
- c->frame_info.cfa_reg_rsp = 0;
- c->frame_info.cfa_reg_offset = 16;
- c->frame_info.rbp_cfa_offset = -16;
- c->dwarf.cfa += 16;
- }
+ /*Test to see if what we think is the previous RIP is valid*/
+ unw_word_t new_ip = 0;
+ if (dwarf_get(&c->dwarf, DWARF_MEM_LOC(c->dwarf, rsp), &new_ip) == 0)
+ {
+ Debug (2, "RSP 0x%lx looks valid\n", rsp);
+ if ((ret = dwarf_get(&c->dwarf, DWARF_MEM_LOC(c->dwarf, new_ip), ¬_used)) == 0)
+ {
+ Debug (2, "new_ip 0x%lx looks valid\n", new_ip);
+ rip_fixup_success = 1;
+ c->frame_info.cfa_reg_offset = 8;
+ c->frame_info.cfa_reg_rsp = 1;
+ c->frame_info.rbp_cfa_offset = -1;
+ c->frame_info.rsp_cfa_offset = -1;
+ c->frame_info.frame_type = UNW_X86_64_FRAME_OTHER;
+ /*
+ * The call should have pushed RIP to the stack
+ * and since there was no preamble RSP hasn't been
+ * touched so RIP should be at RSP.
+ */
+ c->dwarf.cfa += 8;
+ /* Optimised x64 binaries don't use RBP it seems? */
+ rbp_loc = DWARF_LOC (rbp, 0);
+ rsp_loc = DWARF_LOC (rsp, 0);
+ rip_loc = DWARF_LOC (rsp, 0);
+ }
+ else
+ Debug (2, "new_ip 0x%lx dwarf_get(&c->dwarf, DWARF_MEM_LOC(c->dwarf, new_ip), ¬_used) != 0\n", new_ip);
+ }
+ else
+ Debug (2, "rsp 0x%lx dwarf_get(&c->dwarf, DWARF_MEM_LOC(c->dwarf, rsp), &new_ip) != 0\n", rsp);
+ }
+ /*
+ * If the previous rip we found on the stack didn't look valid fall back
+ * to the previous method for finding a valid stack frame
+ */
+ if (!rip_fixup_success)
+ {
+ Debug (2, "RIP fixup didn't work, falling back\n");
+ unw_word_t rbp1 = 0;
+ rbp_loc = DWARF_LOC(rbp, 0);
+ rsp_loc = DWARF_NULL_LOC;
+ rip_loc = DWARF_LOC (rbp + 8, 0);
+ ret = dwarf_get (&c->dwarf, rbp_loc, &rbp1);
+ Debug (1, "[RBP=0x%lx] = 0x%lx (cfa = 0x%lx) -> 0x%lx\n",
+ (unsigned long) DWARF_GET_LOC (c->dwarf.loc[RBP]),
+ rbp, c->dwarf.cfa, rbp1);
+
+ /* Heuristic to determine incorrect guess. For RBP to be a
+ valid frame it needs to be above current CFA, but don't
+ let it go more than a little. Note that we can't deduce
+ anything about new RBP (rbp1) since it may not be a frame
+ pointer in the frame above. Just check we get the value. */
+ if (ret < 0
+ || rbp < c->dwarf.cfa
+ || (rbp - c->dwarf.cfa) > 0x4000)
+ {
+ rip_loc = DWARF_NULL_LOC;
+ rbp_loc = DWARF_NULL_LOC;
+ }
+ c->frame_info.frame_type = UNW_X86_64_FRAME_GUESSED;
+ c->frame_info.cfa_reg_rsp = 0;
+ c->frame_info.cfa_reg_offset = 16;
+ c->frame_info.rbp_cfa_offset = -16;
+ c->dwarf.cfa += 16;
+
+ }
+ }
/* Mark all registers unsaved */
for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i)
- c->dwarf.loc[i] = DWARF_NULL_LOC;
+ c->dwarf.loc[i] = DWARF_NULL_LOC;
c->dwarf.loc[RBP] = rbp_loc;
c->dwarf.loc[RSP] = rsp_loc;
c->dwarf.use_prev_instr = 1;
}
- if (DWARF_IS_NULL_LOC (c->dwarf.loc[RBP]))
+ if (DWARF_IS_NULL_LOC (c->dwarf.loc[RBP]) && invalid_prev_rip == 0)
{
ret = 0;
Debug (2, "NULL %%rbp loc, returning %d\n", ret);
Debug (2, "returning %d\n", ret);
return ret;
}
+#if __sun
+ if (c->dwarf.ip == 0)
+ {
+ Debug (2, "returning 0\n");
+ return ret;
+ }
+#endif
ret = 1;
}
else
break;
/* Record this address in stack trace. We skipped the first address. */
- buffer[depth++] = (void *) (rip - d->use_prev_instr);
+ buffer[depth++] = (void *) rip;
}
#if UNW_DEBUG
--- /dev/null
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
+#include "Gos-solaris.c"
+#endif
movq %rax, UC_MCONTEXT_GREGS_RAX(%rdi)
movq %rcx, UC_MCONTEXT_GREGS_RCX(%rdi)
-#if defined __linux__
+#if defined __linux__ || defined __sun
/* Save fp state (not needed, except for setcontext not
restoring garbage). */
leaq UC_MCONTEXT_FPREGS_MEM(%rdi),%r8
+#ifdef UC_MCONTEXT_FPREGS_PTR
movq %r8, UC_MCONTEXT_FPREGS_PTR(%rdi)
+#endif // UC_MCONTEXT_FPREGS_PTR
fnstenv (%r8)
stmxcsr FPREGS_OFFSET_MXCSR(%r8)
#elif defined __FreeBSD__
_Ux86_64_setcontext:
-#if defined __linux__
+#if defined __linux__ || defined __sun
/* restore fp state */
+#ifdef UC_MCONTEXT_FPREGS_PTR
mov UC_MCONTEXT_FPREGS_PTR(%rdi),%r8
+#else // UC_MCONTEXT_FPREGS_PTR
+ leaq UC_MCONTEXT_FPREGS_MEM(%rdi),%r8
+#endif // UC_MCONTEXT_FPREGS_PTR
fldenv (%r8)
ldmxcsr FPREGS_OFFSET_MXCSR(%r8)
#elif defined __FreeBSD__
#define UC_MCONTEXT_FPOWNED_FPU 0x20001
#define UC_MCONTEXT_FPFMT_XMM 0x10002
#define UC_MCONTEXT_MC_LEN_VAL 0x320
+#elif defined __sun
+#define UC_MCONTEXT_GREGS_R8 0x78
+#define UC_MCONTEXT_GREGS_R9 0x70
+#define UC_MCONTEXT_GREGS_R10 0x68
+#define UC_MCONTEXT_GREGS_R11 0x60
+#define UC_MCONTEXT_GREGS_R12 0x58
+#define UC_MCONTEXT_GREGS_R13 0x50
+#define UC_MCONTEXT_GREGS_R14 0x48
+#define UC_MCONTEXT_GREGS_R15 0x40
+#define UC_MCONTEXT_GREGS_RDI 0x80
+#define UC_MCONTEXT_GREGS_RSI 0x88
+#define UC_MCONTEXT_GREGS_RBP 0x90
+#define UC_MCONTEXT_GREGS_RBX 0x98
+#define UC_MCONTEXT_GREGS_RDX 0xa0
+#define UC_MCONTEXT_GREGS_RAX 0xb0
+#define UC_MCONTEXT_GREGS_RCX 0xa8
+#define UC_MCONTEXT_GREGS_RSP 0xe0
+#define UC_MCONTEXT_GREGS_RIP 0xc8
+#define UC_MCONTEXT_FPREGS_MEM 0x120
+#define FPREGS_OFFSET_MXCSR 0x18
#endif
{
printf ("sighandler: got signal %d, sp=%p", signal, &sp);
#if UNW_TARGET_IA64
-# if defined(__linux__)
+# if defined(__linux__) || defined __sun
printf (" @ %lx", uc->uc_mcontext.sc_ip);
# else
{
}
# endif
#elif UNW_TARGET_X86
-#if defined __linux__
+#if defined __linux__ || defined __sun
printf (" @ %lx", (unsigned long) uc->uc_mcontext.gregs[REG_EIP]);
#elif defined __FreeBSD__
printf (" @ %lx", (unsigned long) uc->uc_mcontext.mc_eip);
#endif
#elif UNW_TARGET_X86_64
-#if defined __linux__
+#if defined __linux__ || defined __sun
printf (" @ %lx", (unsigned long) uc->uc_mcontext.gregs[REG_RIP]);
#elif defined __FreeBSD__
printf (" @ %lx", (unsigned long) uc->uc_mcontext.mc_rip);
printf (" @ %lx", (unsigned long) uc->uc_mcontext.mc_eip);
#endif
#elif UNW_TARGET_X86_64
-#if defined __linux__
+#if defined __linux__ || defined __sun
printf (" @ %lx", (unsigned long) uc->uc_mcontext.gregs[REG_RIP]);
#elif defined __FreeBSD__
printf (" @ %lx", (unsigned long) uc->uc_mcontext.mc_rip);
--- /dev/null
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <libunwind.h>
+
+static int verbose;
+static int nerrors;
+
+#define panic(args...) \
+ do { printf (args); ++nerrors; } while (0)
+
+// Assembly routine which sets up the stack for the test then calls another one
+// which clobbers the stack, and which in turn calls recover_register below
+extern int64_t DW_CFA_expression_testcase(int64_t regnum, int64_t height);
+
+// recover_register is called by the assembly routines. It returns the value of
+// a register at a specified height from the inner-most frame. The return value
+// is propagated back through the assembly routines to the testcase.
+extern int64_t recover_register(int64_t regnum, int64_t height)
+{
+ // Initialize cursor to current frame
+ int rc, i;
+ unw_cursor_t cursor;
+ unw_context_t context;
+ unw_getcontext(&context);
+ unw_init_local(&cursor, &context);
+ // Unwind frames until required height from inner-most frame (i.e. this one)
+ for (i = 0; i < height; ++i)
+ {
+ rc = unw_step(&cursor);
+ if (rc < 0)
+ panic("%s: unw_step failed on step %d with return code %d", __FUNCTION__, i, rc);
+ else if (rc == 0)
+ panic("%s: unw_step failed to reach the end of the stack", __FUNCTION__);
+ unw_word_t pc;
+ rc = unw_get_reg(&cursor, UNW_REG_IP, &pc);
+ if (rc < 0 || pc == 0)
+ panic("%s: unw_get_reg failed to locate the program counter", __FUNCTION__);
+ }
+ // We're now at the required height, extract register
+ uint64_t value;
+ if ((rc = unw_get_reg(&cursor, (unw_regnum_t) regnum, &value)) != 0)
+ panic("%s: unw_get_reg failed to retrieve register %lu", __FUNCTION__, regnum);
+ return value;
+}
+
+int
+main (int argc, char **argv)
+{
+ if (argc > 1)
+ verbose = 1;
+
+ if (DW_CFA_expression_testcase(12, 1) != 0)
+ panic("r12 should be clobbered at height 1 (DW_CFA_expression_inner)");
+ if (DW_CFA_expression_testcase(12, 2) != 111222333)
+ panic("r12 should be restored at height 2 (DW_CFA_expression_testcase)");
+
+ if (nerrors > 0)
+ {
+ fprintf (stderr, "FAILURE: detected %d errors\n", nerrors);
+ exit (-1);
+ }
+
+ if (verbose)
+ printf ("SUCCESS.\n");
+ return 0;
+}
#include <sys/resource.h>
#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/wait.h>
#define panic(args...) \
{ fprintf (stderr, args); exit (-1); }
--- /dev/null
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#if !defined(UNW_REMOTE_ONLY)
+#include "Gx64-test-dwarf-expressions.c"
+#endif
if USE_ALTIVEC
noinst_PROGRAMS_arch += ppc64-test-altivec
endif #USE_ALTIVEC
-endif #ARCH_PPC64
+else #!ARCH_PPC64
+if ARCH_X86_64
+ check_PROGRAMS_arch += Gx64-test-dwarf-expressions Lx64-test-dwarf-expressions x64-unwind-badjmp-signal-frame
+endif #ARCH X86_64
+endif #!ARCH_PPC64
endif #!ARCH_IA64
check_PROGRAMS_cdep += Gtest-bt Ltest-bt Gtest-exc Ltest-exc \
Gtest-init Ltest-init \
ia64_test_dyn1_SOURCES = ia64-test-dyn1.c ia64-dyn-asm.S flush-cache.S \
flush-cache.h
ppc64_test_altivec_SOURCES = ppc64-test-altivec.c ppc64-test-altivec-utils.c
+
+
+Gx64_test_dwarf_expressions_SOURCES = Gx64-test-dwarf-expressions.c \
+ x64-test-dwarf-expressions.S
+Lx64_test_dwarf_expressions_SOURCES = Lx64-test-dwarf-expressions.c \
+ x64-test-dwarf-expressions.S
+
+
Gtest_init_SOURCES = Gtest-init.cxx
Ltest_init_SOURCES = Ltest-init.cxx
Ltest_cxx_exceptions_SOURCES = Ltest-cxx-exceptions.cxx
Ltest_init_local_signal_SOURCES = Ltest-init-local-signal.c Ltest-init-local-signal-lib.c
+x64_unwind_badjmp_signal_frame_SOURCES = x64-unwind-badjmp-signal-frame.c
Gtest_dyn1_SOURCES = Gtest-dyn1.c flush-cache.S flush-cache.h
Ltest_dyn1_SOURCES = Ltest-dyn1.c flush-cache.S flush-cache.h
test_static_link_SOURCES = test-static-link-loc.c test-static-link-gen.c
Gtest_bt_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
Gtest_concurrent_LDADD = $(LIBUNWIND) $(LIBUNWIND_local) -lpthread
+x64_unwind_badjmp_signal_frame_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
Gtest_dyn1_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
Gtest_exc_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
Gtest_init_LDADD = $(LIBUNWIND) $(LIBUNWIND_local) @BACKTRACELIB@
ia64_test_dyn1_LDADD = $(LIBUNWIND)
ia64_test_sig_LDADD = $(LIBUNWIND)
ppc64_test_altivec_LDADD = $(LIBUNWIND)
+
+Gx64_test_dwarf_expressions_LDADD = $(LIBUNWIND) $(LIBUNWIND_local)
+Lx64_test_dwarf_expressions_LDADD = $(LIBUNWIND_local)
ignore _ftext
ignore _gp
fi
+
+ if [ ${os} == "solaris2.11" ]; then
+ ignore _PROCEDURE_LINKAGE_TABLE_
+ ignore _etext
+ fi
}
check_local_unw_abi () {
match _U_dyn_register
match unw_backtrace
- match backtrace
+ @CONFIG_WEAK_BACKTRACE_TRUE@match backtrace
case ${plat} in
arm)
match _U${plat}_get_exe_image_path
match ${plat}_lock
;;
+ s390x)
+ match _U${plat}_get_elf_image
+ match _U${plat}_get_exe_image_path
+ match _U${plat}_is_fpreg
+ match _UL${plat}_dwarf_search_unwind_table
+ match _UL${plat}_dwarf_find_unwind_table
+ match _U${plat}_setcontext
+ ;;
*)
match _U${plat}_is_fpreg
match _U${plat}_local_addr_space_init
match ${plat}_lock
;;
+ s390x)
+ match _U${plat}_is_fpreg
+ match _U${plat}_get_elf_image
+ match _U${plat}_get_exe_image_path
+ match _U${plat}_dwarf_search_unwind_table
+ match _U${plat}_dwarf_find_unwind_table
+ ;;
*)
match _U${plat}_is_fpreg
match _U${plat}_dwarf_search_unwind_table
#endif
#ifdef __GNUC__
+#ifndef __clang__
+// Gcc >= 8 became too good at inlining aliase c into b when using -O2 or -O3,
+// so force -O1 in all cases, otherwise a frame will be missing in the tests.
+#pragma GCC optimize "-O1"
+#endif
int c(int x) NOINLINE ALIAS(b);
-#define compiler_barrier() asm volatile("");
+#define compiler_barrier() __asm__ __volatile__ ("");
#else
int c(int x);
#define compiler_barrier()
#include <grp.h>
/* For SIGSEGV handler code */
-#include <execinfo.h>
+#if HAVE_EXECINFO_H
+# include <execinfo.h>
+#else
+ extern int backtrace (void **, int);
+#endif
#include <sys/ucontext.h>
#include <libunwind-coredump.h>
void *array[50];
int size;
size = backtrace(array, 50);
-#ifdef __linux__
+#if defined __linux__ && HAVE_EXECINFO_H
backtrace_symbols_fd(array, size, 2);
#endif
}
--- /dev/null
+.global DW_CFA_expression_testcase
+
+.extern recover_register
+
+.text
+
+# CFI expressions were added in DWARF v3 to allow compilers to specify memory
+# locations or register values using DWARF programs. These programs are simple
+# stack-based operations which allow the compiler to encode integer mathematics
+# and other complex logic. CFI expressions are therefore more powerful than the
+# conventional register + offset schemes.
+#
+# These tests capture a bug we have fixed in libunwind. CFI expression programs
+# always start with the current CFA pushed onto the stack. This file contains a
+# pair of routines which test CFI expression parsing. Specifically they test
+# DW_CFA_expression logic, which uses DWARF expressions to compute the address
+# where a non-volatile register was stored.
+#
+# Main calls DW_CFA_expression_testcase, which sets up known state in a
+# non-volatile (caller-saved) register. We use r12 for this purpose. After this
+# DW_CFA_expression_testcase then calls DW_CFA_expression_inner, which clobbers
+# r12 after stashing its value on the stack. This routine contains a DWARF3 CFI
+# expression to restore the value of r12 on unwind which should allow libunwind
+# to recover clobbered state. DW_CFA_expression_inner calls recover_register to
+# retrieve the cached register value. This function recovers the register value
+# by using libunwind to unwind the stack through DW_CFA_expression_inner and up
+# to the call site in DW_CFA_expression_testcase. If our expression is correct,
+# libunwind will be able to restore r12 from the stack.
+#
+# BE CAREFUL WITH rdi, rsi, rax HERE! The arguments to recover_register are
+# passed in via rdi, rsi and I just let them flow through unchanged. Similarly
+# RAX flows back unchanged. Adding any function calls to the below may clobber
+# these registers and cause this test to fail mysteriously.
+
+
+########################################################
+# Test: Restoring a register using a DW_CFA_expression #
+# which uses implicit CFA pushed onto stack. #
+########################################################
+
+.type DW_CFA_expression_testcase STT_FUNC
+DW_CFA_expression_testcase:
+ .cfi_startproc
+ push %r12
+ .cfi_adjust_cfa_offset 8
+ # Move our sentinel (known) value into non-volatile (Callee-saved) r12
+ mov $111222333, %r12
+ .cfi_rel_offset %r12, 0
+ call DW_CFA_expression_inner
+ pop %r12
+ .cfi_restore %r12
+ .cfi_adjust_cfa_offset -8
+ ret
+ .cfi_endproc
+.size DW_CFA_expression_testcase,.-DW_CFA_expression_testcase
+
+.type DW_CFA_expression_inner STT_FUNC
+DW_CFA_expression_inner:
+ .cfi_startproc
+ push %r12
+ .cfi_adjust_cfa_offset 8
+ # !! IMPORTANT BIT !! The test is all about how we parse the following bytes.
+ # Now we use an expression to describe where our sentinel value is stored:
+ # DW_CFA_expression(0x10), r12(0x0c), Length(0x02), (preamble)
+ # DW_OP_lit16(0x40), DW_OP_minus(0x1c) (instructions)
+ # Parsing starts with the CFA on the stack, then pushes 16, then does a minus
+ # which is eqivalent to a=pop(), b=pop(), push(b-a), leaving us with a value
+ # of cfa-16 (cfa points at old rsp, cfa-8 is our rip, so we stored r12 at
+ # cfa-16).
+ xor %r12, %r12 # Trash r12
+ .cfi_escape 0x10, 0x0c, 0x2, 0x40, 0x1c # DW_CFA_expression for recovery
+ call recover_register
+ pop %r12
+ .cfi_restore %r12
+ .cfi_adjust_cfa_offset -8
+ ret
+ .cfi_endproc
+.size DW_CFA_expression_inner,.-DW_CFA_expression_inner
--- /dev/null
+/* libunwind - a platform-independent unwind library
+ Copyright (C) 2019 Brock York <twunknown AT gmail.com>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <execinfo.h>
+#include <sys/types.h>
+#include <sys/ucontext.h>
+#include <unistd.h>
+
+#ifdef HAVE_SYS_PTRACE_H
+#include <sys/ptrace.h>
+#endif
+
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+
+/*
+ * unwind in the signal handler checking the backtrace is correct
+ * after a bad jump.
+ */
+void handle_sigsegv(int signal, siginfo_t *info, void *ucontext)
+{
+ /*
+ * 0 = success
+ * !0 = general failure
+ * 77 = test skipped
+ * 99 = complete failure
+ */
+ int test_status = 0;
+ unw_cursor_t cursor; unw_context_t uc;
+ unw_word_t ip, sp, offset;
+ char name[1000];
+ int found_signal_frame = 0;
+ int i = 0;
+ char *names[] = {
+ "",
+ "main",
+ };
+ int names_count = sizeof(names) / sizeof(*names);
+
+ unw_getcontext(&uc);
+ unw_init_local(&cursor, &uc);
+
+ while (unw_step(&cursor) > 0 && !test_status)
+ {
+ if (unw_is_signal_frame(&cursor))
+ {
+ found_signal_frame = 1;
+ }
+ if (found_signal_frame)
+ {
+ unw_get_reg(&cursor, UNW_REG_IP, &ip);
+ unw_get_reg(&cursor, UNW_REG_SP, &sp);
+ memset(name, 0, sizeof(char) * 1000);
+ unw_get_proc_name(&cursor, name, sizeof(char) * 1000, &offset);
+ printf("ip = %lx, sp = %lx offset = %lx name = %s\n", (long) ip, (long) sp, (long) offset, name);
+ if (i < names_count)
+ {
+ if (strcmp(names[i], name) != 0)
+ {
+ test_status = 1;
+ printf("frame %s doesn't match expected frame %s\n", name, names[i]);
+ }
+ else
+ {
+ i += 1;
+ }
+ }
+ }
+ }
+
+ if (i != names_count) //Make sure we found all the frames!
+ {
+ printf("Failed to find all frames i:%d != names_count:%d\n", i, names_count);
+ test_status = 1;
+ }
+
+ /*return test_status to test harness*/
+ exit(test_status);
+}
+
+void (*invalid_function)() = (void*)1;
+
+int main(int argc, char *argv[])
+{
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = handle_sigsegv;
+ sa.sa_flags = SA_SIGINFO;
+ sigaction(SIGSEGV, &sa, NULL);
+
+ invalid_function();
+
+ /*
+ * 99 is the hard error exit status for automake tests:
+ * https://www.gnu.org/software/automake/manual/html_node/Scripts_002dbased-Testsuites.html#Scripts_002dbased-Testsuites
+ * If we dont end up in the signal handler something went horribly wrong.
+ */
+ return 99;
+}