From 3bf65e4c8bc79809b1c189b6203812e3b6777ead Mon Sep 17 00:00:00 2001 From: Delisa Mason Date: Fri, 2 Nov 2018 13:12:40 -0700 Subject: [PATCH 1/2] build: Link nongnu libunwind nongnu libunwind is available on armeabi and armeabi-v7a, and produces a more consistent and stable result. Provided we can conditionally switch based on the device ABI, we will no longer need the single frame mode to avoid segfaults from unwind for thread kill events. --- ndk/src/main/CMakeLists.txt | 7 + .../libunwind/include/__libunwind_config.h | 55 ++ .../external/libunwind/include/libunwind.h | 508 ++++++++++++++++++ .../main/jni/utils/stack_unwinder_libunwind.c | 4 + 4 files changed, 574 insertions(+) create mode 100644 ndk/src/main/jni/external/libunwind/include/__libunwind_config.h create mode 100644 ndk/src/main/jni/external/libunwind/include/libunwind.h diff --git a/ndk/src/main/CMakeLists.txt b/ndk/src/main/CMakeLists.txt index 3f43a24bb1..4547bab85c 100644 --- a/ndk/src/main/CMakeLists.txt +++ b/ndk/src/main/CMakeLists.txt @@ -24,6 +24,7 @@ add_library( # Specifies the name of the library. include_directories( jni/ jni/deps + jni/external/libunwind/include ) target_include_directories(bugsnag-ndk PRIVATE ${BUGSNAG_DIR}/assets/include) @@ -47,3 +48,9 @@ set_target_properties(bugsnag-ndk COMPILE_OPTIONS -Werror -Wall -pedantic) +if(${ANDROID_ABI} STREQUAL "armeabi" OR ${ANDROID_ABI} STREQUAL "armeabi-v7a") + add_library(libunwind STATIC IMPORTED) + set_target_properties(libunwind PROPERTIES IMPORTED_LOCATION + ${ANDROID_NDK}/sources/cxx-stl/llvm-libc++/libs/${ANDROID_ABI}/libunwind.a) + target_link_libraries(bugsnag-ndk libunwind) +endif() diff --git a/ndk/src/main/jni/external/libunwind/include/__libunwind_config.h b/ndk/src/main/jni/external/libunwind/include/__libunwind_config.h new file mode 100644 index 0000000000..bc4e696c0c --- /dev/null +++ b/ndk/src/main/jni/external/libunwind/include/__libunwind_config.h @@ -0,0 +1,55 @@ +//===------------------------- __libunwind_config.h -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef ____LIBUNWIND_CONFIG_H__ +#define ____LIBUNWIND_CONFIG_H__ +#if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && \ + !defined(__ARM_DWARF_EH__) +#define _LIBUNWIND_ARM_EHABI 1 +#else +#define _LIBUNWIND_ARM_EHABI 0 +#endif +#if defined(_LIBUNWIND_IS_NATIVE_ONLY) +# if defined(__i386__) +# define _LIBUNWIND_TARGET_I386 1 +# define _LIBUNWIND_CONTEXT_SIZE 8 +# define _LIBUNWIND_CURSOR_SIZE 19 +# elif defined(__x86_64__) +# define _LIBUNWIND_TARGET_X86_64 1 +# define _LIBUNWIND_CONTEXT_SIZE 21 +# define _LIBUNWIND_CURSOR_SIZE 33 +# elif defined(__ppc__) +# define _LIBUNWIND_TARGET_PPC 1 +# define _LIBUNWIND_CONTEXT_SIZE 117 +# define _LIBUNWIND_CURSOR_SIZE 128 +# elif defined(__aarch64__) +# define _LIBUNWIND_TARGET_AARCH64 1 +# define _LIBUNWIND_CONTEXT_SIZE 66 +# define _LIBUNWIND_CURSOR_SIZE 78 +# elif defined(__arm__) +# define _LIBUNWIND_TARGET_ARM 1 +# define _LIBUNWIND_CONTEXT_SIZE 60 +# define _LIBUNWIND_CURSOR_SIZE 67 +# elif defined(__or1k__) +# define _LIBUNWIND_TARGET_OR1K 1 +# define _LIBUNWIND_CONTEXT_SIZE 16 +# define _LIBUNWIND_CURSOR_SIZE 28 +# else +# error "Unsupported architecture." +# endif +#else // !_LIBUNWIND_IS_NATIVE_ONLY +# define _LIBUNWIND_TARGET_I386 1 +# define _LIBUNWIND_TARGET_X86_64 1 +# define _LIBUNWIND_TARGET_PPC 1 +# define _LIBUNWIND_TARGET_AARCH64 1 +# define _LIBUNWIND_TARGET_ARM 1 +# define _LIBUNWIND_TARGET_OR1K 1 +# define _LIBUNWIND_CONTEXT_SIZE 128 +# define _LIBUNWIND_CURSOR_SIZE 140 +#endif // _LIBUNWIND_IS_NATIVE_ONLY +#endif // ____LIBUNWIND_CONFIG_H__ \ No newline at end of file diff --git a/ndk/src/main/jni/external/libunwind/include/libunwind.h b/ndk/src/main/jni/external/libunwind/include/libunwind.h new file mode 100644 index 0000000000..555b1d3b15 --- /dev/null +++ b/ndk/src/main/jni/external/libunwind/include/libunwind.h @@ -0,0 +1,508 @@ +//===---------------------------- libunwind.h -----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +// +// Compatible with libuwind API documented at: +// http://www.nongnu.org/libunwind/man/libunwind(3).html +// +//===----------------------------------------------------------------------===// +#ifndef __LIBUNWIND__ +#define __LIBUNWIND__ +#include <__libunwind_config.h> +#include +#include +#ifdef __APPLE__ + #include + #ifdef __arm__ + #define LIBUNWIND_AVAIL __attribute__((unavailable)) + #else + #define LIBUNWIND_AVAIL __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_5_0) + #endif +#else + #define LIBUNWIND_AVAIL +#endif +/* error codes */ +enum { + UNW_ESUCCESS = 0, /* no error */ + UNW_EUNSPEC = -6540, /* unspecified (general) error */ + UNW_ENOMEM = -6541, /* out of memory */ + UNW_EBADREG = -6542, /* bad register number */ + UNW_EREADONLYREG = -6543, /* attempt to write read-only register */ + UNW_ESTOPUNWIND = -6544, /* stop unwinding */ + UNW_EINVALIDIP = -6545, /* invalid IP */ + UNW_EBADFRAME = -6546, /* bad frame */ + UNW_EINVAL = -6547, /* unsupported operation or bad value */ + UNW_EBADVERSION = -6548, /* unwind info has unsupported version */ + UNW_ENOINFO = -6549 /* no unwind info found */ +}; +struct unw_context_t { + uint64_t data[_LIBUNWIND_CONTEXT_SIZE]; +}; +typedef struct unw_context_t unw_context_t; +struct unw_cursor_t { + uint64_t data[_LIBUNWIND_CURSOR_SIZE]; +}; +typedef struct unw_cursor_t unw_cursor_t; +typedef struct unw_addr_space *unw_addr_space_t; +typedef int unw_regnum_t; +#if _LIBUNWIND_ARM_EHABI +typedef uint32_t unw_word_t; +typedef uint64_t unw_fpreg_t; +#else +typedef uint64_t unw_word_t; +typedef double unw_fpreg_t; +#endif +struct unw_proc_info_t { + unw_word_t start_ip; /* start address of function */ + unw_word_t end_ip; /* address after end of function */ + unw_word_t lsda; /* address of language specific data area, */ + /* or zero if not used */ + unw_word_t handler; /* personality routine, or zero if not used */ + unw_word_t gp; /* not used */ + unw_word_t flags; /* not used */ + uint32_t format; /* compact unwind encoding, or zero if none */ + uint32_t unwind_info_size; /* size of dwarf unwind info, or zero if none */ + unw_word_t unwind_info; /* address of dwarf unwind info, or zero */ + unw_word_t extra; /* mach_header of mach-o image containing func */ +}; +typedef struct unw_proc_info_t unw_proc_info_t; +#ifdef __cplusplus +extern "C" { +#endif +extern int unw_getcontext(unw_context_t *) LIBUNWIND_AVAIL; +extern int unw_init_local(unw_cursor_t *, unw_context_t *) LIBUNWIND_AVAIL; +extern int unw_step(unw_cursor_t *) LIBUNWIND_AVAIL; +extern int unw_get_reg(unw_cursor_t *, unw_regnum_t, unw_word_t *) LIBUNWIND_AVAIL; +extern int unw_get_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t *) LIBUNWIND_AVAIL; +extern int unw_set_reg(unw_cursor_t *, unw_regnum_t, unw_word_t) LIBUNWIND_AVAIL; +extern int unw_set_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t) LIBUNWIND_AVAIL; +extern int unw_resume(unw_cursor_t *) LIBUNWIND_AVAIL; +#ifdef __arm__ +/* Save VFP registers in FSTMX format (instead of FSTMD). */ +extern void unw_save_vfp_as_X(unw_cursor_t *) LIBUNWIND_AVAIL; +#endif +extern const char *unw_regname(unw_cursor_t *, unw_regnum_t) LIBUNWIND_AVAIL; +extern int unw_get_proc_info(unw_cursor_t *, unw_proc_info_t *) LIBUNWIND_AVAIL; +extern int unw_is_fpreg(unw_cursor_t *, unw_regnum_t) LIBUNWIND_AVAIL; +extern int unw_is_signal_frame(unw_cursor_t *) LIBUNWIND_AVAIL; +extern int unw_get_proc_name(unw_cursor_t *, char *, size_t, unw_word_t *) LIBUNWIND_AVAIL; +//extern int unw_get_save_loc(unw_cursor_t*, int, unw_save_loc_t*); +extern unw_addr_space_t unw_local_addr_space; +#ifdef UNW_REMOTE +/* + * Mac OS X "remote" API for unwinding other processes on same machine + * + */ +extern unw_addr_space_t unw_create_addr_space_for_task(task_t); +extern void unw_destroy_addr_space(unw_addr_space_t); +extern int unw_init_remote_thread(unw_cursor_t *, unw_addr_space_t, thread_t *); +#endif /* UNW_REMOTE */ +/* + * traditional libuwind "remote" API + * NOT IMPLEMENTED on Mac OS X + * + * extern int unw_init_remote(unw_cursor_t*, unw_addr_space_t, + * thread_t*); + * extern unw_accessors_t unw_get_accessors(unw_addr_space_t); + * extern unw_addr_space_t unw_create_addr_space(unw_accessors_t, int); + * extern void unw_flush_cache(unw_addr_space_t, unw_word_t, + * unw_word_t); + * extern int unw_set_caching_policy(unw_addr_space_t, + * unw_caching_policy_t); + * extern void _U_dyn_register(unw_dyn_info_t*); + * extern void _U_dyn_cancel(unw_dyn_info_t*); + */ +#ifdef __cplusplus +} +#endif +// architecture independent register numbers +enum { + UNW_REG_IP = -1, // instruction pointer + UNW_REG_SP = -2, // stack pointer +}; +// 32-bit x86 registers +enum { + UNW_X86_EAX = 0, + UNW_X86_ECX = 1, + UNW_X86_EDX = 2, + UNW_X86_EBX = 3, + UNW_X86_EBP = 4, + UNW_X86_ESP = 5, + UNW_X86_ESI = 6, + UNW_X86_EDI = 7 +}; +// 64-bit x86_64 registers +enum { + UNW_X86_64_RAX = 0, + UNW_X86_64_RDX = 1, + UNW_X86_64_RCX = 2, + UNW_X86_64_RBX = 3, + UNW_X86_64_RSI = 4, + UNW_X86_64_RDI = 5, + UNW_X86_64_RBP = 6, + UNW_X86_64_RSP = 7, + UNW_X86_64_R8 = 8, + UNW_X86_64_R9 = 9, + UNW_X86_64_R10 = 10, + UNW_X86_64_R11 = 11, + UNW_X86_64_R12 = 12, + UNW_X86_64_R13 = 13, + UNW_X86_64_R14 = 14, + UNW_X86_64_R15 = 15 +}; +// 32-bit ppc register numbers +enum { + UNW_PPC_R0 = 0, + UNW_PPC_R1 = 1, + UNW_PPC_R2 = 2, + UNW_PPC_R3 = 3, + UNW_PPC_R4 = 4, + UNW_PPC_R5 = 5, + UNW_PPC_R6 = 6, + UNW_PPC_R7 = 7, + UNW_PPC_R8 = 8, + UNW_PPC_R9 = 9, + UNW_PPC_R10 = 10, + UNW_PPC_R11 = 11, + UNW_PPC_R12 = 12, + UNW_PPC_R13 = 13, + UNW_PPC_R14 = 14, + UNW_PPC_R15 = 15, + UNW_PPC_R16 = 16, + UNW_PPC_R17 = 17, + UNW_PPC_R18 = 18, + UNW_PPC_R19 = 19, + UNW_PPC_R20 = 20, + UNW_PPC_R21 = 21, + UNW_PPC_R22 = 22, + UNW_PPC_R23 = 23, + UNW_PPC_R24 = 24, + UNW_PPC_R25 = 25, + UNW_PPC_R26 = 26, + UNW_PPC_R27 = 27, + UNW_PPC_R28 = 28, + UNW_PPC_R29 = 29, + UNW_PPC_R30 = 30, + UNW_PPC_R31 = 31, + UNW_PPC_F0 = 32, + UNW_PPC_F1 = 33, + UNW_PPC_F2 = 34, + UNW_PPC_F3 = 35, + UNW_PPC_F4 = 36, + UNW_PPC_F5 = 37, + UNW_PPC_F6 = 38, + UNW_PPC_F7 = 39, + UNW_PPC_F8 = 40, + UNW_PPC_F9 = 41, + UNW_PPC_F10 = 42, + UNW_PPC_F11 = 43, + UNW_PPC_F12 = 44, + UNW_PPC_F13 = 45, + UNW_PPC_F14 = 46, + UNW_PPC_F15 = 47, + UNW_PPC_F16 = 48, + UNW_PPC_F17 = 49, + UNW_PPC_F18 = 50, + UNW_PPC_F19 = 51, + UNW_PPC_F20 = 52, + UNW_PPC_F21 = 53, + UNW_PPC_F22 = 54, + UNW_PPC_F23 = 55, + UNW_PPC_F24 = 56, + UNW_PPC_F25 = 57, + UNW_PPC_F26 = 58, + UNW_PPC_F27 = 59, + UNW_PPC_F28 = 60, + UNW_PPC_F29 = 61, + UNW_PPC_F30 = 62, + UNW_PPC_F31 = 63, + UNW_PPC_MQ = 64, + UNW_PPC_LR = 65, + UNW_PPC_CTR = 66, + UNW_PPC_AP = 67, + UNW_PPC_CR0 = 68, + UNW_PPC_CR1 = 69, + UNW_PPC_CR2 = 70, + UNW_PPC_CR3 = 71, + UNW_PPC_CR4 = 72, + UNW_PPC_CR5 = 73, + UNW_PPC_CR6 = 74, + UNW_PPC_CR7 = 75, + UNW_PPC_XER = 76, + UNW_PPC_V0 = 77, + UNW_PPC_V1 = 78, + UNW_PPC_V2 = 79, + UNW_PPC_V3 = 80, + UNW_PPC_V4 = 81, + UNW_PPC_V5 = 82, + UNW_PPC_V6 = 83, + UNW_PPC_V7 = 84, + UNW_PPC_V8 = 85, + UNW_PPC_V9 = 86, + UNW_PPC_V10 = 87, + UNW_PPC_V11 = 88, + UNW_PPC_V12 = 89, + UNW_PPC_V13 = 90, + UNW_PPC_V14 = 91, + UNW_PPC_V15 = 92, + UNW_PPC_V16 = 93, + UNW_PPC_V17 = 94, + UNW_PPC_V18 = 95, + UNW_PPC_V19 = 96, + UNW_PPC_V20 = 97, + UNW_PPC_V21 = 98, + UNW_PPC_V22 = 99, + UNW_PPC_V23 = 100, + UNW_PPC_V24 = 101, + UNW_PPC_V25 = 102, + UNW_PPC_V26 = 103, + UNW_PPC_V27 = 104, + UNW_PPC_V28 = 105, + UNW_PPC_V29 = 106, + UNW_PPC_V30 = 107, + UNW_PPC_V31 = 108, + UNW_PPC_VRSAVE = 109, + UNW_PPC_VSCR = 110, + UNW_PPC_SPE_ACC = 111, + UNW_PPC_SPEFSCR = 112 +}; +// 64-bit ARM64 registers +enum { + UNW_ARM64_X0 = 0, + UNW_ARM64_X1 = 1, + UNW_ARM64_X2 = 2, + UNW_ARM64_X3 = 3, + UNW_ARM64_X4 = 4, + UNW_ARM64_X5 = 5, + UNW_ARM64_X6 = 6, + UNW_ARM64_X7 = 7, + UNW_ARM64_X8 = 8, + UNW_ARM64_X9 = 9, + UNW_ARM64_X10 = 10, + UNW_ARM64_X11 = 11, + UNW_ARM64_X12 = 12, + UNW_ARM64_X13 = 13, + UNW_ARM64_X14 = 14, + UNW_ARM64_X15 = 15, + UNW_ARM64_X16 = 16, + UNW_ARM64_X17 = 17, + UNW_ARM64_X18 = 18, + UNW_ARM64_X19 = 19, + UNW_ARM64_X20 = 20, + UNW_ARM64_X21 = 21, + UNW_ARM64_X22 = 22, + UNW_ARM64_X23 = 23, + UNW_ARM64_X24 = 24, + UNW_ARM64_X25 = 25, + UNW_ARM64_X26 = 26, + UNW_ARM64_X27 = 27, + UNW_ARM64_X28 = 28, + UNW_ARM64_X29 = 29, + UNW_ARM64_FP = 29, + UNW_ARM64_X30 = 30, + UNW_ARM64_LR = 30, + UNW_ARM64_X31 = 31, + UNW_ARM64_SP = 31, + // reserved block + UNW_ARM64_D0 = 64, + UNW_ARM64_D1 = 65, + UNW_ARM64_D2 = 66, + UNW_ARM64_D3 = 67, + UNW_ARM64_D4 = 68, + UNW_ARM64_D5 = 69, + UNW_ARM64_D6 = 70, + UNW_ARM64_D7 = 71, + UNW_ARM64_D8 = 72, + UNW_ARM64_D9 = 73, + UNW_ARM64_D10 = 74, + UNW_ARM64_D11 = 75, + UNW_ARM64_D12 = 76, + UNW_ARM64_D13 = 77, + UNW_ARM64_D14 = 78, + UNW_ARM64_D15 = 79, + UNW_ARM64_D16 = 80, + UNW_ARM64_D17 = 81, + UNW_ARM64_D18 = 82, + UNW_ARM64_D19 = 83, + UNW_ARM64_D20 = 84, + UNW_ARM64_D21 = 85, + UNW_ARM64_D22 = 86, + UNW_ARM64_D23 = 87, + UNW_ARM64_D24 = 88, + UNW_ARM64_D25 = 89, + UNW_ARM64_D26 = 90, + UNW_ARM64_D27 = 91, + UNW_ARM64_D28 = 92, + UNW_ARM64_D29 = 93, + UNW_ARM64_D30 = 94, + UNW_ARM64_D31 = 95, +}; +// 32-bit ARM registers. Numbers match DWARF for ARM spec #3.1 Table 1. +// Naming scheme uses recommendations given in Note 4 for VFP-v2 and VFP-v3. +// In this scheme, even though the 64-bit floating point registers D0-D31 +// overlap physically with the 32-bit floating pointer registers S0-S31, +// they are given a non-overlapping range of register numbers. +// +// Commented out ranges are not preserved during unwinding. +enum { + UNW_ARM_R0 = 0, + UNW_ARM_R1 = 1, + UNW_ARM_R2 = 2, + UNW_ARM_R3 = 3, + UNW_ARM_R4 = 4, + UNW_ARM_R5 = 5, + UNW_ARM_R6 = 6, + UNW_ARM_R7 = 7, + UNW_ARM_R8 = 8, + UNW_ARM_R9 = 9, + UNW_ARM_R10 = 10, + UNW_ARM_R11 = 11, + UNW_ARM_R12 = 12, + UNW_ARM_SP = 13, // Logical alias for UNW_REG_SP + UNW_ARM_R13 = 13, + UNW_ARM_LR = 14, + UNW_ARM_R14 = 14, + UNW_ARM_IP = 15, // Logical alias for UNW_REG_IP + UNW_ARM_R15 = 15, + // 16-63 -- OBSOLETE. Used in VFP1 to represent both S0-S31 and D0-D31. + UNW_ARM_S0 = 64, + UNW_ARM_S1 = 65, + UNW_ARM_S2 = 66, + UNW_ARM_S3 = 67, + UNW_ARM_S4 = 68, + UNW_ARM_S5 = 69, + UNW_ARM_S6 = 70, + UNW_ARM_S7 = 71, + UNW_ARM_S8 = 72, + UNW_ARM_S9 = 73, + UNW_ARM_S10 = 74, + UNW_ARM_S11 = 75, + UNW_ARM_S12 = 76, + UNW_ARM_S13 = 77, + UNW_ARM_S14 = 78, + UNW_ARM_S15 = 79, + UNW_ARM_S16 = 80, + UNW_ARM_S17 = 81, + UNW_ARM_S18 = 82, + UNW_ARM_S19 = 83, + UNW_ARM_S20 = 84, + UNW_ARM_S21 = 85, + UNW_ARM_S22 = 86, + UNW_ARM_S23 = 87, + UNW_ARM_S24 = 88, + UNW_ARM_S25 = 89, + UNW_ARM_S26 = 90, + UNW_ARM_S27 = 91, + UNW_ARM_S28 = 92, + UNW_ARM_S29 = 93, + UNW_ARM_S30 = 94, + UNW_ARM_S31 = 95, + // 96-103 -- OBSOLETE. F0-F7. Used by the FPA system. Superseded by VFP. + // 104-111 -- wCGR0-wCGR7, ACC0-ACC7 (Intel wireless MMX) + UNW_ARM_WR0 = 112, + UNW_ARM_WR1 = 113, + UNW_ARM_WR2 = 114, + UNW_ARM_WR3 = 115, + UNW_ARM_WR4 = 116, + UNW_ARM_WR5 = 117, + UNW_ARM_WR6 = 118, + UNW_ARM_WR7 = 119, + UNW_ARM_WR8 = 120, + UNW_ARM_WR9 = 121, + UNW_ARM_WR10 = 122, + UNW_ARM_WR11 = 123, + UNW_ARM_WR12 = 124, + UNW_ARM_WR13 = 125, + UNW_ARM_WR14 = 126, + UNW_ARM_WR15 = 127, + // 128-133 -- SPSR, SPSR_{FIQ|IRQ|ABT|UND|SVC} + // 134-143 -- Reserved + // 144-150 -- R8_USR-R14_USR + // 151-157 -- R8_FIQ-R14_FIQ + // 158-159 -- R13_IRQ-R14_IRQ + // 160-161 -- R13_ABT-R14_ABT + // 162-163 -- R13_UND-R14_UND + // 164-165 -- R13_SVC-R14_SVC + // 166-191 -- Reserved + UNW_ARM_WC0 = 192, + UNW_ARM_WC1 = 193, + UNW_ARM_WC2 = 194, + UNW_ARM_WC3 = 195, + // 196-199 -- wC4-wC7 (Intel wireless MMX control) + // 200-255 -- Reserved + UNW_ARM_D0 = 256, + UNW_ARM_D1 = 257, + UNW_ARM_D2 = 258, + UNW_ARM_D3 = 259, + UNW_ARM_D4 = 260, + UNW_ARM_D5 = 261, + UNW_ARM_D6 = 262, + UNW_ARM_D7 = 263, + UNW_ARM_D8 = 264, + UNW_ARM_D9 = 265, + UNW_ARM_D10 = 266, + UNW_ARM_D11 = 267, + UNW_ARM_D12 = 268, + UNW_ARM_D13 = 269, + UNW_ARM_D14 = 270, + UNW_ARM_D15 = 271, + UNW_ARM_D16 = 272, + UNW_ARM_D17 = 273, + UNW_ARM_D18 = 274, + UNW_ARM_D19 = 275, + UNW_ARM_D20 = 276, + UNW_ARM_D21 = 277, + UNW_ARM_D22 = 278, + UNW_ARM_D23 = 279, + UNW_ARM_D24 = 280, + UNW_ARM_D25 = 281, + UNW_ARM_D26 = 282, + UNW_ARM_D27 = 283, + UNW_ARM_D28 = 284, + UNW_ARM_D29 = 285, + UNW_ARM_D30 = 286, + UNW_ARM_D31 = 287, + // 288-319 -- Reserved for VFP/Neon + // 320-8191 -- Reserved + // 8192-16383 -- Unspecified vendor co-processor register. +}; +// OpenRISC1000 register numbers +enum { + UNW_OR1K_R0 = 0, + UNW_OR1K_R1 = 1, + UNW_OR1K_R2 = 2, + UNW_OR1K_R3 = 3, + UNW_OR1K_R4 = 4, + UNW_OR1K_R5 = 5, + UNW_OR1K_R6 = 6, + UNW_OR1K_R7 = 7, + UNW_OR1K_R8 = 8, + UNW_OR1K_R9 = 9, + UNW_OR1K_R10 = 10, + UNW_OR1K_R11 = 11, + UNW_OR1K_R12 = 12, + UNW_OR1K_R13 = 13, + UNW_OR1K_R14 = 14, + UNW_OR1K_R15 = 15, + UNW_OR1K_R16 = 16, + UNW_OR1K_R17 = 17, + UNW_OR1K_R18 = 18, + UNW_OR1K_R19 = 19, + UNW_OR1K_R20 = 20, + UNW_OR1K_R21 = 21, + UNW_OR1K_R22 = 22, + UNW_OR1K_R23 = 23, + UNW_OR1K_R24 = 24, + UNW_OR1K_R25 = 25, + UNW_OR1K_R26 = 26, + UNW_OR1K_R27 = 27, + UNW_OR1K_R28 = 28, + UNW_OR1K_R29 = 29, + UNW_OR1K_R30 = 30, + UNW_OR1K_R31 = 31, +}; +#endif \ No newline at end of file diff --git a/ndk/src/main/jni/utils/stack_unwinder_libunwind.c b/ndk/src/main/jni/utils/stack_unwinder_libunwind.c index 99ccc079c9..27a644f10b 100644 --- a/ndk/src/main/jni/utils/stack_unwinder_libunwind.c +++ b/ndk/src/main/jni/utils/stack_unwinder_libunwind.c @@ -4,6 +4,10 @@ #include #include +#if defined(__arm__) +#include +#endif + typedef struct { size_t frame_count; uintptr_t frame_addresses[BUGSNAG_FRAMES_MAX]; From 16c8cf91872e6394fd50a6415e82024b8edddabf Mon Sep 17 00:00:00 2001 From: Delisa Mason Date: Tue, 6 Nov 2018 17:33:20 -0800 Subject: [PATCH 2/2] fix: Only set stack registers / exit unwind early on 32-bit Uses the ABI list to conditionally switch unwinders on ARM. While the stack enhancements added in #378 are intended only to be run on 32-bit devices and gated to the __arm__ build, when 64-bit devices are running the 32-bit library, the stacktraces for those devices were unintentionally affected. This is the case when an app is configured to only run 32-bit binaries (Android cannot run both 64-bit and 32-bit binaries at once), and many popular libraries (e.g. React Native) only distribute 32-bit libraries. To further improve the stack quality on 32-bit, switches the underlying unwinder from gnu unwind to non-gnu libunwind. Improves frame resolution and does not have the crash in unwind when setting registers. Fixes #382 --- CHANGELOG.md | 8 ++ .../com/bugsnag/android/ndk/NativeBridge.java | 13 ++- ndk/src/main/jni/bugsnag_ndk.c | 4 +- ndk/src/main/jni/utils/stack_unwinder.c | 4 +- ndk/src/main/jni/utils/stack_unwinder.h | 2 +- .../main/jni/utils/stack_unwinder_libunwind.c | 108 ++++++++++-------- .../main/jni/utils/stack_unwinder_libunwind.h | 2 +- 7 files changed, 83 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34b5378c56..cb109ac4f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## TBD + +### Bug fixes + +* [NDK] Fix regression in 4.9.0 which truncated stacktraces on 64-bit devices to + a single frame + [#383](https://github.com/bugsnag/bugsnag-android/pull/383) + ## 4.9.1 (2018-11-01) ### Bug fixes diff --git a/ndk/src/main/java/com/bugsnag/android/ndk/NativeBridge.java b/ndk/src/main/java/com/bugsnag/android/ndk/NativeBridge.java index 2ecc505092..aeeebb43bc 100644 --- a/ndk/src/main/java/com/bugsnag/android/ndk/NativeBridge.java +++ b/ndk/src/main/java/com/bugsnag/android/ndk/NativeBridge.java @@ -32,7 +32,8 @@ public class NativeBridge implements Observer { private static final Lock lock = new ReentrantLock(); private static final AtomicBoolean installed = new AtomicBoolean(false); - public static native void install(String reportingDirectory, boolean autoNotify, int apiLevel); + public static native void install(String reportingDirectory, boolean autoNotify, int apiLevel, + boolean is32bit); public static native void deliverReportAtPath(String filePath); @@ -216,7 +217,15 @@ private void handleInstallMessage(Object arg) { return; } String reportPath = reportDirectory + UUID.randomUUID().toString() + ".crash"; - install(reportPath, true, Build.VERSION.SDK_INT); + String[] abis = (String[])NativeInterface.getDeviceData().get("cpuAbi"); + boolean is32bit = true; + for (String abi : abis) { + if (abi.contains("64")) { + is32bit = false; + break; + } + } + install(reportPath, true, Build.VERSION.SDK_INT, is32bit); installed.set(true); } finally { lock.unlock(); diff --git a/ndk/src/main/jni/bugsnag_ndk.c b/ndk/src/main/jni/bugsnag_ndk.c index 04781decbc..d418f0e947 100644 --- a/ndk/src/main/jni/bugsnag_ndk.c +++ b/ndk/src/main/jni/bugsnag_ndk.c @@ -43,9 +43,9 @@ bsg_unwinder bsg_configured_unwind_style() { JNIEXPORT void JNICALL Java_com_bugsnag_android_ndk_NativeBridge_install( JNIEnv *env, jobject _this, jstring _report_path, jboolean auto_notify, - jint _api_level) { + jint _api_level, jboolean is32bit) { bsg_environment *bugsnag_env = calloc(1, sizeof(bsg_environment)); - bugsnag_env->unwind_style = bsg_get_unwind_type((int)_api_level); + bugsnag_env->unwind_style = bsg_get_unwind_type((int)_api_level, (bool)is32bit); bugsnag_env->report_header.big_endian = htonl(47) == 47; // potentially too clever, see man 3 htonl bugsnag_env->report_header.version = BUGSNAG_REPORT_VERSION; diff --git a/ndk/src/main/jni/utils/stack_unwinder.c b/ndk/src/main/jni/utils/stack_unwinder.c index e3d1a849fc..b41811f604 100644 --- a/ndk/src/main/jni/utils/stack_unwinder.c +++ b/ndk/src/main/jni/utils/stack_unwinder.c @@ -12,8 +12,8 @@ #define BSG_LIBCORKSCREW_MIN_LEVEL 16 #define BSG_LIBCORKSCREW_MAX_LEVEL 19 -bsg_unwinder bsg_get_unwind_type(int apiLevel) { - if (apiLevel >= BSG_LIBUNWIND_LEVEL && bsg_configure_libunwind()) { +bsg_unwinder bsg_get_unwind_type(int apiLevel, bool is32bit) { + if (apiLevel >= BSG_LIBUNWIND_LEVEL && bsg_configure_libunwind(is32bit)) { return BSG_LIBUNWIND; } else if (apiLevel <= BSG_LIBCORKSCREW_MAX_LEVEL && apiLevel >= BSG_LIBCORKSCREW_MIN_LEVEL && diff --git a/ndk/src/main/jni/utils/stack_unwinder.h b/ndk/src/main/jni/utils/stack_unwinder.h index 9f97627e96..b4340217b5 100644 --- a/ndk/src/main/jni/utils/stack_unwinder.h +++ b/ndk/src/main/jni/utils/stack_unwinder.h @@ -23,7 +23,7 @@ typedef enum { * Everything else: custom unwinding logic * @return the preferred unwind type */ -bsg_unwinder bsg_get_unwind_type(int apiLevel); +bsg_unwinder bsg_get_unwind_type(int apiLevel, bool is32bit); /** * Unwind the stack using the preferred tool/style. If info and a user diff --git a/ndk/src/main/jni/utils/stack_unwinder_libunwind.c b/ndk/src/main/jni/utils/stack_unwinder_libunwind.c index 27a644f10b..06eb5c7fdb 100644 --- a/ndk/src/main/jni/utils/stack_unwinder_libunwind.c +++ b/ndk/src/main/jni/utils/stack_unwinder_libunwind.c @@ -11,16 +11,14 @@ typedef struct { size_t frame_count; uintptr_t frame_addresses[BUGSNAG_FRAMES_MAX]; -#if defined(__arm__) - void *signal_context; -#endif - siginfo_t *signal_info; } bsg_libunwind_state; bsg_libunwind_state *bsg_global_libunwind_state; +bool bsg_libunwind_global_is32bit = false; -bool bsg_configure_libunwind(void) { +bool bsg_configure_libunwind(bool is32bit) { bsg_global_libunwind_state = calloc(1, sizeof(bsg_libunwind_state)); + bsg_libunwind_global_is32bit = is32bit; return true; } @@ -28,24 +26,31 @@ static _Unwind_Reason_Code bsg_libunwind_callback(struct _Unwind_Context *context, void *arg) __asyncsafe { bsg_libunwind_state *state = (bsg_libunwind_state *)arg; -#if defined(__arm__) - if (state->frame_count == 0 && state->signal_context != NULL) { - const ucontext_t *signal_ucontext = - (const ucontext_t *)state->signal_context; - const struct sigcontext *signal_mcontext = &(signal_ucontext->uc_mcontext); + uintptr_t ip = _Unwind_GetIP(context); - // Include program counter as the first frame - state->frame_addresses[state->frame_count] = signal_mcontext->arm_pc; - state->frame_count++; + if (state->frame_count >= BUGSNAG_FRAMES_MAX) { + return _URC_END_OF_STACK; + } else if (state->frame_count > 0 && (void *)ip == NULL) { // nobody's home + return _URC_NO_REASON; + } + state->frame_addresses[state->frame_count] = ip; + state->frame_count++; + + return _URC_NO_REASON; +} - // Avoid unwinding in cases where there is a risk of segfault - bool single_frame_mode = state->signal_info != NULL && - (state->signal_info->si_code == SI_TKILL || - state->signal_info->si_code == SEGV_MAPERR); - if (single_frame_mode) { - return _URC_END_OF_STACK; - } +#if defined(__arm__) +ssize_t +bsg_unwind_stack_libunwind_arm32(bsg_stackframe stacktrace[BUGSNAG_FRAMES_MAX], + siginfo_t *info, void *user_context) __asyncsafe { + unw_cursor_t cursor; + unw_context_t uc; + int index = 0; + unw_getcontext(&uc); + unw_init_local(&cursor, &uc); + // Initialize cursor state with register data, if any + if (user_context != NULL) { /** * Set the registers and initial frame to the values from the signal * handler user context. @@ -55,46 +60,49 @@ bsg_libunwind_callback(struct _Unwind_Context *context, void *arg) __asyncsafe { * state before unwinding the first frame (using the program counter as the * first frame). Then the stack can be unwound normally. */ - _Unwind_SetGR(context, REG_R0, signal_mcontext->arm_r0); - _Unwind_SetGR(context, REG_R1, signal_mcontext->arm_r1); - _Unwind_SetGR(context, REG_R2, signal_mcontext->arm_r2); - _Unwind_SetGR(context, REG_R3, signal_mcontext->arm_r3); - _Unwind_SetGR(context, REG_R4, signal_mcontext->arm_r4); - _Unwind_SetGR(context, REG_R5, signal_mcontext->arm_r5); - _Unwind_SetGR(context, REG_R6, signal_mcontext->arm_r6); - _Unwind_SetGR(context, REG_R7, signal_mcontext->arm_r7); - _Unwind_SetGR(context, REG_R8, signal_mcontext->arm_r8); - _Unwind_SetGR(context, REG_R9, signal_mcontext->arm_r9); - _Unwind_SetGR(context, REG_R10, signal_mcontext->arm_r10); - _Unwind_SetGR(context, REG_R11, signal_mcontext->arm_fp); - _Unwind_SetGR(context, REG_R12, signal_mcontext->arm_ip); - _Unwind_SetGR(context, REG_R13, signal_mcontext->arm_sp); - _Unwind_SetGR(context, REG_R14, signal_mcontext->arm_lr); - _Unwind_SetGR(context, REG_R15, signal_mcontext->arm_pc); - return _URC_NO_REASON; + const ucontext_t *signal_ucontext = (const ucontext_t *)user_context; + const struct sigcontext *signal_mcontext = &(signal_ucontext->uc_mcontext); + unw_set_reg(&cursor, UNW_ARM_R0, signal_mcontext->arm_r0); + unw_set_reg(&cursor, UNW_ARM_R1, signal_mcontext->arm_r1); + unw_set_reg(&cursor, UNW_ARM_R2, signal_mcontext->arm_r2); + unw_set_reg(&cursor, UNW_ARM_R3, signal_mcontext->arm_r3); + unw_set_reg(&cursor, UNW_ARM_R4, signal_mcontext->arm_r4); + unw_set_reg(&cursor, UNW_ARM_R5, signal_mcontext->arm_r5); + unw_set_reg(&cursor, UNW_ARM_R6, signal_mcontext->arm_r6); + unw_set_reg(&cursor, UNW_ARM_R7, signal_mcontext->arm_r7); + unw_set_reg(&cursor, UNW_ARM_R8, signal_mcontext->arm_r8); + unw_set_reg(&cursor, UNW_ARM_R9, signal_mcontext->arm_r9); + unw_set_reg(&cursor, UNW_ARM_R10, signal_mcontext->arm_r10); + unw_set_reg(&cursor, UNW_ARM_R11, signal_mcontext->arm_fp); + unw_set_reg(&cursor, UNW_ARM_R12, signal_mcontext->arm_ip); + unw_set_reg(&cursor, UNW_ARM_R13, signal_mcontext->arm_sp); + unw_set_reg(&cursor, UNW_ARM_R14, signal_mcontext->arm_lr); + unw_set_reg(&cursor, UNW_ARM_R15, signal_mcontext->arm_pc); + unw_set_reg(&cursor, UNW_REG_IP, signal_mcontext->arm_pc); + unw_set_reg(&cursor, UNW_REG_SP, signal_mcontext->arm_sp); + // Manually insert first frame to avoid being skipped in step() + stacktrace[index++].frame_address = signal_mcontext->arm_pc; } -#endif - uintptr_t ip = _Unwind_GetIP(context); - if (state->frame_count >= BUGSNAG_FRAMES_MAX) { - return _URC_END_OF_STACK; - } else if (state->frame_count > 0 && (void *)ip == NULL) { // nobody's home - return _URC_NO_REASON; + while (unw_step(&cursor) > 0 && index < BUGSNAG_FRAMES_MAX) { + unw_word_t ip = 0; + unw_get_reg(&cursor, UNW_REG_IP, &ip); + stacktrace[index++].frame_address = ip; } - state->frame_addresses[state->frame_count] = ip; - state->frame_count++; - return _URC_NO_REASON; + return index; } - +#endif ssize_t bsg_unwind_stack_libunwind(bsg_stackframe stacktrace[BUGSNAG_FRAMES_MAX], siginfo_t *info, void *user_context) { - bsg_global_libunwind_state->frame_count = 0; #if defined(__arm__) - bsg_global_libunwind_state->signal_context = user_context; + if (bsg_libunwind_global_is32bit) { // avoid this code path if a 64-bit device + // is running 32-bit + return bsg_unwind_stack_libunwind_arm32(stacktrace, info, user_context); + } #endif - bsg_global_libunwind_state->signal_info = info; + bsg_global_libunwind_state->frame_count = 0; // The return value of _Unwind_Backtrace sits on a throne of lies _Unwind_Backtrace(bsg_libunwind_callback, bsg_global_libunwind_state); for (int i = 0; i < bsg_global_libunwind_state->frame_count; ++i) { diff --git a/ndk/src/main/jni/utils/stack_unwinder_libunwind.h b/ndk/src/main/jni/utils/stack_unwinder_libunwind.h index be197384bd..91d22e394f 100644 --- a/ndk/src/main/jni/utils/stack_unwinder_libunwind.h +++ b/ndk/src/main/jni/utils/stack_unwinder_libunwind.h @@ -4,7 +4,7 @@ #include "../report.h" #include -bool bsg_configure_libunwind(void); +bool bsg_configure_libunwind(bool is32bit); ssize_t bsg_unwind_stack_libunwind(bsg_stackframe stacktrace[BUGSNAG_FRAMES_MAX],