diff --git a/Makefile.am b/Makefile.am index 61f4fb96..a7de2fb4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,6 +48,9 @@ EXTRA_DIST = include/dwarf.h include/dwarf_i.h include/dwarf-eh.h \ include/libunwind_i.h include/mempool.h \ include/remote.h \ include/libunwind-common.h.in \ + include/tdep-arm/dwarf-config.h \ + include/tdep-arm/jmpbuf.h include/tdep-arm/libunwind_i.h \ + include/tdep-ia64/jmpbuf.h include/tdep-ia64/rse.h \ include/libunwind-ia64.h include/tdep-ia64/libunwind_i.h \ include/tdep-ia64/jmpbuf.h include/tdep-ia64/rse.h \ include/tdep-ia64/script.h \ diff --git a/README b/README index 180edd27..cf98f4f5 100644 --- a/README +++ b/README @@ -9,6 +9,8 @@ several architecture/operating-system combinations: Linux/PARISC: Works well, but C library missing unwind-info. HP-UX/IA-64: Mostly works but known to have some serious limitations. Linux/PPC64: Newly added. + FreeBSD/i386: Newly added. + FreeBSD/x86-64: Newly added (FreeBSD architecture is known as amd64). * General Build Instructions @@ -16,6 +18,7 @@ several architecture/operating-system combinations: In general, this library can be built and installed with the following commands: + $ autoreconf -i # Needed only for building from git. Depends on libtool. $ ./configure $ make $ make install prefix=PREFIX @@ -79,6 +82,14 @@ If libunwind seems to not work (backtracing failing), try to compile 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. + +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. * Regression Testing diff --git a/configure.in b/configure.in index 1271a560..8e2d138e 100644 --- a/configure.in +++ b/configure.in @@ -23,12 +23,21 @@ AM_PROG_CC_C_O dnl Checks for libraries. AC_CHECK_LIB(uca, __uc_get_grs) +OLD_LIBS=${LIBS} +AC_SEARCH_LIBS(dlopen, dl) +LIBS=${OLD_LIBS} +case "$ac_cv_search_dlopen" in + -l*) DLLIB=$ac_cv_search_dlopen;; + *) DLLIB="";; +esac + CHECK_ATOMIC_OPS dnl Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS(asm/ptrace_offsets.h endian.h execinfo.h ia64intrin.h \ - sys/uc_access.h unistd.h signal.h) +AC_CHECK_HEADERS(asm/ptrace_offsets.h endian.h sys/endian.h execinfo.h \ + ia64intrin.h sys/uc_access.h unistd.h signal.h sys/types.h \ + sys/procfs.h) dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST @@ -51,11 +60,23 @@ AC_CHECK_TYPES([sighandler_t], [], [], #endif ]) +AC_CHECK_DECLS([PTRACE_POKEUSER, PTRACE_POKEDATA, PTRACE_CONT, +PTRACE_TRACEME, PTRACE_CONT, PTRACE_SIGNLESTEP, +PTRACE_SYSCALL, PT_IO, PT_GETREGS, +PT_GETFPREGS, PT_CONTINUE, PT_TRACE_ME, +PT_STEP, PT_SYSCALL], [], [], +[$ac_includes_default +#if HAVE_SYS_TYPES_H +#include +#endif +#include +]) + dnl Checks for library functions. AC_FUNC_MEMCMP AC_TYPE_SIGNAL AC_CHECK_FUNCS(dl_iterate_phdr dl_phdr_removals_counter dlmodinfo getunwind \ - ttrace) + ttrace mincore) is_gcc_m64() { if test `echo $CFLAGS | grep "\-m64" -c` -eq 1 ; then echo ppc64; else @@ -80,6 +101,7 @@ get_arch() { hppa*) echo hppa;; mips*) echo mips;; powerpc*) is_gcc_m64;; + amd64) echo x86_64;; *) echo $1;; esac } @@ -99,6 +121,7 @@ AM_CONDITIONAL(ARCH_PPC32, test x$target_arch = xppc32) AM_CONDITIONAL(ARCH_PPC64, test x$target_arch = xppc64) 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) if test x$target_arch = xppc64; then libdir='${exec_prefix}/lib64' @@ -139,6 +162,20 @@ if test x$enable_debug_frame = xyes; then AC_DEFINE([CONFIG_DEBUG_FRAME], [], [Enable Debug Frame]) fi +AC_ARG_ENABLE(block_signals, +[ --enable-block-signals Block signals before performing mutex operations], +[enable_block_signals=$enableval], [enable_block_signals=yes]) +if test x$enable_block_signals = xyes; then + AC_DEFINE([CONFIG_BLOCK_SIGNALS], [], [Block signals before mutex operations]) +fi + +AC_ARG_ENABLE(conservative_checks, +[ --enable-conservative-checks Validate all memory addresses before use], +[enable_conservative_checks=$enableval], [enable_conservative_checks=yes]) +if test x$enable_conservative_checks = xyes; then + CPPFLAGS="${CPPFLAGS} -DCONSERVATIVE_CHECKS" +fi + LIBUNWIND___THREAD save_LDFLAGS="$LDFLAGS" @@ -180,6 +217,8 @@ AC_SUBST(PKG_MINOR) AC_SUBST(PKG_EXTRA) AC_SUBST(PKG_MAINTAINER) AC_SUBST(enable_cxx_exceptions) +AC_SUBST(enable_debug_frame) +AC_SUBST(DLLIB) AC_CONFIG_FILES(Makefile src/Makefile tests/Makefile tests/check-namespace.sh doc/Makefile doc/common.tex include/libunwind-common.h) diff --git a/include/dwarf.h b/include/dwarf.h index 190745f6..fa5452d4 100644 --- a/include/dwarf.h +++ b/include/dwarf.h @@ -39,6 +39,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ struct dwarf_cursor; /* forward-declaration */ #include "dwarf-config.h" +#include /* DWARF expression opcodes. */ @@ -247,6 +248,7 @@ typedef struct dwarf_reg_state unsigned short lru_chain; /* used for least-recently-used chain */ unsigned short coll_chain; /* used for hash collisions */ unsigned short hint; /* hint for next rs to try (or -1) */ + unsigned short signal_frame; /* optional machine-dependent signal info */ } dwarf_reg_state_t; @@ -266,6 +268,7 @@ typedef struct dwarf_cie_info uint8_t lsda_encoding; unsigned int sized_augmentation : 1; unsigned int have_abi_marker : 1; + unsigned int signal_frame : 1; } dwarf_cie_info_t; @@ -293,6 +296,7 @@ typedef struct dwarf_cursor dwarf_loc_t loc[DWARF_NUM_PRESERVED_REGS]; + unsigned int use_prev_instr :1; /* use previous (= call) or current (= signal) instruction? */ unsigned int pi_valid :1; /* is proc_info valid? */ unsigned int pi_is_dynamic :1; /* proc_info found via dynamic proc info? */ unw_proc_info_t pi; /* info about current procedure */ @@ -312,11 +316,7 @@ typedef unsigned char unw_hash_index_t; struct dwarf_rs_cache { -#ifdef HAVE_ATOMIC_OPS_H - AO_TS_t busy; /* is the rs-cache busy? */ -#else pthread_mutex_t lock; -#endif unsigned short lru_head; /* index of lead-recently used rs */ unsigned short lru_tail; /* index of most-recently used rs */ @@ -349,6 +349,7 @@ struct unw_debug_frame_list /* Convenience macros: */ #define dwarf_init UNW_ARCH_OBJ (dwarf_init) #define dwarf_find_proc_info UNW_OBJ (dwarf_find_proc_info) +#define dwarf_find_debug_frame UNW_OBJ (dwarf_find_debug_frame) #define dwarf_search_unwind_table UNW_OBJ (dwarf_search_unwind_table) #define dwarf_put_unwind_info UNW_OBJ (dwarf_put_unwind_info) #define dwarf_put_unwind_info UNW_OBJ (dwarf_put_unwind_info) @@ -365,6 +366,8 @@ extern int dwarf_init (void); extern int dwarf_find_proc_info (unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, int need_unwind_info, void *arg); +extern int dwarf_find_debug_frame (int found, unw_dyn_info_t *di_debug, + struct dl_phdr_info *info, unw_word_t ip); extern int dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, unw_dyn_info_t *di, diff --git a/include/libunwind-common.h.in b/include/libunwind-common.h.in index 9005a6c2..2fb78e8e 100644 --- a/include/libunwind-common.h.in +++ b/include/libunwind-common.h.in @@ -119,6 +119,8 @@ typedef struct unw_addr_space *unw_addr_space_t; /* Each target may define it's own set of flags, but bits 0-15 are reserved for general libunwind-use. */ #define UNW_PI_FLAG_FIRST_TDEP_BIT 16 +/* The information comes from a .debug_frame section. */ +#define UNW_PI_FLAG_DEBUG_FRAME 32 typedef struct unw_proc_info { @@ -220,6 +222,7 @@ unw_save_loc_t; #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_regname UNW_ARCH_OBJ(regname) @@ -246,6 +249,7 @@ extern int unw_get_fpreg (unw_cursor_t *, int, unw_fpreg_t *); 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); diff --git a/include/libunwind-dynamic.h b/include/libunwind-dynamic.h index 8f6041f1..6c94f05a 100644 --- a/include/libunwind-dynamic.h +++ b/include/libunwind-dynamic.h @@ -75,7 +75,7 @@ typedef enum { UNW_INFO_FORMAT_DYNAMIC, /* unw_dyn_proc_info_t */ UNW_INFO_FORMAT_TABLE, /* unw_dyn_table_t */ - UNW_INFO_FORMAT_REMOTE_TABLE, /* unw_dyn_remote_table_t */ + UNW_INFO_FORMAT_REMOTE_TABLE /* unw_dyn_remote_table_t */ } unw_dyn_info_format_t; diff --git a/include/libunwind-x86.h b/include/libunwind-x86.h index 1e321db1..32533df9 100644 --- a/include/libunwind-x86.h +++ b/include/libunwind-x86.h @@ -30,6 +30,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ extern "C" { #endif +#include #include #include @@ -147,7 +148,7 @@ typedef enum UNW_TDEP_LAST_REG = UNW_X86_XMM7, UNW_TDEP_IP = UNW_X86_EIP, - UNW_TDEP_SP = UNW_X86_CFA, + UNW_TDEP_SP = UNW_X86_ESP, UNW_TDEP_EH = UNW_X86_EAX } x86_regnum_t; @@ -163,12 +164,6 @@ unw_tdep_save_loc_t; /* On x86, we can directly use ucontext_t as the unwind context. */ typedef ucontext_t unw_tdep_context_t; -/* XXX this is not ideal: an application should not be prevented from - using the "getcontext" name just because it's using libunwind. We - can't just use __getcontext() either, because that isn't exported - by glibc... */ -#define unw_tdep_getcontext(uc) (getcontext (uc), 0) - #include "libunwind-dynamic.h" typedef struct @@ -179,6 +174,9 @@ unw_tdep_proc_info_t; #include "libunwind-common.h" +#define unw_tdep_getcontext UNW_ARCH_OBJ(getcontext) +extern int unw_tdep_getcontext (unw_tdep_context_t *); + #define unw_tdep_is_fpreg UNW_ARCH_OBJ(is_fpreg) extern int unw_tdep_is_fpreg (int); diff --git a/include/libunwind-x86_64.h b/include/libunwind-x86_64.h index e3373516..53789cc3 100644 --- a/include/libunwind-x86_64.h +++ b/include/libunwind-x86_64.h @@ -32,6 +32,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ extern "C" { #endif +#include #include #include diff --git a/include/libunwind_i.h b/include/libunwind_i.h index ced4087b..c8c92088 100644 --- a/include/libunwind_i.h +++ b/include/libunwind_i.h @@ -54,8 +54,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include -#ifdef HAVE_ENDIAN_H +#if defined(HAVE_ENDIAN_H) # include +#elif defined(HAVE_SYS_ENDIAN_H) +# include #else # define __LITTLE_ENDIAN 1234 # define __BIG_ENDIAN 4321 @@ -180,22 +182,37 @@ typedef sigset_t intrmask_t; extern intrmask_t unwi_full_mask; +/* Silence compiler warnings about variables which are used only if libunwind + is configured in a certain way */ +static inline void mark_as_used(void *v) { +} + +#if defined(CONFIG_BLOCK_SIGNALS) +# define SIGPROCMASK(how, new_mask, old_mask) \ + sigprocmask((how), (new_mask), (old_mask)) +#else +# define SIGPROCMASK(how, new_mask, old_mask) mark_as_used(old_mask) +#endif + #define define_lock(name) \ pthread_mutex_t name = PTHREAD_MUTEX_INITIALIZER #define lock_init(l) mutex_init (l) #define lock_acquire(l,m) \ do { \ - sigprocmask (SIG_SETMASK, &unwi_full_mask, &(m)); \ + SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &(m)); \ mutex_lock (l); \ } while (0) #define lock_release(l,m) \ do { \ mutex_unlock (l); \ - sigprocmask (SIG_SETMASK, &(m), NULL); \ + SIGPROCMASK (SIG_SETMASK, &(m), NULL); \ } while (0) #define SOS_MEMORY_SIZE 16384 /* see src/mi/mempool.c */ +#ifndef MAP_ANONYMOUS +# define MAP_ANONYMOUS MAP_ANON +#endif #define GET_MEMORY(mem, size_in_bytes) \ do { \ /* Hopefully, mmap() goes straight through to a system call stub... */ \ @@ -267,10 +284,10 @@ do { \ # define Dprintf(format...) #endif -static ALWAYS_INLINE void +static ALWAYS_INLINE int print_error (const char *string) { - write (2, string, strlen (string)); + return write (2, string, strlen (string)); } #define mi_init UNWI_ARCH_OBJ(mi_init) diff --git a/include/tdep-arm/libunwind_i.h b/include/tdep-arm/libunwind_i.h index 3424d86a..bae9c4de 100644 --- a/include/tdep-arm/libunwind_i.h +++ b/include/tdep-arm/libunwind_i.h @@ -217,6 +217,9 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) #define tdep_get_elf_image UNW_ARCH_OBJ(get_elf_image) #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,rs) do {} while(0) +#define tdep_reuse_frame(c,rs) do {} while(0) #ifdef UNW_LOCAL_ONLY # define tdep_find_proc_info(c,ip,n) \ @@ -245,10 +248,21 @@ extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip, int need_unwind_info, void *arg); extern void *tdep_uc_addr (ucontext_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); + unsigned long *segbase, unsigned long *mapoff, + char *path, size_t pathlen); 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); +/* unwinding method selection support */ +#define UNW_ARM_METHOD_ALL 0xFF +#define UNW_ARM_METHOD_DWARF 0x01 +#define UNW_ARM_METHOD_FRAME 0x02 + +#define unwi_unwind_method UNWI_ARCH_OBJ(unwind_method) +extern int unwi_unwind_method; + +#define UNW_TRY_METHOD(x) (unwi_unwind_method & x) + #endif /* ARM_LIBUNWIND_I_H */ diff --git a/include/tdep-hppa/libunwind_i.h b/include/tdep-hppa/libunwind_i.h index 553e97d8..50d1aabd 100644 --- a/include/tdep-hppa/libunwind_i.h +++ b/include/tdep-hppa/libunwind_i.h @@ -224,6 +224,9 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) #define tdep_get_elf_image UNW_ARCH_OBJ(get_elf_image) #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,rs) do {} while(0) +#define tdep_reuse_frame(c,rs) do {} while(0) #ifdef UNW_LOCAL_ONLY # define tdep_find_proc_info(c,ip,n) \ @@ -252,7 +255,8 @@ extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip, int need_unwind_info, void *arg); extern void *tdep_uc_addr (ucontext_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); + unsigned long *segbase, unsigned long *mapoff, + char *path, size_t pathlen); 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, diff --git a/include/tdep-ia64/libunwind_i.h b/include/tdep-ia64/libunwind_i.h index 552c9498..75cc220d 100644 --- a/include/tdep-ia64/libunwind_i.h +++ b/include/tdep-ia64/libunwind_i.h @@ -220,6 +220,9 @@ struct ia64_global_unwind_state #define tdep_get_elf_image UNW_ARCH_OBJ(get_elf_image) #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,rs) do {} while(0) +#define tdep_reuse_frame(c,rs) do {} while(0) #define tdep_get_as(c) ((c)->as) #define tdep_get_as_arg(c) ((c)->as_arg) #define tdep_get_ip(c) ((c)->ip) @@ -244,7 +247,8 @@ extern void tdep_put_unwind_info (unw_addr_space_t as, extern void *tdep_uc_addr (ucontext_t *uc, unw_regnum_t regnum, uint8_t *nat_bitnr); extern int tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip, - unsigned long *segbase, unsigned long *mapoff); + unsigned long *segbase, unsigned long *mapoff, + char *path, size_t pathlen); 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, diff --git a/include/tdep-mips/libunwind_i.h b/include/tdep-mips/libunwind_i.h index c2f756a1..160a6372 100644 --- a/include/tdep-mips/libunwind_i.h +++ b/include/tdep-mips/libunwind_i.h @@ -279,6 +279,9 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) #define tdep_get_elf_image UNW_ARCH_OBJ(get_elf_image) #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,rs) do {} while(0) +#define tdep_reuse_frame(c,rs) do {} while(0) #ifdef UNW_LOCAL_ONLY # define tdep_find_proc_info(c,ip,n) \ @@ -306,7 +309,8 @@ extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip, int need_unwind_info, void *arg); extern void *tdep_uc_addr (ucontext_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); + unsigned long *segbase, unsigned long *mapoff, + char *path, size_t pathlen); 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, diff --git a/include/tdep-ppc32/libunwind_i.h b/include/tdep-ppc32/libunwind_i.h index 05dd1356..e775dd64 100644 --- a/include/tdep-ppc32/libunwind_i.h +++ b/include/tdep-ppc32/libunwind_i.h @@ -256,6 +256,9 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) #define tdep_get_elf_image UNW_ARCH_OBJ(get_elf_image) #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,rs) do {} while(0) +#define tdep_reuse_frame(c,rs) do {} while(0) #define tdep_get_func_addr UNW_OBJ(get_func_addr) #ifdef UNW_LOCAL_ONLY @@ -289,7 +292,8 @@ extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip, int need_unwind_info, void *arg); extern void *tdep_uc_addr (ucontext_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); + unsigned long *segbase, unsigned long *mapoff, + char *path, size_t pathlen); 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, diff --git a/include/tdep-ppc64/libunwind_i.h b/include/tdep-ppc64/libunwind_i.h index 192a0343..bb6c977b 100644 --- a/include/tdep-ppc64/libunwind_i.h +++ b/include/tdep-ppc64/libunwind_i.h @@ -256,6 +256,9 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) #define tdep_get_elf_image UNW_ARCH_OBJ(get_elf_image) #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,rs) do {} while(0) +#define tdep_reuse_frame(c,rs) do {} while(0) #define tdep_get_func_addr UNW_OBJ(get_func_addr) #ifdef UNW_LOCAL_ONLY @@ -289,7 +292,8 @@ extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip, int need_unwind_info, void *arg); extern void *tdep_uc_addr (ucontext_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); + unsigned long *segbase, unsigned long *mapoff, + char *path, size_t pathlen); 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, diff --git a/include/tdep-x86/jmpbuf.h b/include/tdep-x86/jmpbuf.h index a0eb072e..17574144 100644 --- a/include/tdep-x86/jmpbuf.h +++ b/include/tdep-x86/jmpbuf.h @@ -25,7 +25,19 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* Use glibc's jump-buffer indices; NPTL peeks at SP: */ +#if defined __linux__ + #define JB_SP 4 #define JB_RP 5 #define JB_MASK_SAVED 6 #define JB_MASK 7 + +#elif defined __FreeBSD__ + +#define JB_SP 1 +#define JB_RP 0 +/* Pretend the ip cannot be 0 and mask is always saved */ +#define JB_MASK_SAVED 0 +#define JB_MASK 7 + +#endif diff --git a/include/tdep-x86/libunwind_i.h b/include/tdep-x86/libunwind_i.h index e6ee149c..bc381e91 100644 --- a/include/tdep-x86/libunwind_i.h +++ b/include/tdep-x86/libunwind_i.h @@ -60,8 +60,12 @@ struct cursor enum { X86_SCF_NONE, /* no signal frame encountered */ - X86_SCF_LINUX_SIGFRAME, /* classic x86 sigcontext */ - X86_SCF_LINUX_RT_SIGFRAME /* POSIX ucontext_t */ + X86_SCF_LINUX_SIGFRAME, /* Linux x86 sigcontext */ + X86_SCF_LINUX_RT_SIGFRAME, /* POSIX ucontext_t */ + X86_SCF_FREEBSD_SIGFRAME, /* FreeBSD x86 sigcontext */ + X86_SCF_FREEBSD_SIGFRAME4, /* FreeBSD 4.x x86 sigcontext */ + X86_SCF_FREEBSD_OSIGFRAME, /* FreeBSD pre-4.x x86 sigcontext */ + X86_SCF_FREEBSD_SYSCALL, /* FreeBSD x86 syscall */ } sigcontext_format; unw_word_t sigcontext_addr; @@ -236,6 +240,9 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) #define tdep_get_elf_image UNW_ARCH_OBJ(get_elf_image) #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,rs) do {} while(0) +#define tdep_reuse_frame(c,rs) do {} while(0) #ifdef UNW_LOCAL_ONLY # define tdep_find_proc_info(c,ip,n) \ @@ -264,7 +271,8 @@ extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip, int need_unwind_info, void *arg); extern void *tdep_uc_addr (ucontext_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); + unsigned long *segbase, unsigned long *mapoff, + char *path, size_t pathlen); 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, diff --git a/include/tdep-x86_64/jmpbuf.h b/include/tdep-x86_64/jmpbuf.h index 92200689..44fcd69b 100644 --- a/include/tdep-x86_64/jmpbuf.h +++ b/include/tdep-x86_64/jmpbuf.h @@ -23,9 +23,21 @@ 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 6 #define JB_RP 7 #define JB_MASK_SAVED 8 #define JB_MASK 9 + +#elif defined __FreeBSD__ + +#define JB_SP 2 +#define JB_RP 0 +/* Pretend the ip cannot be 0 and mask is always saved */ +#define JB_MASK_SAVED 0 +#define JB_MASK 9 + +#endif diff --git a/include/tdep-x86_64/libunwind_i.h b/include/tdep-x86_64/libunwind_i.h index a949a4cb..ea502ec4 100644 --- a/include/tdep-x86_64/libunwind_i.h +++ b/include/tdep-x86_64/libunwind_i.h @@ -62,7 +62,9 @@ struct cursor enum { X86_64_SCF_NONE, /* no signal frame encountered */ - X86_64_SCF_LINUX_RT_SIGFRAME /* POSIX ucontext_t */ + X86_64_SCF_LINUX_RT_SIGFRAME, /* Linux ucontext_t */ + X86_64_SCF_FREEBSD_SIGFRAME, /* FreeBSD signal frame */ + X86_64_SCF_FREEBSD_SYSCALL, /* FreeBSD syscall */ } sigcontext_format; unw_word_t sigcontext_addr; @@ -161,6 +163,15 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) #define tdep_get_elf_image UNW_ARCH_OBJ(get_elf_image) #define tdep_access_reg UNW_OBJ(access_reg) #define tdep_access_fpreg UNW_OBJ(access_fpreg) +#if __linux__ +# define tdep_fetch_frame UNW_OBJ(fetch_frame) +# define tdep_cache_frame UNW_OBJ(cache_frame) +# define tdep_reuse_frame UNW_OBJ(reuse_frame) +#else +# define tdep_fetch_frame(c,ip,n) do {} while(0) +# define tdep_cache_frame(c,rs) do {} while(0) +# define tdep_reuse_frame(c,rs) do {} while(0) +#endif #ifdef UNW_LOCAL_ONLY # define tdep_find_proc_info(c,ip,n) \ @@ -189,10 +200,20 @@ extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip, int need_unwind_info, void *arg); extern void *tdep_uc_addr (ucontext_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); + unsigned long *segbase, unsigned long *mapoff, + char *path, size_t pathlen); 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); +#if __linux__ +extern void tdep_fetch_frame (struct dwarf_cursor *c, unw_word_t ip, + int need_unwind_info); +extern void tdep_cache_frame (struct dwarf_cursor *c, + struct dwarf_reg_state *rs); +extern void tdep_reuse_frame (struct dwarf_cursor *c, + struct dwarf_reg_state *rs); +#endif + #endif /* X86_64_LIBUNWIND_I_H */ diff --git a/include/tdep/dwarf-config.h b/include/tdep/dwarf-config.h index ab36637b..ff5715f6 100644 --- a/include/tdep/dwarf-config.h +++ b/include/tdep/dwarf-config.h @@ -15,7 +15,7 @@ # include "tdep-ppc64/dwarf-config.h" #elif defined __i386__ # include "tdep-x86/dwarf-config.h" -#elif defined __x86_64__ +#elif defined __x86_64__ || defined __amd64__ # include "tdep-x86_64/dwarf-config.h" #else # error "Unsupported arch" diff --git a/include/unwind.h b/include/unwind.h index d9a767dc..f8d43d0d 100644 --- a/include/unwind.h +++ b/include/unwind.h @@ -26,6 +26,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef _UNWIND_H #define _UNWIND_H +/* For uint64_t */ +#include + #ifdef __cplusplus extern "C" { #endif @@ -62,23 +65,22 @@ typedef void (*_Unwind_Exception_Cleanup_Fn) (_Unwind_Reason_Code, struct _Unwind_Exception *); typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn) (int, _Unwind_Action, - unsigned long, + uint64_t, struct _Unwind_Exception *, struct _Unwind_Context *, void *); /* The C++ ABI requires exception_class, private_1, and private_2 to be of type uint64 and the entire structure to be - double-word-aligned, but that seems a bit overly IA-64-specific. - Using "unsigned long" instead should give us the desired effect on - IA-64, while being more general. */ + double-word-aligned. Please note that exception_class stays 64-bit + even on 32-bit machines for gcc compatibility. */ struct _Unwind_Exception { - unsigned long exception_class; + uint64_t exception_class; _Unwind_Exception_Cleanup_Fn exception_cleanup; unsigned long private_1; unsigned long private_2; - }; + } __attribute__((__aligned__)); extern _Unwind_Reason_Code _Unwind_RaiseException (struct _Unwind_Exception *); extern _Unwind_Reason_Code _Unwind_ForcedUnwind (struct _Unwind_Exception *, diff --git a/src/Makefile.am b/src/Makefile.am index e6e3b330..3107b00f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -97,6 +97,8 @@ libunwind_la_SOURCES_os_linux = os-linux.h os-linux.c libunwind_la_SOURCES_os_hpux = os-hpux.c +libunwind_la_SOURCES_os_freebsd = os-freebsd.c + dwarf_SOURCES_common = \ dwarf/global.c @@ -106,11 +108,11 @@ dwarf_SOURCES_local = \ dwarf_SOURCES_generic = \ dwarf/Gexpr.c dwarf/Gfde.c dwarf/Gparser.c dwarf/Gpe.c dwarf/Gstep.c -# The list of files that go info libunwind and libunwind-arm: +# The list of files that go into libunwind and libunwind-arm: libunwind_la_SOURCES_arm_common = $(libunwind_la_SOURCES_common) \ $(dwarf_SOURCES_common) \ elf32.c elf32.h \ - arm/init.h arm/offsets.h arm/regs.h \ + arm/init.h arm/offsets.h arm/unwind_i.h \ arm/is_fpreg.c arm/regname.c # The list of files that go into libunwind: @@ -191,7 +193,7 @@ libunwind_hppa_la_SOURCES_hppa = $(libunwind_la_SOURCES_hppa_common) \ libunwind_la_SOURCES_mips_common = $(libunwind_la_SOURCES_common) \ $(dwarf_SOURCES_common) \ elfxx.c \ - mips/init.h mips/offsets.h mips/regs.h \ + mips/init.h mips/offsets.h \ mips/is_fpreg.c mips/regname.c # The list of files that go into libunwind: @@ -221,50 +223,54 @@ libunwind_la_SOURCES_x86_common = $(libunwind_la_SOURCES_common) \ # The list of files that go into libunwind: libunwind_la_SOURCES_x86 = $(libunwind_la_SOURCES_x86_common) \ + $(libunwind_la_SOURCES_x86_os_local) \ $(libunwind_la_SOURCES_local) \ $(dwarf_SOURCES_local) \ dwarf/Lfind_proc_info-lsb.c \ x86/Lcreate_addr_space.c x86/Lget_save_loc.c x86/Lglobal.c \ x86/Linit.c x86/Linit_local.c x86/Linit_remote.c \ - x86/Lis_signal_frame.c x86/Lget_proc_info.c x86/Lregs.c \ + x86/Lget_proc_info.c x86/Lregs.c \ x86/Lresume.c x86/Lstep.c # The list of files that go into libunwind-x86: libunwind_x86_la_SOURCES_x86 = $(libunwind_la_SOURCES_x86_common) \ + $(libunwind_la_SOURCES_x86_os) \ $(libunwind_la_SOURCES_generic) \ $(dwarf_SOURCES_generic) \ dwarf/Gfind_proc_info-lsb.c \ x86/Gcreate_addr_space.c x86/Gget_save_loc.c x86/Gglobal.c \ x86/Ginit.c x86/Ginit_local.c x86/Ginit_remote.c \ - x86/Gis_signal_frame.c x86/Gget_proc_info.c x86/Gregs.c \ + x86/Gget_proc_info.c x86/Gregs.c \ x86/Gresume.c x86/Gstep.c # The list of files that go both into libunwind and libunwind-x86_64: libunwind_la_SOURCES_x86_64_common = $(libunwind_la_SOURCES_common) \ $(dwarf_SOURCES_common) \ - elf64.c elf64.h \ + elf64.c elf64.h x86_64/offsets.h \ x86_64/init.h x86_64/unwind_i.h x86_64/ucontext_i.h \ x86_64/is_fpreg.c x86_64/regname.c # The list of files that go into libunwind: libunwind_la_SOURCES_x86_64 = $(libunwind_la_SOURCES_x86_64_common) \ + $(libunwind_la_SOURCES_x86_64_os_local) \ $(libunwind_la_SOURCES_local) \ $(dwarf_SOURCES_local) \ dwarf/Lfind_proc_info-lsb.c \ - x86_64/setcontext.S \ + x86_64/setcontext.S \ x86_64/Lcreate_addr_space.c x86_64/Lget_save_loc.c x86_64/Lglobal.c \ x86_64/Linit.c x86_64/Linit_local.c x86_64/Linit_remote.c \ - x86_64/Lis_signal_frame.c x86_64/Lget_proc_info.c x86_64/Lregs.c \ + x86_64/Lget_proc_info.c x86_64/Lregs.c \ x86_64/Lresume.c x86_64/Lstep.c x86_64/getcontext.S # The list of files that go into libunwind-x86_64: libunwind_x86_64_la_SOURCES_x86_64 = $(libunwind_la_SOURCES_x86_64_common) \ + $(libunwind_la_SOURCES_x86_64_os) \ $(libunwind_la_SOURCES_generic) \ $(dwarf_SOURCES_generic) \ dwarf/Gfind_proc_info-lsb.c \ x86_64/Gcreate_addr_space.c x86_64/Gget_save_loc.c x86_64/Gglobal.c \ x86_64/Ginit.c x86_64/Ginit_local.c x86_64/Ginit_remote.c \ - x86_64/Gis_signal_frame.c x86_64/Gget_proc_info.c x86_64/Gregs.c \ + x86_64/Gget_proc_info.c x86_64/Gregs.c \ x86_64/Gresume.c x86_64/Gstep.c # The list of local files that go to Power 64 and 32: @@ -342,8 +348,13 @@ install-exec-hook: endif if OS_LINUX - libunwind_la_SOURCES_os = $(libunwind_la_SOURCES_os_linux) - libunwind_la_SOURCES_os_local = $(libunwind_la_SOURCES_os_linux_local) + libunwind_la_SOURCES_os = $(libunwind_la_SOURCES_os_linux) + libunwind_la_SOURCES_os_local = $(libunwind_la_SOURCES_os_linux_local) + libunwind_la_SOURCES_x86_os = x86/Gos-linux.c + libunwind_x86_la_SOURCES_os = x86/getcontext-linux.S + libunwind_la_SOURCES_x86_os_local = x86/Los-linux.c + libunwind_la_SOURCES_x86_64_os = x86_64/Gos-linux.c + libunwind_la_SOURCES_x86_64_os_local = x86_64/Los-linux.c endif if OS_HPUX @@ -351,6 +362,16 @@ if OS_HPUX libunwind_la_SOURCES_os_local = $(libunwind_la_SOURCES_os_hpux_local) endif +if OS_FREEBSD + libunwind_la_SOURCES_os = $(libunwind_la_SOURCES_os_freebsd) + libunwind_la_SOURCES_os_local = $(libunwind_la_SOURCES_os_freebsd_local) + libunwind_la_SOURCES_x86_os = x86/Gos-freebsd.c + libunwind_x86_la_SOURCES_os = x86/getcontext-freebsd.S + libunwind_la_SOURCES_x86_os_local = x86/Los-freebsd.c + libunwind_la_SOURCES_x86_64_os = x86_64/Gos-freebsd.c + libunwind_la_SOURCES_x86_64_os_local = x86_64/Los-freebsd.c +endif + if ARCH_ARM lib_LTLIBRARIES_arch = libunwind-arm.la libunwind_la_SOURCES = $(libunwind_la_SOURCES_arm) @@ -405,7 +426,7 @@ endif else if ARCH_X86 lib_LTLIBRARIES_arch = libunwind-x86.la - libunwind_la_SOURCES = $(libunwind_la_SOURCES_x86) + libunwind_la_SOURCES = $(libunwind_la_SOURCES_x86) $(libunwind_x86_la_SOURCES_os) libunwind_x86_la_SOURCES = $(libunwind_x86_la_SOURCES_x86) libunwind_x86_la_LDFLAGS = $(COMMON_SO_LDFLAGS) -version-info $(SOVERSION) if !REMOTE_ONLY diff --git a/src/arm/Gglobal.c b/src/arm/Gglobal.c index 40374327..473a0b85 100644 --- a/src/arm/Gglobal.c +++ b/src/arm/Gglobal.c @@ -29,6 +29,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ HIDDEN pthread_mutex_t arm_lock = PTHREAD_MUTEX_INITIALIZER; HIDDEN int tdep_needs_initialization = 1; +/* Unwinding methods to use. See UNW_METHOD_ enums */ +HIDDEN int unwi_unwind_method = UNW_ARM_METHOD_ALL; + /* FIXME: I'm pretty sure we don't need this at all for ARM, but "generic" code (include/dwarf_i.h) seems to expect it to be here at present. */ @@ -44,13 +47,19 @@ tdep_init (void) sigfillset (&unwi_full_mask); - sigprocmask (SIG_SETMASK, &unwi_full_mask, &saved_mask); - mutex_lock (&arm_lock); + lock_acquire (&arm_lock, saved_mask); { if (!tdep_needs_initialization) /* another thread else beat us to it... */ goto out; + /* read ARM unwind method setting */ + const char* str = getenv ("UNW_ARM_UNWIND_METHOD"); + if (str) + { + unwi_unwind_method = atoi (str); + } + mi_init (); dwarf_init (); @@ -61,6 +70,5 @@ tdep_init (void) tdep_needs_initialization = 0; /* signal that we're initialized... */ } out: - mutex_unlock (&arm_lock); - sigprocmask (SIG_SETMASK, &saved_mask, NULL); + lock_release (&arm_lock, saved_mask); } diff --git a/src/arm/Ginit_local.c b/src/arm/Ginit_local.c index 7b2881e3..debf5bb5 100644 --- a/src/arm/Ginit_local.c +++ b/src/arm/Ginit_local.c @@ -47,7 +47,7 @@ unw_init_local (unw_cursor_t *cursor, ucontext_t *uc) c->dwarf.as = unw_local_addr_space; c->dwarf.as_arg = uc; - return common_init (c); + return common_init (c, 1); } #endif /* !UNW_REMOTE_ONLY */ diff --git a/src/arm/Ginit_remote.c b/src/arm/Ginit_remote.c index 3baf3f60..854f3b62 100644 --- a/src/arm/Ginit_remote.c +++ b/src/arm/Ginit_remote.c @@ -40,6 +40,6 @@ unw_init_remote (unw_cursor_t *cursor, unw_addr_space_t as, void *as_arg) c->dwarf.as = as; c->dwarf.as_arg = as_arg; - return common_init (c); + return common_init (c, 0); #endif /* !UNW_LOCAL_ONLY */ } diff --git a/src/arm/Gstep.c b/src/arm/Gstep.c index eff1fde3..5eac5f09 100644 --- a/src/arm/Gstep.c +++ b/src/arm/Gstep.c @@ -29,20 +29,83 @@ PROTECTED int unw_step (unw_cursor_t *cursor) { struct cursor *c = (struct cursor *) cursor; - int ret; + int ret = -UNW_EUNSPEC; Debug (1, "(cursor=%p)\n", c); /* Try DWARF-based unwinding... this is the only method likely to work for ARM. */ - ret = dwarf_step (&c->dwarf); + if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF)) + { + ret = dwarf_step (&c->dwarf); + Debug(1, "dwarf_step()=%d\n", ret); - if (unlikely (ret == -UNW_ESTOPUNWIND)) - return ret; + if (unlikely (ret == -UNW_ESTOPUNWIND)) + return ret; + + if (ret < 0 && ret != -UNW_ENOINFO) + { + Debug (2, "returning %d\n", ret); + return ret; + } + } - /* Dwarf unwinding didn't work, stop. */ if (unlikely (ret < 0)) - return 0; - - return (c->dwarf.ip == 0) ? 0 : 1; + { + if (UNW_TRY_METHOD(UNW_ARM_METHOD_FRAME)) + { + ret = UNW_ESUCCESS; + /* DWARF unwinding failed, try to follow APCS/optimized APCS frame chain */ + unw_word_t instr, i; + Debug (13, "dwarf_step() failed (ret=%d), trying frame-chain\n", ret); + dwarf_loc_t ip_loc, fp_loc; + unw_word_t frame; + /* Mark all registers unsaved, since we don't know where + they are saved (if at all), except for the EBP and + EIP. */ + if (dwarf_get(&c->dwarf, c->dwarf.loc[UNW_ARM_R11], &frame) < 0) + { + return 0; + } + for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) { + c->dwarf.loc[i] = DWARF_NULL_LOC; + } + if (frame) + { + if (dwarf_get(&c->dwarf, DWARF_LOC(frame, 0), &instr) < 0) + { + return 0; + } + instr -= 8; + if (dwarf_get(&c->dwarf, DWARF_LOC(instr, 0), &instr) < 0) + { + return 0; + } + if ((instr & 0xFFFFD800) == 0xE92DD800) + { + /* Standard APCS frame. */ + ip_loc = DWARF_LOC(frame - 4, 0); + fp_loc = DWARF_LOC(frame - 12, 0); + } + else + { + /* Codesourcery optimized normal frame. */ + ip_loc = DWARF_LOC(frame, 0); + fp_loc = DWARF_LOC(frame - 4, 0); + } + if (dwarf_get(&c->dwarf, ip_loc, &c->dwarf.ip) < 0) + { + return 0; + } + c->dwarf.loc[UNW_ARM_R12] = ip_loc; + c->dwarf.loc[UNW_ARM_R11] = fp_loc; + Debug(15, "ip=%lx\n", c->dwarf.ip); + } + else + { + ret = -UNW_ENOINFO; + } + } + } + return ret == -UNW_ENOINFO ? 0 : 1; } diff --git a/src/arm/getcontext.S b/src/arm/getcontext.S index 3b9abdae..c52992bf 100644 --- a/src/arm/getcontext.S +++ b/src/arm/getcontext.S @@ -50,3 +50,7 @@ _Uarm_getcontext: str r1, [r0, #15 * 4] ldmfd sp!, {r0, r1} bx lr +#ifdef __linux__ + /* We do not need executable stack. */ + .section .note.GNU-stack,"",%progbits +#endif diff --git a/src/arm/init.h b/src/arm/init.h index 5b3f927d..a3025698 100644 --- a/src/arm/init.h +++ b/src/arm/init.h @@ -25,7 +25,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "unwind_i.h" static inline int -common_init (struct cursor *c) +common_init (struct cursor *c, unsigned use_prev_instr) { int ret, i; @@ -62,6 +62,7 @@ common_init (struct cursor *c) c->dwarf.args_size = 0; c->dwarf.ret_addr_column = 0; + c->dwarf.use_prev_instr = use_prev_instr; c->dwarf.pi_valid = 0; c->dwarf.pi_is_dynamic = 0; c->dwarf.hint = 0; diff --git a/src/arm/siglongjmp.S b/src/arm/siglongjmp.S index 87f939e9..4df07366 100644 --- a/src/arm/siglongjmp.S +++ b/src/arm/siglongjmp.S @@ -6,3 +6,7 @@ _UI_siglongjmp_cont: _UI_longjmp_cont: bx lr +#ifdef __linux__ + /* We do not need executable stack. */ + .section .note.GNU-stack,"",%progbits +#endif diff --git a/src/dwarf/Gfde.c b/src/dwarf/Gfde.c index 2d12af20..027d2f59 100644 --- a/src/dwarf/Gfde.c +++ b/src/dwarf/Gfde.c @@ -187,6 +187,9 @@ parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr, break; case 'S': + /* This is a signal frame. */ + dci->signal_frame = 1; + /* Temporarily set it to one so dwarf_parse_fde() knows that it should fetch the actual ABI/TAG pair from the FDE. */ dci->have_abi_marker = 1; @@ -293,7 +296,7 @@ dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a, cie_addr = (unw_word_t) ((uint64_t) cie_offset_addr - cie_offset); } - Debug (15, "looking for CIE at address %x\n", (int) cie_addr); + Debug (15, "looking for CIE at address %lx\n", (long) cie_addr); if ((ret = parse_cie (as, a, cie_addr, pi, &dci, base, arg)) < 0) return ret; diff --git a/src/dwarf/Gfind_proc_info-lsb.c b/src/dwarf/Gfind_proc_info-lsb.c index b1f03a3e..79cdf3eb 100644 --- a/src/dwarf/Gfind_proc_info-lsb.c +++ b/src/dwarf/Gfind_proc_info-lsb.c @@ -186,8 +186,8 @@ load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local) FILE *f; Elf_W (Ehdr) ehdr; Elf_W (Half) shstrndx; - Elf_W (Shdr) *sec_hdrs; - char *stringtab; + Elf_W (Shdr) *sec_hdrs = NULL; + char *stringtab = NULL; unsigned int i; size_t linksize = 0; char *linkbuf = NULL; @@ -200,7 +200,8 @@ load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local) if (!f) return 1; - fread (&ehdr, sizeof (Elf_W (Ehdr)), 1, f); + if (fread (&ehdr, sizeof (Elf_W (Ehdr)), 1, f) != 1) + goto file_error; shstrndx = ehdr.e_shstrndx; @@ -209,13 +210,15 @@ load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local) fseek (f, ehdr.e_shoff, SEEK_SET); sec_hdrs = calloc (ehdr.e_shnum, sizeof (Elf_W (Shdr))); - fread (sec_hdrs, sizeof (Elf_W (Shdr)), ehdr.e_shnum, f); + if (fread (sec_hdrs, sizeof (Elf_W (Shdr)), ehdr.e_shnum, f) != ehdr.e_shnum) + goto file_error; Debug (4, "loading string table of size %zd\n", sec_hdrs[shstrndx].sh_size); stringtab = malloc (sec_hdrs[shstrndx].sh_size); fseek (f, sec_hdrs[shstrndx].sh_offset, SEEK_SET); - fread (stringtab, 1, sec_hdrs[shstrndx].sh_size, f); + if (fread (stringtab, 1, sec_hdrs[shstrndx].sh_size, f) != sec_hdrs[shstrndx].sh_size) + goto file_error; for (i = 1; i < ehdr.e_shnum && *buf == NULL; i++) { @@ -227,21 +230,23 @@ load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local) *buf = malloc (*bufsize); fseek (f, sec_hdrs[i].sh_offset, SEEK_SET); - fread (*buf, 1, *bufsize, f); + if (fread (*buf, 1, *bufsize, f) != *bufsize) + goto file_error; Debug (4, "read %zd bytes of .debug_frame from offset %zd\n", *bufsize, sec_hdrs[i].sh_offset); } - else if (is_local >= 0 && strcmp (secname, ".gnu_debuglink") == 0) + else if (strcmp (secname, ".gnu_debuglink") == 0) { linksize = sec_hdrs[i].sh_size; linkbuf = malloc (linksize); fseek (f, sec_hdrs[i].sh_offset, SEEK_SET); - fread (linkbuf, 1, linksize, f); + if (fread (linkbuf, 1, linksize, f) != linksize) + goto file_error; Debug (4, "read %zd bytes of .gnu_debuglink from offset %zd\n", - *bufsize, sec_hdrs[i].sh_offset); + linksize, sec_hdrs[i].sh_offset); } } @@ -250,6 +255,13 @@ load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local) fclose (f); + /* Ignore separate debug files which contain a .gnu_debuglink section. */ + if (linkbuf && is_local == -1) + { + free (linkbuf); + return 1; + } + if (*buf == NULL && linkbuf != NULL && memchr (linkbuf, 0, linksize) != NULL) { char *newname, *basedir, *p; @@ -298,15 +310,16 @@ load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local) free (linkbuf); return 0; -} -#else -static int -load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local) -{ + +/* An error reading image file. Release resources and return error code */ +file_error: + if (stringtab) free(stringtab); + if (sec_hdrs) free(sec_hdrs); + if (linkbuf) free(linkbuf); + fclose(f); + return 1; } -#endif /* CONFIG_DEBUG_FRAME */ - /* Locate the binary which originated the contents of address ADDR. Return the name of the binary in *name (space is allocated by the caller) @@ -322,10 +335,16 @@ find_binary_for_address (unw_word_t ip, char *name, size_t name_size) unsigned long segbase, mapoff, hi; maps_init (&mi, pid); - while (maps_next (&mi, &segbase, &hi, &mapoff, name, name_size)) + while (maps_next (&mi, &segbase, &hi, &mapoff)) if (ip >= segbase && ip < hi) { - found = 1; + size_t len = strlen (mi.path); + + if (len + 1 <= name_size) + { + memcpy (name, mi.path, len + 1); + found = 1; + } break; } maps_close (&mi); @@ -355,7 +374,7 @@ locate_debug_info (unw_addr_space_t as, struct dl_phdr_info *info, for (w = as->debug_frames; w; w = w->next) { - Debug (4, "checking %p: %x-%x\n", w, (int)w->start, (int)w->end); + Debug (4, "checking %p: %lx-%lx\n", w, (long)w->start, (long)w->end); if (addr >= w->start && addr < w->end) return w; } @@ -396,7 +415,7 @@ locate_debug_info (unw_addr_space_t as, struct dl_phdr_info *info, end = hdrlimit; } - Debug (4, "calculated bounds of %x-%x for '%s'\n", (int)start, (int)end, + Debug (4, "calculated bounds of %lx-%lx for '%s'\n", (long)start, (long)end, name); err = load_debug_frame (name, &buf, &bufsize, as == unw_local_addr_space); @@ -478,6 +497,154 @@ debug_frame_tab_compare (const void *a, const void *b) return 0; } +PROTECTED int +dwarf_find_debug_frame (int found, unw_dyn_info_t *di_debug, + struct dl_phdr_info *info, unw_word_t ip) +{ + unw_dyn_info_t *di; + struct unw_debug_frame_list *fdesc = 0; + unw_accessors_t *a; + unw_word_t addr; + + Debug (15, "Trying to find .debug_frame info->dlpi_name=%s\n", + info->dlpi_name); + di = di_debug; + + fdesc = locate_debug_info (unw_local_addr_space, info, ip, info->dlpi_name); + + if (!fdesc) + { + Debug (15, "couldn't load .debug_frame\n"); + return found; + } + else + { + 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, "loaded .debug_frame\n"); + + buf = fdesc->debug_frame; + bufsize = fdesc->debug_frame_size; + + if (bufsize == 0) + { + Debug (15, "zero-length .debug_frame\n"); + return found; + } + + /* 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 (unw_local_addr_space); + + /* Find all FDE entries in debug_frame, and make into a sorted + index. */ + + tab = debug_frame_tab_new (16); + + 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, 0, + (uintptr_t) buf, + 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; + free (tab); + } + + 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) info->dlpi_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 = (unw_word_t) (uintptr_t) info->dlpi_addr; + + 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; +} + +#endif /* CONFIG_DEBUG_FRAME */ + /* ptr is a pointer to a callback_data structure and, on entry, member ip contains the instruction-pointer we're looking for. */ @@ -492,7 +659,6 @@ callback (struct dl_phdr_info *info, size_t size, void *ptr) struct dwarf_eh_frame_hdr *hdr; unw_accessors_t *a; long n; - struct unw_debug_frame_list *fdesc = 0; int found = 0; ip = cb_data->ip; @@ -595,144 +761,13 @@ callback (struct dl_phdr_info *info, size_t size, void *ptr) return 1; } - Debug (15, "Trying to find .debug_frame\n"); - fdesc = locate_debug_info (unw_local_addr_space, info, ip, info->dlpi_name); - - if (!fdesc) - { - Debug (15, "couldn't load .debug_frame\n"); - return 0; - } - +#ifdef CONFIG_DEBUG_FRAME { - 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; - int err; - unw_word_t fde_addr; - - Debug (15, "loaded .debug_frame\n"); - - buf = fdesc->debug_frame; - bufsize = fdesc->debug_frame_size; - - if (bufsize == 0) - { - Debug (15, "zero-length .debug_frame\n"); - return found; - } - - /* 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 (unw_local_addr_space); - - /* Find all FDE entries in debug_frame, and make into a sorted - index. */ - - tab = debug_frame_tab_new (16); - - 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, 0, - (uintptr_t) buf, - NULL); - if (err == 0) - { - Debug (15, "start_ip = %x, end_ip = %x\n", - (int) this_pi.start_ip, (int) 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; - free (tab); - } - - Debug (15, "found debug_frame table `%s': segbase=0x%lx, len=%lu, " - "gp=0x%lx, table_data=0x%lx\n", - (char *) (uintptr_t) info->dlpi_name, - (unw_word_t) (uintptr_t) info->dlpi_addr, - (long) sizeof (*fdesc) / sizeof (unw_word_t), - (long) cb_data->gp, (long) fdesc); - - err = dwarf_search_unwind_table_local (unw_local_addr_space, cb_data->ip, fdesc, - (unw_word_t) (uintptr_t) info->dlpi_addr, - &cb_data->fde_addr, - cb_data->arg); - if(err < 0) - return err; - - /* .debug_frame uses an absolute encoding that does not know about any - shared library relocation. */ - cb_data->ip_offset = segbase; - cb_data->fde_base = (uintptr_t) fdesc->debug_frame; - return 1; + found = dwarf_find_debug_frame (found, &cb_data->di_debug, info, ip); } +#endif /* CONFIG_DEBUG_FRAME */ + + return found; } static int @@ -747,9 +782,9 @@ find_proc_fde (unw_word_t ip, unw_word_t *fde_addr, cb_data.ip = ip; cb_data.arg = arg; - sigprocmask (SIG_SETMASK, &unwi_full_mask, &saved_mask); + SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask); ret = dl_iterate_phdr (callback, &cb_data); - sigprocmask (SIG_SETMASK, &saved_mask, NULL); + SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL); if (ret < 0) return ret; @@ -865,7 +900,7 @@ lookup (const struct table_entry *table, size_t table_size, int32_t rel_ip) { mid = (lo + hi) / 2; e = table + mid; - Debug (1, "e->start_ip_offset = %x\n", (int) e->start_ip_offset); + Debug (1, "e->start_ip_offset = %lx\n", (long) e->start_ip_offset); if (rel_ip < e->start_ip_offset) hi = mid; else @@ -1043,13 +1078,11 @@ dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, #ifndef UNW_REMOTE_ONLY struct unw_debug_frame_list *fdesc = (void *) di->u.ti.table_data; - /* UNW_INFO_FORMAT_TABLE (i.e. .debug_frame) is currently only - supported for the local address space. Both the index and - the unwind tables live in local memory, but the address space - to check for properties like the address size and endianness - is the target one. When the ptrace code adds support for - .debug_frame something will have to change. */ - assert (as == unw_local_addr_space); + /* UNW_INFO_FORMAT_TABLE (i.e. .debug_frame) is read from local address + space. Both the index and the unwind tables live in local memory, but + the address space to check for properties like the address size and + endianness is the target one. */ + as = unw_local_addr_space; table = fdesc->index; table_len = fdesc->index_size * sizeof (struct table_entry); debug_frame_base = (uintptr_t) fdesc->debug_frame; @@ -1080,8 +1113,8 @@ dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, } if (!e) { - Debug (1, "IP %x inside range %x-%x, but no explicit unwind info found\n", - (int) ip, (int) di->start_ip, (int) di->end_ip); + Debug (1, "IP %lx inside range %lx-%lx, but no explicit unwind info found\n", + (long) ip, (long) di->start_ip, (long) di->end_ip); /* IP is inside this table's range, but there is no explicit unwind info. */ return -UNW_ENOINFO; @@ -1092,9 +1125,9 @@ dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, fde_addr = e->fde_offset + debug_frame_base; else fde_addr = e->fde_offset + segbase; - Debug (1, "e->fde_offset = %x, segbase = %x, debug_frame_base = %x, " - "fde_addr = %x\n", (int) e->fde_offset, (int) segbase, - (int) debug_frame_base, (int) fde_addr); + Debug (1, "e->fde_offset = %lx, segbase = %lx, debug_frame_base = %lx, " + "fde_addr = %lx\n", (long) e->fde_offset, (long) segbase, + (long) debug_frame_base, (long) fde_addr); if ((ret = dwarf_extract_proc_info_from_fde (as, a, &fde_addr, pi, need_unwind_info, debug_frame_base, arg)) < 0) @@ -1106,6 +1139,7 @@ dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, { pi->start_ip += segbase; pi->end_ip += segbase; + pi->flags = UNW_PI_FLAG_DEBUG_FRAME; } if (ip < pi->start_ip || ip >= pi->end_ip) diff --git a/src/dwarf/Gparser.c b/src/dwarf/Gparser.c index 98833f75..e1e8b2b6 100644 --- a/src/dwarf/Gparser.c +++ b/src/dwarf/Gparser.c @@ -73,10 +73,19 @@ run_cfi_program (struct dwarf_cursor *c, dwarf_state_record_t *sr, as = c->as; arg = c->as_arg; + if (c->pi.flags & UNW_PI_FLAG_DEBUG_FRAME) + { + /* .debug_frame CFI is stored in local address space. */ + as = unw_local_addr_space; + arg = NULL; + } a = unw_get_accessors (as); curr_ip = c->pi.start_ip; - while (curr_ip < ip && *addr < end_addr) + /* Process everything up to and including the current 'ip', + including all the DW_CFA_advance_loc instructions. See + 'c->use_prev_instr' use in 'fetch_proc_info' for details. */ + while (curr_ip <= ip && *addr < end_addr) { if ((ret = dwarf_readu8 (as, a, addr, &op, arg)) < 0) return ret; @@ -381,7 +390,23 @@ fetch_proc_info (struct dwarf_cursor *c, unw_word_t ip, int need_unwind_info) { int ret, dynamic = 1; - --ip; + /* The 'ip' can point either to the previous or next instruction + depending on what type of frame we have: normal call or a place + to resume execution (e.g. after signal frame). + + For a normal call frame we need to back up so we point within the + call itself; this is important because a) the call might be the + very last instruction of the function and the edge of the FDE, + and b) so that run_cfi_program() runs locations up to the call + but not more. + + For execution resume, we need to do the exact opposite and look + up using the current 'ip' value. That is where execution will + 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 (c->pi_valid && !need_unwind_info) return 0; @@ -400,6 +425,19 @@ fetch_proc_info (struct dwarf_cursor *c, unw_word_t ip, int need_unwind_info) c->pi_valid = 1; c->pi_is_dynamic = dynamic; + + /* Let system/machine-dependent code determine frame-specific attributes. */ + if (ret >= 0) + tdep_fetch_frame (c, ip, need_unwind_info); + + /* Update use_prev_instr for the next frame. */ + if (need_unwind_info) + { + assert(c->pi.unwind_info); + struct dwarf_cie_info *dci = c->pi.unwind_info; + c->use_prev_instr = ! dci->signal_frame; + } + return ret; } @@ -422,7 +460,7 @@ put_unwind_info (struct dwarf_cursor *c, unw_proc_info_t *pi) if (c->pi_is_dynamic) unwi_put_dynamic_unwind_info (c->as, pi, c->as_arg); - else if (pi->unwind_info); + else if (pi->unwind_info) { mempool_free (&dwarf_cie_info_pool, pi->unwind_info); pi->unwind_info = NULL; @@ -481,22 +519,11 @@ get_rs_cache (unw_addr_space_t as, intrmask_t *saved_maskp) if (caching == UNW_CACHE_NONE) return NULL; -#ifdef HAVE_ATOMIC_H - if (!spin_trylock_irqsave (&cache->busy, *saved_maskp)) - return NULL; -#else -# ifdef HAVE_ATOMIC_OPS_H - if (AO_test_and_set (&cache->busy) == AO_TS_SET) - return NULL; -# else - sigprocmask (SIG_SETMASK, &unwi_full_mask, saved_maskp); if (likely (caching == UNW_CACHE_GLOBAL)) { Debug (16, "%s: acquiring lock\n", __FUNCTION__); - mutex_lock (&cache->lock); + lock_acquire (&cache->lock, *saved_maskp); } -# endif -#endif if (atomic_read (&as->cache_generation) != atomic_read (&cache->generation)) { @@ -514,17 +541,8 @@ put_rs_cache (unw_addr_space_t as, struct dwarf_rs_cache *cache, assert (as->caching_policy != UNW_CACHE_NONE); Debug (16, "unmasking signals/interrupts and releasing lock\n"); -#ifdef HAVE_ATOMIC_H - spin_unlock_irqrestore (&cache->busy, *saved_maskp); -#else -# ifdef HAVE_ATOMIC_OPS_H - AO_CLEAR (&cache->busy); -# else if (likely (as->caching_policy == UNW_CACHE_GLOBAL)) - mutex_unlock (&cache->lock); - sigprocmask (SIG_SETMASK, saved_maskp, NULL); -# endif -#endif + lock_release (&cache->lock, *saved_maskp); } static inline unw_hash_index_t @@ -624,6 +642,8 @@ rs_new (struct dwarf_rs_cache *cache, struct dwarf_cursor * c) rs->hint = 0; rs->ip = c->ip; rs->ret_addr_column = c->ret_addr_column; + rs->signal_frame = 0; + tdep_cache_frame (c, rs); return rs; } @@ -711,6 +731,7 @@ apply_reg_state (struct dwarf_cursor *c, struct dwarf_reg_state *rs) stack-pointer wasn't saved, popping the CFA implicitly pops the stack-pointer as well. */ if ((rs->reg[DWARF_CFA_REG_COLUMN].val == UNW_TDEP_SP) + && (UNW_TDEP_SP < ARRAY_SIZE(rs->reg)) && (rs->reg[UNW_TDEP_SP].where == DWARF_WHERE_SAME)) cfa = c->cfa; else @@ -762,13 +783,20 @@ apply_reg_state (struct dwarf_cursor *c, struct dwarf_reg_state *rs) break; } } - c->cfa = cfa; - ret = dwarf_get (c, c->loc[c->ret_addr_column], &ip); - if (ret < 0) - return ret; - c->ip = ip; - /* XXX: check for ip to be code_aligned */ + c->cfa = cfa; + /* DWARF spec says undefined return address location means end of stack. */ + if (DWARF_IS_NULL_LOC (c->loc[c->ret_addr_column])) + c->ip = 0; + else + { + ret = dwarf_get (c, c->loc[c->ret_addr_column], &ip); + if (ret < 0) + return ret; + c->ip = ip; + } + + /* XXX: check for ip to be code_aligned */ if (c->ip == prev_ip && c->cfa == prev_cfa) { Dprintf ("%s: ip and cfa unchanged; stopping here (ip=0x%lx)\n", @@ -803,7 +831,7 @@ HIDDEN int dwarf_find_save_locs (struct dwarf_cursor *c) { dwarf_state_record_t sr; - dwarf_reg_state_t *rs, *rs1; + dwarf_reg_state_t *rs, rs_copy; struct dwarf_rs_cache *cache; int ret = 0; intrmask_t saved_mask; @@ -812,49 +840,37 @@ dwarf_find_save_locs (struct dwarf_cursor *c) return uncached_dwarf_find_save_locs (c); cache = get_rs_cache(c->as, &saved_mask); - if (!cache) - return -UNW_ENOINFO; /* cache is busy */ rs = rs_lookup(cache, c); if (rs) { c->ret_addr_column = rs->ret_addr_column; - goto apply; + c->use_prev_instr = ! rs->signal_frame; } - - if ((ret = fetch_proc_info (c, c->ip, 1)) < 0) - goto out; - - if ((ret = create_state_record_for (c, &sr, c->ip)) < 0) - goto out; - - rs1 = &sr.rs_current; - if (rs1) + else { + if ((ret = fetch_proc_info (c, c->ip, 1)) < 0 || + (ret = create_state_record_for (c, &sr, c->ip)) < 0) + { + put_rs_cache (c->as, cache, &saved_mask); + return ret; + } + rs = rs_new (cache, c); - memcpy(rs, rs1, offsetof(struct dwarf_reg_state, ip)); - if (!rs) - { - Dprintf ("%s: failed to create unwind rs\n", __FUNCTION__); - ret = -UNW_EUNSPEC; - goto out; - } + memcpy(rs, &sr.rs_current, offsetof(struct dwarf_reg_state, ip)); + cache->buckets[c->prev_rs].hint = rs - cache->buckets; + + c->hint = rs->hint; + c->prev_rs = rs - cache->buckets; + + put_unwind_info (c, &c->pi); } - cache->buckets[c->prev_rs].hint = rs - cache->buckets; - c->hint = rs->hint; - c->prev_rs = rs - cache->buckets; - - put_unwind_info (c, &c->pi); - ret = apply_reg_state (c, rs); - -out: + memcpy (&rs_copy, rs, sizeof (rs_copy)); put_rs_cache (c->as, cache, &saved_mask); - return ret; -apply: - put_rs_cache (c->as, cache, &saved_mask); - if ((ret = apply_reg_state (c, rs)) < 0) + tdep_reuse_frame (c, &rs_copy); + if ((ret = apply_reg_state (c, &rs_copy)) < 0) return ret; return 0; diff --git a/src/dwarf/Gstep.c b/src/dwarf/Gstep.c index a9b789c6..59138e6f 100644 --- a/src/dwarf/Gstep.c +++ b/src/dwarf/Gstep.c @@ -29,7 +29,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ HIDDEN int dwarf_step (struct dwarf_cursor *c) { - unw_word_t prev_cfa = c->cfa; int ret; if ((ret = dwarf_find_save_locs (c)) >= 0) { diff --git a/src/elf32.h b/src/elf32.h index d27646cc..63fef836 100644 --- a/src/elf32.h +++ b/src/elf32.h @@ -1,7 +1,9 @@ #ifndef elf32_h #define elf32_h +#ifndef ELF_CLASS #define ELF_CLASS ELFCLASS32 +#endif #include "elfxx.h" #endif /* elf32_h */ diff --git a/src/elf64.h b/src/elf64.h index 8f22384a..fd10ed82 100644 --- a/src/elf64.h +++ b/src/elf64.h @@ -1,7 +1,9 @@ #ifndef elf64_h #define elf64_h +#ifndef ELF_CLASS #define ELF_CLASS ELFCLASS64 +#endif #include "elfxx.h" #endif /* elf64_h */ diff --git a/src/elfxx.c b/src/elfxx.c index 05b99438..06cc9bb6 100644 --- a/src/elfxx.c +++ b/src/elfxx.c @@ -25,6 +25,7 @@ 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 +#include #include "libunwind_i.h" @@ -147,7 +148,7 @@ elf_w (get_proc_name) (unw_addr_space_t as, pid_t pid, unw_word_t ip, Elf_W (Phdr) *phdr; int i, ret; - ret = tdep_get_elf_image (&ei, pid, ip, &segbase, &mapoff); + ret = tdep_get_elf_image (&ei, pid, ip, &segbase, &mapoff, NULL, 0); if (ret < 0) return ret; diff --git a/src/hppa/Gglobal.c b/src/hppa/Gglobal.c index a56426da..f453a43b 100644 --- a/src/hppa/Gglobal.c +++ b/src/hppa/Gglobal.c @@ -35,8 +35,7 @@ tdep_init (void) sigfillset (&unwi_full_mask); - sigprocmask (SIG_SETMASK, &unwi_full_mask, &saved_mask); - mutex_lock (&hppa_lock); + lock_acquire (&hppa_lock, saved_mask); { if (!tdep_needs_initialization) /* another thread else beat us to it... */ @@ -52,6 +51,5 @@ tdep_init (void) tdep_needs_initialization = 0; /* signal that we're initialized... */ } out: - mutex_unlock (&hppa_lock); - sigprocmask (SIG_SETMASK, &saved_mask, NULL); + lock_release (&hppa_lock, saved_mask); } diff --git a/src/hppa/Ginit_local.c b/src/hppa/Ginit_local.c index 243ffd49..6d4dd3df 100644 --- a/src/hppa/Ginit_local.c +++ b/src/hppa/Ginit_local.c @@ -48,7 +48,7 @@ unw_init_local (unw_cursor_t *cursor, ucontext_t *uc) c->dwarf.as = unw_local_addr_space; c->dwarf.as_arg = uc; - return common_init (c); + return common_init (c, 1); } #endif /* !UNW_REMOTE_ONLY */ diff --git a/src/hppa/Ginit_remote.c b/src/hppa/Ginit_remote.c index 3d6606df..bd6093f1 100644 --- a/src/hppa/Ginit_remote.c +++ b/src/hppa/Ginit_remote.c @@ -41,6 +41,6 @@ unw_init_remote (unw_cursor_t *cursor, unw_addr_space_t as, void *as_arg) c->dwarf.as = as; c->dwarf.as_arg = as_arg; - return common_init (c); + return common_init (c, 0); #endif /* !UNW_LOCAL_ONLY */ } diff --git a/src/hppa/init.h b/src/hppa/init.h index d5f900d4..d14354f6 100644 --- a/src/hppa/init.h +++ b/src/hppa/init.h @@ -26,7 +26,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "unwind_i.h" static inline int -common_init (struct cursor *c) +common_init (struct cursor *c, unsigned use_prev_instr) { int ret; @@ -40,5 +40,7 @@ common_init (struct cursor *c) ret = hppa_get (c, HPPA_REG_LOC (c, UNW_HPPA_SP), &c->sp); if (ret < 0) return ret; + + c->dwarf.use_prev_instr = use_prev_instr; return 0; } diff --git a/src/ia64/Gglobal.c b/src/ia64/Gglobal.c index 9525f8c2..b04a5783 100644 --- a/src/ia64/Gglobal.c +++ b/src/ia64/Gglobal.c @@ -71,8 +71,7 @@ tdep_init (void) sigfillset (&unwi_full_mask); - sigprocmask (SIG_SETMASK, &unwi_full_mask, &saved_mask); - mutex_lock (&unw.lock); + lock_acquire (&unw.lock, saved_mask); { if (!tdep_needs_initialization) /* another thread else beat us to it... */ @@ -120,6 +119,5 @@ tdep_init (void) tdep_needs_initialization = 0; /* signal that we're initialized... */ } out: - mutex_unlock (&unw.lock); - sigprocmask (SIG_SETMASK, &saved_mask, NULL); + lock_release (&unw.lock, saved_mask); } diff --git a/src/ia64/Gscript.c b/src/ia64/Gscript.c index edeee1de..b3e8ef6e 100644 --- a/src/ia64/Gscript.c +++ b/src/ia64/Gscript.c @@ -113,11 +113,10 @@ get_script_cache (unw_addr_space_t as, intrmask_t *saved_maskp) if (AO_test_and_set (&cache->busy) == AO_TS_SET) return NULL; # else - sigprocmask (SIG_SETMASK, &unwi_full_mask, saved_maskp); if (likely (caching == UNW_CACHE_GLOBAL)) { Debug (16, "%s: acquiring lock\n", __FUNCTION__); - mutex_lock (&cache->lock); + lock_acquire (&cache->lock, *saved_maskp); } # endif #endif @@ -144,8 +143,7 @@ put_script_cache (unw_addr_space_t as, struct ia64_script_cache *cache, AO_CLEAR (&cache->busy); # else if (likely (as->caching_policy == UNW_CACHE_GLOBAL)) - mutex_unlock (&cache->lock); - sigprocmask (SIG_SETMASK, saved_maskp, NULL); + lock_release (&cache->lock, *saved_maskp); # endif #endif } diff --git a/src/ia64/Gtables.c b/src/ia64/Gtables.c index 63725e80..1a16a2e7 100644 --- a/src/ia64/Gtables.c +++ b/src/ia64/Gtables.c @@ -622,9 +622,9 @@ validate_cache (unw_addr_space_t as) intrmask_t saved_mask; int ret; - sigprocmask (SIG_SETMASK, &unwi_full_mask, &saved_mask); + SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask); ret = dl_iterate_phdr (check_callback, as); - sigprocmask (SIG_SETMASK, &saved_mask, NULL); + SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL); return ret; } @@ -653,9 +653,9 @@ tdep_find_proc_info (unw_addr_space_t as, unw_word_t ip, di.u.ti.segbase = ip; /* this is cheap... */ - sigprocmask (SIG_SETMASK, &unwi_full_mask, &saved_mask); + SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask); ret = dl_iterate_phdr (callback, &di); - sigprocmask (SIG_SETMASK, &saved_mask, NULL); + SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL); if (ret <= 0) { diff --git a/src/libunwind-common.h.cmake.in b/src/libunwind-common.h.cmake.in index 18129f88..d507660a 100644 --- a/src/libunwind-common.h.cmake.in +++ b/src/libunwind-common.h.cmake.in @@ -119,6 +119,8 @@ typedef struct unw_addr_space *unw_addr_space_t; /* Each target may define it's own set of flags, but bits 0-15 are reserved for general libunwind-use. */ #define UNW_PI_FLAG_FIRST_TDEP_BIT 16 +/* The information comes from a .debug_frame section. */ +#define UNW_PI_FLAG_DEBUG_FRAME 32 typedef struct unw_proc_info { @@ -220,6 +222,7 @@ unw_save_loc_t; #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_regname UNW_ARCH_OBJ(regname) @@ -246,6 +249,7 @@ extern int unw_get_fpreg (unw_cursor_t *, int, unw_fpreg_t *); 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); diff --git a/src/mips/Gglobal.c b/src/mips/Gglobal.c index 1874a5bc..1aa77eac 100644 --- a/src/mips/Gglobal.c +++ b/src/mips/Gglobal.c @@ -44,8 +44,7 @@ tdep_init (void) sigfillset (&unwi_full_mask); - sigprocmask (SIG_SETMASK, &unwi_full_mask, &saved_mask); - mutex_lock (&mips_lock); + lock_acquire (&mips_lock, saved_mask); { if (!tdep_needs_initialization) /* another thread else beat us to it... */ @@ -61,6 +60,5 @@ tdep_init (void) tdep_needs_initialization = 0; /* signal that we're initialized... */ } out: - mutex_unlock (&mips_lock); - sigprocmask (SIG_SETMASK, &saved_mask, NULL); + lock_release (&mips_lock, saved_mask); } diff --git a/src/mips/Ginit_local.c b/src/mips/Ginit_local.c index 7b2881e3..debf5bb5 100644 --- a/src/mips/Ginit_local.c +++ b/src/mips/Ginit_local.c @@ -47,7 +47,7 @@ unw_init_local (unw_cursor_t *cursor, ucontext_t *uc) c->dwarf.as = unw_local_addr_space; c->dwarf.as_arg = uc; - return common_init (c); + return common_init (c, 1); } #endif /* !UNW_REMOTE_ONLY */ diff --git a/src/mips/Ginit_remote.c b/src/mips/Ginit_remote.c index 3baf3f60..854f3b62 100644 --- a/src/mips/Ginit_remote.c +++ b/src/mips/Ginit_remote.c @@ -40,6 +40,6 @@ unw_init_remote (unw_cursor_t *cursor, unw_addr_space_t as, void *as_arg) c->dwarf.as = as; c->dwarf.as_arg = as_arg; - return common_init (c); + return common_init (c, 0); #endif /* !UNW_LOCAL_ONLY */ } diff --git a/src/mips/init.h b/src/mips/init.h index 98956d4a..e32e3c9e 100644 --- a/src/mips/init.h +++ b/src/mips/init.h @@ -25,7 +25,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "unwind_i.h" static inline int -common_init (struct cursor *c) +common_init (struct cursor *c, unsigned use_prev_instr) { int ret, i; @@ -47,6 +47,7 @@ common_init (struct cursor *c) c->dwarf.args_size = 0; c->dwarf.ret_addr_column = 0; + c->dwarf.use_prev_instr = use_prev_instr; c->dwarf.pi_valid = 0; c->dwarf.pi_is_dynamic = 0; c->dwarf.hint = 0; diff --git a/src/os-freebsd.c b/src/os-freebsd.c new file mode 100644 index 00000000..a3151886 --- /dev/null +++ b/src/os-freebsd.c @@ -0,0 +1,101 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2010 Konstantin Belousov + +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 UNW_REMOTE_ONLY + +#include +#include +#include +#include +#include +#include + +#include "libunwind_i.h" + +static void * +get_mem(size_t sz) +{ + void *res; + + res = mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); + if (res == MAP_FAILED) + return (NULL); + return (res); +} + +static void +free_mem(void *ptr, size_t sz) +{ + munmap(ptr, sz); +} + +PROTECTED 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) +{ + int mib[4], error, ret; + size_t len, len1; + char *buf, *bp, *eb; + struct kinfo_vmentry *kv; + + len = 0; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_VMMAP; + mib[3] = pid; + + error = sysctl(mib, 4, NULL, &len, NULL, 0); + if (error) + return (-1); + len1 = len * 4 / 3; + buf = get_mem(len1); + if (buf == NULL) + return (-1); + len = len1; + error = sysctl(mib, 4, buf, &len, NULL, 0); + if (error) { + free_mem(buf, len1); + return (-1); + } + ret = -1; + for (bp = buf, eb = buf + len; bp < eb; bp += kv->kve_structsize) { + kv = (struct kinfo_vmentry *)(uintptr_t)bp; + if (ip < kv->kve_start || ip >= kv->kve_end) + continue; + if (kv->kve_type != KVME_TYPE_VNODE) + break; + *segbase = kv->kve_start; + *mapoff = kv->kve_offset; + if (path) + { + strncpy(path, kv->kve_path, pathlen); + } + ret = elf_map_image(ei, kv->kve_path); + break; + } + free_mem(buf, len1); + return (ret); +} + +#endif /* UNW_REMOTE_ONLY */ diff --git a/src/os-hpux.c b/src/os-hpux.c index 586ace2e..09100d0f 100644 --- a/src/os-hpux.c +++ b/src/os-hpux.c @@ -26,6 +26,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UNW_REMOTE_ONLY #include +#include #include #include "libunwind_i.h" @@ -34,10 +35,11 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ HIDDEN int tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip, - unsigned long *segbase, unsigned long *mapoff) + unsigned long *segbase, unsigned long *mapoff, + char *path, size_t pathlen); { struct load_module_desc lmd; - const char *path; + const char *path2; if (pid != getpid ()) { @@ -51,10 +53,16 @@ tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip, *segbase = lmd.text_base; *mapoff = 0; /* XXX fix me? */ - path = dlgetname (&lmd, sizeof (lmd), NULL, 0, 0); - if (!path) + path2 = dlgetname (&lmd, sizeof (lmd), NULL, 0, 0); + if (!path2) return -UNW_ENOINFO; - + if (path) + { + strncpy(path, path2, pathlen); + path[pathlen - 1] = '\0'; + if (strcmp(path, path2) != 0) + Debug(1, "buffer size (%d) not big enough to hold path\n", pathlen); + } Debug(1, "segbase=%lx, mapoff=%lx, path=%s\n", *segbase, *mapoff, path); return elf_map_image (ei, path); diff --git a/src/os-linux.c b/src/os-linux.c index ae37d8f8..791b27eb 100644 --- a/src/os-linux.c +++ b/src/os-linux.c @@ -33,26 +33,35 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ PROTECTED int tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip, - unsigned long *segbase, unsigned long *mapoff) + unsigned long *segbase, unsigned long *mapoff, + char *path, size_t pathlen) { struct map_iterator mi; - char path[PATH_MAX]; - int found = 0; + int found = 0, rc; unsigned long hi; - maps_init (&mi, pid); - while (maps_next (&mi, segbase, &hi, mapoff, path, sizeof (path))) + if (maps_init (&mi, pid) < 0) + return -1; + + while (maps_next (&mi, segbase, &hi, mapoff)) if (ip >= *segbase && ip < hi) { found = 1; break; } - maps_close (&mi); if (!found) - return -1; - - return elf_map_image (ei, path); + { + maps_close (&mi); + return -1; + } + if (path) + { + strncpy(path, mi.path, pathlen); + } + rc = elf_map_image (ei, mi.path); + maps_close (&mi); + return rc; } #endif /* UNW_REMOTE_ONLY */ diff --git a/src/os-linux.h b/src/os-linux.h index b2c9a8be..af301ce0 100644 --- a/src/os-linux.h +++ b/src/os-linux.h @@ -34,6 +34,7 @@ struct map_iterator size_t buf_size; char *buf; char *buf_end; + char *path; }; static inline char * @@ -61,33 +62,33 @@ ltoa (char *buf, long val) return buf + len; } -static inline void +static inline int maps_init (struct map_iterator *mi, pid_t pid) { - char path[PATH_MAX], *cp; + char path[sizeof ("/proc/0123456789/maps")], *cp; memcpy (path, "/proc/", 6); cp = ltoa (path + 6, pid); + assert (cp + 6 < path + sizeof (path)); memcpy (cp, "/maps", 6); mi->fd = open (path, O_RDONLY); - mi->offset = 0; - mi->buf_size = 0; - - cp = NULL; if (mi->fd >= 0) { - /* Try to allocate a page-sized buffer. If that fails, we'll - fall back on reading one line at a time. */ + /* Try to allocate a page-sized buffer. */ mi->buf_size = getpagesize (); cp = mmap (0, mi->buf_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (cp == MAP_FAILED) - cp = NULL; + return -1; else - cp += mi->buf_size; + { + mi->offset = 0; + mi->buf = mi->buf_end = cp + mi->buf_size; + return 0; + } } - mi->buf = mi->buf_end = cp; + return -1; } static inline char * @@ -184,7 +185,7 @@ scan_string (char *cp, char *valp, size_t buf_size) while (*cp != ' ' && *cp != '\t' && *cp != '\0') { - if (i < buf_size - 1) + if ((valp != NULL) && (i < buf_size - 1)) valp[i++] = *cp; ++cp; } @@ -196,12 +197,10 @@ scan_string (char *cp, char *valp, size_t buf_size) static inline int maps_next (struct map_iterator *mi, - unsigned long *low, unsigned long *high, unsigned long *offset, - char *path, size_t path_size) + unsigned long *low, unsigned long *high, unsigned long *offset) { - char line[256 + PATH_MAX], perm[16], dash, colon, *cp; + char perm[16], dash, colon, *cp; unsigned long major, minor, inum; - size_t to_read = 256; /* most lines fit in 256 characters easy */ ssize_t i, nread; if (mi->fd < 0) @@ -209,80 +208,52 @@ maps_next (struct map_iterator *mi, while (1) { - if (mi->buf) + ssize_t bytes_left = mi->buf_end - mi->buf; + char *eol = NULL; + + for (i = 0; i < bytes_left; ++i) { - ssize_t bytes_left = mi->buf_end - mi->buf; - char *eol = NULL; - - for (i = 0; i < bytes_left; ++i) + if (mi->buf[i] == '\n') { - if (mi->buf[i] == '\n') - { - eol = mi->buf + i; - break; - } - else if (mi->buf[i] == '\0') - break; + eol = mi->buf + i; + break; } - if (!eol) - { - /* copy down the remaining bytes, if any */ - if (bytes_left > 0) - memmove (mi->buf_end - mi->buf_size, mi->buf, bytes_left); - - mi->buf = mi->buf_end - mi->buf_size; - nread = read (mi->fd, mi->buf + bytes_left, - mi->buf_size - bytes_left); - if (nread <= 0) - return 0; - else if ((size_t) (nread + bytes_left) < mi->buf_size) - { - /* Move contents to the end of the buffer so we - maintain the invariant that all bytes between - mi->buf and mi->buf_end are valid. */ - memmove (mi->buf_end - nread - bytes_left, mi->buf, - nread + bytes_left); - mi->buf = mi->buf_end - nread - bytes_left; - } - - eol = mi->buf + bytes_left + nread - 1; - - for (i = bytes_left; i < bytes_left + nread; ++i) - if (mi->buf[i] == '\n') - { - eol = mi->buf + i; - break; - } - } - cp = mi->buf; - mi->buf = eol + 1; - *eol = '\0'; + else if (mi->buf[i] == '\0') + break; } - else + if (!eol) { - /* maps_init() wasn't able to allocate a buffer; do it the - slow way. */ - lseek (mi->fd, mi->offset, SEEK_SET); + /* copy down the remaining bytes, if any */ + if (bytes_left > 0) + memmove (mi->buf_end - mi->buf_size, mi->buf, bytes_left); - if ((nread = read (mi->fd, line, to_read)) <= 0) + mi->buf = mi->buf_end - mi->buf_size; + nread = read (mi->fd, mi->buf + bytes_left, + mi->buf_size - bytes_left); + if (nread <= 0) return 0; - for (i = 0; i < nread && line[i] != '\n'; ++i) - /* skip */; - if (i < nread) + else if ((size_t) (nread + bytes_left) < mi->buf_size) { - line[i] = '\0'; - mi->offset += i + 1; + /* Move contents to the end of the buffer so we + maintain the invariant that all bytes between + mi->buf and mi->buf_end are valid. */ + memmove (mi->buf_end - nread - bytes_left, mi->buf, + nread + bytes_left); + mi->buf = mi->buf_end - nread - bytes_left; } - else - { - if (to_read < sizeof (line)) - to_read = sizeof (line) - 1; - else - mi->offset += nread; /* not supposed to happen... */ - continue; /* duh, no newline found */ - } - cp = line; + + eol = mi->buf + bytes_left + nread - 1; + + for (i = bytes_left; i < bytes_left + nread; ++i) + if (mi->buf[i] == '\n') + { + eol = mi->buf + i; + break; + } } + cp = mi->buf; + mi->buf = eol + 1; + *eol = '\0'; /* scan: "LOW-HIGH PERM OFFSET MAJOR:MINOR INUM PATH" */ cp = scan_hex (cp, low); @@ -294,8 +265,11 @@ maps_next (struct map_iterator *mi, cp = scan_char (cp, &colon); cp = scan_hex (cp, &minor); cp = scan_dec (cp, &inum); - cp = scan_string (cp, path, path_size); - if (!cp || dash != '-' || colon != ':') + cp = mi->path = skip_whitespace (cp); + if (!cp) + continue; + cp = scan_string (cp, NULL, 0); + if (dash != '-' || colon != ':') continue; /* skip line with unknown or bad format */ return 1; } diff --git a/src/ppc/Ginit_local.c b/src/ppc/Ginit_local.c index 2d9ab2ce..b931b5b6 100644 --- a/src/ppc/Ginit_local.c +++ b/src/ppc/Ginit_local.c @@ -56,9 +56,9 @@ unw_init_local (unw_cursor_t *cursor, ucontext_t *uc) c->dwarf.as = unw_local_addr_space; c->dwarf.as_arg = uc; #ifdef UNW_TARGET_PPC64 - return common_init_ppc64 (c); + return common_init_ppc64 (c, 1); #else - return common_init_ppc32 (c); + return common_init_ppc32 (c, 1); #endif } diff --git a/src/ppc/Ginit_remote.c b/src/ppc/Ginit_remote.c index 66269d2c..0f4b0fdb 100644 --- a/src/ppc/Ginit_remote.c +++ b/src/ppc/Ginit_remote.c @@ -50,9 +50,9 @@ unw_init_remote (unw_cursor_t *cursor, unw_addr_space_t as, void *as_arg) c->dwarf.as_arg = as_arg; #ifdef UNW_TARGET_PPC64 - return common_init_ppc64(c); + return common_init_ppc64 (c, 0); #elif UNW_TARGET_PPC32 - return common_init_ppc32 (c); + return common_init_ppc32 (c, 0); #else #error init_remote :: NO VALID PPC ARCH! #endif diff --git a/src/ppc32/Gglobal.c b/src/ppc32/Gglobal.c index c2d4604d..b7cebff5 100644 --- a/src/ppc32/Gglobal.c +++ b/src/ppc32/Gglobal.c @@ -115,8 +115,7 @@ tdep_init (void) sigfillset (&unwi_full_mask); - sigprocmask (SIG_SETMASK, &unwi_full_mask, &saved_mask); - mutex_lock (&ppc32_lock); + lock_acquire (&ppc32_lock, saved_mask); { if (!tdep_needs_initialization) /* another thread else beat us to it... */ @@ -132,6 +131,5 @@ tdep_init (void) tdep_needs_initialization = 0; /* signal that we're initialized... */ } out: - mutex_unlock (&ppc32_lock); - sigprocmask (SIG_SETMASK, &saved_mask, NULL); + lock_release (&ppc32_lock, saved_mask); } diff --git a/src/ppc32/init.h b/src/ppc32/init.h index 8badb178..c2208ad9 100644 --- a/src/ppc32/init.h +++ b/src/ppc32/init.h @@ -30,7 +30,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* Here is the "common" init, for remote and local debuging" */ static inline int -common_init_ppc32 (struct cursor *c) +common_init_ppc32 (struct cursor *c, unsigned use_prev_instr) { int ret; int i; @@ -62,6 +62,7 @@ common_init_ppc32 (struct cursor *c) c->dwarf.args_size = 0; c->dwarf.ret_addr_column = 0; + c->dwarf.use_prev_instr = use_prev_instr; c->dwarf.pi_valid = 0; c->dwarf.pi_is_dynamic = 0; c->dwarf.hint = 0; diff --git a/src/ppc64/Gglobal.c b/src/ppc64/Gglobal.c index 1506b002..64813571 100644 --- a/src/ppc64/Gglobal.c +++ b/src/ppc64/Gglobal.c @@ -162,8 +162,7 @@ tdep_init (void) sigfillset (&unwi_full_mask); - sigprocmask (SIG_SETMASK, &unwi_full_mask, &saved_mask); - mutex_lock (&ppc64_lock); + lock_acquire (&ppc64_lock, saved_mask); { if (!tdep_needs_initialization) /* another thread else beat us to it... */ @@ -179,6 +178,5 @@ tdep_init (void) tdep_needs_initialization = 0; /* signal that we're initialized... */ } out: - mutex_unlock (&ppc64_lock); - sigprocmask (SIG_SETMASK, &saved_mask, NULL); + lock_release (&ppc64_lock, saved_mask); } diff --git a/src/ppc64/init.h b/src/ppc64/init.h index 886f14c4..64847b84 100644 --- a/src/ppc64/init.h +++ b/src/ppc64/init.h @@ -28,7 +28,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "unwind_i.h" static inline int -common_init_ppc64 (struct cursor *c) +common_init_ppc64 (struct cursor *c, unsigned use_prev_instr) { int ret; int i; @@ -72,6 +72,7 @@ common_init_ppc64 (struct cursor *c) c->dwarf.args_size = 0; c->dwarf.ret_addr_column = 0; + c->dwarf.use_prev_instr = use_prev_instr; c->dwarf.pi_valid = 0; c->dwarf.pi_is_dynamic = 0; c->dwarf.hint = 0; diff --git a/src/ptrace/_UPT_access_fpreg.c b/src/ptrace/_UPT_access_fpreg.c index 28e08922..283dcd4f 100644 --- a/src/ptrace/_UPT_access_fpreg.c +++ b/src/ptrace/_UPT_access_fpreg.c @@ -1,6 +1,7 @@ /* libunwind - a platform-independent unwind library Copyright (C) 2003 Hewlett-Packard Co Contributed by David Mosberger-Tang + Copyright (C) 2010 Konstantin Belousov This file is part of libunwind. @@ -25,6 +26,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "_UPT_internal.h" +#if HAVE_DECL_PTRACE_POKEUSER || HAVE_TTRACE int _UPT_access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, int write, void *arg) @@ -64,3 +66,40 @@ _UPT_access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, } return 0; } +#elif HAVE_DECL_PT_GETFPREGS +int +_UPT_access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, + int write, void *arg) +{ + struct UPT_info *ui = arg; + pid_t pid = ui->pid; + fpregset_t fpreg; + + if ((unsigned) reg >= sizeof (_UPT_reg_offset) / sizeof (_UPT_reg_offset[0])) + return -UNW_EBADREG; + + if (ptrace(PT_GETFPREGS, pid, (caddr_t)&fpreg, 0) == -1) + return -UNW_EBADREG; + if (write) { +#if defined(__amd64__) + memcpy(&fpreg.fpr_xacc[reg], val, sizeof(unw_fpreg_t)); +#elif defined(__i386__) + memcpy(&fpreg.fpr_acc[reg], val, sizeof(unw_fpreg_t)); +#else +#error Fix me +#endif + if (ptrace(PT_SETFPREGS, pid, (caddr_t)&fpreg, 0) == -1) + return -UNW_EBADREG; + } else +#if defined(__amd64__) + memcpy(val, &fpreg.fpr_xacc[reg], sizeof(unw_fpreg_t)); +#elif defined(__i386__) + memcpy(val, &fpreg.fpr_acc[reg], sizeof(unw_fpreg_t)); +#else +#error Fix me +#endif + return 0; +} +#else +#error Fix me +#endif diff --git a/src/ptrace/_UPT_access_mem.c b/src/ptrace/_UPT_access_mem.c index 6900c19d..b0c2ef04 100644 --- a/src/ptrace/_UPT_access_mem.c +++ b/src/ptrace/_UPT_access_mem.c @@ -1,6 +1,7 @@ /* libunwind - a platform-independent unwind library Copyright (C) 2003-2004 Hewlett-Packard Co Contributed by David Mosberger-Tang + Copyright (C) 2010 Konstantin Belousov This file is part of libunwind. @@ -25,6 +26,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "_UPT_internal.h" +#if HAVE_DECL_PTRACE_POKEDATA || HAVE_TTRACE int _UPT_access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, void *arg) @@ -57,3 +59,27 @@ _UPT_access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, } return 0; } +#elif HAVE_DECL_PT_IO +int +_UPT_access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, + int write, void *arg) +{ + struct UPT_info *ui = arg; + pid_t pid = ui->pid; + struct ptrace_io_desc iod; + + iod.piod_offs = (void *)addr; + iod.piod_addr = val; + iod.piod_len = sizeof(*val); + iod.piod_op = write ? PIOD_WRITE_D : PIOD_READ_D; + if (write) + Debug (16, "mem[%lx] <- %lx\n", (long) addr, (long) *val); + if (ptrace(PT_IO, pid, (caddr_t)&iod, 0) == -1) + return -UNW_EINVAL; + if (!write) + Debug (16, "mem[%lx] -> %lx\n", (long) addr, (long) *val); + return 0; +} +#else +#error Fix me +#endif diff --git a/src/ptrace/_UPT_access_reg.c b/src/ptrace/_UPT_access_reg.c index 60e006b9..00ba0cc7 100644 --- a/src/ptrace/_UPT_access_reg.c +++ b/src/ptrace/_UPT_access_reg.c @@ -1,6 +1,7 @@ /* libunwind - a platform-independent unwind library Copyright (C) 2003-2005 Hewlett-Packard Co Contributed by David Mosberger-Tang + Copyright (C) 2010 Konstantin Belousov This file is part of libunwind. @@ -33,6 +34,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ # include "tdep-ia64/rse.h" #endif +#if HAVE_DECL_PTRACE_POKEUSER || HAVE_TTRACE int _UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, void *arg) @@ -252,3 +254,40 @@ _UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, Debug (1, "bad register number %u (error: %s)\n", reg, strerror (errno)); return -UNW_EBADREG; } +#elif HAVE_DECL_PT_GETREGS +int +_UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, + int write, void *arg) +{ + struct UPT_info *ui = arg; + pid_t pid = ui->pid; + gregset_t regs; + char *r; + +#if UNW_DEBUG + if (write) + Debug (16, "%s <- %lx\n", unw_regname (reg), (long) *val); +#endif + if ((unsigned) reg >= sizeof (_UPT_reg_offset) / sizeof (_UPT_reg_offset[0])) + { + errno = EINVAL; + goto badreg; + } + r = (char *)®s + _UPT_reg_offset[reg]; + if (ptrace(PT_GETREGS, pid, (caddr_t)®s, 0) == -1) + goto badreg; + if (write) { + memcpy(r, val, sizeof(unw_word_t)); + if (ptrace(PT_SETREGS, pid, (caddr_t)®s, 0) == -1) + goto badreg; + } else + memcpy(val, r, sizeof(unw_word_t)); + return 0; + + badreg: + Debug (1, "bad register number %u (error: %s)\n", reg, strerror (errno)); + return -UNW_EBADREG; +} +#else +#error Port me +#endif diff --git a/src/ptrace/_UPT_destroy.c b/src/ptrace/_UPT_destroy.c index 52cf5434..088bbe3d 100644 --- a/src/ptrace/_UPT_destroy.c +++ b/src/ptrace/_UPT_destroy.c @@ -26,7 +26,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "_UPT_internal.h" void -_UPT_destroy (void *ui) +_UPT_destroy (void *ptr) { - free (ui); + struct UPT_info *ui = (struct UPT_info *) ptr; + if (ui->ei.image) + { + munmap(ui->ei.image, ui->ei.size); + } + + free (ptr); } diff --git a/src/ptrace/_UPT_find_proc_info.c b/src/ptrace/_UPT_find_proc_info.c index 2b81e59a..0c000392 100644 --- a/src/ptrace/_UPT_find_proc_info.c +++ b/src/ptrace/_UPT_find_proc_info.c @@ -97,7 +97,8 @@ find_gp (struct UPT_info *ui, Elf64_Phdr *pdyn, Elf64_Addr load_base) HIDDEN unw_dyn_info_t * _UPTi_find_unwind_table (struct UPT_info *ui, unw_addr_space_t as, - char *path, unw_word_t segbase, unw_word_t mapoff) + char *path, unw_word_t segbase, unw_word_t mapoff, + unw_word_t ip) { Elf64_Phdr *phdr, *ptxt = NULL, *punw = NULL, *pdyn = NULL; Elf64_Ehdr *ehdr; @@ -165,10 +166,12 @@ dwarf_read_encoded_pointer (unw_addr_space_t as, unw_accessors_t *a, HIDDEN unw_dyn_info_t * _UPTi_find_unwind_table (struct UPT_info *ui, unw_addr_space_t as, - char *path, unw_word_t segbase, unw_word_t mapoff) + char *path, unw_word_t segbase, unw_word_t mapoff, + unw_word_t ip) { Elf_W(Phdr) *phdr, *ptxt = NULL, *peh_hdr = NULL, *pdyn = NULL; unw_word_t addr, eh_frame_start, fde_count, load_base; + unw_word_t max_load_addr = 0; struct dwarf_eh_frame_hdr *hdr; unw_proc_info_t pi; unw_accessors_t *a; @@ -190,6 +193,8 @@ _UPTi_find_unwind_table (struct UPT_info *ui, unw_addr_space_t as, case PT_LOAD: if (phdr[i].p_offset == mapoff) ptxt = phdr + i; + if ((uintptr_t) ui->ei.image + phdr->p_filesz > max_load_addr) + max_load_addr = (uintptr_t) ui->ei.image + phdr->p_filesz; break; case PT_GNU_EH_FRAME: @@ -205,7 +210,35 @@ _UPTi_find_unwind_table (struct UPT_info *ui, unw_addr_space_t as, } } if (!ptxt || !peh_hdr) +#ifdef CONFIG_DEBUG_FRAME + { + /* No .eh_frame found, try .debug_frame. */ + struct dl_phdr_info info; + + info.dlpi_name = path; + info.dlpi_phdr = phdr; + info.dlpi_phnum = ehdr->e_phnum; + + /* Fixup segbase to match correct base address. */ + for (i = 0; i < info.dlpi_phnum; i++) + { + if (info.dlpi_phdr[i].p_type == PT_LOAD && + info.dlpi_phdr[i].p_offset == 0) + { + segbase -= info.dlpi_phdr[i].p_vaddr; + break; + } + } + info.dlpi_addr = segbase; + + if (dwarf_find_debug_frame (0, &ui->di_cache, &info, ip)) + return &ui->di_cache; + else + return NULL; + } +#else return NULL; +#endif if (pdyn) { @@ -262,16 +295,18 @@ _UPTi_find_unwind_table (struct UPT_info *ui, unw_addr_space_t as, if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) { +#if 1 abort (); -#if 0 +#else + unw_word_t eh_frame_end; + /* 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, "EH lacks search table; doing linear search\n"); else - Debug (4, "table `%s' has encoding 0x%x; doing linear search\n", - info->dlpi_name, hdr->table_enc); + Debug (4, "EH table has encoding 0x%x; doing linear search\n", + hdr->table_enc); eh_frame_end = max_load_addr; /* XXX can we do better? */ @@ -280,7 +315,6 @@ _UPTi_find_unwind_table (struct UPT_info *ui, unw_addr_space_t as, if (hdr->eh_frame_ptr_enc == DW_EH_PE_omit) abort (); - cb_data->single_fde = 1; return linear_search (unw_local_addr_space, ip, eh_frame_start, eh_frame_end, fde_count, pi, need_unwind_info, NULL); @@ -338,12 +372,13 @@ get_unwind_info (struct UPT_info *ui, unw_addr_space_t as, unw_word_t ip) ui->di_cache.start_ip = ui->di_cache.end_ip = 0; } - if (tdep_get_elf_image (&ui->ei, ui->pid, ip, &segbase, &mapoff) < 0) + if (tdep_get_elf_image (&ui->ei, ui->pid, ip, &segbase, &mapoff, path, + sizeof(path)) < 0) return NULL; /* Here, SEGBASE is the starting-address of the (mmap'ped) segment which covers the IP we're looking for. */ - di = _UPTi_find_unwind_table (ui, as, path, segbase, mapoff); + di = _UPTi_find_unwind_table (ui, as, path, segbase, mapoff, ip); if (!di /* This can happen in corner cases where dynamically generated code falls into the same page that contains the data-segment diff --git a/src/ptrace/_UPT_internal.h b/src/ptrace/_UPT_internal.h index 0577097a..ff11e738 100644 --- a/src/ptrace/_UPT_internal.h +++ b/src/ptrace/_UPT_internal.h @@ -26,14 +26,24 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef _UPT_internal_h #define _UPT_internal_h +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include +#ifdef HAVE_SYS_PROCFS_H +#include +#endif + #include #include #include #include #include -#include - #include "libunwind_i.h" struct UPT_info @@ -52,6 +62,7 @@ extern unw_dyn_info_t *_UPTi_find_unwind_table (struct UPT_info *ui, unw_addr_space_t as, char *path, unw_word_t segbase, - unw_word_t mapoff); + unw_word_t mapoff, + unw_word_t ip); #endif /* _UPT_internal_h */ diff --git a/src/ptrace/_UPT_reg_offset.c b/src/ptrace/_UPT_reg_offset.c index f586fb72..d6e12741 100644 --- a/src/ptrace/_UPT_reg_offset.c +++ b/src/ptrace/_UPT_reg_offset.c @@ -25,6 +25,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "_UPT_internal.h" +#include + #ifdef HAVE_ASM_PTRACE_OFFSETS_H # include #endif @@ -247,6 +249,22 @@ int _UPT_reg_offset[UNW_REG_LAST + 1] = [UNW_HPPA_IP] = 0x1a8 /* IAOQ[0] */ #elif defined(UNW_TARGET_X86) +#if defined __FreeBSD__ +#define UNW_R_OFF(R, r) \ + [UNW_X86_##R] = offsetof(gregset_t, r_##r), + UNW_R_OFF(EAX, eax) + UNW_R_OFF(EDX, edx) + UNW_R_OFF(ECX, ecx) + UNW_R_OFF(EBX, ebx) + UNW_R_OFF(ESI, esi) + UNW_R_OFF(EDI, edi) + UNW_R_OFF(EBP, ebp) + UNW_R_OFF(ESP, esp) + UNW_R_OFF(EIP, eip) +// UNW_R_OFF(CS, cs) +// UNW_R_OFF(EFLAGS, eflags) +// UNW_R_OFF(SS, ss) +#elif defined __linux__ [UNW_X86_EAX] = 0x18, [UNW_X86_EBX] = 0x00, [UNW_X86_ECX] = 0x04, @@ -264,7 +282,35 @@ int _UPT_reg_offset[UNW_REG_LAST + 1] = /* ORIG_EAX = 0x2c, */ /* EFLAGS = 0x38, */ /* SS = 0x40 */ +#else +#error Port me +#endif #elif defined(UNW_TARGET_X86_64) +#if defined __FreeBSD__ +#define UNW_R_OFF(R, r) \ + [UNW_X86_64_##R] = offsetof(gregset_t, r_##r), + UNW_R_OFF(RAX, rax) + UNW_R_OFF(RDX, rdx) + UNW_R_OFF(RCX, rcx) + UNW_R_OFF(RBX, rbx) + UNW_R_OFF(RSI, rsi) + UNW_R_OFF(RDI, rdi) + UNW_R_OFF(RBP, rbp) + UNW_R_OFF(RSP, rsp) + UNW_R_OFF(R8, r8) + UNW_R_OFF(R9, r9) + UNW_R_OFF(R10, r10) + UNW_R_OFF(R11, r11) + UNW_R_OFF(R12, r12) + UNW_R_OFF(R13, r13) + UNW_R_OFF(R14, r14) + UNW_R_OFF(R15, r15) + UNW_R_OFF(RIP, rip) +// UNW_R_OFF(CS, cs) +// UNW_R_OFF(EFLAGS, rflags) +// UNW_R_OFF(SS, ss) +#undef UNW_R_OFF +#elif defined __linux__ [UNW_X86_64_RAX] = 0x50, [UNW_X86_64_RDX] = 0x60, [UNW_X86_64_RCX] = 0x58, @@ -286,9 +332,28 @@ int _UPT_reg_offset[UNW_REG_LAST + 1] = // [UNW_X86_64_EFLAGS] = 0x90, // [UNW_X86_64_RSP] = 0x98, // [UNW_X86_64_SS] = 0xa0 +#else +#error Port me +#endif #elif defined(UNW_TARGET_PPC32) #elif defined(UNW_TARGET_PPC64) #elif defined(UNW_TARGET_ARM) + [UNW_ARM_R0] = 0x00, + [UNW_ARM_R1] = 0x04, + [UNW_ARM_R2] = 0x08, + [UNW_ARM_R3] = 0x0c, + [UNW_ARM_R4] = 0x10, + [UNW_ARM_R5] = 0x14, + [UNW_ARM_R6] = 0x18, + [UNW_ARM_R7] = 0x1c, + [UNW_ARM_R8] = 0x20, + [UNW_ARM_R9] = 0x24, + [UNW_ARM_R10] = 0x28, + [UNW_ARM_R11] = 0x2c, + [UNW_ARM_R12] = 0x30, + [UNW_ARM_R13] = 0x34, + [UNW_ARM_R14] = 0x38, + [UNW_ARM_R15] = 0x3c, #elif defined(UNW_TARGET_MIPS) #else # error Fix me. diff --git a/src/ptrace/_UPT_resume.c b/src/ptrace/_UPT_resume.c index 11242e8d..f419fed3 100644 --- a/src/ptrace/_UPT_resume.c +++ b/src/ptrace/_UPT_resume.c @@ -32,7 +32,9 @@ _UPT_resume (unw_addr_space_t as, unw_cursor_t *c, void *arg) #ifdef HAVE_TTRACE # warning No support for ttrace() yet. -#else +#elif HAVE_DECL_PTRACE_CONT return ptrace (PTRACE_CONT, ui->pid, 0, 0); +#elif HAVE_DECL_PT_CONTINUE + return ptrace(PT_CONTINUE, ui->pid, (caddr_t)1, 0); #endif } diff --git a/src/setjmp/longjmp.c b/src/setjmp/longjmp.c index ca5f1e42..1b9f7c4e 100644 --- a/src/setjmp/longjmp.c +++ b/src/setjmp/longjmp.c @@ -50,7 +50,11 @@ _longjmp (jmp_buf env, int val) { if (unw_get_reg (&c, UNW_REG_SP, &sp) < 0) abort (); +#ifdef __FreeBSD__ + if (sp != wp[JB_SP] + sizeof(unw_word_t)) +#else if (sp != wp[JB_SP]) +#endif continue; if (!bsp_match (&c, wp)) diff --git a/src/setjmp/siglongjmp.c b/src/setjmp/siglongjmp.c index 9a091fae..da757e3c 100644 --- a/src/setjmp/siglongjmp.c +++ b/src/setjmp/siglongjmp.c @@ -31,6 +31,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "jmpbuf.h" #include "setjmp_i.h" +#if !defined(_NSIG) && defined(_SIG_MAXSIG) +# define _NSIG (_SIG_MAXSIG - 1) +#endif + void siglongjmp (sigjmp_buf env, int val) { @@ -49,7 +53,11 @@ siglongjmp (sigjmp_buf env, int val) { if (unw_get_reg (&c, UNW_REG_SP, &sp) < 0) abort (); +#ifdef __FreeBSD__ + if (sp != wp[JB_SP] + sizeof(unw_word_t)) +#else if (sp != wp[JB_SP]) +#endif continue; if (!bsp_match (&c, wp)) diff --git a/src/unwind/RaiseException.c b/src/unwind/RaiseException.c index 55338766..66a681db 100644 --- a/src/unwind/RaiseException.c +++ b/src/unwind/RaiseException.c @@ -28,7 +28,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ PROTECTED _Unwind_Reason_Code _Unwind_RaiseException (struct _Unwind_Exception *exception_object) { - unsigned long exception_class = exception_object->exception_class; + uint64_t exception_class = exception_object->exception_class; _Unwind_Personality_Fn personality; struct _Unwind_Context context; _Unwind_Reason_Code reason; diff --git a/src/unwind/SetGR.c b/src/unwind/SetGR.c index 056bfc70..96533643 100644 --- a/src/unwind/SetGR.c +++ b/src/unwind/SetGR.c @@ -24,11 +24,17 @@ 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-internal.h" +#ifdef UNW_TARGET_X86 +#include "dwarf_i.h" +#endif PROTECTED void _Unwind_SetGR (struct _Unwind_Context *context, int index, unsigned long new_value) { +#ifdef UNW_TARGET_X86 + index = dwarf_to_unw_regnum(index); +#endif unw_set_reg (&context->cursor, index, new_value); #ifdef UNW_TARGET_IA64 if (index >= UNW_IA64_GR && index <= UNW_IA64_GR + 127) diff --git a/src/unwind/unwind-internal.h b/src/unwind/unwind-internal.h index 169bad57..130cb81a 100644 --- a/src/unwind/unwind-internal.h +++ b/src/unwind/unwind-internal.h @@ -38,7 +38,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define _U_VERSION 1 typedef _Unwind_Reason_Code (*_Unwind_Personality_Fn) - (int, _Unwind_Action, unsigned long, struct _Unwind_Exception *, + (int, _Unwind_Action, uint64_t, struct _Unwind_Exception *, struct _Unwind_Context *); struct _Unwind_Context { @@ -59,7 +59,7 @@ _Unwind_Phase2 (struct _Unwind_Exception *exception_object, struct _Unwind_Context *context) { _Unwind_Stop_Fn stop = (_Unwind_Stop_Fn) exception_object->private_1; - unsigned long exception_class = exception_object->exception_class; + uint64_t exception_class = exception_object->exception_class; void *stop_parameter = (void *) exception_object->private_2; _Unwind_Personality_Fn personality; _Unwind_Reason_Code reason; diff --git a/src/x86/Gcreate_addr_space.c b/src/x86/Gcreate_addr_space.c index fd850c47..5dd6367a 100644 --- a/src/x86/Gcreate_addr_space.c +++ b/src/x86/Gcreate_addr_space.c @@ -27,6 +27,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "unwind_i.h" +#if defined(_LITTLE_ENDIAN) && !defined(__LITTLE_ENDIAN) +#define __LITTLE_ENDIAN _LITTLE_ENDIAN +#endif + PROTECTED unw_addr_space_t unw_create_addr_space (unw_accessors_t *a, int byte_order) { diff --git a/src/x86/Gglobal.c b/src/x86/Gglobal.c index e55644c9..3c97853c 100644 --- a/src/x86/Gglobal.c +++ b/src/x86/Gglobal.c @@ -47,8 +47,7 @@ tdep_init (void) sigfillset (&unwi_full_mask); - sigprocmask (SIG_SETMASK, &unwi_full_mask, &saved_mask); - mutex_lock (&x86_lock); + lock_acquire (&x86_lock, saved_mask); { if (!tdep_needs_initialization) /* another thread else beat us to it... */ @@ -64,6 +63,5 @@ tdep_init (void) tdep_needs_initialization = 0; /* signal that we're initialized... */ } out: - mutex_unlock (&x86_lock); - sigprocmask (SIG_SETMASK, &saved_mask, NULL); + lock_release (&x86_lock, saved_mask); } diff --git a/src/x86/Ginit.c b/src/x86/Ginit.c index e1b1dcfc..b8890a3c 100644 --- a/src/x86/Ginit.c +++ b/src/x86/Ginit.c @@ -24,6 +24,10 @@ 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 +#endif + #include #include @@ -40,43 +44,12 @@ static struct unw_addr_space local_addr_space; PROTECTED unw_addr_space_t unw_local_addr_space = &local_addr_space; -static inline void * -uc_addr (ucontext_t *uc, int reg) -{ - void *addr; - - switch (reg) - { - case UNW_X86_GS: addr = &uc->uc_mcontext.gregs[REG_GS]; break; - case UNW_X86_FS: addr = &uc->uc_mcontext.gregs[REG_FS]; break; - case UNW_X86_ES: addr = &uc->uc_mcontext.gregs[REG_ES]; break; - case UNW_X86_DS: addr = &uc->uc_mcontext.gregs[REG_DS]; break; - case UNW_X86_EAX: addr = &uc->uc_mcontext.gregs[REG_EAX]; break; - case UNW_X86_EBX: addr = &uc->uc_mcontext.gregs[REG_EBX]; break; - case UNW_X86_ECX: addr = &uc->uc_mcontext.gregs[REG_ECX]; break; - case UNW_X86_EDX: addr = &uc->uc_mcontext.gregs[REG_EDX]; break; - case UNW_X86_ESI: addr = &uc->uc_mcontext.gregs[REG_ESI]; break; - case UNW_X86_EDI: addr = &uc->uc_mcontext.gregs[REG_EDI]; break; - case UNW_X86_EBP: addr = &uc->uc_mcontext.gregs[REG_EBP]; break; - case UNW_X86_EIP: addr = &uc->uc_mcontext.gregs[REG_EIP]; break; - case UNW_X86_ESP: addr = &uc->uc_mcontext.gregs[REG_ESP]; break; - case UNW_X86_TRAPNO: addr = &uc->uc_mcontext.gregs[REG_TRAPNO]; break; - case UNW_X86_CS: addr = &uc->uc_mcontext.gregs[REG_CS]; break; - case UNW_X86_EFLAGS: addr = &uc->uc_mcontext.gregs[REG_EFL]; break; - case UNW_X86_SS: addr = &uc->uc_mcontext.gregs[REG_SS]; break; - - default: - addr = NULL; - } - return addr; -} - # ifdef UNW_LOCAL_ONLY HIDDEN void * tdep_uc_addr (ucontext_t *uc, int reg) { - return uc_addr (uc, reg); + return x86_r_uc_addr (uc, reg); } # endif /* UNW_LOCAL_ONLY */ @@ -114,16 +87,32 @@ static int validate_mem (unw_word_t addr) { int i, victim; +#ifdef HAVE_MINCORE + unsigned char mvec[2]; /* Unaligned access may cross page boundary */ +#endif + 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 (msync ((void *) addr, 1, MS_SYNC) == -1) +#ifdef HAVE_MINCORE + if (mincore ((void *) addr, len, mvec) == -1) +#else + if (msync ((void *) addr, len, MS_ASYNC) == -1) +#endif return -1; victim = lga_victim; @@ -174,7 +163,7 @@ access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, if (unw_is_fpreg (reg)) goto badreg; - if (!(addr = uc_addr (uc, reg))) + if (!(addr = x86_r_uc_addr (uc, reg))) goto badreg; if (write) @@ -204,7 +193,7 @@ access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, if (!unw_is_fpreg (reg)) goto badreg; - if (!(addr = uc_addr (uc, reg))) + if (!(addr = x86_r_uc_addr (uc, reg))) goto badreg; if (write) diff --git a/src/x86/Ginit_local.c b/src/x86/Ginit_local.c index 55ab7490..5e8b6972 100644 --- a/src/x86/Ginit_local.c +++ b/src/x86/Ginit_local.c @@ -50,7 +50,7 @@ unw_init_local (unw_cursor_t *cursor, ucontext_t *uc) c->dwarf.as_arg = c; c->uc = uc; c->validate = 0; - return common_init (c); + return common_init (c, 1); } #endif /* !UNW_REMOTE_ONLY */ diff --git a/src/x86/Ginit_remote.c b/src/x86/Ginit_remote.c index 6949a73e..aa924052 100644 --- a/src/x86/Ginit_remote.c +++ b/src/x86/Ginit_remote.c @@ -51,6 +51,6 @@ unw_init_remote (unw_cursor_t *cursor, unw_addr_space_t as, void *as_arg) c->dwarf.as_arg = as_arg; c->uc = 0; } - return common_init (c); + return common_init (c, 0); #endif /* !UNW_LOCAL_ONLY */ } diff --git a/src/x86/Gis_signal_frame.c b/src/x86/Gis_signal_frame.c deleted file mode 100644 index df381ebe..00000000 --- a/src/x86/Gis_signal_frame.c +++ /dev/null @@ -1,71 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2002-2003 Hewlett-Packard Co - Contributed by David Mosberger-Tang - -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" - -PROTECTED int -unw_is_signal_frame (unw_cursor_t *cursor) -{ -#ifdef __linux__ - struct cursor *c = (struct cursor *) cursor; - unw_word_t w0, w1, ip; - unw_addr_space_t as; - unw_accessors_t *a; - void *arg; - int ret; - - as = c->dwarf.as; - a = unw_get_accessors (as); - arg = c->dwarf.as_arg; - - /* Check if EIP points at sigreturn() sequence. On Linux, this is: - - __restore: - 0x58 pop %eax - 0xb8 0x77 0x00 0x00 0x00 movl 0x77,%eax - 0xcd 0x80 int 0x80 - - without SA_SIGINFO, and - - __restore_rt: - 0xb8 0xad 0x00 0x00 0x00 movl 0x80,%eax - 0xcd 0x80 int 0x80 - 0x90 nop - - if SA_SIGINFO is specified. - */ - ip = c->dwarf.ip; - if ((ret = (*a->access_mem) (as, ip, &w0, 0, arg)) < 0 - || (ret = (*a->access_mem) (as, ip + 4, &w1, 0, arg)) < 0) - return ret; - ret = ((w0 == 0x0077b858 && w1 == 0x80cd0000) - || (w0 == 0x0000adb8 && w1 == 0x9080cd00)); - Debug (16, "returning %d\n", ret); - return ret; -#else - printf ("%s: implement me\n", __FUNCTION__); -#endif - return -UNW_ENOINFO; -} diff --git a/src/x86/Gos-freebsd.c b/src/x86/Gos-freebsd.c new file mode 100644 index 00000000..247c35fa --- /dev/null +++ b/src/x86/Gos-freebsd.c @@ -0,0 +1,364 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2010 Konstantin Belousov + +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 +#include +#include +#include +#include + +#include "unwind_i.h" +#include "offsets.h" + +PROTECTED int +unw_is_signal_frame (unw_cursor_t *cursor) +{ + struct cursor *c = (struct cursor *) cursor; + unw_word_t w0, w1, w2, w3, w4, w5, ip; + unw_addr_space_t as; + unw_accessors_t *a; + void *arg; + int ret; + + as = c->dwarf.as; + a = unw_get_accessors (as); + arg = c->dwarf.as_arg; + + /* Check if EIP points at sigreturn() sequence. It can be: +sigcode+4: from amd64 freebsd32 environment +8d 44 24 20 lea 0x20(%esp),%eax +50 push %eax +b8 a1 01 00 00 mov $0x1a1,%eax +50 push %eax +cd 80 int $0x80 + +sigcode+4: from real i386 +8d 44 24 20 lea 0x20(%esp),%eax +50 push %eax +f7 40 54 00 02 00 testl $0x20000,0x54(%eax) +75 03 jne sigcode+21 +8e 68 14 mov 0x14(%eax),%gs +b8 a1 01 00 00 mov $0x1a1,%eax +50 push %eax +cd 80 int $0x80 + +freebsd4_sigcode+4: +XXX +osigcode: +XXX + */ + ip = c->dwarf.ip; + ret = X86_SCF_NONE; + c->sigcontext_format = ret; + if ((*a->access_mem) (as, ip, &w0, 0, arg) < 0 || + (*a->access_mem) (as, ip + 4, &w1, 0, arg) < 0 || + (*a->access_mem) (as, ip + 8, &w2, 0, arg) < 0 || + (*a->access_mem) (as, ip + 12, &w3, 0, arg) < 0) + return ret; + if (w0 == 0x2024448d && w1 == 0x01a1b850 && w2 == 0xcd500000 && + (w3 & 0xff) == 0x80) + ret = X86_SCF_FREEBSD_SIGFRAME; + else { + if ((*a->access_mem) (as, ip + 16, &w4, 0, arg) < 0 || + (*a->access_mem) (as, ip + 20, &w5, 0, arg) < 0) + return ret; + if (w0 == 0x2024448d && w1 == 0x5440f750 && w2 == 0x75000200 && + w3 == 0x14688e03 && w4 == 0x0001a1b8 && w5 == 0x80cd5000) + ret = X86_SCF_FREEBSD_SIGFRAME; + } + Debug (16, "returning %d\n", ret); + c->sigcontext_format = ret; + return (ret); +} + +PROTECTED int +unw_handle_signal_frame (unw_cursor_t *cursor) +{ + struct cursor *c = (struct cursor *) cursor; + int ret; + + if (c->sigcontext_format == X86_SCF_FREEBSD_SIGFRAME) { + struct sigframe *sf; + uintptr_t uc_addr; + struct dwarf_loc esp_loc; + + sf = (struct sigframe *)c->dwarf.cfa; + uc_addr = (uintptr_t)&(sf->sf_uc); + c->sigcontext_addr = c->dwarf.cfa; + + esp_loc = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_ESP_OFF, 0); + ret = dwarf_get (&c->dwarf, esp_loc, &c->dwarf.cfa); + if (ret < 0) + { + Debug (2, "returning 0\n"); + return 0; + } + + c->dwarf.loc[EIP] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EIP_OFF, 0); + c->dwarf.loc[ESP] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_ESP_OFF, 0); + c->dwarf.loc[EAX] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EAX_OFF, 0); + c->dwarf.loc[ECX] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_ECX_OFF, 0); + c->dwarf.loc[EDX] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EDX_OFF, 0); + c->dwarf.loc[EBX] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EBX_OFF, 0); + c->dwarf.loc[EBP] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EBP_OFF, 0); + c->dwarf.loc[ESI] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_ESI_OFF, 0); + c->dwarf.loc[EDI] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EDI_OFF, 0); + c->dwarf.loc[EFLAGS] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EFLAGS_OFF, 0); + c->dwarf.loc[TRAPNO] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_TRAPNO_OFF, 0); + c->dwarf.loc[ST0] = DWARF_NULL_LOC; + } else { + Debug (8, "Gstep: not handling frame format %d\n", c->sigcontext_format); + abort(); + } + return 0; +} + +HIDDEN dwarf_loc_t +x86_get_scratch_loc (struct cursor *c, unw_regnum_t reg) +{ + unw_word_t addr = c->sigcontext_addr, off, xmm_off; + unw_word_t fpstate, fpformat; + int ret, is_fpstate = 0, is_xmmstate = 0; + + switch (c->sigcontext_format) + { + case X86_SCF_NONE: + return DWARF_REG_LOC (&c->dwarf, reg); + + case X86_SCF_FREEBSD_SIGFRAME: + addr += offsetof(struct sigframe, sf_uc) + FREEBSD_UC_MCONTEXT_OFF; + break; + + case X86_SCF_FREEBSD_SIGFRAME4: + abort(); + break; + + case X86_SCF_FREEBSD_OSIGFRAME: + /* XXXKIB */ + abort(); + break; + + case X86_SCF_FREEBSD_SYSCALL: + /* XXXKIB */ + abort(); + break; + + default: + /* XXXKIB */ + abort(); + break; + } + + off = 0; /* shut gcc warning */ + switch (reg) + { + case UNW_X86_GS: off = FREEBSD_UC_MCONTEXT_GS_OFF; break; + case UNW_X86_FS: off = FREEBSD_UC_MCONTEXT_FS_OFF; break; + case UNW_X86_ES: off = FREEBSD_UC_MCONTEXT_ES_OFF; break; + case UNW_X86_DS: off = FREEBSD_UC_MCONTEXT_SS_OFF; break; + case UNW_X86_EDI: off = FREEBSD_UC_MCONTEXT_EDI_OFF; break; + case UNW_X86_ESI: off = FREEBSD_UC_MCONTEXT_ESI_OFF; break; + case UNW_X86_EBP: off = FREEBSD_UC_MCONTEXT_EBP_OFF; break; + case UNW_X86_ESP: off = FREEBSD_UC_MCONTEXT_ESP_OFF; break; + case UNW_X86_EBX: off = FREEBSD_UC_MCONTEXT_EBX_OFF; break; + case UNW_X86_EDX: off = FREEBSD_UC_MCONTEXT_EDX_OFF; break; + case UNW_X86_ECX: off = FREEBSD_UC_MCONTEXT_ECX_OFF; break; + case UNW_X86_EAX: off = FREEBSD_UC_MCONTEXT_EAX_OFF; break; + case UNW_X86_TRAPNO: off = FREEBSD_UC_MCONTEXT_TRAPNO_OFF; break; + case UNW_X86_EIP: off = FREEBSD_UC_MCONTEXT_EIP_OFF; break; + case UNW_X86_CS: off = FREEBSD_UC_MCONTEXT_CS_OFF; break; + case UNW_X86_EFLAGS: off = FREEBSD_UC_MCONTEXT_EFLAGS_OFF; break; + case UNW_X86_SS: off = FREEBSD_UC_MCONTEXT_SS_OFF; break; + + case UNW_X86_FCW: + is_fpstate = 1; + off = FREEBSD_UC_MCONTEXT_CW_OFF; + xmm_off = FREEBSD_UC_MCONTEXT_CW_XMM_OFF; + break; + case UNW_X86_FSW: + is_fpstate = 1; + off = FREEBSD_UC_MCONTEXT_SW_OFF; + xmm_off = FREEBSD_UC_MCONTEXT_SW_XMM_OFF; + break; + case UNW_X86_FTW: + is_fpstate = 1; + xmm_off = FREEBSD_UC_MCONTEXT_TAG_XMM_OFF; + off = FREEBSD_UC_MCONTEXT_TAG_OFF; + break; + case UNW_X86_FCS: + is_fpstate = 1; + off = FREEBSD_UC_MCONTEXT_CSSEL_OFF; + xmm_off = FREEBSD_UC_MCONTEXT_CSSEL_XMM_OFF; + break; + case UNW_X86_FIP: + is_fpstate = 1; + off = FREEBSD_UC_MCONTEXT_IPOFF_OFF; + xmm_off = FREEBSD_UC_MCONTEXT_IPOFF_XMM_OFF; + break; + case UNW_X86_FEA: + is_fpstate = 1; + off = FREEBSD_UC_MCONTEXT_DATAOFF_OFF; + xmm_off = FREEBSD_UC_MCONTEXT_DATAOFF_XMM_OFF; + break; + case UNW_X86_FDS: + is_fpstate = 1; + off = FREEBSD_US_MCONTEXT_DATASEL_OFF; + xmm_off = FREEBSD_US_MCONTEXT_DATASEL_XMM_OFF; + break; + case UNW_X86_MXCSR: + is_fpstate = 1; + is_xmmstate = 1; + xmm_off = FREEBSD_UC_MCONTEXT_MXCSR_XMM_OFF; + break; + + /* stacked fp registers */ + case UNW_X86_ST0: case UNW_X86_ST1: case UNW_X86_ST2: case UNW_X86_ST3: + case UNW_X86_ST4: case UNW_X86_ST5: case UNW_X86_ST6: case UNW_X86_ST7: + is_fpstate = 1; + off = FREEBSD_UC_MCONTEXT_ST0_OFF + 10*(reg - UNW_X86_ST0); + xmm_off = FREEBSD_UC_MCONTEXT_ST0_XMM_OFF + 10*(reg - UNW_X86_ST0); + break; + + /* SSE fp registers */ + case UNW_X86_XMM0_lo: case UNW_X86_XMM0_hi: + case UNW_X86_XMM1_lo: case UNW_X86_XMM1_hi: + case UNW_X86_XMM2_lo: case UNW_X86_XMM2_hi: + case UNW_X86_XMM3_lo: case UNW_X86_XMM3_hi: + case UNW_X86_XMM4_lo: case UNW_X86_XMM4_hi: + case UNW_X86_XMM5_lo: case UNW_X86_XMM5_hi: + case UNW_X86_XMM6_lo: case UNW_X86_XMM6_hi: + case UNW_X86_XMM7_lo: case UNW_X86_XMM7_hi: + is_fpstate = 1; + is_xmmstate = 1; + xmm_off = FREEBSD_UC_MCONTEXT_XMM0_OFF + 8*(reg - UNW_X86_XMM0_lo); + break; + case UNW_X86_XMM0: + case UNW_X86_XMM1: + case UNW_X86_XMM2: + case UNW_X86_XMM3: + case UNW_X86_XMM4: + case UNW_X86_XMM5: + case UNW_X86_XMM6: + case UNW_X86_XMM7: + is_fpstate = 1; + is_xmmstate = 1; + xmm_off = FREEBSD_UC_MCONTEXT_XMM0_OFF + 16*(reg - UNW_X86_XMM0); + break; + + case UNW_X86_FOP: + case UNW_X86_TSS: + case UNW_X86_LDT: + default: + return DWARF_REG_LOC (&c->dwarf, reg); + } + + if (is_fpstate) + { + if ((ret = dwarf_get (&c->dwarf, + DWARF_MEM_LOC (&c->dwarf, addr + FREEBSD_UC_MCONTEXT_FPSTATE_OFF), + &fpstate)) < 0) + return DWARF_NULL_LOC; + if (fpstate == FREEBSD_UC_MCONTEXT_FPOWNED_NONE) + return DWARF_NULL_LOC; + if ((ret = dwarf_get (&c->dwarf, + DWARF_MEM_LOC (&c->dwarf, addr + FREEBSD_UC_MCONTEXT_FPFORMAT_OFF), + &fpformat)) < 0) + return DWARF_NULL_LOC; + if (fpformat == FREEBSD_UC_MCONTEXT_FPFMT_NODEV || + (is_xmmstate && fpformat != FREEBSD_UC_MCONTEXT_FPFMT_XMM)) + return DWARF_NULL_LOC; + if (is_xmmstate) + off = xmm_off; + } + + return DWARF_MEM_LOC (c, addr + off); +} + +#ifndef UNW_REMOTE_ONLY +HIDDEN void * +x86_r_uc_addr (ucontext_t *uc, int reg) +{ + void *addr; + + switch (reg) + { + case UNW_X86_GS: addr = &uc->uc_mcontext.mc_gs; break; + case UNW_X86_FS: addr = &uc->uc_mcontext.mc_fs; break; + case UNW_X86_ES: addr = &uc->uc_mcontext.mc_es; break; + case UNW_X86_DS: addr = &uc->uc_mcontext.mc_ds; break; + case UNW_X86_EAX: addr = &uc->uc_mcontext.mc_eax; break; + case UNW_X86_EBX: addr = &uc->uc_mcontext.mc_ebx; break; + case UNW_X86_ECX: addr = &uc->uc_mcontext.mc_ecx; break; + case UNW_X86_EDX: addr = &uc->uc_mcontext.mc_edx; break; + case UNW_X86_ESI: addr = &uc->uc_mcontext.mc_esi; break; + case UNW_X86_EDI: addr = &uc->uc_mcontext.mc_edi; break; + case UNW_X86_EBP: addr = &uc->uc_mcontext.mc_ebp; break; + case UNW_X86_EIP: addr = &uc->uc_mcontext.mc_eip; break; + case UNW_X86_ESP: addr = &uc->uc_mcontext.mc_esp; break; + case UNW_X86_TRAPNO: addr = &uc->uc_mcontext.mc_trapno; break; + case UNW_X86_CS: addr = &uc->uc_mcontext.mc_cs; break; + case UNW_X86_EFLAGS: addr = &uc->uc_mcontext.mc_eflags; break; + case UNW_X86_SS: addr = &uc->uc_mcontext.mc_ss; break; + + default: + addr = NULL; + } + return addr; +} + +HIDDEN int +x86_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg) +{ + struct cursor *c = (struct cursor *) cursor; + ucontext_t *uc = c->uc; + + /* Ensure c->pi is up-to-date. On x86, 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); + + if (c->sigcontext_format == X86_SCF_NONE) { + Debug (8, "resuming at ip=%x via setcontext()\n", c->dwarf.ip); + setcontext (uc); + abort(); + } else if (c->sigcontext_format == X86_SCF_FREEBSD_SIGFRAME) { + struct sigcontext *sc = (struct sigcontext *) c->sigcontext_addr; + + Debug (8, "resuming at ip=%x via sigreturn(%p)\n", c->dwarf.ip, sc); + sigreturn((ucontext_t *)((const char *)sc + FREEBSD_SC_UCONTEXT_OFF)); + abort(); + } else { + Debug (8, "resuming at ip=%x for sigcontext format %d not implemented\n", + c->dwarf.ip, c->sigcontext_format); + abort(); + } + return -UNW_EINVAL; +} + +#endif diff --git a/src/x86/Gos-linux.c b/src/x86/Gos-linux.c new file mode 100644 index 00000000..31f83bae --- /dev/null +++ b/src/x86/Gos-linux.c @@ -0,0 +1,307 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2002-2004 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +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 "offsets.h" + +PROTECTED int +unw_is_signal_frame (unw_cursor_t *cursor) +{ + struct cursor *c = (struct cursor *) cursor; + unw_word_t w0, w1, ip; + unw_addr_space_t as; + unw_accessors_t *a; + void *arg; + int ret; + + as = c->dwarf.as; + a = unw_get_accessors (as); + arg = c->dwarf.as_arg; + + /* Check if EIP points at sigreturn() sequence. On Linux, this is: + + __restore: + 0x58 pop %eax + 0xb8 0x77 0x00 0x00 0x00 movl 0x77,%eax + 0xcd 0x80 int 0x80 + + without SA_SIGINFO, and + + __restore_rt: + 0xb8 0xad 0x00 0x00 0x00 movl 0xad,%eax + 0xcd 0x80 int 0x80 + 0x00 + + if SA_SIGINFO is specified. + */ + ip = c->dwarf.ip; + if ((ret = (*a->access_mem) (as, ip, &w0, 0, arg)) < 0 + || (ret = (*a->access_mem) (as, ip + 4, &w1, 0, arg)) < 0) + return ret; + ret = ((w0 == 0x0077b858 && w1 == 0x80cd0000) + || (w0 == 0x0000adb8 && (w1 & 0xffffff) == 0x80cd00)); + Debug (16, "returning %d\n", ret); + return ret; +} + +PROTECTED int +unw_handle_signal_frame (unw_cursor_t *cursor) +{ + struct cursor *c = (struct cursor *) cursor; + int ret; + + /* c->esp points at the arguments to the handler. Without + SA_SIGINFO, the arguments consist of a signal number + followed by a struct sigcontext. With SA_SIGINFO, the + arguments consist a signal number, a siginfo *, and a + ucontext *. */ + unw_word_t sc_addr; + unw_word_t siginfo_ptr_addr = c->dwarf.cfa + 4; + unw_word_t sigcontext_ptr_addr = c->dwarf.cfa + 8; + unw_word_t siginfo_ptr, sigcontext_ptr; + struct dwarf_loc esp_loc, siginfo_ptr_loc, sigcontext_ptr_loc; + + siginfo_ptr_loc = DWARF_LOC (siginfo_ptr_addr, 0); + sigcontext_ptr_loc = DWARF_LOC (sigcontext_ptr_addr, 0); + ret = (dwarf_get (&c->dwarf, siginfo_ptr_loc, &siginfo_ptr) + | dwarf_get (&c->dwarf, sigcontext_ptr_loc, &sigcontext_ptr)); + if (ret < 0) + { + Debug (2, "returning 0\n"); + return 0; + } + if (siginfo_ptr < c->dwarf.cfa + || siginfo_ptr > c->dwarf.cfa + 256 + || sigcontext_ptr < c->dwarf.cfa + || sigcontext_ptr > c->dwarf.cfa + 256) + { + /* Not plausible for SA_SIGINFO signal */ + c->sigcontext_format = X86_SCF_LINUX_SIGFRAME; + c->sigcontext_addr = sc_addr = c->dwarf.cfa + 4; + } + else + { + /* If SA_SIGINFO were not specified, we actually read + various segment pointers instead. We believe that at + least fs and _fsh are always zero for linux, so it is + not just unlikely, but impossible that we would end + up here. */ + c->sigcontext_format = X86_SCF_LINUX_RT_SIGFRAME; + c->sigcontext_addr = sigcontext_ptr; + sc_addr = sigcontext_ptr + LINUX_UC_MCONTEXT_OFF; + } + esp_loc = DWARF_LOC (sc_addr + LINUX_SC_ESP_OFF, 0); + ret = dwarf_get (&c->dwarf, esp_loc, &c->dwarf.cfa); + if (ret < 0) + { + Debug (2, "returning 0\n"); + return 0; + } + + c->dwarf.loc[EAX] = DWARF_LOC (sc_addr + LINUX_SC_EAX_OFF, 0); + c->dwarf.loc[ECX] = DWARF_LOC (sc_addr + LINUX_SC_ECX_OFF, 0); + c->dwarf.loc[EDX] = DWARF_LOC (sc_addr + LINUX_SC_EDX_OFF, 0); + c->dwarf.loc[EBX] = DWARF_LOC (sc_addr + LINUX_SC_EBX_OFF, 0); + c->dwarf.loc[EBP] = DWARF_LOC (sc_addr + LINUX_SC_EBP_OFF, 0); + c->dwarf.loc[ESI] = DWARF_LOC (sc_addr + LINUX_SC_ESI_OFF, 0); + c->dwarf.loc[EDI] = DWARF_LOC (sc_addr + LINUX_SC_EDI_OFF, 0); + c->dwarf.loc[EFLAGS] = DWARF_NULL_LOC; + c->dwarf.loc[TRAPNO] = DWARF_NULL_LOC; + c->dwarf.loc[ST0] = DWARF_NULL_LOC; + c->dwarf.loc[EIP] = DWARF_LOC (sc_addr + LINUX_SC_EIP_OFF, 0); + c->dwarf.loc[ESP] = DWARF_LOC (sc_addr + LINUX_SC_ESP_OFF, 0); + + return 0; +} + +HIDDEN dwarf_loc_t +x86_get_scratch_loc (struct cursor *c, unw_regnum_t reg) +{ + unw_word_t addr = c->sigcontext_addr, fpstate_addr, off; + int ret, is_fpstate = 0; + + switch (c->sigcontext_format) + { + case X86_SCF_NONE: + return DWARF_REG_LOC (&c->dwarf, reg); + + case X86_SCF_LINUX_SIGFRAME: + break; + + case X86_SCF_LINUX_RT_SIGFRAME: + addr += LINUX_UC_MCONTEXT_OFF; + break; + + default: + return DWARF_NULL_LOC; + } + + switch (reg) + { + case UNW_X86_GS: off = LINUX_SC_GS_OFF; break; + case UNW_X86_FS: off = LINUX_SC_FS_OFF; break; + case UNW_X86_ES: off = LINUX_SC_ES_OFF; break; + case UNW_X86_DS: off = LINUX_SC_DS_OFF; break; + case UNW_X86_EDI: off = LINUX_SC_EDI_OFF; break; + case UNW_X86_ESI: off = LINUX_SC_ESI_OFF; break; + case UNW_X86_EBP: off = LINUX_SC_EBP_OFF; break; + case UNW_X86_ESP: off = LINUX_SC_ESP_OFF; break; + case UNW_X86_EBX: off = LINUX_SC_EBX_OFF; break; + case UNW_X86_EDX: off = LINUX_SC_EDX_OFF; break; + case UNW_X86_ECX: off = LINUX_SC_ECX_OFF; break; + case UNW_X86_EAX: off = LINUX_SC_EAX_OFF; break; + case UNW_X86_TRAPNO: off = LINUX_SC_TRAPNO_OFF; break; + case UNW_X86_EIP: off = LINUX_SC_EIP_OFF; break; + case UNW_X86_CS: off = LINUX_SC_CS_OFF; break; + case UNW_X86_EFLAGS: off = LINUX_SC_EFLAGS_OFF; break; + case UNW_X86_SS: off = LINUX_SC_SS_OFF; break; + + /* The following is probably not correct for all possible cases. + Somebody who understands this better should review this for + correctness. */ + + case UNW_X86_FCW: is_fpstate = 1; off = LINUX_FPSTATE_CW_OFF; break; + case UNW_X86_FSW: is_fpstate = 1; off = LINUX_FPSTATE_SW_OFF; break; + case UNW_X86_FTW: is_fpstate = 1; off = LINUX_FPSTATE_TAG_OFF; break; + case UNW_X86_FCS: is_fpstate = 1; off = LINUX_FPSTATE_CSSEL_OFF; break; + case UNW_X86_FIP: is_fpstate = 1; off = LINUX_FPSTATE_IPOFF_OFF; break; + case UNW_X86_FEA: is_fpstate = 1; off = LINUX_FPSTATE_DATAOFF_OFF; break; + case UNW_X86_FDS: is_fpstate = 1; off = LINUX_FPSTATE_DATASEL_OFF; break; + case UNW_X86_MXCSR: is_fpstate = 1; off = LINUX_FPSTATE_MXCSR_OFF; break; + + /* stacked fp registers */ + case UNW_X86_ST0: case UNW_X86_ST1: case UNW_X86_ST2: case UNW_X86_ST3: + case UNW_X86_ST4: case UNW_X86_ST5: case UNW_X86_ST6: case UNW_X86_ST7: + is_fpstate = 1; + off = LINUX_FPSTATE_ST0_OFF + 10*(reg - UNW_X86_ST0); + break; + + /* SSE fp registers */ + case UNW_X86_XMM0_lo: case UNW_X86_XMM0_hi: + case UNW_X86_XMM1_lo: case UNW_X86_XMM1_hi: + case UNW_X86_XMM2_lo: case UNW_X86_XMM2_hi: + case UNW_X86_XMM3_lo: case UNW_X86_XMM3_hi: + case UNW_X86_XMM4_lo: case UNW_X86_XMM4_hi: + case UNW_X86_XMM5_lo: case UNW_X86_XMM5_hi: + case UNW_X86_XMM6_lo: case UNW_X86_XMM6_hi: + case UNW_X86_XMM7_lo: case UNW_X86_XMM7_hi: + is_fpstate = 1; + off = LINUX_FPSTATE_XMM0_OFF + 8*(reg - UNW_X86_XMM0_lo); + break; + case UNW_X86_XMM0: + case UNW_X86_XMM1: + case UNW_X86_XMM2: + case UNW_X86_XMM3: + case UNW_X86_XMM4: + case UNW_X86_XMM5: + case UNW_X86_XMM6: + case UNW_X86_XMM7: + is_fpstate = 1; + off = LINUX_FPSTATE_XMM0_OFF + 16*(reg - UNW_X86_XMM0); + break; + + case UNW_X86_FOP: + case UNW_X86_TSS: + case UNW_X86_LDT: + default: + return DWARF_REG_LOC (&c->dwarf, reg); + } + + if (is_fpstate) + { + if ((ret = dwarf_get (&c->dwarf, + DWARF_MEM_LOC (&c->dwarf, + addr + LINUX_SC_FPSTATE_OFF), + &fpstate_addr)) < 0) + return DWARF_NULL_LOC; + + if (!fpstate_addr) + return DWARF_NULL_LOC; + + return DWARF_MEM_LOC (c, fpstate_addr + off); + } + else + return DWARF_MEM_LOC (c, addr + off); +} + +#ifndef UNW_REMOTE_ONLY +HIDDEN void * +x86_r_uc_addr (ucontext_t *uc, int reg) +{ + void *addr; + + switch (reg) + { + case UNW_X86_GS: addr = &uc->uc_mcontext.gregs[REG_GS]; break; + case UNW_X86_FS: addr = &uc->uc_mcontext.gregs[REG_FS]; break; + case UNW_X86_ES: addr = &uc->uc_mcontext.gregs[REG_ES]; break; + case UNW_X86_DS: addr = &uc->uc_mcontext.gregs[REG_DS]; break; + case UNW_X86_EAX: addr = &uc->uc_mcontext.gregs[REG_EAX]; break; + case UNW_X86_EBX: addr = &uc->uc_mcontext.gregs[REG_EBX]; break; + case UNW_X86_ECX: addr = &uc->uc_mcontext.gregs[REG_ECX]; break; + case UNW_X86_EDX: addr = &uc->uc_mcontext.gregs[REG_EDX]; break; + case UNW_X86_ESI: addr = &uc->uc_mcontext.gregs[REG_ESI]; break; + case UNW_X86_EDI: addr = &uc->uc_mcontext.gregs[REG_EDI]; break; + case UNW_X86_EBP: addr = &uc->uc_mcontext.gregs[REG_EBP]; break; + case UNW_X86_EIP: addr = &uc->uc_mcontext.gregs[REG_EIP]; break; + case UNW_X86_ESP: addr = &uc->uc_mcontext.gregs[REG_ESP]; break; + case UNW_X86_TRAPNO: addr = &uc->uc_mcontext.gregs[REG_TRAPNO]; break; + case UNW_X86_CS: addr = &uc->uc_mcontext.gregs[REG_CS]; break; + case UNW_X86_EFLAGS: addr = &uc->uc_mcontext.gregs[REG_EFL]; break; + case UNW_X86_SS: addr = &uc->uc_mcontext.gregs[REG_SS]; break; + + default: + addr = NULL; + } + return addr; +} + +HIDDEN int +x86_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg) +{ + struct cursor *c = (struct cursor *) cursor; + ucontext_t *uc = c->uc; + + /* Ensure c->pi is up-to-date. On x86, 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); + + if (unlikely (c->sigcontext_format != X86_SCF_NONE)) + { + struct sigcontext *sc = (struct sigcontext *) c->sigcontext_addr; + + Debug (8, "resuming at ip=%x via sigreturn(%p)\n", c->dwarf.ip, sc); + sigreturn (sc); + } + else + { + Debug (8, "resuming at ip=%x via setcontext()\n", c->dwarf.ip); + setcontext (uc); + } + return -UNW_EINVAL; +} +#endif diff --git a/src/x86/Gregs.c b/src/x86/Gregs.c index 78cfabcc..05bc1a30 100644 --- a/src/x86/Gregs.c +++ b/src/x86/Gregs.c @@ -26,118 +26,11 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "offsets.h" #include "unwind_i.h" -static inline dwarf_loc_t -linux_scratch_loc (struct cursor *c, unw_regnum_t reg) -{ - unw_word_t addr = c->sigcontext_addr, fpstate_addr, off; - int ret, is_fpstate = 0; - - switch (c->sigcontext_format) - { - case X86_SCF_NONE: - return DWARF_REG_LOC (&c->dwarf, reg); - - case X86_SCF_LINUX_SIGFRAME: - break; - - case X86_SCF_LINUX_RT_SIGFRAME: - addr += LINUX_UC_MCONTEXT_OFF; - break; - } - - switch (reg) - { - case UNW_X86_GS: off = LINUX_SC_GS_OFF; break; - case UNW_X86_FS: off = LINUX_SC_FS_OFF; break; - case UNW_X86_ES: off = LINUX_SC_ES_OFF; break; - case UNW_X86_DS: off = LINUX_SC_DS_OFF; break; - case UNW_X86_EDI: off = LINUX_SC_EDI_OFF; break; - case UNW_X86_ESI: off = LINUX_SC_ESI_OFF; break; - case UNW_X86_EBP: off = LINUX_SC_EBP_OFF; break; - case UNW_X86_ESP: off = LINUX_SC_ESP_OFF; break; - case UNW_X86_EBX: off = LINUX_SC_EBX_OFF; break; - case UNW_X86_EDX: off = LINUX_SC_EDX_OFF; break; - case UNW_X86_ECX: off = LINUX_SC_ECX_OFF; break; - case UNW_X86_EAX: off = LINUX_SC_EAX_OFF; break; - case UNW_X86_TRAPNO: off = LINUX_SC_TRAPNO_OFF; break; - case UNW_X86_EIP: off = LINUX_SC_EIP_OFF; break; - case UNW_X86_CS: off = LINUX_SC_CS_OFF; break; - case UNW_X86_EFLAGS: off = LINUX_SC_EFLAGS_OFF; break; - case UNW_X86_SS: off = LINUX_SC_SS_OFF; break; - - /* The following is probably not correct for all possible cases. - Somebody who understands this better should review this for - correctness. */ - - case UNW_X86_FCW: is_fpstate = 1; off = LINUX_FPSTATE_CW_OFF; break; - case UNW_X86_FSW: is_fpstate = 1; off = LINUX_FPSTATE_SW_OFF; break; - case UNW_X86_FTW: is_fpstate = 1; off = LINUX_FPSTATE_TAG_OFF; break; - case UNW_X86_FCS: is_fpstate = 1; off = LINUX_FPSTATE_CSSEL_OFF; break; - case UNW_X86_FIP: is_fpstate = 1; off = LINUX_FPSTATE_IPOFF_OFF; break; - case UNW_X86_FEA: is_fpstate = 1; off = LINUX_FPSTATE_DATAOFF_OFF; break; - case UNW_X86_FDS: is_fpstate = 1; off = LINUX_FPSTATE_DATASEL_OFF; break; - case UNW_X86_MXCSR: is_fpstate = 1; off = LINUX_FPSTATE_MXCSR_OFF; break; - - /* stacked fp registers */ - case UNW_X86_ST0: case UNW_X86_ST1: case UNW_X86_ST2: case UNW_X86_ST3: - case UNW_X86_ST4: case UNW_X86_ST5: case UNW_X86_ST6: case UNW_X86_ST7: - is_fpstate = 1; - off = LINUX_FPSTATE_ST0_OFF + 10*(reg - UNW_X86_ST0); - break; - - /* SSE fp registers */ - case UNW_X86_XMM0_lo: case UNW_X86_XMM0_hi: - case UNW_X86_XMM1_lo: case UNW_X86_XMM1_hi: - case UNW_X86_XMM2_lo: case UNW_X86_XMM2_hi: - case UNW_X86_XMM3_lo: case UNW_X86_XMM3_hi: - case UNW_X86_XMM4_lo: case UNW_X86_XMM4_hi: - case UNW_X86_XMM5_lo: case UNW_X86_XMM5_hi: - case UNW_X86_XMM6_lo: case UNW_X86_XMM6_hi: - case UNW_X86_XMM7_lo: case UNW_X86_XMM7_hi: - is_fpstate = 1; - off = LINUX_FPSTATE_XMM0_OFF + 8*(reg - UNW_X86_XMM0_lo); - break; - case UNW_X86_XMM0: - case UNW_X86_XMM1: - case UNW_X86_XMM2: - case UNW_X86_XMM3: - case UNW_X86_XMM4: - case UNW_X86_XMM5: - case UNW_X86_XMM6: - case UNW_X86_XMM7: - is_fpstate = 1; - off = LINUX_FPSTATE_XMM0_OFF + 16*(reg - UNW_X86_XMM0); - break; - - case UNW_X86_FOP: - case UNW_X86_TSS: - case UNW_X86_LDT: - default: - return DWARF_REG_LOC (&c->dwarf, reg); - } - - if (is_fpstate) - { - if ((ret = dwarf_get (&c->dwarf, - DWARF_MEM_LOC (&c->dwarf, - addr + LINUX_SC_FPSTATE_OFF), - &fpstate_addr)) < 0) - return DWARF_NULL_LOC; - - if (!fpstate_addr) - return DWARF_NULL_LOC; - - return DWARF_MEM_LOC (c, fpstate_addr + off); - } - else - return DWARF_MEM_LOC (c, addr + off); -} - HIDDEN dwarf_loc_t x86_scratch_loc (struct cursor *c, unw_regnum_t reg) { if (c->sigcontext_addr) - return linux_scratch_loc (c, reg); + return x86_get_scratch_loc (c, reg); else return DWARF_REG_LOC (&c->dwarf, reg); } diff --git a/src/x86/Gresume.c b/src/x86/Gresume.c index cf914786..c39c5884 100644 --- a/src/x86/Gresume.c +++ b/src/x86/Gresume.c @@ -26,41 +26,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include "unwind_i.h" - -#ifndef UNW_REMOTE_ONLY - -HIDDEN inline int -x86_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg) -{ -#if defined(__linux) - struct cursor *c = (struct cursor *) cursor; - ucontext_t *uc = c->uc; - - /* Ensure c->pi is up-to-date. On x86, 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); - - if (unlikely (c->sigcontext_format != X86_SCF_NONE)) - { - struct sigcontext *sc = (struct sigcontext *) c->sigcontext_addr; - - Debug (8, "resuming at ip=%x via sigreturn(%p)\n", c->dwarf.ip, sc); - sigreturn (sc); - } - else - { - Debug (8, "resuming at ip=%x via setcontext()\n", c->dwarf.ip); - setcontext (uc); - } -#else -# warning Implement me! -#endif - return -UNW_EINVAL; -} - -#endif /* !UNW_REMOTE_ONLY */ +#include "offsets.h" /* This routine is responsible for copying the register values in cursor C and establishing them as the current machine state. */ diff --git a/src/x86/Gstep.c b/src/x86/Gstep.c index 266f89f4..0965db2f 100644 --- a/src/x86/Gstep.c +++ b/src/x86/Gstep.c @@ -56,70 +56,14 @@ unw_step (unw_cursor_t *cursor) Debug (13, "dwarf_step() failed (ret=%d), trying frame-chain\n", ret); if (unw_is_signal_frame (cursor)) - { - /* XXX This code is Linux-specific! */ - - /* c->esp points at the arguments to the handler. Without - SA_SIGINFO, the arguments consist of a signal number - followed by a struct sigcontext. With SA_SIGINFO, the - arguments consist a signal number, a siginfo *, and a - ucontext *. */ - unw_word_t sc_addr; - unw_word_t siginfo_ptr_addr = c->dwarf.cfa + 4; - unw_word_t sigcontext_ptr_addr = c->dwarf.cfa + 8; - unw_word_t siginfo_ptr, sigcontext_ptr; - struct dwarf_loc esp_loc, siginfo_ptr_loc, sigcontext_ptr_loc; - - siginfo_ptr_loc = DWARF_LOC (siginfo_ptr_addr, 0); - sigcontext_ptr_loc = DWARF_LOC (sigcontext_ptr_addr, 0); - ret = (dwarf_get (&c->dwarf, siginfo_ptr_loc, &siginfo_ptr) - | dwarf_get (&c->dwarf, sigcontext_ptr_loc, &sigcontext_ptr)); + { + ret = unw_handle_signal_frame(cursor); if (ret < 0) { Debug (2, "returning 0\n"); return 0; } - if (siginfo_ptr < c->dwarf.cfa - || siginfo_ptr > c->dwarf.cfa + 256 - || sigcontext_ptr < c->dwarf.cfa - || sigcontext_ptr > c->dwarf.cfa + 256) - { - /* Not plausible for SA_SIGINFO signal */ - c->sigcontext_format = X86_SCF_LINUX_SIGFRAME; - c->sigcontext_addr = sc_addr = c->dwarf.cfa + 4; - } - else - { - /* If SA_SIGINFO were not specified, we actually read - various segment pointers instead. We believe that at - least fs and _fsh are always zero for linux, so it is - not just unlikely, but impossible that we would end - up here. */ - c->sigcontext_format = X86_SCF_LINUX_RT_SIGFRAME; - c->sigcontext_addr = sigcontext_ptr; - sc_addr = sigcontext_ptr + LINUX_UC_MCONTEXT_OFF; - } - esp_loc = DWARF_LOC (sc_addr + LINUX_SC_ESP_OFF, 0); - ebp_loc = DWARF_LOC (sc_addr + LINUX_SC_EBP_OFF, 0); - eip_loc = DWARF_LOC (sc_addr + LINUX_SC_EIP_OFF, 0); - ret = dwarf_get (&c->dwarf, esp_loc, &c->dwarf.cfa); - if (ret < 0) - { - Debug (2, "returning 0\n"); - return 0; - } - - c->dwarf.loc[EAX] = DWARF_LOC (sc_addr + LINUX_SC_EAX_OFF, 0); - c->dwarf.loc[ECX] = DWARF_LOC (sc_addr + LINUX_SC_ECX_OFF, 0); - c->dwarf.loc[EDX] = DWARF_LOC (sc_addr + LINUX_SC_EDX_OFF, 0); - c->dwarf.loc[EBX] = DWARF_LOC (sc_addr + LINUX_SC_EBX_OFF, 0); - c->dwarf.loc[EBP] = DWARF_LOC (sc_addr + LINUX_SC_EBP_OFF, 0); - c->dwarf.loc[ESI] = DWARF_LOC (sc_addr + LINUX_SC_ESI_OFF, 0); - c->dwarf.loc[EDI] = DWARF_LOC (sc_addr + LINUX_SC_EDI_OFF, 0); - c->dwarf.loc[EFLAGS] = DWARF_NULL_LOC; - c->dwarf.loc[TRAPNO] = DWARF_NULL_LOC; - c->dwarf.loc[ST0] = DWARF_NULL_LOC; - } + } else { ret = dwarf_get (&c->dwarf, c->dwarf.loc[EBP], &c->dwarf.cfa); @@ -141,9 +85,10 @@ unw_step (unw_cursor_t *cursor) EIP. */ for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) c->dwarf.loc[i] = DWARF_NULL_LOC; + + c->dwarf.loc[EBP] = ebp_loc; + c->dwarf.loc[EIP] = eip_loc; } - c->dwarf.loc[EBP] = ebp_loc; - c->dwarf.loc[EIP] = eip_loc; c->dwarf.ret_addr_column = EIP; if (!DWARF_IS_NULL_LOC (c->dwarf.loc[EBP])) @@ -151,9 +96,15 @@ unw_step (unw_cursor_t *cursor) ret = dwarf_get (&c->dwarf, c->dwarf.loc[EIP], &c->dwarf.ip); if (ret < 0) { + Debug (13, "dwarf_get([EIP=0x%x]) failed\n", DWARF_GET_LOC (c->dwarf.loc[EIP])); Debug (2, "returning %d\n", ret); return ret; } + else + { + Debug (13, "[EIP=0x%x] = 0x%x\n", DWARF_GET_LOC (c->dwarf.loc[EIP]), + c->dwarf.ip); + } } else c->dwarf.ip = 0; diff --git a/src/x86/Lis_signal_frame.c b/src/x86/Los-freebsd.c similarity index 78% rename from src/x86/Lis_signal_frame.c rename to src/x86/Los-freebsd.c index b9a7c4f5..a75a205d 100644 --- a/src/x86/Lis_signal_frame.c +++ b/src/x86/Los-freebsd.c @@ -1,5 +1,5 @@ #define UNW_LOCAL_ONLY #include #if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) -#include "Gis_signal_frame.c" +#include "Gos-freebsd.c" #endif diff --git a/src/x86/Los-linux.c b/src/x86/Los-linux.c new file mode 100644 index 00000000..3cc18aab --- /dev/null +++ b/src/x86/Los-linux.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gos-linux.c" +#endif diff --git a/src/x86/getcontext-freebsd.S b/src/x86/getcontext-freebsd.S new file mode 100644 index 00000000..670eff1a --- /dev/null +++ b/src/x86/getcontext-freebsd.S @@ -0,0 +1,112 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2010 Konstantin Belousov + +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 "offsets.h" + + .global _Ux86_getcontext + .type _Ux86_getcontext, @function +_Ux86_getcontext: + .cfi_startproc + pushl %eax + .cfi_adjust_cfa_offset 4 + mov 8(%esp),%eax /* ucontext_t* */ + popl FREEBSD_UC_MCONTEXT_EAX_OFF(%eax) + .cfi_adjust_cfa_offset 4 + movl %ebx, FREEBSD_UC_MCONTEXT_EBX_OFF(%eax) + movl %ecx, FREEBSD_UC_MCONTEXT_ECX_OFF(%eax) + movl %edx, FREEBSD_UC_MCONTEXT_EDX_OFF(%eax) + movl %edi, FREEBSD_UC_MCONTEXT_EDI_OFF(%eax) + movl %esi, FREEBSD_UC_MCONTEXT_ESI_OFF(%eax) + movl %ebp, FREEBSD_UC_MCONTEXT_EBP_OFF(%eax) + + movl (%esp), %ecx + movl %ecx, FREEBSD_UC_MCONTEXT_EIP_OFF(%eax) + + leal 4(%esp), %ecx /* Exclude the return address. */ + movl %ecx, FREEBSD_UC_MCONTEXT_ESP_OFF(%eax) + + xorl %ecx, %ecx + movw %fs, %cx + movl %ecx, FREEBSD_UC_MCONTEXT_FS_OFF(%eax) + movw %gs, %cx + movl %ecx, FREEBSD_UC_MCONTEXT_GS_OFF(%eax) + movw %ds, %cx + movl %ecx, FREEBSD_UC_MCONTEXT_DS_OFF(%eax) + movw %es, %cx + movl %ecx, FREEBSD_UC_MCONTEXT_ES_OFF(%eax) + movw %ss, %cx + movl %ecx, FREEBSD_UC_MCONTEXT_SS_OFF(%eax) + movw %cs, %cx + movl %ecx, FREEBSD_UC_MCONTEXT_CS_OFF(%eax) + + pushfl + .cfi_adjust_cfa_offset 4 + popl FREEBSD_UC_MCONTEXT_EFLAGS_OFF(%eax) + .cfi_adjust_cfa_offset -4 + + movl $0, FREEBSD_UC_MCONTEXT_TRAPNO_OFF(%eax) + + movl $FREEBSD_UC_MCONTEXT_FPOWNED_FPU,\ + FREEBSD_UC_MCONTEXT_OWNEDFP_OFF(%eax) + movl $FREEBSD_UC_MCONTEXT_FPFMT_XMM,\ + FREEBSD_UC_MCONTEXT_FPFORMAT_OFF(%eax) + + /* + * Require CPU with fxsave implemented, and enabled by OS. + * + * If passed ucontext is not aligned to 16-byte boundary, + * save fpu context into temporary aligned location on stack + * and then copy. + */ + leal FREEBSD_UC_MCONTEXT_FPSTATE_OFF(%eax), %edx + testl $0xf, %edx + jne 2f + fxsave (%edx) /* fast path, passed ucontext save area was aligned */ +1: movl $FREEBSD_UC_MCONTEXT_MC_LEN_VAL,\ + FREEBSD_UC_MCONTEXT_MC_LEN_OFF(%eax) + + xorl %eax, %eax + ret + +2: movl %edx, %edi /* not aligned, do the dance */ + subl $512 + 16, %esp /* save area and 16 bytes for alignment */ + .cfi_adjust_cfa_offset 512 + 16 + movl %esp, %edx + orl $0xf, %edx /* align *%edx to 16-byte up */ + incl %edx + fxsave (%edx) + movl %edx, %esi /* copy to the final destination */ + movl $512/4,%ecx + rep; movsl + addl $512 + 16, %esp /* restore the stack */ + .cfi_adjust_cfa_offset -512 - 16 + movl FREEBSD_UC_MCONTEXT_ESI_OFF(%eax), %esi + movl FREEBSD_UC_MCONTEXT_EDI_OFF(%eax), %edi + jmp 1b + + .cfi_endproc + .size _Ux86_getcontext, . - _Ux86_getcontext + + /* We do not need executable stack. */ + .section .note.GNU-stack,"",@progbits diff --git a/src/x86/getcontext-linux.S b/src/x86/getcontext-linux.S new file mode 100644 index 00000000..c469dadb --- /dev/null +++ b/src/x86/getcontext-linux.S @@ -0,0 +1,74 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2009 Google, Inc + Contributed by Paul Pluzhnikov + +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 "offsets.h" + +/* int _Ux86_getcontext (ucontext_t *ucp) + + Saves the machine context in UCP necessary for libunwind. + Unlike the libc implementation, we don't save the signal mask + and hence avoid the cost of a system call per unwind. + +*/ + + .global _Ux86_getcontext + .type _Ux86_getcontext, @function +_Ux86_getcontext: + .cfi_startproc + mov 4(%esp),%eax /* ucontext_t* */ + + /* EAX is not preserved. */ + movl $0, (LINUX_UC_MCONTEXT_OFF+LINUX_SC_EAX_OFF)(%eax) + + movl %ebx, (LINUX_UC_MCONTEXT_OFF+LINUX_SC_EBX_OFF)(%eax) + movl %ecx, (LINUX_UC_MCONTEXT_OFF+LINUX_SC_ECX_OFF)(%eax) + movl %edx, (LINUX_UC_MCONTEXT_OFF+LINUX_SC_EDX_OFF)(%eax) + movl %edi, (LINUX_UC_MCONTEXT_OFF+LINUX_SC_EDI_OFF)(%eax) + movl %esi, (LINUX_UC_MCONTEXT_OFF+LINUX_SC_ESI_OFF)(%eax) + movl %ebp, (LINUX_UC_MCONTEXT_OFF+LINUX_SC_EBP_OFF)(%eax) + + movl (%esp), %ecx + movl %ecx, (LINUX_UC_MCONTEXT_OFF+LINUX_SC_EIP_OFF)(%eax) + + leal 4(%esp), %ecx /* Exclude the return address. */ + movl %ecx, (LINUX_UC_MCONTEXT_OFF+LINUX_SC_ESP_OFF)(%eax) + + /* glibc getcontext saves FS, but not GS */ + xorl %ecx, %ecx + movw %fs, %cx + movl %ecx, (LINUX_UC_MCONTEXT_OFF+LINUX_SC_FS_OFF)(%eax) + + leal LINUX_UC_FPREGS_MEM_OFF(%eax), %ecx + movl %ecx, (LINUX_UC_MCONTEXT_OFF+LINUX_SC_FPSTATE_OFF)(%eax) + fnstenv (%ecx) + fldenv (%ecx) + + xor %eax, %eax + ret + .cfi_endproc + .size _Ux86_getcontext, . - _Ux86_getcontext + + /* We do not need executable stack. */ + .section .note.GNU-stack,"",@progbits diff --git a/src/x86/init.h b/src/x86/init.h index 675b77e5..b59ad842 100644 --- a/src/x86/init.h +++ b/src/x86/init.h @@ -26,7 +26,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "unwind_i.h" static inline int -common_init (struct cursor *c) +common_init (struct cursor *c, unsigned use_prev_instr) { int ret, i; @@ -59,6 +59,7 @@ common_init (struct cursor *c) c->dwarf.args_size = 0; c->dwarf.ret_addr_column = 0; + c->dwarf.use_prev_instr = use_prev_instr; c->dwarf.pi_valid = 0; c->dwarf.pi_is_dynamic = 0; c->dwarf.hint = 0; diff --git a/src/x86/offsets.h b/src/x86/offsets.h index 96f06d1f..e8a255cd 100644 --- a/src/x86/offsets.h +++ b/src/x86/offsets.h @@ -9,6 +9,7 @@ #define LINUX_UC_STACK_OFF 0x08 #define LINUX_UC_MCONTEXT_OFF 0x14 #define LINUX_UC_SIGMASK_OFF 0x6c +#define LINUX_UC_FPREGS_MEM_OFF 0xec /* The struct sigcontext is located at an offset of 4 from the stack pointer in the signal frame. */ @@ -81,3 +82,59 @@ #define LINUX_FPSTATE_XMM5_OFF 0x160 #define LINUX_FPSTATE_XMM6_OFF 0x170 #define LINUX_FPSTATE_XMM7_OFF 0x180 + +/* FreeBSD-specific definitions: */ + +#define FREEBSD_SC_UCONTEXT_OFF 0x20 +#define FREEBSD_UC_MCONTEXT_OFF 0x10 + +#define FREEBSD_UC_MCONTEXT_GS_OFF 0x14 +#define FREEBSD_UC_MCONTEXT_FS_OFF 0x18 +#define FREEBSD_UC_MCONTEXT_ES_OFF 0x1c +#define FREEBSD_UC_MCONTEXT_DS_OFF 0x20 +#define FREEBSD_UC_MCONTEXT_EDI_OFF 0x24 +#define FREEBSD_UC_MCONTEXT_ESI_OFF 0x28 +#define FREEBSD_UC_MCONTEXT_EBP_OFF 0x2c +#define FREEBSD_UC_MCONTEXT_EBX_OFF 0x34 +#define FREEBSD_UC_MCONTEXT_EDX_OFF 0x38 +#define FREEBSD_UC_MCONTEXT_ECX_OFF 0x3c +#define FREEBSD_UC_MCONTEXT_EAX_OFF 0x40 +#define FREEBSD_UC_MCONTEXT_TRAPNO_OFF 0x44 +#define FREEBSD_UC_MCONTEXT_EIP_OFF 0x4c +#define FREEBSD_UC_MCONTEXT_ESP_OFF 0x58 +#define FREEBSD_UC_MCONTEXT_CS_OFF 0x50 +#define FREEBSD_UC_MCONTEXT_EFLAGS_OFF 0x54 +#define FREEBSD_UC_MCONTEXT_SS_OFF 0x5c +#define FREEBSD_UC_MCONTEXT_MC_LEN_OFF 0x60 +#define FREEBSD_UC_MCONTEXT_FPFORMAT_OFF 0x64 +#define FREEBSD_UC_MCONTEXT_OWNEDFP_OFF 0x68 +#define FREEBSD_UC_MCONTEXT_FPSTATE_OFF 0x70 + +#define FREEBSD_UC_MCONTEXT_CW_OFF 0x70 +#define FREEBSD_UC_MCONTEXT_SW_OFF 0x74 +#define FREEBSD_UC_MCONTEXT_TAG_OFF 0x78 +#define FREEBSD_UC_MCONTEXT_IPOFF_OFF 0x7c +#define FREEBSD_UC_MCONTEXT_CSSEL_OFF 0x80 +#define FREEBSD_UC_MCONTEXT_DATAOFF_OFF 0x84 +#define FREEBSD_US_MCONTEXT_DATASEL_OFF 0x88 +#define FREEBSD_UC_MCONTEXT_ST0_OFF 0x8c + +#define FREEBSD_UC_MCONTEXT_CW_XMM_OFF 0x70 +#define FREEBSD_UC_MCONTEXT_SW_XMM_OFF 0x72 +#define FREEBSD_UC_MCONTEXT_TAG_XMM_OFF 0x74 +#define FREEBSD_UC_MCONTEXT_IPOFF_XMM_OFF 0x78 +#define FREEBSD_UC_MCONTEXT_CSSEL_XMM_OFF 0x7c +#define FREEBSD_UC_MCONTEXT_DATAOFF_XMM_OFF 0x80 +#define FREEBSD_US_MCONTEXT_DATASEL_XMM_OFF 0x84 +#define FREEBSD_UC_MCONTEXT_MXCSR_XMM_OFF 0x88 +#define FREEBSD_UC_MCONTEXT_ST0_XMM_OFF 0x90 +#define FREEBSD_UC_MCONTEXT_XMM0_OFF 0x110 + +#define FREEBSD_UC_MCONTEXT_MC_LEN_VAL 0x280 +#define FREEBSD_UC_MCONTEXT_FPFMT_NODEV 0x10000 +#define FREEBSD_UC_MCONTEXT_FPFMT_387 0x10001 +#define FREEBSD_UC_MCONTEXT_FPFMT_XMM 0x10002 +#define FREEBSD_UC_MCONTEXT_FPOWNED_NONE 0x20000 +#define FREEBSD_UC_MCONTEXT_FPOWNED_FPU 0x20001 +#define FREEBSD_UC_MCONTEXT_FPOWNED_PCB 0x20002 + diff --git a/src/x86/unwind_i.h b/src/x86/unwind_i.h index 63477191..6d1d5e9f 100644 --- a/src/x86/unwind_i.h +++ b/src/x86/unwind_i.h @@ -51,10 +51,14 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define x86_local_resume UNW_OBJ(local_resume) #define x86_local_addr_space_init UNW_OBJ(local_addr_space_init) #define x86_scratch_loc UNW_OBJ(scratch_loc) +#define x86_get_scratch_loc UNW_OBJ(get_scratch_loc) +#define x86_r_uc_addr UNW_OBJ(r_uc_addr) extern void x86_local_addr_space_init (void); extern int x86_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg); extern dwarf_loc_t x86_scratch_loc (struct cursor *c, unw_regnum_t reg); +extern dwarf_loc_t x86_get_scratch_loc (struct cursor *c, unw_regnum_t reg); +extern void *x86_r_uc_addr (ucontext_t *uc, int reg); #endif /* unwind_i_h */ diff --git a/src/x86_64/Gcreate_addr_space.c b/src/x86_64/Gcreate_addr_space.c index 975387a6..02b93ebb 100644 --- a/src/x86_64/Gcreate_addr_space.c +++ b/src/x86_64/Gcreate_addr_space.c @@ -29,6 +29,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "unwind_i.h" +#if defined(_LITTLE_ENDIAN) && !defined(__LITTLE_ENDIAN) +#define __LITTLE_ENDIAN _LITTLE_ENDIAN +#endif + PROTECTED unw_addr_space_t unw_create_addr_space (unw_accessors_t *a, int byte_order) { diff --git a/src/x86_64/Gglobal.c b/src/x86_64/Gglobal.c index ec14efe1..b24d779c 100644 --- a/src/x86_64/Gglobal.c +++ b/src/x86_64/Gglobal.c @@ -61,8 +61,7 @@ tdep_init (void) sigfillset (&unwi_full_mask); - sigprocmask (SIG_SETMASK, &unwi_full_mask, &saved_mask); - mutex_lock (&x86_64_lock); + lock_acquire (&x86_64_lock, saved_mask); { if (!tdep_needs_initialization) /* another thread else beat us to it... */ @@ -78,6 +77,5 @@ tdep_init (void) tdep_needs_initialization = 0; /* signal that we're initialized... */ } out: - mutex_unlock (&x86_64_lock); - sigprocmask (SIG_SETMASK, &saved_mask, NULL); + lock_release (&x86_64_lock, saved_mask); } diff --git a/src/x86_64/Ginit.c b/src/x86_64/Ginit.c index 031deaa2..8c69b841 100644 --- a/src/x86_64/Ginit.c +++ b/src/x86_64/Ginit.c @@ -26,6 +26,10 @@ 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 +#endif + #include #include #include @@ -43,43 +47,12 @@ static struct unw_addr_space local_addr_space; PROTECTED unw_addr_space_t unw_local_addr_space = &local_addr_space; -static inline void * -uc_addr (ucontext_t *uc, int reg) -{ - 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; -} - # ifdef UNW_LOCAL_ONLY HIDDEN void * tdep_uc_addr (ucontext_t *uc, int reg) { - return uc_addr (uc, reg); + return x86_64_r_uc_addr (uc, reg); } # endif /* UNW_LOCAL_ONLY */ @@ -117,16 +90,32 @@ static int validate_mem (unw_word_t addr) { int i, victim; +#ifdef HAVE_MINCORE + unsigned char mvec[2]; /* Unaligned access may cross page boundary */ +#endif + 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 (msync ((void *) addr, 1, MS_SYNC) == -1) +#ifdef HAVE_MINCORE + if (mincore ((void *) addr, len, mvec) == -1) +#else + if (msync ((void *) addr, len, MS_ASYNC) == -1) +#endif return -1; victim = lga_victim; @@ -177,7 +166,7 @@ access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, if (unw_is_fpreg (reg)) goto badreg; - if (!(addr = uc_addr (uc, reg))) + if (!(addr = x86_64_r_uc_addr (uc, reg))) goto badreg; if (write) @@ -207,7 +196,7 @@ access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, if (!unw_is_fpreg (reg)) goto badreg; - if (!(addr = uc_addr (uc, reg))) + if (!(addr = x86_64_r_uc_addr (uc, reg))) goto badreg; if (write) diff --git a/src/x86_64/Ginit_local.c b/src/x86_64/Ginit_local.c index 1e80cb85..18b3d989 100644 --- a/src/x86_64/Ginit_local.c +++ b/src/x86_64/Ginit_local.c @@ -51,8 +51,12 @@ unw_init_local (unw_cursor_t *cursor, ucontext_t *uc) c->dwarf.as = unw_local_addr_space; c->dwarf.as_arg = c; c->uc = uc; +#if CONSERVATIVE_CHECKS + c->validate = 1; +#else c->validate = 0; - return common_init (c); +#endif + return common_init (c, 1); } #endif /* !UNW_REMOTE_ONLY */ diff --git a/src/x86_64/Ginit_remote.c b/src/x86_64/Ginit_remote.c index 8aa644d3..25dd6860 100644 --- a/src/x86_64/Ginit_remote.c +++ b/src/x86_64/Ginit_remote.c @@ -52,6 +52,6 @@ unw_init_remote (unw_cursor_t *cursor, unw_addr_space_t as, void *as_arg) c->dwarf.as_arg = as_arg; c->uc = 0; } - return common_init (c); + return common_init (c, 0); #endif /* !UNW_LOCAL_ONLY */ } diff --git a/src/x86_64/Gis_signal_frame.c b/src/x86_64/Gis_signal_frame.c deleted file mode 100644 index 318b1159..00000000 --- a/src/x86_64/Gis_signal_frame.c +++ /dev/null @@ -1,76 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2002-2003 Hewlett-Packard Co - Contributed by David Mosberger-Tang - - Modified for x86_64 by Max Asbock - -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" - -#ifdef __linux__ -PROTECTED int -unw_is_signal_frame (unw_cursor_t *cursor) -{ - struct cursor *c = (struct cursor *) cursor; - unw_word_t w0, w1, ip; - unw_addr_space_t as; - unw_accessors_t *a; - void *arg; - int ret; - - as = c->dwarf.as; - a = unw_get_accessors (as); - arg = c->dwarf.as_arg; - - /* Check if RIP points at sigreturn sequence. - on x86_64 Linux that is (see libc.so): - 48 c7 c0 0f 00 00 00 mov $0xf,%rax - 0f 05 syscall - 66 data16 - */ - - ip = c->dwarf.ip; - if ((ret = (*a->access_mem) (as, ip, &w0, 0, arg)) < 0 - || (ret = (*a->access_mem) (as, ip + 8, &w1, 0, arg)) < 0) - return 0; - w1 &= 0xff; - return (w0 == 0x0f0000000fc0c748 && w1 == 0x05); -} - -#elif defined(__sun) /* __linux__ */ - -PROTECTED int -unw_is_signal_frame (unw_cursor_t *cursor) -{ - return 0; -} - -#else /* __sun */ - -PROTECTED int -unw_is_signal_frame (unw_cursor_t *cursor) -{ - printf ("%s: implement me\n", __FUNCTION__); - return -UNW_ENOINFO; -} -#endif /* __linux__ */ diff --git a/src/x86_64/Gos-freebsd.c b/src/x86_64/Gos-freebsd.c new file mode 100644 index 00000000..50ee60bd --- /dev/null +++ b/src/x86_64/Gos-freebsd.c @@ -0,0 +1,197 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2010 Konstantin Belousov + +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 +#include +#include +#include +#include "unwind_i.h" +#include "ucontext_i.h" + +PROTECTED int +unw_is_signal_frame (unw_cursor_t *cursor) +{ + /* XXXKIB */ + struct cursor *c = (struct cursor *) cursor; + unw_word_t w0, w1, w2, b0, ip; + unw_addr_space_t as; + unw_accessors_t *a; + void *arg; + int ret; + + as = c->dwarf.as; + a = unw_get_accessors (as); + arg = c->dwarf.as_arg; + + /* Check if RIP points at sigreturn sequence. +48 8d 7c 24 10 lea SIGF_UC(%rsp),%rdi +6a 00 pushq $0 +48 c7 c0 a1 01 00 00 movq $SYS_sigreturn,%rax +0f 05 syscall +f4 0: hlt +eb fd jmp 0b + */ + + ip = c->dwarf.ip; + c->sigcontext_format = X86_64_SCF_NONE; + if ((ret = (*a->access_mem) (as, ip, &w0, 0, arg)) < 0 + || (ret = (*a->access_mem) (as, ip + 8, &w1, 0, arg)) < 0 + || (ret = (*a->access_mem) (as, ip + 16, &w2, 0, arg)) < 0) + return 0; + w2 &= 0xffffff; + if (w0 == 0x48006a10247c8d48 && + w1 == 0x050f000001a1c0c7 && + w2 == 0x0000000000fdebf4) + { + c->sigcontext_format = X86_64_SCF_FREEBSD_SIGFRAME; + return (c->sigcontext_format); + } + /* Check if RIP points at standard syscall sequence. +49 89 ca mov %rcx,%r10 +0f 05 syscall + */ + if ((ret = (*a->access_mem) (as, ip - 5, &b0, 0, arg)) < 0) + return (0); + b0 &= 0xffffffffff; + if (b0 == 0x000000050fca8949) + { + c->sigcontext_format = X86_64_SCF_FREEBSD_SYSCALL; + return (c->sigcontext_format); + } + return (X86_64_SCF_NONE); +} + +PROTECTED int +unw_handle_signal_frame (unw_cursor_t *cursor) +{ + struct cursor *c = (struct cursor *) cursor; + unw_word_t ucontext; + int ret; + + if (c->sigcontext_format == X86_64_SCF_FREEBSD_SIGFRAME) + { + ucontext = c->dwarf.cfa + offsetof(struct sigframe, sf_uc); + c->sigcontext_addr = c->dwarf.cfa; + Debug(1, "signal frame, skip over trampoline\n"); + + struct dwarf_loc rsp_loc = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RSP, 0); + ret = dwarf_get (&c->dwarf, rsp_loc, &c->dwarf.cfa); + if (ret < 0) + { + Debug (2, "returning %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); + + return 0; + } + else if (c->sigcontext_format == X86_64_SCF_FREEBSD_SYSCALL) + { + c->dwarf.loc[RCX] = c->dwarf.loc[R10]; + /* rsp_loc = DWARF_LOC(c->dwarf.cfa - 8, 0); */ + /* rbp_loc = c->dwarf.loc[RBP]; */ + c->dwarf.loc[RIP] = DWARF_LOC (c->dwarf.cfa, 0); + ret = dwarf_get (&c->dwarf, c->dwarf.loc[RIP], &c->dwarf.ip); + Debug (1, "Frame Chain [RIP=0x%Lx] = 0x%Lx\n", + (unsigned long long) DWARF_GET_LOC (c->dwarf.loc[RIP]), + (unsigned long long) c->dwarf.ip); + if (ret < 0) + { + Debug (2, "returning %d\n", ret); + return ret; + } + c->dwarf.cfa += 8; + return 1; + } + else + return -UNW_EBADFRAME; + +} + +#ifndef UNW_REMOTE_ONLY +HIDDEN void * +x86_64_r_uc_addr (ucontext_t *uc, int reg) +{ + void *addr; + + switch (reg) + { + case UNW_X86_64_R8: addr = &uc->uc_mcontext.mc_r8; break; + case UNW_X86_64_R9: addr = &uc->uc_mcontext.mc_r9; break; + case UNW_X86_64_R10: addr = &uc->uc_mcontext.mc_r10; break; + case UNW_X86_64_R11: addr = &uc->uc_mcontext.mc_r11; break; + case UNW_X86_64_R12: addr = &uc->uc_mcontext.mc_r12; break; + case UNW_X86_64_R13: addr = &uc->uc_mcontext.mc_r13; break; + case UNW_X86_64_R14: addr = &uc->uc_mcontext.mc_r14; break; + case UNW_X86_64_R15: addr = &uc->uc_mcontext.mc_r15; break; + case UNW_X86_64_RDI: addr = &uc->uc_mcontext.mc_rdi; break; + case UNW_X86_64_RSI: addr = &uc->uc_mcontext.mc_rsi; break; + case UNW_X86_64_RBP: addr = &uc->uc_mcontext.mc_rbp; break; + case UNW_X86_64_RBX: addr = &uc->uc_mcontext.mc_rbx; break; + case UNW_X86_64_RDX: addr = &uc->uc_mcontext.mc_rdx; break; + case UNW_X86_64_RAX: addr = &uc->uc_mcontext.mc_rax; break; + case UNW_X86_64_RCX: addr = &uc->uc_mcontext.mc_rcx; break; + case UNW_X86_64_RSP: addr = &uc->uc_mcontext.mc_rsp; break; + case UNW_X86_64_RIP: addr = &uc->uc_mcontext.mc_rip; break; + + default: + addr = NULL; + } + return addr; +} + +HIDDEN NORETURN void +x86_64_sigreturn (unw_cursor_t *cursor) +{ + struct cursor *c = (struct cursor *) cursor; + ucontext_t *uc = (ucontext_t *)(c->sigcontext_addr + + offsetof(struct sigframe, sf_uc)); + + Debug (8, "resuming at ip=%llx via sigreturn(%p)\n", + (unsigned long long) c->dwarf.ip, uc); + sigreturn(uc); + abort(); +} +#endif diff --git a/src/x86_64/Gos-linux.c b/src/x86_64/Gos-linux.c new file mode 100644 index 00000000..b7f832ca --- /dev/null +++ b/src/x86_64/Gos-linux.c @@ -0,0 +1,155 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2002-2003 Hewlett-Packard Co + Contributed by David Mosberger-Tang + + Modified for x86_64 by Max Asbock + +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 + +HIDDEN void +tdep_fetch_frame (struct dwarf_cursor *dw, unw_word_t ip, int need_unwind_info) +{ + struct cursor *c = (struct cursor *) dw; + assert(! need_unwind_info || dw->pi_valid); + assert(! need_unwind_info || dw->pi.unwind_info); + if (dw->pi_valid + && dw->pi.unwind_info + && ((struct dwarf_cie_info *) dw->pi.unwind_info)->signal_frame) + { + c->sigcontext_format = X86_64_SCF_LINUX_RT_SIGFRAME; + c->sigcontext_addr = dw->cfa; + } + else + { + c->sigcontext_format = X86_64_SCF_NONE; + c->sigcontext_addr = 0; + } + + Debug(15, "fetch frame ip=0x%lx cfa=0x%lx format=%d addr=0x%lx\n", + dw->ip, dw->cfa, c->sigcontext_format, c->sigcontext_addr); +} + +HIDDEN void +tdep_cache_frame (struct dwarf_cursor *dw, struct dwarf_reg_state *rs) +{ + struct cursor *c = (struct cursor *) dw; + rs->signal_frame = c->sigcontext_format; + + Debug(15, "cache frame ip=0x%lx cfa=0x%lx format=%d addr=0x%lx\n", + dw->ip, dw->cfa, c->sigcontext_format, c->sigcontext_addr); +} + +HIDDEN void +tdep_reuse_frame (struct dwarf_cursor *dw, struct dwarf_reg_state *rs) +{ + struct cursor *c = (struct cursor *) dw; + c->sigcontext_format = rs->signal_frame; + if (c->sigcontext_format == X86_64_SCF_LINUX_RT_SIGFRAME) + c->sigcontext_addr = dw->cfa; + else + c->sigcontext_addr = 0; + + Debug(15, "reuse frame ip=0x%lx cfa=0x%lx format=%d addr=0x%lx\n", + dw->ip, dw->cfa, c->sigcontext_format, c->sigcontext_addr); +} + +PROTECTED int +unw_is_signal_frame (unw_cursor_t *cursor) +{ + struct cursor *c = (struct cursor *) cursor; + return c->sigcontext_format != X86_64_SCF_NONE; +} + +PROTECTED int +unw_handle_signal_frame (unw_cursor_t *cursor) +{ +#if UNW_DEBUG /* To silence compiler warnings */ + /* Should not get here because we now use kernel-provided dwarf + information for the signal trampoline and dwarf_step() works. + Hence dwarf_step() should never call this function. Maybe + restore old non-dwarf signal handling here, but then the + gating on unw_is_signal_frame() needs to be removed. */ + struct cursor *c = (struct cursor *) cursor; + Debug(1, "old format signal frame? format=%d addr=0x%lx cfa=0x%lx\n", + c->sigcontext_format, c->sigcontext_addr, c->dwarf.cfa); + assert(c->sigcontext_format == X86_64_SCF_LINUX_RT_SIGFRAME); + assert(c->sigcontext_addr == c->dwarf.cfa); + assert(0); +#endif + return 1; +} + +#ifndef UNW_REMOTE_ONLY +HIDDEN void * +x86_64_r_uc_addr (ucontext_t *uc, int reg) +{ + 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; +} + +/* sigreturn() is a no-op on x86_64 glibc. */ +HIDDEN NORETURN void +x86_64_sigreturn (unw_cursor_t *cursor) +{ + struct cursor *c = (struct cursor *) cursor; + struct sigcontext *sc = (struct sigcontext *) c->sigcontext_addr; + + Debug (8, "resuming at ip=%llx via sigreturn(%p)\n", + (unsigned long long) c->dwarf.ip, sc); + __asm__ __volatile__ ("mov %0, %%rsp;" + "mov %1, %%rax;" + "syscall" + :: "r"(sc), "i"(SYS_rt_sigreturn) + : "memory"); + abort(); +} + +#endif diff --git a/src/x86_64/Gregs.c b/src/x86_64/Gregs.c index ddf7b7e3..5dd7b0b6 100644 --- a/src/x86_64/Gregs.c +++ b/src/x86_64/Gregs.c @@ -41,6 +41,10 @@ linux_scratch_loc (struct cursor *c, unw_regnum_t reg) case X86_64_SCF_LINUX_RT_SIGFRAME: addr += LINUX_UC_MCONTEXT_OFF; break; + + case X86_64_SCF_FREEBSD_SIGFRAME: + addr += FREEBSD_UC_MCONTEXT_OFF; + break; } return DWARF_REG_LOC (&c->dwarf, reg); diff --git a/src/x86_64/Gresume.c b/src/x86_64/Gresume.c index cb11d483..d5239fc1 100644 --- a/src/x86_64/Gresume.c +++ b/src/x86_64/Gresume.c @@ -27,29 +27,11 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include +#include "offsets.h" #include "unwind_i.h" #ifndef UNW_REMOTE_ONLY -#include - -#ifdef __linux__ - -/* sigreturn() is a no-op on x86_64 glibc. */ - -static NORETURN inline long -my_rt_sigreturn (void *new_sp) -{ - __asm__ __volatile__ ("mov %0, %%rsp;" - "mov %1, %%rax;" - "syscall" - :: "r"(new_sp), "i"(SYS_rt_sigreturn) - : "memory"); - abort (); -} - -#endif // __linux__ - HIDDEN inline int x86_64_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg) { @@ -64,21 +46,14 @@ x86_64_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg) if (unlikely (c->sigcontext_format != X86_64_SCF_NONE)) { -#ifdef __linux__ - struct sigcontext *sc = (struct sigcontext *) c->sigcontext_addr; - - Debug (8, "resuming at ip=%llx via sigreturn(%p)\n", - (unsigned long long) c->dwarf.ip, sc); - my_rt_sigreturn (sc); -#else // __linux__ - assert(0 && "Unimplemented"); -#endif // __linux__ + x86_64_sigreturn(cursor); + abort(); } else { Debug (8, "resuming at ip=%llx via setcontext()\n", (unsigned long long) c->dwarf.ip); - _Ux86_64_setcontext (uc); + setcontext (uc); } return -UNW_EINVAL; } diff --git a/src/x86_64/Gstep.c b/src/x86_64/Gstep.c index 2da1c25d..85e3989a 100644 --- a/src/x86_64/Gstep.c +++ b/src/x86_64/Gstep.c @@ -26,19 +26,43 @@ 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 +/* Recognise PLT entries such as: + 3bdf0: ff 25 e2 49 13 00 jmpq *0x1349e2(%rip) + 3bdf6: 68 ae 03 00 00 pushq $0x3ae + 3bdfb: e9 00 c5 ff ff jmpq 38300 <_init+0x18> */ +static int +is_plt_entry (struct dwarf_cursor *c) +{ + unw_word_t w0, w1; + unw_accessors_t *a; + int ret; + + a = unw_get_accessors (c->as); + if ((ret = (*a->access_mem) (c->as, c->ip, &w0, 0, c->as_arg)) < 0 + || (ret = (*a->access_mem) (c->as, c->ip + 8, &w1, 0, c->as_arg)) < 0) + return 0; + + ret = (((w0 & 0xffff) == 0x25ff) + && (((w0 >> 48) & 0xff) == 0x68) + && (((w1 >> 24) & 0xff) == 0xe9)); + + Debug (14, "ip=0x%lx => 0x%016lx 0x%016lx, ret = %d\n", c->ip, w0, w1, ret); + return ret; +} + PROTECTED int unw_step (unw_cursor_t *cursor) { struct cursor *c = (struct cursor *) cursor; int ret, i; - Debug (1, "(cursor=%p, ip=0x%016llx)\n", - c, (unsigned long long) c->dwarf.ip); + 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 = X86_64_SCF_NONE; ret = dwarf_step (&c->dwarf); if (ret < 0 && ret != -UNW_ENOINFO) @@ -79,40 +103,23 @@ unw_step (unw_cursor_t *cursor) if (unw_is_signal_frame (cursor)) { - unw_word_t ucontext = c->dwarf.cfa; - - Debug(1, "signal frame, skip over trampoline\n"); - - c->sigcontext_format = X86_64_SCF_LINUX_RT_SIGFRAME; - c->sigcontext_addr = c->dwarf.cfa; - - rsp_loc = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RSP, 0); - rbp_loc = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RBP, 0); - rip_loc = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RIP, 0); - - ret = dwarf_get (&c->dwarf, rsp_loc, &c->dwarf.cfa); + ret = unw_handle_signal_frame(cursor); if (ret < 0) { - Debug (2, "returning %d\n", ret); - return ret; + Debug (2, "returning 0\n"); + return 0; } - - 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[ 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); + } + else if (is_plt_entry (&c->dwarf)) + { + Debug (2, "found plt entry\n"); + c->dwarf.loc[RIP] = DWARF_LOC (c->dwarf.cfa, 0); + c->dwarf.cfa += 8; + } + else if (DWARF_IS_NULL_LOC (c->dwarf.loc[RBP])) + { + for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) + c->dwarf.loc[i] = DWARF_NULL_LOC; } else { @@ -121,7 +128,8 @@ unw_step (unw_cursor_t *cursor) ret = dwarf_get (&c->dwarf, c->dwarf.loc[RBP], &rbp); if (ret < 0) { - Debug (2, "returning %d\n", ret); + Debug (2, "returning %d [RBP=0x%lx]\n", ret, + DWARF_GET_LOC (c->dwarf.loc[RBP])); return ret; } @@ -153,14 +161,15 @@ unw_step (unw_cursor_t *cursor) /* Mark all registers unsaved */ for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) c->dwarf.loc[i] = DWARF_NULL_LOC; + + c->dwarf.loc[RBP] = rbp_loc; + c->dwarf.loc[RSP] = rsp_loc; + c->dwarf.loc[RIP] = rip_loc; } - c->dwarf.loc[RBP] = rbp_loc; - c->dwarf.loc[RSP] = rsp_loc; - c->dwarf.loc[RIP] = rip_loc; c->dwarf.ret_addr_column = RIP; - if (!DWARF_IS_NULL_LOC (c->dwarf.loc[RBP])) + if (!DWARF_IS_NULL_LOC (c->dwarf.loc[RIP])) { ret = dwarf_get (&c->dwarf, c->dwarf.loc[RIP], &c->dwarf.ip); Debug (1, "Frame Chain [RIP=0x%Lx] = 0x%Lx\n", diff --git a/src/x86_64/Los-freebsd.c b/src/x86_64/Los-freebsd.c new file mode 100644 index 00000000..a75a205d --- /dev/null +++ b/src/x86_64/Los-freebsd.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gos-freebsd.c" +#endif diff --git a/src/x86_64/Los-linux.c b/src/x86_64/Los-linux.c new file mode 100644 index 00000000..3cc18aab --- /dev/null +++ b/src/x86_64/Los-linux.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gos-linux.c" +#endif diff --git a/src/x86_64/getcontext.S b/src/x86_64/getcontext.S index f6fe6ddb..28c61d0c 100644 --- a/src/x86_64/getcontext.S +++ b/src/x86_64/getcontext.S @@ -1,6 +1,7 @@ /* libunwind - a platform-independent unwind library Copyright (C) 2008 Google, Inc Contributed by Paul Pluzhnikov + Copyright (C) 2010 Konstantin Belousov This file is part of libunwind. @@ -36,6 +37,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ .global _Ux86_64_getcontext .type _Ux86_64_getcontext, @function _Ux86_64_getcontext: + .cfi_startproc /* Callee saved: RBX, RBP, R12-R15 */ movq %r12, UC_MCONTEXT_GREGS_R12(%rdi) @@ -55,6 +57,7 @@ _Ux86_64_getcontext: movq %rax, UC_MCONTEXT_GREGS_RAX(%rdi) movq %rcx, UC_MCONTEXT_GREGS_RCX(%rdi) +#if defined __linux__ /* Save fp state (not needed, except for setcontext not restoring garbage). */ leaq UC_MCONTEXT_FPREGS_MEM(%rdi),%r8 @@ -63,6 +66,31 @@ _Ux86_64_getcontext: #endif // UC_MCONTEXT_FPREGS_PTR fnstenv (%r8) stmxcsr FPREGS_OFFSET_MXCSR(%r8) +#elif defined __FreeBSD__ + fxsave UC_MCONTEXT_FPSTATE(%rdi) + movq $UC_MCONTEXT_FPOWNED_FPU,UC_MCONTEXT_OWNEDFP(%rdi) + movq $UC_MCONTEXT_FPFMT_XMM,UC_MCONTEXT_FPFORMAT(%rdi) + /* Save rflags and segment registers, so that sigreturn(2) + does not complain. */ + pushfq + .cfi_adjust_cfa_offset 8 + popq UC_MCONTEXT_RFLAGS(%rdi) + .cfi_adjust_cfa_offset -8 + movl $0, UC_MCONTEXT_FLAGS(%rdi) + movw %cs, UC_MCONTEXT_CS(%rdi) + movw %ss, UC_MCONTEXT_SS(%rdi) +#if 0 + /* Setting the flags to 0 above disables restore of segment + registers from the context */ + movw %ds, UC_MCONTEXT_DS(%rdi) + movw %es, UC_MCONTEXT_ES(%rdi) + movw %fs, UC_MCONTEXT_FS(%rdi) + movw %gs, UC_MCONTEXT_GS(%rdi) +#endif + movq $UC_MCONTEXT_MC_LEN_VAL, UC_MCONTEXT_MC_LEN(%rdi) +#else +#error Port me +#endif leaq 8(%rsp), %rax /* exclude this call. */ movq %rax, UC_MCONTEXT_GREGS_RSP(%rdi) @@ -72,8 +100,8 @@ _Ux86_64_getcontext: xorq %rax, %rax retq + .cfi_endproc + .size _Ux86_64_getcontext, . - _Ux86_64_getcontext -#ifdef __linux__ /* We do not need executable stack. */ .section .note.GNU-stack,"",@progbits -#endif diff --git a/src/x86_64/init.h b/src/x86_64/init.h index ae108b24..dcd4aea2 100644 --- a/src/x86_64/init.h +++ b/src/x86_64/init.h @@ -28,7 +28,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "unwind_i.h" static inline int -common_init (struct cursor *c) +common_init (struct cursor *c, unsigned use_prev_instr) { int ret; @@ -64,6 +64,7 @@ common_init (struct cursor *c) c->dwarf.args_size = 0; c->dwarf.ret_addr_column = RIP; + c->dwarf.use_prev_instr = use_prev_instr; c->dwarf.pi_valid = 0; c->dwarf.pi_is_dynamic = 0; c->dwarf.hint = 0; diff --git a/src/x86_64/longjmp.S b/src/x86_64/longjmp.S index bda9d066..274778fd 100644 --- a/src/x86_64/longjmp.S +++ b/src/x86_64/longjmp.S @@ -24,14 +24,11 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ .globl _UI_longjmp_cont - .type _UI_longjmp_cont, @function _UI_longjmp_cont: push %rax /* push target IP as return address */ mov %rdx, %rax /* set up return-value */ retq .size _UI_longjmp_cont, .-_UI_longjmp_cont -#ifdef __linux__ /* We do not need executable stack. */ .section .note.GNU-stack,"",@progbits -#endif diff --git a/src/x86_64/offsets.h b/src/x86_64/offsets.h new file mode 100644 index 00000000..80cfe361 --- /dev/null +++ b/src/x86_64/offsets.h @@ -0,0 +1,3 @@ +/* FreeBSD specific definitions */ + +#define FREEBSD_UC_MCONTEXT_OFF 0x10 diff --git a/src/x86_64/setcontext.S b/src/x86_64/setcontext.S index e1455d45..061745f6 100644 --- a/src/x86_64/setcontext.S +++ b/src/x86_64/setcontext.S @@ -1,6 +1,7 @@ /* libunwind - a platform-independent unwind library Copyright (C) 2007 Google, Inc Contributed by Arun Sharma + Copyright (C) 2010 Konstantin Belousov This file is part of libunwind. @@ -24,7 +25,13 @@ 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 "ucontext_i.h" - +#if defined __linux__ +#include +#define SIG_SETMASK 2 +#define SIGSET_BYTE_SIZE (64/8) +#elif defined __FreeBSD__ +#include +#endif /* int _Ux86_64_setcontext (const ucontext_t *ucp) @@ -33,9 +40,22 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ */ .global _Ux86_64_setcontext + .type _Ux86_64_setcontext, @function _Ux86_64_setcontext: +#if defined __linux__ + /* restore signal mask + sigprocmask(SIG_SETMASK, ucp->uc_sigmask, NULL, sizeof(sigset_t)) */ + push %rdi + mov $__NR_rt_sigprocmask, %rax + lea UC_SIGMASK(%rdi), %rsi + mov $SIG_SETMASK, %rdi + xor %rdx, %rdx + mov $SIGSET_BYTE_SIZE, %r10 + syscall + pop %rdi + /* restore fp state */ // TODO: why not just use leaq? #ifdef UC_MCONTEXT_FPREGS_PTR @@ -45,6 +65,27 @@ _Ux86_64_setcontext: #endif // UC_MCONTEXT_FPREGS_PTR fldenv (%r8) ldmxcsr FPREGS_OFFSET_MXCSR(%r8) +#elif defined __FreeBSD__ + /* restore signal mask */ + pushq %rdi + xorl %edx,%edx + leaq UC_SIGMASK(%rdi),%rsi + movl $3,%edi/* SIG_SETMASK */ + movl $SYS_sigprocmask,%eax + movq %rcx,%r10 + syscall + popq %rdi + + /* restore fp state */ + cmpq $UC_MCONTEXT_FPOWNED_FPU,UC_MCONTEXT_OWNEDFP(%rdi) + jne 1f + cmpq $UC_MCONTEXT_FPFMT_XMM,UC_MCONTEXT_FPFORMAT(%rdi) + jne 1f + fxrstor UC_MCONTEXT_FPSTATE(%rdi) +1: +#else +#error Port me +#endif /* restore the rest of the state */ mov UC_MCONTEXT_GREGS_R8(%rdi),%r8 @@ -69,7 +110,7 @@ _Ux86_64_setcontext: mov UC_MCONTEXT_GREGS_RDI(%rdi),%rdi retq -#ifdef __linux__ + .size _Ux86_64_setcontext, . - _Ux86_64_setcontext + /* We do not need executable stack. */ .section .note.GNU-stack,"",@progbits -#endif diff --git a/src/x86_64/siglongjmp.S b/src/x86_64/siglongjmp.S index 8ca7968d..32489e53 100644 --- a/src/x86_64/siglongjmp.S +++ b/src/x86_64/siglongjmp.S @@ -24,10 +24,9 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ .globl _UI_siglongjmp_cont - + .type _UI_siglongjmp_cont, @function _UI_siglongjmp_cont: retq -#ifdef __linux__ + .size _UI_siglongjmp_cont, . - _UI_siglongjmp_cont /* We do not need executable stack. */ .section .note.GNU-stack,"",@progbits -#endif diff --git a/src/x86_64/ucontext_i.h b/src/x86_64/ucontext_i.h index a3a711b7..b75b8c2d 100644 --- a/src/x86_64/ucontext_i.h +++ b/src/x86_64/ucontext_i.h @@ -22,8 +22,7 @@ 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 __linux__ - +#if defined __linux__ #define UC_MCONTEXT_GREGS_R8 0x28 #define UC_MCONTEXT_GREGS_R9 0x30 #define UC_MCONTEXT_GREGS_R10 0x38 @@ -43,10 +42,43 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define UC_MCONTEXT_GREGS_RIP 0xa8 #define UC_MCONTEXT_FPREGS_PTR 0x1a8 #define UC_MCONTEXT_FPREGS_MEM 0xe0 +#define UC_SIGMASK 0x128 #define FPREGS_OFFSET_MXCSR 0x18 - +#elif defined __FreeBSD__ +#define UC_SIGMASK 0x0 +#define UC_MCONTEXT_GREGS_RDI 0x18 +#define UC_MCONTEXT_GREGS_RSI 0x20 +#define UC_MCONTEXT_GREGS_RDX 0x28 +#define UC_MCONTEXT_GREGS_RCX 0x30 +#define UC_MCONTEXT_GREGS_R8 0x38 +#define UC_MCONTEXT_GREGS_R9 0x40 +#define UC_MCONTEXT_GREGS_RAX 0x48 +#define UC_MCONTEXT_GREGS_RBX 0x50 +#define UC_MCONTEXT_GREGS_RBP 0x58 +#define UC_MCONTEXT_GREGS_R10 0x60 +#define UC_MCONTEXT_GREGS_R11 0x68 +#define UC_MCONTEXT_GREGS_R12 0x70 +#define UC_MCONTEXT_GREGS_R13 0x78 +#define UC_MCONTEXT_GREGS_R14 0x80 +#define UC_MCONTEXT_GREGS_R15 0x88 +#define UC_MCONTEXT_FS 0x94 +#define UC_MCONTEXT_GS 0x96 +#define UC_MCONTEXT_FLAGS 0xa0 +#define UC_MCONTEXT_ES 0xa4 +#define UC_MCONTEXT_DS 0xa6 +#define UC_MCONTEXT_GREGS_RIP 0xb0 +#define UC_MCONTEXT_CS 0xb8 +#define UC_MCONTEXT_RFLAGS 0xc0 +#define UC_MCONTEXT_GREGS_RSP 0xc8 +#define UC_MCONTEXT_SS 0xd0 +#define UC_MCONTEXT_MC_LEN 0xd8 +#define UC_MCONTEXT_FPFORMAT 0xe0 +#define UC_MCONTEXT_OWNEDFP 0xe8 +#define UC_MCONTEXT_FPSTATE 0xf0 +#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 @@ -66,12 +98,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define UC_MCONTEXT_GREGS_RIP 0xc8 #define UC_MCONTEXT_FPREGS_MEM 0x120 #define FPREGS_OFFSET_MXCSR 0x18 - -#else // __sun - +#else #error "Define fields offsets in ucontext structure for your platform" - #endif // __sun - - diff --git a/src/x86_64/unwind_i.h b/src/x86_64/unwind_i.h index af779bb2..699a6b38 100644 --- a/src/x86_64/unwind_i.h +++ b/src/x86_64/unwind_i.h @@ -58,15 +58,23 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define x86_64_lock UNW_OBJ(lock) #define x86_64_local_resume UNW_OBJ(local_resume) #define x86_64_local_addr_space_init UNW_OBJ(local_addr_space_init) +#define setcontext UNW_ARCH_OBJ (setcontext) #if 0 #define x86_64_scratch_loc UNW_OBJ(scratch_loc) #endif +#define x86_64_r_uc_addr UNW_OBJ(r_uc_addr) +#define x86_64_sigreturn UNW_OBJ(sigreturn) extern void x86_64_local_addr_space_init (void); extern int x86_64_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg); +extern int setcontext (const ucontext_t *ucp); + #if 0 extern dwarf_loc_t x86_64_scratch_loc (struct cursor *c, unw_regnum_t reg); #endif +extern void *x86_64_r_uc_addr (ucontext_t *uc, int reg); +extern NORETURN void x86_64_sigreturn (unw_cursor_t *cursor); + #endif /* unwind_i_h */ diff --git a/tests/Gtest-bt.c b/tests/Gtest-bt.c index 8211f732..152e2af3 100644 --- a/tests/Gtest-bt.c +++ b/tests/Gtest-bt.c @@ -38,12 +38,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include -#if UNW_TARGET_X86 || UNW_TARGET_X86_64 || UNW_TARGET_ARM -# define STACK_SIZE (128*1024) /* On x86/-64 and ARM, SIGSTKSZ is too small */ -#else -# define STACK_SIZE SIGSTKSZ -#endif - #define panic(args...) \ { fprintf (stderr, args); exit (-1); } @@ -54,14 +48,17 @@ typedef RETSIGTYPE (*sighandler_t) (int); int verbose; int num_errors; +/* These variables are global because they + * cause the signal stack to overflow */ +char buf[512], name[256]; +unw_cursor_t cursor; +ucontext_t uc; + static void do_backtrace (void) { - char buf[512], name[256]; unw_word_t ip, sp, off; - unw_cursor_t cursor; unw_proc_info_t pi; - unw_context_t uc; int ret; if (verbose) @@ -187,7 +184,17 @@ sighandler (int signal, void *siginfo, void *context) } # endif #elif UNW_TARGET_X86 +#if defined __linux__ 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__ + printf (" @ %lx", (unsigned long) uc->uc_mcontext.gregs[REG_RIP]); +#elif defined __FreeBSD__ + printf (" @ %lx", (unsigned long) uc->uc_mcontext.mc_rip); +#endif #endif printf ("\n"); } @@ -219,10 +226,10 @@ main (int argc, char **argv) if (verbose) printf ("\nBacktrace across signal handler on alternate stack:\n"); - stk.ss_sp = malloc (STACK_SIZE); + stk.ss_sp = malloc (SIGSTKSZ); if (!stk.ss_sp) panic ("failed to allocate SIGSTKSZ (%u) bytes\n", SIGSTKSZ); - stk.ss_size = STACK_SIZE; + stk.ss_size = SIGSTKSZ; stk.ss_flags = 0; if (sigaltstack (&stk, NULL) < 0) panic ("sigaltstack: %s\n", strerror (errno)); diff --git a/tests/Gtest-concurrent.c b/tests/Gtest-concurrent.c index 8166fc59..126acedd 100644 --- a/tests/Gtest-concurrent.c +++ b/tests/Gtest-concurrent.c @@ -115,10 +115,12 @@ main (int argc, char **argv) if (verbose) printf ("Caching: global\n"); unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_GLOBAL); + doit (); if (verbose) printf ("Caching: per-thread\n"); unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_PER_THREAD); + doit (); if (nerrors) { diff --git a/tests/Gtest-dyn1.c b/tests/Gtest-dyn1.c index 039c086d..ad49a5c2 100644 --- a/tests/Gtest-dyn1.c +++ b/tests/Gtest-dyn1.c @@ -26,7 +26,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* This file tests dynamic code-generation via function-cloning. */ #include -#include #include #include #include @@ -120,7 +119,7 @@ sighandler (int signal) name[0] = '\0'; off[0] = '\0'; if (unw_get_proc_name (&cursor, name, sizeof (name), &offset) == 0 - && off > 0) + && offset > 0) snprintf (off, sizeof (off), "+0x%lx", (long) offset); if (verbose) printf ("ip = %lx <%s%s>\n", (long) ip, name, off); diff --git a/tests/Gtest-resume-sig.c b/tests/Gtest-resume-sig.c index 0f0a6f97..68e17ebb 100644 --- a/tests/Gtest-resume-sig.c +++ b/tests/Gtest-resume-sig.c @@ -63,7 +63,7 @@ void handler (int sig) { unw_word_t ip; - sigset_t mask; + sigset_t mask, oldmask; unw_context_t uc; unw_cursor_t c; char foo; @@ -84,7 +84,7 @@ handler (int sig) sigemptyset (&mask); sigaddset (&mask, SIGUSR2); - sigprocmask (SIG_BLOCK, &mask, NULL); + sigprocmask (SIG_BLOCK, &mask, &oldmask); kill (getpid (), SIGUSR2); /* pend SIGUSR2 */ signal (SIGUSR1, SIG_IGN); @@ -92,6 +92,10 @@ handler (int sig) if ((ret = unw_getcontext (&uc)) < 0) panic ("unw_getcontext() failed: ret=%d\n", ret); +#if UNW_TARGET_X86_64 + /* unw_getcontext() doesn't save signal mask to avoid a syscall */ + uc.uc_sigmask = oldmask; +#endif if ((ret = unw_init_local (&c, &uc)) < 0) panic ("unw_init_local() failed: ret=%d\n", ret); @@ -113,10 +117,7 @@ handler (int sig) ++got_usr2; if (got_usr1) { - if (sigusr1_sp != &foo) - panic ("Stack pointer changed from %p to %p between signals\n", - sigusr1_sp, &foo); - else if (verbose) + if (verbose) printf ("OK: stack still at %p\n", &foo); } signal (SIGUSR2, SIG_IGN); diff --git a/include/x86_64/jmpbuf.h b/tests/Ltest-cxx-exceptions.cxx similarity index 51% rename from include/x86_64/jmpbuf.h rename to tests/Ltest-cxx-exceptions.cxx index 92200689..827ba98a 100644 --- a/include/x86_64/jmpbuf.h +++ b/tests/Ltest-cxx-exceptions.cxx @@ -1,8 +1,6 @@ /* libunwind - a platform-independent unwind library - Copyright (C) 2004 Hewlett-Packard Co - Contributed by David Mosberger-Tang - -This file is part of libunwind. + Copyright (C) 2010 stefan.demharter@gmx.net + Copyright (C) 2010 arun.sharma@google.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -23,9 +21,58 @@ 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. */ -/* Use glibc's jump-buffer indices; NPTL peeks at SP: */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif -#define JB_SP 6 -#define JB_RP 7 -#define JB_MASK_SAVED 8 -#define JB_MASK 9 +#include +#include +#include +#include + +#define panic(args...) \ + { fprintf (stderr, args); exit (-1); } + +struct Test +{ + public: // --- ctor/dtor --- + Test() { ++counter_; } + ~Test() { -- counter_; } + Test(const Test&) { ++counter_; } + + public: // --- static members --- + static int counter_; +}; + +int Test::counter_ = 0; + +// Called by foo +extern "C" void bar() +{ + Test t; + try { + Test t; + throw 5; + } catch (...) { + Test t; + printf("Throwing an int\n"); + throw 6; + } +} + +int main() +{ + try { + Test t; + bar(); + } catch (int) { + // Dtor of all Test-object has to be called. + if (Test::counter_ != 0) + panic("Counter non-zero\n"); + return Test::counter_; + } catch (...) { + // An int was thrown - we should not get here. + panic("Int was thrown why are we here?\n"); + } + exit(0); +} diff --git a/tests/Makefile.am b/tests/Makefile.am index f43dc609..794f33fb 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,7 +1,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/include EXTRA_DIST = run-ia64-test-dyn1 run-ptrace-mapper run-ptrace-misc \ - run-check-namespace check-namespace.sh.in + run-check-namespace check-namespace.sh.in Gtest-nomalloc.c noinst_PROGRAMS_common = check_PROGRAMS_common = test-proc-info test-static-link \ @@ -41,10 +41,14 @@ endif #ARCH_IA64 Gtest-dyn1 Ltest-dyn1 \ test-async-sig test-flush-cache test-init-remote \ test-mem test-setjmp test-ptrace \ - Ltest-nomalloc + Ltest-nomalloc rs-race noinst_PROGRAMS_cdep = forker mapper test-ptrace-misc test-varargs \ Gperf-simple Lperf-simple +if SUPPORT_CXX_EXCEPTIONS + check_PROGRAMS_cdep += Ltest-cxx-exceptions +endif + perf: perf-startup Gperf-simple Lperf-simple @echo "########## Basic performance of generic libunwind:" @./Gperf-simple @@ -81,6 +85,8 @@ ppc64_test_altivec_SOURCES = ppc64-test-altivec.c ppc64-test-altivec-utils.c ppc64_test_wchar_SOURCES = ppc64-test-wchar.c Gtest_init_SOURCES = Gtest-init.cxx Ltest_init_SOURCES = Ltest-init.cxx +Ltest_cxx_exceptions_SOURCES = Ltest-cxx-exceptions.cxx + Gtest_dyn1_SOURCES = Gtest-dyn1.c flush-cache.S Ltest_dyn1_SOURCES = Ltest-dyn1.c flush-cache.S test_static_link_SOURCES = test-static-link-loc.c test-static-link-gen.c @@ -99,7 +105,8 @@ test_ptrace_LDADD = ../src/libunwind-ptrace.a $(LIBUNWIND) Ltest_concurrent_LDADD = $(LIBUNWIND) -lpthread Gtest_concurrent_LDADD = $(LIBUNWIND) -lpthread test_async_sig_LDADD = $(LIBUNWIND) -lpthread +rs_race_LDADD = $(LIBUNWIND) -lpthread -LDADD += -ldl +LDADD += @DLLIB@ Ltest_nomalloc_SOURCES = Ltest-nomalloc.c diff --git a/tests/check-namespace.sh.in b/tests/check-namespace.sh.in index 66078a12..9c6b12dc 100644 --- a/tests/check-namespace.sh.in +++ b/tests/check-namespace.sh.in @@ -83,6 +83,7 @@ check_local_unw_abi () { match _UL${plat}_init_local match _UL${plat}_init_remote match _UL${plat}_is_signal_frame + match _UL${plat}_handle_signal_frame match _UL${plat}_local_addr_space match _UL${plat}_resume match _UL${plat}_set_caching_policy @@ -128,6 +129,11 @@ check_local_unw_abi () { match _UL${plat}_dwarf_search_unwind_table ;; esac + + if [ x@enable_debug_frame@ = xyes ]; then + match _UL${plat}_dwarf_find_debug_frame + fi + } check_generic_unw_abi () { @@ -144,6 +150,7 @@ check_generic_unw_abi () { match _U${plat}_init_local match _U${plat}_init_remote match _U${plat}_is_signal_frame + match _U${plat}_handle_signal_frame match _U${plat}_local_addr_space match _U${plat}_regname match _U${plat}_resume @@ -185,6 +192,10 @@ check_generic_unw_abi () { match _U${plat}_dwarf_search_unwind_table ;; esac + + if [ x@enable_debug_frame@ = xyes ]; then + match _U${plat}_dwarf_find_debug_frame + fi } check_cxx_abi () { diff --git a/tests/mapper.c b/tests/mapper.c index 1006a8c4..6edce711 100644 --- a/tests/mapper.c +++ b/tests/mapper.c @@ -36,6 +36,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +# define MAP_ANONYMOUS MAP_ANON +#endif + int main (int argc, char **argv) { diff --git a/tests/rs-race.c b/tests/rs-race.c new file mode 100644 index 00000000..c67584c1 --- /dev/null +++ b/tests/rs-race.c @@ -0,0 +1,1511 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2003-2005 Hewlett-Packard Co + Contributed by Paul Pluzhnikov + +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. */ + +/* Verify that register state caches work under all caching policies + in a multi-threaded environment with a large number IPs */ +#include +#include +#include +#include +#include + +/* ITERS=1000, NTHREAD=10 caught some bugs in the past */ +#ifndef ITERS +#define ITERS 100 +#endif + +#ifndef NTHREAD +#define NTHREAD 2 +#endif + +int verbose; + +void +foo_0 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_1 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_2 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_3 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_4 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_5 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_6 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_7 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_8 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_9 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_10 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_11 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_12 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_13 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_14 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_15 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_16 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_17 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_18 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_19 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_20 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_21 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_22 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_23 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_24 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_25 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_26 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_27 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_28 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_29 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_30 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_31 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_32 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_33 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_34 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_35 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_36 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_37 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_38 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_39 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_40 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_41 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_42 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_43 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_44 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_45 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_46 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_47 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_48 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_49 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_50 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_51 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_52 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_53 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_54 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_55 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_56 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_57 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_58 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_59 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_60 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_61 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_62 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_63 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_64 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_65 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_66 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_67 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_68 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_69 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_70 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_71 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_72 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_73 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_74 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_75 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_76 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_77 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_78 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_79 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_80 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_81 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_82 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_83 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_84 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_85 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_86 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_87 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_88 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_89 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_90 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_91 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_92 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_93 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_94 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_95 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_96 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_97 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_98 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_99 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_100 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_101 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_102 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_103 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_104 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_105 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_106 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_107 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_108 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_109 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_110 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_111 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_112 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_113 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_114 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_115 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_116 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_117 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_118 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_119 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_120 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_121 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_122 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_123 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_124 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_125 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_126 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_127 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void +foo_128 (void) +{ + void *buf[20]; + int n; + + if ((n = backtrace (buf, 20)) < 3) + abort (); +} + +void * +bar(void *p) +{ + int i; + for (i = 0; i < ITERS; ++i) { + foo_0 (); + foo_1 (); + foo_2 (); + foo_3 (); + foo_4 (); + foo_5 (); + foo_6 (); + foo_7 (); + foo_8 (); + foo_9 (); + foo_10 (); + foo_11 (); + foo_12 (); + foo_13 (); + foo_14 (); + foo_15 (); + foo_16 (); + foo_17 (); + foo_18 (); + foo_19 (); + foo_20 (); + foo_21 (); + foo_22 (); + foo_23 (); + foo_24 (); + foo_25 (); + foo_26 (); + foo_27 (); + foo_28 (); + foo_29 (); + foo_30 (); + foo_31 (); + foo_32 (); + foo_33 (); + foo_34 (); + foo_35 (); + foo_36 (); + foo_37 (); + foo_38 (); + foo_39 (); + foo_40 (); + foo_41 (); + foo_42 (); + foo_43 (); + foo_44 (); + foo_45 (); + foo_46 (); + foo_47 (); + foo_48 (); + foo_49 (); + foo_50 (); + foo_51 (); + foo_52 (); + foo_53 (); + foo_54 (); + foo_55 (); + foo_56 (); + foo_57 (); + foo_58 (); + foo_59 (); + foo_60 (); + foo_61 (); + foo_62 (); + foo_63 (); + foo_64 (); + foo_65 (); + foo_66 (); + foo_67 (); + foo_68 (); + foo_69 (); + foo_70 (); + foo_71 (); + foo_72 (); + foo_73 (); + foo_74 (); + foo_75 (); + foo_76 (); + foo_77 (); + foo_78 (); + foo_79 (); + foo_80 (); + foo_81 (); + foo_82 (); + foo_83 (); + foo_84 (); + foo_85 (); + foo_86 (); + foo_87 (); + foo_88 (); + foo_89 (); + foo_90 (); + foo_91 (); + foo_92 (); + foo_93 (); + foo_94 (); + foo_95 (); + foo_96 (); + foo_97 (); + foo_98 (); + foo_99 (); + foo_100 (); + foo_101 (); + foo_102 (); + foo_103 (); + foo_104 (); + foo_105 (); + foo_106 (); + foo_107 (); + foo_108 (); + foo_109 (); + foo_110 (); + foo_111 (); + foo_112 (); + foo_113 (); + foo_114 (); + foo_115 (); + foo_116 (); + foo_117 (); + foo_118 (); + foo_119 (); + foo_120 (); + foo_121 (); + foo_122 (); + foo_123 (); + foo_124 (); + foo_125 (); + foo_126 (); + foo_127 (); + foo_128 (); + } + return NULL; +} + +int doit () +{ + pthread_t tid[NTHREAD]; + int i; + + for (i = 0; i < NTHREAD; ++i) + if (pthread_create (&tid[i], NULL, bar, NULL)) + return 1; + + for (i = 0; i < NTHREAD; ++i) + if (pthread_join (tid[i], NULL)) + return 1; + + return 0; +} + +int +main (int argc, char **argv) +{ + if (argc > 1) + verbose = 1; + + if (verbose) + printf ("Caching: none\n"); + unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_NONE); + doit (); + + if (verbose) + printf ("Caching: global\n"); + unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_GLOBAL); + doit (); + + if (verbose) + printf ("Caching: per-thread\n"); + unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_PER_THREAD); + doit (); + + if (verbose) + printf ("SUCCESS\n"); + return 0; +} diff --git a/tests/run-ptrace-mapper b/tests/run-ptrace-mapper index f51cf9a1..dc3010d4 100755 --- a/tests/run-ptrace-mapper +++ b/tests/run-ptrace-mapper @@ -1,2 +1,2 @@ #!/bin/sh -./test-ptrace -c -n -t ./mapper +./test-ptrace -c -n -t ./mapper $* diff --git a/tests/test-async-sig.c b/tests/test-async-sig.c index 9309076c..5e906902 100644 --- a/tests/test-async-sig.c +++ b/tests/test-async-sig.c @@ -23,6 +23,11 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* Check whether basic unwinding truly is async-signal safe. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include #include #include #include @@ -45,6 +50,14 @@ int verbose; int nerrors; int sigcount; +#ifndef CONFIG_BLOCK_SIGNALS +/* When libunwind is configured with --enable-block-signals=no, the caller + is responsible for preventing recursion via signal handlers. + We use a simple global here. In a multithreaded program, one would use + a thread-local variable. */ +int recurcount; +#endif + #define panic(args...) \ { ++nerrors; fprintf (stderr, args); return; } @@ -58,6 +71,12 @@ do_backtrace (int may_print, int get_proc_name) int ret; int depth = 0; +#ifndef CONFIG_BLOCK_SIGNALS + if (recurcount > 0) + return; + recurcount += 1; +#endif + unw_getcontext (&uc); if (unw_init_local (&cursor, &uc) < 0) panic ("unw_init_local failed!\n"); @@ -102,6 +121,10 @@ do_backtrace (int may_print, int get_proc_name) } } while (ret > 0); + +#ifndef CONFIG_BLOCK_SIGNALS + recurcount -= 1; +#endif } void @@ -163,4 +186,5 @@ main (int argc, char **argv) exit (-1); } } + return (0); } diff --git a/tests/test-ptrace.c b/tests/test-ptrace.c index 1f46f7d8..b7de6827 100644 --- a/tests/test-ptrace.c +++ b/tests/test-ptrace.c @@ -39,6 +39,7 @@ main (int argc, char **argv) #include #include #include +#include #include #include #include @@ -47,6 +48,8 @@ main (int argc, char **argv) #include #include +extern char **environ; + static const int nerrors_max = 100; int nerrors; @@ -209,7 +212,13 @@ main (int argc, char **argv) if (!verbose) dup2 (open ("/dev/null", O_WRONLY), 1); +#if HAVE_DECL_PTRACE_TRACEME ptrace (PTRACE_TRACEME, 0, 0, 0); +#elif HAVE_DECL_PT_TRACE_ME + ptrace (PT_TRACE_ME, 0, 0, 0); +#else +#error Trace me +#endif execve (argv[optind], argv + optind, environ); _exit (-1); } @@ -274,11 +283,23 @@ main (int argc, char **argv) { case TRIGGER: if (state) +#if HAVE_DECL_PTRACE_CONT ptrace (PTRACE_CONT, target_pid, 0, 0); +#elif HAVE_DECL_PT_CONTINUE + ptrace (PT_CONTINUE, target_pid, (caddr_t)1, 0); +#else +#error Port me +#endif else { do_backtrace (target_pid); +#if HAVE_DECL_PTRACE_SINGLESTEP ptrace (PTRACE_SINGLESTEP, target_pid, 0, pending_sig); +#elif HAVE_DECL_PT_STEP + ptrace (PT_STEP, target_pid, (caddr_t)1, pending_sig); +#else +#error Singlestep me +#endif } break; @@ -286,12 +307,24 @@ main (int argc, char **argv) if (!state) do_backtrace (target_pid); state ^= 1; +#if HAVE_DECL_PTRACE_SYSCALL ptrace (PTRACE_SYSCALL, target_pid, 0, pending_sig); +#elif HAVE_DECL_PT_SYSCALL + ptrace (PT_SYSCALL, target_pid, (caddr_t)1, pending_sig); +#else +#error Syscall me +#endif break; case INSTRUCTION: do_backtrace (target_pid); - ptrace (PTRACE_SINGLESTEP, target_pid, 0, pending_sig); +#if HAVE_DECL_PTRACE_SINGLESTEP + ptrace (PTRACE_SINGLESTEP, target_pid, 0, pending_sig); +#elif HAVE_DECL_PT_STEP + ptrace (PT_STEP, target_pid, (caddr_t)1, pending_sig); +#else +#error Singlestep me +#endif break; } if (killed)