diff --git a/Makefile.am b/Makefile.am index 6b4e1d8..0c68c6d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,9 +1,15 @@ +if ARCH_ARM +include_HEADERS_tdep = include/libunwind-arm.h +else if ARCH_IA64 include_HEADERS_tdep = include/libunwind-ia64.h else if ARCH_HPPA include_HEADERS_tdep = include/libunwind-hppa.h else +if ARCH_MIPS +include_HEADERS_tdep = include/libunwind-mips.h +else if ARCH_X86 include_HEADERS_tdep = include/libunwind-x86.h else @@ -19,8 +25,10 @@ endif # ARCH_PPC64 endif # ARCH_PPC32 endif # ARCH_X86_64 endif # ARCH_X86 +endif # ARCH_MIPS endif # ARCH_HPPA endif # ARCH_IA64 +endif # ARCH_ARM include_HEADERS_common = $(include_HEADERS_tdep) \ include/libunwind-dynamic.h include/libunwind-ptrace.h diff --git a/Makefile.in b/Makefile.in index 09c1df1..921c2fa 100644 diff --git a/aclocal.m4 b/aclocal.m4 index 7380e7e..98d9744 100644 diff --git a/configure b/configure index 0156c45..5a1a36f 100755 diff --git a/configure.in b/configure.in index bf44eaa..c2374c5 100644 --- a/configure.in +++ b/configure.in @@ -75,19 +75,24 @@ AM_CONDITIONAL(USE_ALTIVEC, test x$use_altivec = xhas_altivec) get_arch() { case "$1" in + arm*) echo arm;; i?86) echo x86;; hppa*) echo hppa;; + mips*) echo mips;; powerpc64) is_gcc_m64;; *) echo $1;; esac } build_arch=`get_arch $build_cpu` +host_arch=`get_arch $host_cpu` target_arch=`get_arch $target_cpu` -AM_CONDITIONAL(REMOTE_ONLY, test x$target_arch != x$build_arch) +AM_CONDITIONAL(REMOTE_ONLY, test x$target_arch != x$host_arch) +AM_CONDITIONAL(ARCH_ARM, test x$target_arch = xarm) AM_CONDITIONAL(ARCH_IA64, test x$target_arch = xia64) AM_CONDITIONAL(ARCH_HPPA, test x$target_arch = xhppa) +AM_CONDITIONAL(ARCH_MIPS, test x$target_arch = xmips) AM_CONDITIONAL(ARCH_X86, test x$target_arch = xx86) AM_CONDITIONAL(ARCH_X86_64, test x$target_arch = xx86_64) AM_CONDITIONAL(ARCH_PPC32, test x$target_arch = xppc32) @@ -101,7 +106,7 @@ if test x$target_arch = xppc64; then AC_SUBST([libdir]) fi -if test x$target_arch != x$build_arch; then +if test x$target_arch != x$host_arch; then CPPFLAGS="${CPPFLAGS} -DUNW_REMOTE_ONLY" fi AC_CONFIG_LINKS(include/libunwind.h:include/libunwind-$target_arch.h diff --git a/doc/Makefile.in b/doc/Makefile.in index 26cb508..0369aa8 100644 diff --git a/include/dwarf.h b/include/dwarf.h index 3dbf520..c7c757e 100644 --- a/include/dwarf.h +++ b/include/dwarf.h @@ -329,6 +329,23 @@ struct dwarf_rs_cache dwarf_reg_state_t buckets[DWARF_UNW_CACHE_SIZE]; }; +/* A list of descriptors for loaded .debug_frame sections. */ + +struct unw_debug_frame_list + { + /* The start (inclusive) and end (exclusive) of the described region. */ + unw_word_t start; + unw_word_t end; + /* The debug frame itself. */ + char *debug_frame; + size_t debug_frame_size; + /* Index (for binary search). */ + struct table_entry *index; + size_t index_size; + /* Pointer to next descriptor. */ + struct unw_debug_frame_list *next; + }; + /* Convenience macros: */ #define dwarf_init UNW_ARCH_OBJ (dwarf_init) #define dwarf_find_proc_info UNW_OBJ (dwarf_find_proc_info) @@ -363,6 +380,7 @@ extern int dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_word_t *fde_addr, unw_proc_info_t *pi, int need_unwind_info, + unw_word_t base, void *arg); extern int dwarf_find_save_locs (struct dwarf_cursor *c); extern int dwarf_create_state_record (struct dwarf_cursor *c, diff --git a/include/dwarf_i.h b/include/dwarf_i.h index 5b78dc0..d01eab8 100644 --- a/include/dwarf_i.h +++ b/include/dwarf_i.h @@ -9,6 +9,12 @@ #include "dwarf.h" #include "libunwind_i.h" +/* Unless we are told otherwise, assume that a "machine address" is + the size of an unw_word_t. */ +#ifndef dwarf_addr_size +# define dwarf_addr_size(as) (sizeof (unw_word_t)) +#endif + #define dwarf_to_unw_regnum_map UNW_OBJ (dwarf_to_unw_regnum_map) extern uint8_t dwarf_to_unw_regnum_map[DWARF_REGNUM_MAP_LENGTH]; @@ -22,7 +28,7 @@ extern uint8_t dwarf_to_unw_regnum_map[DWARF_REGNUM_MAP_LENGTH]; /* In the local-only case, we can let the compiler directly access memory and don't need to worry about differing byte-order. */ -typedef union +typedef union __attribute__ ((packed)) { int8_t s8; int16_t s16; @@ -32,16 +38,15 @@ typedef union uint16_t u16; uint32_t u32; uint64_t u64; - unw_word_t w; void *ptr; } -dwarf_misaligned_value_t __attribute__ ((packed)); +dwarf_misaligned_value_t; static inline int dwarf_reads8 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, int8_t *val, void *arg) { - dwarf_misaligned_value_t *mvp = (void *) *addr; + dwarf_misaligned_value_t *mvp = (void *) (uintptr_t) *addr; *val = mvp->s8; *addr += sizeof (mvp->s8); @@ -52,7 +57,7 @@ static inline int dwarf_reads16 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, int16_t *val, void *arg) { - dwarf_misaligned_value_t *mvp = (void *) *addr; + dwarf_misaligned_value_t *mvp = (void *) (uintptr_t) *addr; *val = mvp->s16; *addr += sizeof (mvp->s16); @@ -63,7 +68,7 @@ static inline int dwarf_reads32 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, int32_t *val, void *arg) { - dwarf_misaligned_value_t *mvp = (void *) *addr; + dwarf_misaligned_value_t *mvp = (void *) (uintptr_t) *addr; *val = mvp->s32; *addr += sizeof (mvp->s32); @@ -74,7 +79,7 @@ static inline int dwarf_reads64 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, int64_t *val, void *arg) { - dwarf_misaligned_value_t *mvp = (void *) *addr; + dwarf_misaligned_value_t *mvp = (void *) (uintptr_t) *addr; *val = mvp->s64; *addr += sizeof (mvp->s64); @@ -85,7 +90,7 @@ static inline int dwarf_readu8 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, uint8_t *val, void *arg) { - dwarf_misaligned_value_t *mvp = (void *) *addr; + dwarf_misaligned_value_t *mvp = (void *) (uintptr_t) *addr; *val = mvp->u8; *addr += sizeof (mvp->u8); @@ -96,7 +101,7 @@ static inline int dwarf_readu16 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, uint16_t *val, void *arg) { - dwarf_misaligned_value_t *mvp = (void *) *addr; + dwarf_misaligned_value_t *mvp = (void *) (uintptr_t) *addr; *val = mvp->u16; *addr += sizeof (mvp->u16); @@ -107,7 +112,7 @@ static inline int dwarf_readu32 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, uint32_t *val, void *arg) { - dwarf_misaligned_value_t *mvp = (void *) *addr; + dwarf_misaligned_value_t *mvp = (void *) (uintptr_t) *addr; *val = mvp->u32; *addr += sizeof (mvp->u32); @@ -118,24 +123,13 @@ static inline int dwarf_readu64 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, uint64_t *val, void *arg) { - dwarf_misaligned_value_t *mvp = (void *) *addr; + dwarf_misaligned_value_t *mvp = (void *) (uintptr_t) *addr; *val = mvp->u64; *addr += sizeof (mvp->u64); return 0; } -static inline int -dwarf_readw (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, - unw_word_t *val, void *arg) -{ - dwarf_misaligned_value_t *mvp = (void *) *addr; - - *val = mvp->w; - *addr += sizeof (mvp->w); - return 0; -} - #else /* !UNW_LOCAL_ONLY */ static inline int @@ -263,25 +257,37 @@ dwarf_reads64 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, return 0; } +#endif /* !UNW_LOCAL_ONLY */ + static inline int dwarf_readw (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, unw_word_t *val, void *arg) { - switch (sizeof (unw_word_t)) + uint32_t u32; + uint64_t u64; + int ret; + + switch (dwarf_addr_size (as)) { case 4: - return dwarf_readu32 (as, a, addr, (uint32_t *) val, arg); + ret = dwarf_readu32 (as, a, addr, &u32, arg); + if (ret < 0) + return ret; + *val = u32; + return ret; case 8: - return dwarf_readu64 (as, a, addr, (uint64_t *) val, arg); + ret = dwarf_readu64 (as, a, addr, &u64, arg); + if (ret < 0) + return ret; + *val = u64; + return ret; default: abort (); } } -#endif /* !UNW_LOCAL_ONLY */ - /* Read an unsigned "little-endian base 128" value. See Chapter 7.6 of DWARF spec v3. */ @@ -360,7 +366,8 @@ dwarf_read_encoded_pointer_inlined (unw_addr_space_t as, unw_accessors_t *a, } else if (encoding == DW_EH_PE_aligned) { - *addr = (initial_addr + sizeof (unw_word_t) - 1) & -sizeof (unw_word_t); + int size = dwarf_addr_size (as); + *addr = (initial_addr + size - 1) & -size; return dwarf_readw (as, a, addr, valp, arg); } @@ -459,6 +466,15 @@ dwarf_read_encoded_pointer_inlined (unw_addr_space_t as, unw_accessors_t *a, return -UNW_EINVAL; } + /* Trim off any extra bits. Assume that sign extension isn't + required; the only place it is needed is MIPS kernel space + addresses. */ + if (sizeof (val) > dwarf_addr_size (as)) + { + assert (dwarf_addr_size (as) == 4); + val = (uint32_t) val; + } + if (encoding & DW_EH_PE_indirect) { unw_word_t indirect_addr = val; diff --git a/include/libunwind-arm.h b/include/libunwind-arm.h new file mode 100644 index 0000000..492331e --- /dev/null +++ b/include/libunwind-arm.h @@ -0,0 +1,298 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef LIBUNWIND_H +#define LIBUNWIND_H + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#include +#include + +#define UNW_TARGET arm +#define UNW_TARGET_ARM 1 + +#define _U_TDEP_QP_TRUE 0 /* see libunwind-dynamic.h */ + +/* This needs to be big enough to accommodate "struct cursor", while + leaving some slack for future expansion. Changing this value will + require recompiling all users of this library. Stack allocation is + relatively cheap and unwind-state copying is relatively rare, so we + want to err on making it rather too big than too small. */ + +/* FIXME for ARM. Too big? What do other things use for similar tasks? */ +#define UNW_TDEP_CURSOR_LEN 4096 + +typedef uint32_t unw_word_t; +typedef int32_t unw_sword_t; + +typedef long double unw_tdep_fpreg_t; + +typedef enum + { + UNW_ARM_R0, + UNW_ARM_R1, + UNW_ARM_R2, + UNW_ARM_R3, + UNW_ARM_R4, + UNW_ARM_R5, + UNW_ARM_R6, + UNW_ARM_R7, + UNW_ARM_R8, + UNW_ARM_R9, + UNW_ARM_R10, + UNW_ARM_R11, + UNW_ARM_R12, + UNW_ARM_R13, + UNW_ARM_R14, + UNW_ARM_R15, + + /* VFPv2 s0-s31 (obsolescent numberings). */ + UNW_ARM_S0 = 64, + UNW_ARM_S1, + UNW_ARM_S2, + UNW_ARM_S3, + UNW_ARM_S4, + UNW_ARM_S5, + UNW_ARM_S6, + UNW_ARM_S7, + UNW_ARM_S8, + UNW_ARM_S9, + UNW_ARM_S10, + UNW_ARM_S11, + UNW_ARM_S12, + UNW_ARM_S13, + UNW_ARM_S14, + UNW_ARM_S15, + UNW_ARM_S16, + UNW_ARM_S17, + UNW_ARM_S18, + UNW_ARM_S19, + UNW_ARM_S20, + UNW_ARM_S21, + UNW_ARM_S22, + UNW_ARM_S23, + UNW_ARM_S24, + UNW_ARM_S25, + UNW_ARM_S26, + UNW_ARM_S27, + UNW_ARM_S28, + UNW_ARM_S29, + UNW_ARM_S30, + UNW_ARM_S31, + + /* FPA register numberings. */ + UNW_ARM_F0 = 96, + UNW_ARM_F1, + UNW_ARM_F2, + UNW_ARM_F3, + UNW_ARM_F4, + UNW_ARM_F5, + UNW_ARM_F6, + UNW_ARM_F7, + + /* iWMMXt GR register numberings. */ + UNW_ARM_wCGR0 = 104, + UNW_ARM_wCGR1, + UNW_ARM_wCGR2, + UNW_ARM_wCGR3, + UNW_ARM_wCGR4, + UNW_ARM_wCGR5, + UNW_ARM_wCGR6, + UNW_ARM_wCGR7, + + /* iWMMXt register numberings. */ + UNW_ARM_wR0 = 112, + UNW_ARM_wR1, + UNW_ARM_wR2, + UNW_ARM_wR3, + UNW_ARM_wR4, + UNW_ARM_wR5, + UNW_ARM_wR6, + UNW_ARM_wR7, + UNW_ARM_wR8, + UNW_ARM_wR9, + UNW_ARM_wR10, + UNW_ARM_wR11, + UNW_ARM_wR12, + UNW_ARM_wR13, + UNW_ARM_wR14, + UNW_ARM_wR15, + + /* Two-byte encodings from here on. */ + + /* SPSR. */ + UNW_ARM_SPSR = 128, + UNW_ARM_SPSR_FIQ, + UNW_ARM_SPSR_IRQ, + UNW_ARM_SPSR_ABT, + UNW_ARM_SPSR_UND, + UNW_ARM_SPSR_SVC, + + /* User mode registers. */ + UNW_ARM_R8_USR = 144, + UNW_ARM_R9_USR, + UNW_ARM_R10_USR, + UNW_ARM_R11_USR, + UNW_ARM_R12_USR, + UNW_ARM_R13_USR, + UNW_ARM_R14_USR, + + /* FIQ registers. */ + UNW_ARM_R8_FIQ = 151, + UNW_ARM_R9_FIQ, + UNW_ARM_R10_FIQ, + UNW_ARM_R11_FIQ, + UNW_ARM_R12_FIQ, + UNW_ARM_R13_FIQ, + UNW_ARM_R14_FIQ, + + /* IRQ registers. */ + UNW_ARM_R13_IRQ = 158, + UNW_ARM_R14_IRQ, + + /* ABT registers. */ + UNW_ARM_R13_ABT = 160, + UNW_ARM_R14_ABT, + + /* UND registers. */ + UNW_ARM_R13_UND = 162, + UNW_ARM_R14_UND, + + /* SVC registers. */ + UNW_ARM_R13_SVC = 164, + UNW_ARM_R14_SVC, + + /* iWMMXt control registers. */ + UNW_ARM_wC0 = 192, + UNW_ARM_wC1, + UNW_ARM_wC2, + UNW_ARM_wC3, + UNW_ARM_wC4, + UNW_ARM_wC5, + UNW_ARM_wC6, + UNW_ARM_wC7, + + /* VFPv3/Neon 64-bit registers. */ + UNW_ARM_D0 = 256, + UNW_ARM_D1, + UNW_ARM_D2, + UNW_ARM_D3, + UNW_ARM_D4, + UNW_ARM_D5, + UNW_ARM_D6, + UNW_ARM_D7, + UNW_ARM_D8, + UNW_ARM_D9, + UNW_ARM_D10, + UNW_ARM_D11, + UNW_ARM_D12, + UNW_ARM_D13, + UNW_ARM_D14, + UNW_ARM_D15, + UNW_ARM_D16, + UNW_ARM_D17, + UNW_ARM_D18, + UNW_ARM_D19, + UNW_ARM_D20, + UNW_ARM_D21, + UNW_ARM_D22, + UNW_ARM_D23, + UNW_ARM_D24, + UNW_ARM_D25, + UNW_ARM_D26, + UNW_ARM_D27, + UNW_ARM_D28, + UNW_ARM_D29, + UNW_ARM_D30, + UNW_ARM_D31, + + /* For ARM, the CFA is the value of SP (r13) at the call site in the + previous frame. */ + UNW_ARM_CFA, + + UNW_TDEP_LAST_REG = UNW_ARM_D31, + + UNW_TDEP_IP = UNW_ARM_R14, /* A little white lie. */ + UNW_TDEP_SP = UNW_ARM_R13, + UNW_TDEP_EH = UNW_ARM_R0 /* FIXME. */ + } +arm_regnum_t; + +#define UNW_TDEP_NUM_EH_REGS 2 /* FIXME for ARM. */ + +typedef struct unw_tdep_save_loc + { + /* Additional target-dependent info on a save location. */ + } +unw_tdep_save_loc_t; + +/* On ARM, we can directly use ucontext_t as the unwind context. */ +typedef ucontext_t unw_tdep_context_t; + +/* There is no getcontext() on ARM. Use a stub version which only saves GP + registers. FIXME: Not ideal, may not be sufficient for all libunwind + use cases. Stores pc+8, which is only approximately correct, really. */ +#ifndef __thumb__ +#define unw_tdep_getcontext(uc) (({ \ + unw_tdep_context_t *unw_ctx = (uc); \ + register int unw_base asm ("r0") \ + = (int) (&unw_ctx->uc_mcontext.arm_r0); \ + __asm__ __volatile__ ( \ + "stmia %[base], {r0-r15}" \ + : : [base] "r" (unw_base) : "memory"); \ + }), 0) +#else /* __thumb__ */ +#define unw_tdep_getcontext(uc) (({ \ + unw_tdep_context_t *unw_ctx = (uc); \ + register int unw_base asm ("r0") \ + = (int) (&unw_ctx->uc_mcontext.arm_r0); \ + __asm__ __volatile__ ( \ + ".align 2\nbx pc\nnop\n.code 32\n" \ + "stmia %[base], {r0-r15}\n" \ + "orr %[base], pc, #1\nbx %[base]" \ + : [base] "+r" (unw_base) : : "memory", "cc"); \ + }), 0) +#endif + +#include "libunwind-dynamic.h" + +typedef struct + { + /* no arm-specific auxiliary proc-info */ + } +unw_tdep_proc_info_t; + +#include "libunwind-common.h" + +#define unw_tdep_is_fpreg UNW_ARCH_OBJ(is_fpreg) +extern int unw_tdep_is_fpreg (int); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif /* LIBUNWIND_H */ diff --git a/include/libunwind-mips.h b/include/libunwind-mips.h new file mode 100644 index 0000000..91f7001 --- /dev/null +++ b/include/libunwind-mips.h @@ -0,0 +1,155 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef LIBUNWIND_H +#define LIBUNWIND_H + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#include +#include + +#ifdef mips +# undef mips +#endif + +#define UNW_TARGET mips +#define UNW_TARGET_MIPS 1 + +#define _U_TDEP_QP_TRUE 0 /* see libunwind-dynamic.h */ + +/* This needs to be big enough to accommodate "struct cursor", while + leaving some slack for future expansion. Changing this value will + require recompiling all users of this library. Stack allocation is + relatively cheap and unwind-state copying is relatively rare, so we + want to err on making it rather too big than too small. */ + +/* FIXME for MIPS. Too big? What do other things use for similar tasks? */ +#define UNW_TDEP_CURSOR_LEN 4096 + +/* The size of a "word" varies on MIPS. This type is used for memory + addresses and register values. To allow a single library to support + multiple ABIs, and to support N32 at all, we must use a 64-bit type + even when addresses are only 32 bits. */ +typedef uint64_t unw_word_t; +typedef int32_t unw_sword_t; + +/* FIXME: MIPS ABIs. */ +typedef long double unw_tdep_fpreg_t; + +typedef enum + { + UNW_MIPS_R0, + UNW_MIPS_R1, + UNW_MIPS_R2, + UNW_MIPS_R3, + UNW_MIPS_R4, + UNW_MIPS_R5, + UNW_MIPS_R6, + UNW_MIPS_R7, + UNW_MIPS_R8, + UNW_MIPS_R9, + UNW_MIPS_R10, + UNW_MIPS_R11, + UNW_MIPS_R12, + UNW_MIPS_R13, + UNW_MIPS_R14, + UNW_MIPS_R15, + UNW_MIPS_R16, + UNW_MIPS_R17, + UNW_MIPS_R18, + UNW_MIPS_R19, + UNW_MIPS_R20, + UNW_MIPS_R21, + UNW_MIPS_R22, + UNW_MIPS_R23, + UNW_MIPS_R24, + UNW_MIPS_R25, + UNW_MIPS_R26, + UNW_MIPS_R27, + UNW_MIPS_R28, + UNW_MIPS_R29, + UNW_MIPS_R30, + UNW_MIPS_R31, + + /* FIXME: Other registers! */ + + /* For MIPS, the CFA is the value of SP (r29) at the call site in the + previous frame. */ + UNW_MIPS_CFA, + + UNW_TDEP_LAST_REG = UNW_MIPS_R31, + + UNW_TDEP_IP = UNW_MIPS_R31, + UNW_TDEP_SP = UNW_MIPS_R29, + UNW_TDEP_EH = UNW_MIPS_R0 /* FIXME. */ + } +mips_regnum_t; + +typedef enum + { + UNW_MIPS_ABI_O32, + UNW_MIPS_ABI_N32, + UNW_MIPS_ABI_N64 + } +mips_abi_t; + +#define UNW_TDEP_NUM_EH_REGS 2 /* FIXME for MIPS. */ + +typedef struct unw_tdep_save_loc + { + /* Additional target-dependent info on a save location. */ + } +unw_tdep_save_loc_t; + +/* On x86, we can directly use ucontext_t as the unwind context. FIXME for + MIPS. */ +typedef ucontext_t unw_tdep_context_t; + +#include "libunwind-dynamic.h" + +typedef struct + { + /* no mips-specific auxiliary proc-info */ + } +unw_tdep_proc_info_t; + +#include "libunwind-common.h" + +/* There is no getcontext() on MIPS. Use a stub version which only saves GP + registers. FIXME: Not ideal, may not be sufficient for all libunwind + use cases. */ +#define unw_tdep_getcontext UNW_ARCH_OBJ(getcontext) +extern int unw_tdep_getcontext (ucontext_t *uc); + +#define unw_tdep_is_fpreg UNW_ARCH_OBJ(is_fpreg) +extern int unw_tdep_is_fpreg (int); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif /* LIBUNWIND_H */ diff --git a/include/remote.h b/include/remote.h index a5ecab9..9fb90c3 100644 --- a/include/remote.h +++ b/include/remote.h @@ -11,7 +11,7 @@ static inline int fetch8 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, int8_t *valp, void *arg) { - *valp = *(int8_t *) *addr; + *valp = *(int8_t *) (uintptr_t) *addr; *addr += 1; return 0; } @@ -20,7 +20,7 @@ static inline int fetch16 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, int16_t *valp, void *arg) { - *valp = *(int16_t *) *addr; + *valp = *(int16_t *) (uintptr_t) *addr; *addr += 2; return 0; } @@ -29,7 +29,7 @@ static inline int fetch32 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, int32_t *valp, void *arg) { - *valp = *(int32_t *) *addr; + *valp = *(int32_t *) (uintptr_t) *addr; *addr += 4; return 0; } @@ -38,7 +38,7 @@ static inline int fetchw (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, unw_word_t *valp, void *arg) { - *valp = *(unw_word_t *) *addr; + *valp = *(unw_word_t *) (uintptr_t) *addr; *addr += sizeof (unw_word_t); return 0; } diff --git a/include/tdep-arm/dwarf-config.h b/include/tdep-arm/dwarf-config.h new file mode 100644 index 0000000..455d15f --- /dev/null +++ b/include/tdep-arm/dwarf-config.h @@ -0,0 +1,51 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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 dwarf_config_h +#define dwarf_config_h + +/* This is FIRST_PSEUDO_REGISTER in GCC, since DWARF_FRAME_REGISTERS is not + explicitly defined. */ +#define DWARF_NUM_PRESERVED_REGS 128 + +/* FIXME: Probably unnecessary on ARM. See arm/Gglobal.c. */ +#define DWARF_REGNUM_MAP_LENGTH 16 + +/* Return TRUE if the ADDR_SPACE uses big-endian byte-order. */ +#define dwarf_is_big_endian(addr_space) 0 + +/* Convert a pointer to a dwarf_cursor structure to a pointer to + unw_cursor_t. */ +#define dwarf_to_cursor(c) ((unw_cursor_t *) (c)) + +typedef struct dwarf_loc + { + unw_word_t val; +#ifndef UNW_LOCAL_ONLY + unw_word_t type; /* see DWARF_LOC_TYPE_* macros. */ +#endif + } +dwarf_loc_t; + +#endif /* dwarf_config_h */ diff --git a/include/tdep-arm/jmpbuf.h b/include/tdep-arm/jmpbuf.h new file mode 100644 index 0000000..499e7de --- /dev/null +++ b/include/tdep-arm/jmpbuf.h @@ -0,0 +1,32 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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. */ + +/* Use glibc's jump-buffer indices; NPTL peeks at SP: */ + +/* FIXME for ARM! */ + +#define JB_SP 4 +#define JB_RP 5 +#define JB_MASK_SAVED 6 +#define JB_MASK 7 diff --git a/include/tdep-arm/libunwind_i.h b/include/tdep-arm/libunwind_i.h new file mode 100644 index 0000000..3424d86 --- /dev/null +++ b/include/tdep-arm/libunwind_i.h @@ -0,0 +1,254 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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 ARM_LIBUNWIND_I_H +#define ARM_LIBUNWIND_I_H + +/* Target-dependent definitions that are internal to libunwind but need + to be shared with target-independent code. */ + +#include +#include + +#include "elf32.h" +#include "mempool.h" +#include "dwarf.h" + +struct unw_addr_space + { + struct unw_accessors acc; + int big_endian; + unw_caching_policy_t caching_policy; +#ifdef HAVE_ATOMIC_OPS_H + AO_t cache_generation; +#else + uint32_t cache_generation; +#endif + unw_word_t dyn_generation; /* see dyn-common.h */ + unw_word_t dyn_info_list_addr; /* (cached) dyn_info_list_addr */ + struct dwarf_rs_cache global_cache; + struct unw_debug_frame_list *debug_frames; + }; + +struct cursor + { + struct dwarf_cursor dwarf; /* must be first */ + unw_word_t sigcontext_addr; + }; + +#define DWARF_GET_LOC(l) ((l).val) + +#ifdef UNW_LOCAL_ONLY +# define DWARF_NULL_LOC DWARF_LOC (0, 0) +# define DWARF_IS_NULL_LOC(l) (DWARF_GET_LOC (l) == 0) +# define DWARF_LOC(r, t) ((dwarf_loc_t) { .val = (r) }) +# define DWARF_IS_REG_LOC(l) 0 +# define DWARF_REG_LOC(c,r) (DWARF_LOC((unw_word_t) \ + tdep_uc_addr((c)->as_arg, (r)), 0)) +# define DWARF_MEM_LOC(c,m) DWARF_LOC ((m), 0) +# define DWARF_FPREG_LOC(c,r) (DWARF_LOC((unw_word_t) \ + tdep_uc_addr((c)->as_arg, (r)), 0)) + +static inline int +dwarf_getfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t *val) +{ + if (!DWARF_GET_LOC (loc)) + return -1; + *val = *(unw_fpreg_t *) DWARF_GET_LOC (loc); + return 0; +} + +static inline int +dwarf_putfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t val) +{ + if (!DWARF_GET_LOC (loc)) + return -1; + *(unw_fpreg_t *) DWARF_GET_LOC (loc) = val; + return 0; +} + +static inline int +dwarf_get (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t *val) +{ + if (!DWARF_GET_LOC (loc)) + return -1; + *val = *(unw_word_t *) DWARF_GET_LOC (loc); + return 0; +} + +static inline int +dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) +{ + if (!DWARF_GET_LOC (loc)) + return -1; + *(unw_word_t *) DWARF_GET_LOC (loc) = val; + return 0; +} + +#else /* !UNW_LOCAL_ONLY */ +# define DWARF_LOC_TYPE_FP (1 << 0) +# define DWARF_LOC_TYPE_REG (1 << 1) +# define DWARF_NULL_LOC DWARF_LOC (0, 0) +# define DWARF_IS_NULL_LOC(l) \ + ({ dwarf_loc_t _l = (l); _l.val == 0 && _l.type == 0; }) +# define DWARF_LOC(r, t) ((dwarf_loc_t) { .val = (r), .type = (t) }) +# define DWARF_IS_REG_LOC(l) (((l).type & DWARF_LOC_TYPE_REG) != 0) +# define DWARF_IS_FP_LOC(l) (((l).type & DWARF_LOC_TYPE_FP) != 0) +# define DWARF_REG_LOC(c,r) DWARF_LOC((r), DWARF_LOC_TYPE_REG) +# define DWARF_MEM_LOC(c,m) DWARF_LOC ((m), 0) +# define DWARF_FPREG_LOC(c,r) DWARF_LOC((r), (DWARF_LOC_TYPE_REG \ + | DWARF_LOC_TYPE_FP)) + +static inline int +dwarf_getfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t *val) +{ + char *valp = (char *) &val; + unw_word_t addr; + int ret; + + if (DWARF_IS_NULL_LOC (loc)) + return -UNW_EBADREG; + + if (DWARF_IS_REG_LOC (loc)) + return (*c->as->acc.access_fpreg) (c->as, DWARF_GET_LOC (loc), + val, 0, c->as_arg); + + addr = DWARF_GET_LOC (loc); + if ((ret = (*c->as->acc.access_mem) (c->as, addr + 0, (unw_word_t *) valp, + 0, c->as_arg)) < 0) + return ret; + + return (*c->as->acc.access_mem) (c->as, addr + 4, (unw_word_t *) valp + 1, 0, + c->as_arg); +} + +static inline int +dwarf_putfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t val) +{ + char *valp = (char *) &val; + unw_word_t addr; + int ret; + + if (DWARF_IS_NULL_LOC (loc)) + return -UNW_EBADREG; + + if (DWARF_IS_REG_LOC (loc)) + return (*c->as->acc.access_fpreg) (c->as, DWARF_GET_LOC (loc), + &val, 1, c->as_arg); + + addr = DWARF_GET_LOC (loc); + if ((ret = (*c->as->acc.access_mem) (c->as, addr + 0, (unw_word_t *) valp, + 1, c->as_arg)) < 0) + return ret; + + return (*c->as->acc.access_mem) (c->as, addr + 4, (unw_word_t *) valp + 1, + 1, c->as_arg); +} + +static inline int +dwarf_get (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t *val) +{ + if (DWARF_IS_NULL_LOC (loc)) + return -UNW_EBADREG; + + /* If a code-generator were to save a value of type unw_word_t in a + floating-point register, we would have to support this case. I + suppose it could happen with MMX registers, but does it really + happen? */ + assert (!DWARF_IS_FP_LOC (loc)); + + if (DWARF_IS_REG_LOC (loc)) + return (*c->as->acc.access_reg) (c->as, DWARF_GET_LOC (loc), val, + 0, c->as_arg); + else + return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc), val, + 0, c->as_arg); +} + +static inline int +dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) +{ + if (DWARF_IS_NULL_LOC (loc)) + return -UNW_EBADREG; + + /* If a code-generator were to save a value of type unw_word_t in a + floating-point register, we would have to support this case. I + suppose it could happen with MMX registers, but does it really + happen? */ + assert (!DWARF_IS_FP_LOC (loc)); + + if (DWARF_IS_REG_LOC (loc)) + return (*c->as->acc.access_reg) (c->as, DWARF_GET_LOC (loc), &val, + 1, c->as_arg); + else + return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc), &val, + 1, c->as_arg); +} + +#endif /* !UNW_LOCAL_ONLY */ + +#define tdep_needs_initialization UNW_OBJ(needs_initialization) +#define tdep_init UNW_OBJ(init) +/* Platforms that support UNW_INFO_FORMAT_TABLE need to define + tdep_search_unwind_table. */ +#define tdep_search_unwind_table dwarf_search_unwind_table +#define tdep_uc_addr UNW_ARCH_OBJ(uc_addr) +#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) + +#ifdef UNW_LOCAL_ONLY +# define tdep_find_proc_info(c,ip,n) \ + dwarf_find_proc_info((c)->as, (ip), &(c)->pi, (n), \ + (c)->as_arg) +# define tdep_put_unwind_info(as,pi,arg) \ + dwarf_put_unwind_info((as), (pi), (arg)) +#else +# define tdep_find_proc_info(c,ip,n) \ + (*(c)->as->acc.find_proc_info)((c)->as, (ip), &(c)->pi, (n), \ + (c)->as_arg) +# define tdep_put_unwind_info(as,pi,arg) \ + (*(as)->acc.put_unwind_info)((as), (pi), (arg)) +#endif + +#define tdep_get_as(c) ((c)->dwarf.as) +#define tdep_get_as_arg(c) ((c)->dwarf.as_arg) +#define tdep_get_ip(c) ((c)->dwarf.ip) +#define tdep_big_endian(as) ((as)->big_endian) + +extern int tdep_needs_initialization; + +extern void tdep_init (void); +extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip, + unw_dyn_info_t *di, unw_proc_info_t *pi, + int need_unwind_info, void *arg); +extern void *tdep_uc_addr (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); +extern int tdep_access_reg (struct cursor *c, unw_regnum_t reg, + unw_word_t *valp, int write); +extern int tdep_access_fpreg (struct cursor *c, unw_regnum_t reg, + unw_fpreg_t *valp, int write); + +#endif /* ARM_LIBUNWIND_I_H */ diff --git a/include/tdep-hppa/libunwind_i.h b/include/tdep-hppa/libunwind_i.h index 52cfc56..553e97d 100644 --- a/include/tdep-hppa/libunwind_i.h +++ b/include/tdep-hppa/libunwind_i.h @@ -46,6 +46,7 @@ struct unw_addr_space #endif unw_word_t dyn_generation; /* see dyn-common.h */ unw_word_t dyn_info_list_addr; /* (cached) dyn_info_list_addr */ + struct unw_debug_frame_list *debug_frames; }; struct cursor diff --git a/include/tdep-mips/dwarf-config.h b/include/tdep-mips/dwarf-config.h new file mode 100644 index 0000000..250746f --- /dev/null +++ b/include/tdep-mips/dwarf-config.h @@ -0,0 +1,54 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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 dwarf_config_h +#define dwarf_config_h + +/* This is FIRST_PSEUDO_REGISTER in GCC, since DWARF_FRAME_REGISTERS is not + explicitly defined. */ +#define DWARF_NUM_PRESERVED_REGS 188 + +/* FIXME: Probably unnecessary on MIPS. See mips/Gglobal.c. */ +#define DWARF_REGNUM_MAP_LENGTH 32 + +/* Return TRUE if the ADDR_SPACE uses big-endian byte-order. */ +#define dwarf_is_big_endian(addr_space) ((addr_space)->big_endian) + +/* Return the size of an address, for DWARF purposes. */ +#define dwarf_addr_size(addr_space) ((addr_space)->addr_size) + +/* Convert a pointer to a dwarf_cursor structure to a pointer to + unw_cursor_t. */ +#define dwarf_to_cursor(c) ((unw_cursor_t *) (c)) + +typedef struct dwarf_loc + { + unw_word_t val; +#ifndef UNW_LOCAL_ONLY + unw_word_t type; /* see DWARF_LOC_TYPE_* macros. */ +#endif + } +dwarf_loc_t; + +#endif /* dwarf_config_h */ diff --git a/include/tdep-mips/jmpbuf.h b/include/tdep-mips/jmpbuf.h new file mode 100644 index 0000000..1adb2bc --- /dev/null +++ b/include/tdep-mips/jmpbuf.h @@ -0,0 +1,32 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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. */ + +/* Use glibc's jump-buffer indices; NPTL peeks at SP: */ + +/* FIXME for MIPS! */ + +#define JB_SP 4 +#define JB_RP 5 +#define JB_MASK_SAVED 6 +#define JB_MASK 7 diff --git a/include/tdep-mips/libunwind_i.h b/include/tdep-mips/libunwind_i.h new file mode 100644 index 0000000..c2f756a --- /dev/null +++ b/include/tdep-mips/libunwind_i.h @@ -0,0 +1,315 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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 MIPS_LIBUNWIND_I_H +#define MIPS_LIBUNWIND_I_H + +/* Target-dependent definitions that are internal to libunwind but need + to be shared with target-independent code. */ + +#include +#include + +#if !defined(UNW_REMOTE_ONLY) && _MIPS_SIM == _ABI64 +# include "elf64.h" +#else +# include "elf32.h" +#endif +#include "mempool.h" +#include "dwarf.h" + +struct unw_addr_space + { + struct unw_accessors acc; + + int big_endian; + mips_abi_t abi; + unsigned int addr_size; + + unw_caching_policy_t caching_policy; +#ifdef HAVE_ATOMIC_OPS_H + AO_t cache_generation; +#else + uint32_t cache_generation; +#endif + unw_word_t dyn_generation; /* see dyn-common.h */ + unw_word_t dyn_info_list_addr; /* (cached) dyn_info_list_addr */ + struct dwarf_rs_cache global_cache; + struct unw_debug_frame_list *debug_frames; +}; + +#define tdep_big_endian(as) ((as)->big_endian) + +struct cursor + { + struct dwarf_cursor dwarf; /* must be first */ + unw_word_t sigcontext_addr; + }; + +#define DWARF_GET_LOC(l) ((l).val) + +#ifndef UNW_REMOTE_ONLY +# if _MIPS_SIM == _ABIN32 +typedef long long mips_reg_t; +# else +typedef long mips_reg_t; +# endif +#endif + +#ifdef UNW_LOCAL_ONLY +# define DWARF_NULL_LOC DWARF_LOC (0, 0) +# define DWARF_IS_NULL_LOC(l) (DWARF_GET_LOC (l) == 0) +# define DWARF_LOC(r, t) ((dwarf_loc_t) { .val = (r) }) +# define DWARF_IS_REG_LOC(l) 0 +# define DWARF_REG_LOC(c,r) (DWARF_LOC((unw_word_t) (intptr_t) \ + tdep_uc_addr((c)->as_arg, (r)), 0)) +# define DWARF_MEM_LOC(c,m) DWARF_LOC ((m), 0) +# define DWARF_FPREG_LOC(c,r) (DWARF_LOC((unw_word_t) (intptr_t) \ + tdep_uc_addr((c)->as_arg, (r)), 0)) + +/* FIXME: Implement these for the MIPS FPU. */ +static inline int +dwarf_getfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t *val) +{ + if (!DWARF_GET_LOC (loc)) + return -1; + *val = *(unw_fpreg_t *) (intptr_t) DWARF_GET_LOC (loc); + return 0; +} + +static inline int +dwarf_putfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t val) +{ + if (!DWARF_GET_LOC (loc)) + return -1; + *(unw_fpreg_t *) (intptr_t) DWARF_GET_LOC (loc) = val; + return 0; +} + +static inline int +dwarf_get (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t *val) +{ + if (!DWARF_GET_LOC (loc)) + return -1; + *val = *(mips_reg_t *) (intptr_t) DWARF_GET_LOC (loc); + return 0; +} + +static inline int +dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) +{ + if (!DWARF_GET_LOC (loc)) + return -1; + *(mips_reg_t *) (intptr_t) DWARF_GET_LOC (loc) = val; + return 0; +} + +#else /* !UNW_LOCAL_ONLY */ +# define DWARF_LOC_TYPE_FP (1 << 0) +# define DWARF_LOC_TYPE_REG (1 << 1) +# define DWARF_NULL_LOC DWARF_LOC (0, 0) +# define DWARF_IS_NULL_LOC(l) \ + ({ dwarf_loc_t _l = (l); _l.val == 0 && _l.type == 0; }) +# define DWARF_LOC(r, t) ((dwarf_loc_t) { .val = (r), .type = (t) }) +# define DWARF_IS_REG_LOC(l) (((l).type & DWARF_LOC_TYPE_REG) != 0) +# define DWARF_IS_FP_LOC(l) (((l).type & DWARF_LOC_TYPE_FP) != 0) +# define DWARF_REG_LOC(c,r) DWARF_LOC((r), DWARF_LOC_TYPE_REG) +# define DWARF_MEM_LOC(c,m) DWARF_LOC ((m), 0) +# define DWARF_FPREG_LOC(c,r) DWARF_LOC((r), (DWARF_LOC_TYPE_REG \ + | DWARF_LOC_TYPE_FP)) + +static inline int +read_s32 (struct dwarf_cursor *c, unw_word_t addr, unw_word_t *val) +{ + int offset = addr & 4; + int ret; + unw_word_t memval; + + ret = (*c->as->acc.access_mem) (c->as, addr - offset, &memval, 0, c->as_arg); + if (ret < 0) + return ret; + + if ((offset != 0) == tdep_big_endian (c->as)) + *val = (int32_t) memval; + else + *val = (int32_t) (memval >> 32); + + return 0; +} + +static inline int +write_s32 (struct dwarf_cursor *c, unw_word_t addr, const unw_word_t *val) +{ + int offset = addr & 4; + int ret; + unw_word_t memval; + + ret = (*c->as->acc.access_mem) (c->as, addr - offset, &memval, 0, c->as_arg); + if (ret < 0) + return ret; + + if ((offset != 0) == tdep_big_endian (c->as)) + memval = (memval & ~0xffffffffLL) | (uint32_t) *val; + else + memval = (memval & 0xffffffffLL) | (uint32_t) (*val << 32); + + return (*c->as->acc.access_mem) (c->as, addr - offset, &memval, 1, c->as_arg); +} + +/* FIXME: Implement these for the MIPS FPU. */ +static inline int +dwarf_getfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t *val) +{ + char *valp = (char *) &val; + unw_word_t addr; + int ret; + + if (DWARF_IS_NULL_LOC (loc)) + return -UNW_EBADREG; + + if (DWARF_IS_REG_LOC (loc)) + return (*c->as->acc.access_fpreg) (c->as, DWARF_GET_LOC (loc), + val, 0, c->as_arg); + + addr = DWARF_GET_LOC (loc); + if ((ret = (*c->as->acc.access_mem) (c->as, addr + 0, (unw_word_t *) valp, + 0, c->as_arg)) < 0) + return ret; + + return (*c->as->acc.access_mem) (c->as, addr + 4, (unw_word_t *) valp + 1, 0, + c->as_arg); +} + +static inline int +dwarf_putfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t val) +{ + char *valp = (char *) &val; + unw_word_t addr; + int ret; + + if (DWARF_IS_NULL_LOC (loc)) + return -UNW_EBADREG; + + if (DWARF_IS_REG_LOC (loc)) + return (*c->as->acc.access_fpreg) (c->as, DWARF_GET_LOC (loc), + &val, 1, c->as_arg); + + addr = DWARF_GET_LOC (loc); + if ((ret = (*c->as->acc.access_mem) (c->as, addr + 0, (unw_word_t *) valp, + 1, c->as_arg)) < 0) + return ret; + + return (*c->as->acc.access_mem) (c->as, addr + 4, (unw_word_t *) valp + 1, + 1, c->as_arg); +} + +static inline int +dwarf_get (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t *val) +{ + if (DWARF_IS_NULL_LOC (loc)) + return -UNW_EBADREG; + + /* If a code-generator were to save a value of type unw_word_t in a + floating-point register, we would have to support this case. I + suppose it could happen with MMX registers, but does it really + happen? */ + assert (!DWARF_IS_FP_LOC (loc)); + + if (DWARF_IS_REG_LOC (loc)) + return (*c->as->acc.access_reg) (c->as, DWARF_GET_LOC (loc), val, + 0, c->as_arg); + else if (c->as->abi == UNW_MIPS_ABI_O32) + return read_s32 (c, DWARF_GET_LOC (loc), val); + else + return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc), val, + 0, c->as_arg); +} + +static inline int +dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) +{ + if (DWARF_IS_NULL_LOC (loc)) + return -UNW_EBADREG; + + /* If a code-generator were to save a value of type unw_word_t in a + floating-point register, we would have to support this case. I + suppose it could happen with MMX registers, but does it really + happen? */ + assert (!DWARF_IS_FP_LOC (loc)); + + if (DWARF_IS_REG_LOC (loc)) + return (*c->as->acc.access_reg) (c->as, DWARF_GET_LOC (loc), &val, + 1, c->as_arg); + else if (c->as->abi == UNW_MIPS_ABI_O32) + return write_s32 (c, DWARF_GET_LOC (loc), &val); + else + return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc), &val, + 1, c->as_arg); +} + +#endif /* !UNW_LOCAL_ONLY */ + +#define tdep_needs_initialization UNW_OBJ(needs_initialization) +#define tdep_init UNW_OBJ(init) +/* Platforms that support UNW_INFO_FORMAT_TABLE need to define + tdep_search_unwind_table. */ +#define tdep_search_unwind_table dwarf_search_unwind_table +#define tdep_uc_addr UNW_ARCH_OBJ(uc_addr) +#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) + +#ifdef UNW_LOCAL_ONLY +# define tdep_find_proc_info(c,ip,n) \ + dwarf_find_proc_info((c)->as, (ip), &(c)->pi, (n), \ + (c)->as_arg) +# define tdep_put_unwind_info(as,pi,arg) \ + dwarf_put_unwind_info((as), (pi), (arg)) +#else +# define tdep_find_proc_info(c,ip,n) \ + (*(c)->as->acc.find_proc_info)((c)->as, (ip), &(c)->pi, (n), \ + (c)->as_arg) +# define tdep_put_unwind_info(as,pi,arg) \ + (*(as)->acc.put_unwind_info)((as), (pi), (arg)) +#endif + +#define tdep_get_as(c) ((c)->dwarf.as) +#define tdep_get_as_arg(c) ((c)->dwarf.as_arg) +#define tdep_get_ip(c) ((c)->dwarf.ip) + +extern int tdep_needs_initialization; + +extern void tdep_init (void); +extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip, + unw_dyn_info_t *di, unw_proc_info_t *pi, + int need_unwind_info, void *arg); +extern void *tdep_uc_addr (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); +extern int tdep_access_reg (struct cursor *c, unw_regnum_t reg, + unw_word_t *valp, int write); +extern int tdep_access_fpreg (struct cursor *c, unw_regnum_t reg, + unw_fpreg_t *valp, int write); + +#endif /* MIPS_LIBUNWIND_I_H */ diff --git a/include/tdep-ppc32/libunwind_i.h b/include/tdep-ppc32/libunwind_i.h index 2a9f9c1..05dd135 100644 --- a/include/tdep-ppc32/libunwind_i.h +++ b/include/tdep-ppc32/libunwind_i.h @@ -54,6 +54,7 @@ struct unw_addr_space unw_word_t dyn_generation; /* see dyn-common.h */ unw_word_t dyn_info_list_addr; /* (cached) dyn_info_list_addr */ struct dwarf_rs_cache global_cache; + struct unw_debug_frame_list *debug_frames; int validate; }; diff --git a/include/tdep-ppc64/libunwind_i.h b/include/tdep-ppc64/libunwind_i.h index 8b5c4de..192a034 100644 --- a/include/tdep-ppc64/libunwind_i.h +++ b/include/tdep-ppc64/libunwind_i.h @@ -54,6 +54,7 @@ struct unw_addr_space unw_word_t dyn_generation; /* see dyn-common.h */ unw_word_t dyn_info_list_addr; /* (cached) dyn_info_list_addr */ struct dwarf_rs_cache global_cache; + struct unw_debug_frame_list *debug_frames; int validate; }; diff --git a/include/tdep-x86/libunwind_i.h b/include/tdep-x86/libunwind_i.h index 1a43882..43c22f1 100644 --- a/include/tdep-x86/libunwind_i.h +++ b/include/tdep-x86/libunwind_i.h @@ -48,6 +48,7 @@ struct unw_addr_space unw_word_t dyn_generation; /* see dyn-common.h */ unw_word_t dyn_info_list_addr; /* (cached) dyn_info_list_addr */ struct dwarf_rs_cache global_cache; + struct unw_debug_frame_list *debug_frames; }; struct cursor diff --git a/include/tdep-x86_64/libunwind_i.h b/include/tdep-x86_64/libunwind_i.h index 04a3c37..ad4987b 100644 --- a/include/tdep-x86_64/libunwind_i.h +++ b/include/tdep-x86_64/libunwind_i.h @@ -50,6 +50,7 @@ struct unw_addr_space unw_word_t dyn_generation; /* see dyn-common.h */ unw_word_t dyn_info_list_addr; /* (cached) dyn_info_list_addr */ struct dwarf_rs_cache global_cache; + struct unw_debug_frame_list *debug_frames; int validate; }; diff --git a/src/Makefile.am b/src/Makefile.am index 88bb20b..ae113f4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -33,9 +33,11 @@ libunwind_setjmp_la_LIBADD = libunwind-$(arch).la -lc libunwind_setjmp_la_SOURCES_common = setjmp/setjmp_i.h \ setjmp/longjmp.c \ setjmp/siglongjmp.c +libunwind_setjmp_la_SOURCES_arm = arm/siglongjmp.S libunwind_setjmp_la_SOURCES_ia64 = ia64/setjmp.S ia64/sigsetjmp.S \ ia64/longjmp.S ia64/siglongjmp.S libunwind_setjmp_la_SOURCES_hppa = hppa/siglongjmp.S +libunwind_setjmp_la_SOURCES_mips = mips/siglongjmp.S libunwind_setjmp_la_SOURCES_x86 = x86/longjmp.S x86/siglongjmp.S libunwind_setjmp_la_SOURCES_x86_64 = x86_64/longjmp.S x86_64/siglongjmp.S libunwind_setjmp_la_SOURCES_ppc64 = ppc/longjmp.S ppc/siglongjmp.S @@ -59,8 +61,17 @@ libunwind_la_SOURCES_generic = \ mi/Gget_fpreg.c mi/Gset_fpreg.c \ mi/Gset_caching_policy.c +libunwind_la_SOURCES_local_unwind = \ + unwind/Backtrace.c unwind/DeleteException.c \ + unwind/FindEnclosingFunction.c unwind/ForcedUnwind.c \ + unwind/GetBSP.c unwind/GetCFA.c unwind/GetDataRelBase.c \ + unwind/GetGR.c unwind/GetIP.c unwind/GetLanguageSpecificData.c \ + unwind/GetRegionStart.c unwind/GetTextRelBase.c \ + unwind/RaiseException.c unwind/Resume.c \ + unwind/Resume_or_Rethrow.c unwind/SetGR.c unwind/SetIP.c + # List of arch-independent files needed by local-only library (libunwind): -libunwind_la_SOURCES_local = \ +libunwind_la_SOURCES_local_nounwind = \ $(libunwind_la_SOURCES_os_local) \ mi/backtrace.c \ mi/dyn-cancel.c mi/dyn-info-list.c mi/dyn-register.c \ @@ -70,14 +81,23 @@ libunwind_la_SOURCES_local = \ mi/Lput_dynamic_unwind_info.c mi/Ldestroy_addr_space.c \ mi/Lget_reg.c mi/Lset_reg.c \ mi/Lget_fpreg.c mi/Lset_fpreg.c \ - mi/Lset_caching_policy.c \ - unwind/Backtrace.c unwind/DeleteException.c \ - unwind/FindEnclosingFunction.c unwind/ForcedUnwind.c \ - unwind/GetBSP.c unwind/GetCFA.c unwind/GetDataRelBase.c \ - unwind/GetGR.c unwind/GetIP.c unwind/GetLanguageSpecificData.c \ - unwind/GetRegionStart.c unwind/GetTextRelBase.c \ - unwind/RaiseException.c unwind/Resume.c \ - unwind/Resume_or_Rethrow.c unwind/SetGR.c unwind/SetIP.c + mi/Lset_caching_policy.c + +# On some platforms, defining _Unwind replacements really upsets +# exception-handling. Turn off those functions for those platforms. +if ARCH_ARM +libunwind_la_SOURCES_local = \ + $(libunwind_la_SOURCES_local_nounwind) +else +if ARCH_MIPS +libunwind_la_SOURCES_local = \ + $(libunwind_la_SOURCES_local_nounwind) +else +libunwind_la_SOURCES_local = \ + $(libunwind_la_SOURCES_local_nounwind) \ + $(libunwind_la_SOURCES_local_unwind) +endif # ARCH_MIPS +endif # ARCH_ARM libunwind_la_SOURCES_os_linux = os-linux.h os-linux.c @@ -94,6 +114,31 @@ 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: +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/is_fpreg.c arm/regname.c + +# The list of files that go into libunwind: +libunwind_la_SOURCES_arm = $(libunwind_la_SOURCES_arm_common) \ + $(libunwind_la_SOURCES_local) \ + arm/getcontext.S \ + $(dwarf_SOURCES_local) \ + dwarf/Lfind_proc_info-lsb.c \ + arm/Lcreate_addr_space.c arm/Lget_proc_info.c arm/Lget_save_loc.c \ + arm/Lglobal.c arm/Linit.c arm/Linit_local.c arm/Linit_remote.c \ + arm/Lis_signal_frame.c arm/Lregs.c arm/Lresume.c arm/Lstep.c + +libunwind_arm_la_SOURCES_arm = $(libunwind_la_SOURCES_arm_common) \ + $(libunwind_la_SOURCES_generic) \ + $(dwarf_SOURCES_generic) \ + dwarf/Gfind_proc_info-lsb.c \ + arm/Gcreate_addr_space.c arm/Gget_proc_info.c arm/Gget_save_loc.c \ + arm/Gglobal.c arm/Ginit.c arm/Ginit_local.c arm/Ginit_remote.c \ + arm/Gis_signal_frame.c arm/Gregs.c arm/Gresume.c arm/Gstep.c + # The list of files that go both into libunwind and libunwind-ia64: libunwind_la_SOURCES_ia64_common = $(libunwind_la_SOURCES_common) \ elf64.c elf64.h \ @@ -150,6 +195,31 @@ libunwind_hppa_la_SOURCES_hppa = $(libunwind_la_SOURCES_hppa_common) \ hppa/Gis_signal_frame.c hppa/Gget_proc_info.c hppa/Gregs.c \ hppa/Gresume.c hppa/Gstep.c +# The list of files that go info libunwind and libunwind-mips: +libunwind_la_SOURCES_mips_common = $(libunwind_la_SOURCES_common) \ + $(dwarf_SOURCES_common) \ + elfxx.c \ + mips/init.h mips/offsets.h mips/regs.h \ + mips/is_fpreg.c mips/regname.c + +# The list of files that go into libunwind: +libunwind_la_SOURCES_mips = $(libunwind_la_SOURCES_mips_common) \ + $(libunwind_la_SOURCES_local) \ + mips/getcontext.S \ + $(dwarf_SOURCES_local) \ + dwarf/Lfind_proc_info-lsb.c \ + mips/Lcreate_addr_space.c mips/Lget_proc_info.c mips/Lget_save_loc.c \ + mips/Lglobal.c mips/Linit.c mips/Linit_local.c mips/Linit_remote.c \ + mips/Lis_signal_frame.c mips/Lregs.c mips/Lresume.c mips/Lstep.c + +libunwind_mips_la_SOURCES_mips = $(libunwind_la_SOURCES_mips_common) \ + $(libunwind_la_SOURCES_generic) \ + $(dwarf_SOURCES_generic) \ + dwarf/Gfind_proc_info-lsb.c \ + mips/Gcreate_addr_space.c mips/Gget_proc_info.c mips/Gget_save_loc.c \ + mips/Gglobal.c mips/Ginit.c mips/Ginit_local.c mips/Ginit_remote.c \ + mips/Gis_signal_frame.c mips/Gregs.c mips/Gresume.c mips/Gstep.c + # The list of files that go both into libunwind and libunwind-x86: libunwind_la_SOURCES_x86_common = $(libunwind_la_SOURCES_common) \ $(dwarf_SOURCES_common) \ @@ -289,6 +359,17 @@ if OS_HPUX libunwind_la_SOURCES_os_local = $(libunwind_la_SOURCES_os_hpux_local) endif +if ARCH_ARM + lib_LTLIBRARIES_arch = libunwind-arm.la + libunwind_la_SOURCES = $(libunwind_la_SOURCES_arm) + libunwind_arm_la_SOURCES = $(libunwind_arm_la_SOURCES_arm) + libunwind_arm_la_LDFLAGS = $(COMMON_SO_LDFLAGS) -version-info $(SOVERSION) +if !REMOTE_ONLY + libunwind_arm_la_LIBADD = libunwind.la -lc +endif + libunwind_setjmp_la_SOURCES = $(libunwind_setjmp_la_SOURCES_common) \ + $(libunwind_setjmp_la_SOURCES_arm) +else if ARCH_IA64 ia64_mk_Gcursor_i_SOURCES = ia64/mk_Gcursor_i.c ia64_mk_Lcursor_i_SOURCES = ia64/mk_Lcursor_i.c @@ -319,6 +400,17 @@ endif libunwind_setjmp_la_SOURCES = $(libunwind_setjmp_la_SOURCES_common) \ $(libunwind_setjmp_la_SOURCES_hppa) else +if ARCH_MIPS + lib_LTLIBRARIES_arch = libunwind-mips.la + libunwind_la_SOURCES = $(libunwind_la_SOURCES_mips) + libunwind_mips_la_SOURCES = $(libunwind_mips_la_SOURCES_mips) + libunwind_mips_la_LDFLAGS = $(COMMON_SO_LDFLAGS) -version-info $(SOVERSION) +if !REMOTE_ONLY + libunwind_mips_la_LIBADD = libunwind.la -lc +endif + libunwind_setjmp_la_SOURCES = $(libunwind_setjmp_la_SOURCES_common) \ + $(libunwind_setjmp_la_SOURCES_mips) +else if ARCH_X86 lib_LTLIBRARIES_arch = libunwind-x86.la libunwind_la_SOURCES = $(libunwind_la_SOURCES_x86) @@ -367,8 +459,10 @@ endif # ARCH_PPC64 endif # ARCH_PPC32 endif # ARCH_X86_64 endif # ARCH_X86 +endif # ARCH_MIPS endif # ARCH_HPPA endif # ARCH_IA64 +endif # ARCH_ARM # # Don't link with standard libraries, because those may mention @@ -386,22 +480,28 @@ AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/include/tdep-$(arch) -I. AM_CCASFLAGS = $(AM_CPPFLAGS) EXTRA_DIST = elfxx.h elfxx.c unwind/unwind-internal.h \ + $(libunwind_la_SOURCES_arm) \ $(libunwind_la_SOURCES_hppa) \ $(libunwind_la_SOURCES_ia64) \ + $(libunwind_la_SOURCES_mips) \ $(libunwind_la_SOURCES_x86) \ $(libunwind_la_SOURCES_os_linux) \ $(libunwind_la_SOURCES_os_hpux) \ $(libunwind_la_SOURCES_common) \ $(libunwind_la_SOURCES_local) \ $(libunwind_la_SOURCES_generic) \ + $(libunwind_arm_la_SOURCES_arm) \ $(libunwind_hppa_la_SOURCES_hppa) \ $(libunwind_ia64_la_SOURCES_ia64) \ + $(libunwind_mips_la_SOURCES_mips) \ $(libunwind_x86_la_SOURCES_x86) \ $(libunwind_x86_64_la_SOURCES_x86_64) \ $(libunwind_ptrace_a_SOURCES) \ $(libunwind_setjmp_la_SOURCES_common) \ + $(libunwind_setjmp_la_SOURCES_arm) \ $(libunwind_setjmp_la_SOURCES_hppa) \ $(libunwind_setjmp_la_SOURCES_ia64) \ + $(libunwind_setjmp_la_SOURCES_mips) \ $(libunwind_setjmp_la_SOURCES_x86) \ $(libunwind_setjmp_la_SOURCES_x86_64) \ $(libunwind_setjmp_la_SOURCES_ppc32) \ diff --git a/src/Makefile.in b/src/Makefile.in index 11f3c61..9c5950f 100644 diff --git a/src/arm/Gcreate_addr_space.c b/src/arm/Gcreate_addr_space.c new file mode 100644 index 0000000..c3e1360 --- /dev/null +++ b/src/arm/Gcreate_addr_space.c @@ -0,0 +1,59 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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 + +#include "unwind_i.h" + +PROTECTED unw_addr_space_t +unw_create_addr_space (unw_accessors_t *a, int byte_order) +{ +#ifdef UNW_LOCAL_ONLY + return NULL; +#else + unw_addr_space_t as = malloc (sizeof (*as)); + + if (!as) + return NULL; + + memset (as, 0, sizeof (*as)); + + as->acc = *a; + + /* + * ARM supports little-endian and big-endian. + */ + if (byte_order != 0 && byte_order != __LITTLE_ENDIAN + && byte_order != __BIG_ENDIAN) + return NULL; + + /* Default to little-endian for ARM. */ + if (byte_order == 0 || byte_order == __LITTLE_ENDIAN) + as->big_endian = 0; + else + as->big_endian = 1; + + return as; +#endif +} diff --git a/src/arm/Gget_proc_info.c b/src/arm/Gget_proc_info.c new file mode 100644 index 0000000..acb78a4 --- /dev/null +++ b/src/arm/Gget_proc_info.c @@ -0,0 +1,41 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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_get_proc_info (unw_cursor_t *cursor, unw_proc_info_t *pi) +{ + struct cursor *c = (struct cursor *) cursor; + int ret; + + /* We can only unwind using Dwarf into on ARM: return failure code + if it's not present. */ + ret = dwarf_make_proc_info (&c->dwarf); + if (ret < 0) + return ret; + + *pi = c->dwarf.pi; + return 0; +} diff --git a/src/arm/Gget_save_loc.c b/src/arm/Gget_save_loc.c new file mode 100644 index 0000000..ba7bf17 --- /dev/null +++ b/src/arm/Gget_save_loc.c @@ -0,0 +1,81 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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_get_save_loc (unw_cursor_t *cursor, int reg, unw_save_loc_t *sloc) +{ + struct cursor *c = (struct cursor *) cursor; + dwarf_loc_t loc; + + loc = DWARF_NULL_LOC; /* default to "not saved" */ + + switch (reg) + { + case UNW_ARM_R0: + case UNW_ARM_R1: + case UNW_ARM_R2: + case UNW_ARM_R3: + case UNW_ARM_R4: + case UNW_ARM_R5: + case UNW_ARM_R6: + case UNW_ARM_R7: + case UNW_ARM_R8: + case UNW_ARM_R9: + case UNW_ARM_R10: + case UNW_ARM_R11: + case UNW_ARM_R12: + case UNW_ARM_R13: + case UNW_ARM_R14: + case UNW_ARM_R15: + loc = c->dwarf.loc[reg - UNW_ARM_R0]; + break; + + default: + break; + } + + memset (sloc, 0, sizeof (sloc)); + + if (DWARF_IS_NULL_LOC (loc)) + { + sloc->type = UNW_SLT_NONE; + return 0; + } + +#if !defined(UNW_LOCAL_ONLY) + if (DWARF_IS_REG_LOC (loc)) + { + sloc->type = UNW_SLT_REG; + sloc->u.regnum = DWARF_GET_LOC (loc); + } + else +#endif + { + sloc->type = UNW_SLT_MEMORY; + sloc->u.addr = DWARF_GET_LOC (loc); + } + return 0; +} diff --git a/src/arm/Gglobal.c b/src/arm/Gglobal.c new file mode 100644 index 0000000..4037432 --- /dev/null +++ b/src/arm/Gglobal.c @@ -0,0 +1,66 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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 +#include "unwind_i.h" +#include "dwarf_i.h" + +HIDDEN pthread_mutex_t arm_lock = PTHREAD_MUTEX_INITIALIZER; +HIDDEN int tdep_needs_initialization = 1; + +/* 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. */ + +HIDDEN uint8_t dwarf_to_unw_regnum_map[16] = + { + R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15 + }; + +HIDDEN void +tdep_init (void) +{ + intrmask_t saved_mask; + + sigfillset (&unwi_full_mask); + + sigprocmask (SIG_SETMASK, &unwi_full_mask, &saved_mask); + mutex_lock (&arm_lock); + { + if (!tdep_needs_initialization) + /* another thread else beat us to it... */ + goto out; + + mi_init (); + + dwarf_init (); + +#ifndef UNW_REMOTE_ONLY + arm_local_addr_space_init (); +#endif + tdep_needs_initialization = 0; /* signal that we're initialized... */ + } + out: + mutex_unlock (&arm_lock); + sigprocmask (SIG_SETMASK, &saved_mask, NULL); +} diff --git a/src/arm/Ginit.c b/src/arm/Ginit.c new file mode 100644 index 0000000..c45a8ef --- /dev/null +++ b/src/arm/Ginit.c @@ -0,0 +1,207 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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 +#include + +#include "unwind_i.h" + +#ifdef UNW_REMOTE_ONLY + +/* unw_local_addr_space is a NULL pointer in this case. */ +PROTECTED unw_addr_space_t unw_local_addr_space; + +#else /* !UNW_REMOTE_ONLY */ + +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_ARM_R0: addr = &uc->uc_mcontext.arm_r0; break; + case UNW_ARM_R1: addr = &uc->uc_mcontext.arm_r1; break; + case UNW_ARM_R2: addr = &uc->uc_mcontext.arm_r2; break; + case UNW_ARM_R3: addr = &uc->uc_mcontext.arm_r3; break; + case UNW_ARM_R4: addr = &uc->uc_mcontext.arm_r4; break; + case UNW_ARM_R5: addr = &uc->uc_mcontext.arm_r5; break; + case UNW_ARM_R6: addr = &uc->uc_mcontext.arm_r6; break; + case UNW_ARM_R7: addr = &uc->uc_mcontext.arm_r7; break; + case UNW_ARM_R8: addr = &uc->uc_mcontext.arm_r8; break; + case UNW_ARM_R9: addr = &uc->uc_mcontext.arm_r9; break; + case UNW_ARM_R10: addr = &uc->uc_mcontext.arm_r10; break; + case UNW_ARM_R11: addr = &uc->uc_mcontext.arm_fp; break; + case UNW_ARM_R12: addr = &uc->uc_mcontext.arm_ip; break; + case UNW_ARM_R13: addr = &uc->uc_mcontext.arm_sp; break; + case UNW_ARM_R14: addr = &uc->uc_mcontext.arm_lr; break; + case UNW_ARM_R15: addr = &uc->uc_mcontext.arm_pc; 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); +} + +# endif /* UNW_LOCAL_ONLY */ + +HIDDEN unw_dyn_info_list_t _U_dyn_info_list; + +/* XXX fix me: there is currently no way to locate the dyn-info list + by a remote unwinder. On ia64, this is done via a special + unwind-table entry. Perhaps something similar can be done with + DWARF2 unwind info. */ + +static void +put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg) +{ + /* it's a no-op */ +} + +static int +get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr, + void *arg) +{ + *dyn_info_list_addr = (unw_word_t) &_U_dyn_info_list; + return 0; +} + +static int +access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, + void *arg) +{ + if (write) + { + Debug (16, "mem[%x] <- %x\n", addr, *val); + *(unw_word_t *) addr = *val; + } + else + { + *val = *(unw_word_t *) addr; + Debug (16, "mem[%x] -> %x\n", addr, *val); + } + return 0; +} + +static int +access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, + void *arg) +{ + unw_word_t *addr; + ucontext_t *uc = arg; + + if (unw_is_fpreg (reg)) + goto badreg; + +Debug (16, "reg = %s\n", unw_regname (reg)); + if (!(addr = uc_addr (uc, reg))) + goto badreg; + + if (write) + { + *(unw_word_t *) addr = *val; + Debug (12, "%s <- %x\n", unw_regname (reg), *val); + } + else + { + *val = *(unw_word_t *) addr; + Debug (12, "%s -> %x\n", unw_regname (reg), *val); + } + return 0; + + badreg: + Debug (1, "bad register number %u\n", reg); + return -UNW_EBADREG; +} + +static int +access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, + int write, void *arg) +{ + ucontext_t *uc = arg; + unw_fpreg_t *addr; + + if (!unw_is_fpreg (reg)) + goto badreg; + + if (!(addr = uc_addr (uc, reg))) + goto badreg; + + if (write) + { + Debug (12, "%s <- %08lx.%08lx.%08lx\n", unw_regname (reg), + ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]); + *(unw_fpreg_t *) addr = *val; + } + else + { + *val = *(unw_fpreg_t *) addr; + Debug (12, "%s -> %08lx.%08lx.%08lx\n", unw_regname (reg), + ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]); + } + return 0; + + badreg: + Debug (1, "bad register number %u\n", reg); + /* attempt to access a non-preserved register */ + return -UNW_EBADREG; +} + +static int +get_static_proc_name (unw_addr_space_t as, unw_word_t ip, + char *buf, size_t buf_len, unw_word_t *offp, + void *arg) +{ + return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp); +} + +HIDDEN void +arm_local_addr_space_init (void) +{ + memset (&local_addr_space, 0, sizeof (local_addr_space)); + local_addr_space.caching_policy = UNW_CACHE_GLOBAL; + local_addr_space.acc.find_proc_info = dwarf_find_proc_info; + local_addr_space.acc.put_unwind_info = put_unwind_info; + local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr; + local_addr_space.acc.access_mem = access_mem; + local_addr_space.acc.access_reg = access_reg; + local_addr_space.acc.access_fpreg = access_fpreg; + local_addr_space.acc.resume = 0; /* arm_local_resume? FIXME! */ + local_addr_space.acc.get_proc_name = get_static_proc_name; + unw_flush_cache (&local_addr_space, 0, 0); +} + +#endif /* !UNW_REMOTE_ONLY */ diff --git a/src/arm/Ginit_local.c b/src/arm/Ginit_local.c new file mode 100644 index 0000000..7b2881e --- /dev/null +++ b/src/arm/Ginit_local.c @@ -0,0 +1,53 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "unwind_i.h" +#include "init.h" + +#ifdef UNW_REMOTE_ONLY + +PROTECTED int +unw_init_local (unw_cursor_t *cursor, ucontext_t *uc) +{ + return -UNW_EINVAL; +} + +#else /* !UNW_REMOTE_ONLY */ + +PROTECTED int +unw_init_local (unw_cursor_t *cursor, ucontext_t *uc) +{ + struct cursor *c = (struct cursor *) cursor; + + if (tdep_needs_initialization) + tdep_init (); + + Debug (1, "(cursor=%p)\n", c); + + c->dwarf.as = unw_local_addr_space; + c->dwarf.as_arg = uc; + return common_init (c); +} + +#endif /* !UNW_REMOTE_ONLY */ diff --git a/src/arm/Ginit_remote.c b/src/arm/Ginit_remote.c new file mode 100644 index 0000000..3baf3f6 --- /dev/null +++ b/src/arm/Ginit_remote.c @@ -0,0 +1,45 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "init.h" +#include "unwind_i.h" + +PROTECTED int +unw_init_remote (unw_cursor_t *cursor, unw_addr_space_t as, void *as_arg) +{ +#ifdef UNW_LOCAL_ONLY + return -UNW_EINVAL; +#else /* !UNW_LOCAL_ONLY */ + struct cursor *c = (struct cursor *) cursor; + + if (tdep_needs_initialization) + tdep_init (); + + Debug (1, "(cursor=%p)\n", c); + + c->dwarf.as = as; + c->dwarf.as_arg = as_arg; + return common_init (c); +#endif /* !UNW_LOCAL_ONLY */ +} diff --git a/src/arm/Gis_signal_frame.c b/src/arm/Gis_signal_frame.c new file mode 100644 index 0000000..77548d5 --- /dev/null +++ b/src/arm/Gis_signal_frame.c @@ -0,0 +1,35 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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 +#include "unwind_i.h" + +/* FIXME for ARM. */ + +PROTECTED int +unw_is_signal_frame (unw_cursor_t *cursor) +{ + printf ("%s: implement me\n", __FUNCTION__); + return -UNW_ENOINFO; +} diff --git a/src/arm/Gregs.c b/src/arm/Gregs.c new file mode 100644 index 0000000..1546f58 --- /dev/null +++ b/src/arm/Gregs.c @@ -0,0 +1,81 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "unwind_i.h" + +HIDDEN int +tdep_access_reg (struct cursor *c, unw_regnum_t reg, unw_word_t *valp, + int write) +{ + dwarf_loc_t loc = DWARF_NULL_LOC; + + switch (reg) + { + case UNW_ARM_R0: + case UNW_ARM_R1: + case UNW_ARM_R2: + case UNW_ARM_R3: + case UNW_ARM_R4: + case UNW_ARM_R5: + case UNW_ARM_R6: + case UNW_ARM_R7: + case UNW_ARM_R8: + case UNW_ARM_R9: + case UNW_ARM_R10: + case UNW_ARM_R11: + case UNW_ARM_R12: + case UNW_ARM_R13: + case UNW_ARM_R14: + case UNW_ARM_R15: + loc = c->dwarf.loc[reg - UNW_ARM_R0]; + break; + + case UNW_ARM_CFA: + if (write) + return -UNW_EREADONLYREG; + *valp = c->dwarf.cfa; + return 0; + + /* FIXME: Initialise coprocessor & shadow registers? */ + + default: + Debug (1, "bad register number %u\n", reg); + return -UNW_EBADREG; + } + + if (write) + return dwarf_put (&c->dwarf, loc, *valp); + else + return dwarf_get (&c->dwarf, loc, valp); +} + +/* FIXME for ARM. */ + +HIDDEN int +tdep_access_fpreg (struct cursor *c, unw_regnum_t reg, unw_fpreg_t *valp, + int write) +{ + Debug (1, "bad register number %u\n", reg); + return -UNW_EBADREG; +} diff --git a/src/arm/Gresume.c b/src/arm/Gresume.c new file mode 100644 index 0000000..4db1738 --- /dev/null +++ b/src/arm/Gresume.c @@ -0,0 +1,45 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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. */ + +/* FIXME for ARM. */ + +#include + +#include "unwind_i.h" + +#ifndef UNW_REMOTE_ONLY + +HIDDEN inline int +arm_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg) +{ + return -UNW_EINVAL; +} + +#endif /* !UNW_REMOTE_ONLY */ + +PROTECTED int +unw_resume (unw_cursor_t *cursor) +{ + return -UNW_EINVAL; +} diff --git a/src/arm/Gstep.c b/src/arm/Gstep.c new file mode 100644 index 0000000..eff1fde --- /dev/null +++ b/src/arm/Gstep.c @@ -0,0 +1,48 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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_step (unw_cursor_t *cursor) +{ + struct cursor *c = (struct cursor *) cursor; + int ret; + + 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 (unlikely (ret == -UNW_ESTOPUNWIND)) + return ret; + + /* Dwarf unwinding didn't work, stop. */ + if (unlikely (ret < 0)) + return 0; + + return (c->dwarf.ip == 0) ? 0 : 1; +} diff --git a/src/arm/Lcreate_addr_space.c b/src/arm/Lcreate_addr_space.c new file mode 100644 index 0000000..0f2dc6b --- /dev/null +++ b/src/arm/Lcreate_addr_space.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gcreate_addr_space.c" +#endif diff --git a/src/arm/Lget_proc_info.c b/src/arm/Lget_proc_info.c new file mode 100644 index 0000000..69028b0 --- /dev/null +++ b/src/arm/Lget_proc_info.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gget_proc_info.c" +#endif diff --git a/src/arm/Lget_save_loc.c b/src/arm/Lget_save_loc.c new file mode 100644 index 0000000..9ea048a --- /dev/null +++ b/src/arm/Lget_save_loc.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gget_save_loc.c" +#endif diff --git a/src/arm/Lglobal.c b/src/arm/Lglobal.c new file mode 100644 index 0000000..6d7b489 --- /dev/null +++ b/src/arm/Lglobal.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gglobal.c" +#endif diff --git a/src/arm/Linit.c b/src/arm/Linit.c new file mode 100644 index 0000000..e9abfdd --- /dev/null +++ b/src/arm/Linit.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Ginit.c" +#endif diff --git a/src/arm/Linit_local.c b/src/arm/Linit_local.c new file mode 100644 index 0000000..68a1687 --- /dev/null +++ b/src/arm/Linit_local.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Ginit_local.c" +#endif diff --git a/src/arm/Linit_remote.c b/src/arm/Linit_remote.c new file mode 100644 index 0000000..58cb04a --- /dev/null +++ b/src/arm/Linit_remote.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Ginit_remote.c" +#endif diff --git a/src/arm/Lis_signal_frame.c b/src/arm/Lis_signal_frame.c new file mode 100644 index 0000000..b9a7c4f --- /dev/null +++ b/src/arm/Lis_signal_frame.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gis_signal_frame.c" +#endif diff --git a/src/arm/Lregs.c b/src/arm/Lregs.c new file mode 100644 index 0000000..2c9c75c --- /dev/null +++ b/src/arm/Lregs.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gregs.c" +#endif diff --git a/src/arm/Lresume.c b/src/arm/Lresume.c new file mode 100644 index 0000000..41a8cf0 --- /dev/null +++ b/src/arm/Lresume.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gresume.c" +#endif diff --git a/src/arm/Lstep.c b/src/arm/Lstep.c new file mode 100644 index 0000000..c1ac3c7 --- /dev/null +++ b/src/arm/Lstep.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gstep.c" +#endif diff --git a/src/arm/gen-offsets.c b/src/arm/gen-offsets.c new file mode 100644 index 0000000..7d6bf2f --- /dev/null +++ b/src/arm/gen-offsets.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include + +#define UC(N,X) \ + printf ("#define LINUX_UC_" N "_OFF\t0x%X\n", offsetof (ucontext_t, X)) + +#define SC(N,X) \ + printf ("#define LINUX_SC_" N "_OFF\t0x%X\n", offsetof (struct sigcontext, X)) + +int +main (void) +{ + printf ( +"/* Linux-specific definitions: */\n\n" + +"/* Define various structure offsets to simplify cross-compilation. */\n\n" + +"/* Offsets for ARM Linux \"ucontext_t\": */\n\n"); + + UC ("FLAGS", uc_flags); + UC ("LINK", uc_link); + UC ("STACK", uc_stack); + UC ("MCONTEXT", uc_mcontext); + UC ("SIGMASK", uc_sigmask); + UC ("REGSPACE", uc_regspace); + + printf ("\n/* Offsets for ARM Linux \"struct sigcontext\": */\n\n"); + + SC ("TRAPNO", trap_no); + SC ("ERRORCODE", error_code); + SC ("OLDMASK", oldmask); + SC ("R0", arm_r0); + SC ("R1", arm_r1); + SC ("R2", arm_r2); + SC ("R3", arm_r3); + SC ("R4", arm_r4); + SC ("R5", arm_r5); + SC ("R6", arm_r6); + SC ("R7", arm_r7); + SC ("R8", arm_r8); + SC ("R9", arm_r9); + SC ("R10", arm_r10); + SC ("FP", arm_fp); + SC ("IP", arm_ip); + SC ("SP", arm_sp); + SC ("LR", arm_lr); + SC ("PC", arm_pc); + SC ("CPSR", arm_cpsr); + SC ("FAULTADDR", fault_address); + + return 0; +} diff --git a/src/arm/getcontext.S b/src/arm/getcontext.S new file mode 100644 index 0000000..3b9abda --- /dev/null +++ b/src/arm/getcontext.S @@ -0,0 +1,52 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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" + + .text + .arm + + .global _Uarm_getcontext + .type _Uarm_getcontext, %function + @ This is a stub version of getcontext() for ARM which only stores core + @ registers. It must be called in a special way, not as a regular + @ function -- see also the libunwind-arm.h:unw_tdep_getcontext macro. +_Uarm_getcontext: + stmfd sp!, {r0, r1} + @ store r0 + str r0, [r0, #LINUX_UC_MCONTEXT_OFF + LINUX_SC_R0_OFF] + add r0, r0, #LINUX_UC_MCONTEXT_OFF + LINUX_SC_R0_OFF + @ store r1 to r12 + stmib r0, {r1-r12} + @ reconstruct r13 at call site, then store + add r1, sp, #12 + str r1, [r0, #13 * 4] + @ retrieve r14 from call site, then store + ldr r1, [sp, #8] + str r1, [r0, #14 * 4] + @ point lr to instruction after call site's stack adjustment + add r1, lr, #4 + str r1, [r0, #15 * 4] + ldmfd sp!, {r0, r1} + bx lr diff --git a/src/arm/init.h b/src/arm/init.h new file mode 100644 index 0000000..5b3f927 --- /dev/null +++ b/src/arm/init.h @@ -0,0 +1,71 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "unwind_i.h" + +static inline int +common_init (struct cursor *c) +{ + int ret, i; + + c->dwarf.loc[R0] = DWARF_REG_LOC (&c->dwarf, UNW_ARM_R0); + c->dwarf.loc[R1] = DWARF_REG_LOC (&c->dwarf, UNW_ARM_R1); + c->dwarf.loc[R2] = DWARF_REG_LOC (&c->dwarf, UNW_ARM_R2); + c->dwarf.loc[R3] = DWARF_REG_LOC (&c->dwarf, UNW_ARM_R3); + c->dwarf.loc[R4] = DWARF_REG_LOC (&c->dwarf, UNW_ARM_R4); + c->dwarf.loc[R5] = DWARF_REG_LOC (&c->dwarf, UNW_ARM_R5); + c->dwarf.loc[R6] = DWARF_REG_LOC (&c->dwarf, UNW_ARM_R6); + c->dwarf.loc[R7] = DWARF_REG_LOC (&c->dwarf, UNW_ARM_R7); + c->dwarf.loc[R8] = DWARF_REG_LOC (&c->dwarf, UNW_ARM_R8); + c->dwarf.loc[R9] = DWARF_REG_LOC (&c->dwarf, UNW_ARM_R9); + c->dwarf.loc[R10] = DWARF_REG_LOC (&c->dwarf, UNW_ARM_R10); + c->dwarf.loc[R11] = DWARF_REG_LOC (&c->dwarf, UNW_ARM_R11); + c->dwarf.loc[R12] = DWARF_REG_LOC (&c->dwarf, UNW_ARM_R12); + c->dwarf.loc[R13] = DWARF_REG_LOC (&c->dwarf, UNW_ARM_R13); + c->dwarf.loc[R14] = DWARF_REG_LOC (&c->dwarf, UNW_ARM_R14); + c->dwarf.loc[R15] = DWARF_REG_LOC (&c->dwarf, UNW_ARM_R15); + for (i = R15 + 1; i < DWARF_NUM_PRESERVED_REGS; ++i) + c->dwarf.loc[i] = DWARF_NULL_LOC; + + ret = dwarf_get (&c->dwarf, c->dwarf.loc[R15], &c->dwarf.ip); + if (ret < 0) + return ret; + + /* FIXME: correct for ARM? */ + ret = dwarf_get (&c->dwarf, DWARF_REG_LOC (&c->dwarf, UNW_ARM_R13), + &c->dwarf.cfa); + if (ret < 0) + return ret; + + /* FIXME: Initialisation for other registers. */ + + c->dwarf.args_size = 0; + c->dwarf.ret_addr_column = 0; + c->dwarf.pi_valid = 0; + c->dwarf.pi_is_dynamic = 0; + c->dwarf.hint = 0; + c->dwarf.prev_rs = 0; + + return 0; +} diff --git a/src/arm/is_fpreg.c b/src/arm/is_fpreg.c new file mode 100644 index 0000000..d8b17ae --- /dev/null +++ b/src/arm/is_fpreg.c @@ -0,0 +1,39 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "libunwind_i.h" + +/* FIXME: I'm not sure if libunwind's GP/FP register distinction is very useful + on ARM. Count all the FP or coprocessor registers we know about for now. */ + +PROTECTED int +unw_is_fpreg (int regnum) +{ + return ((regnum >= UNW_ARM_S0 && regnum <= UNW_ARM_S31) + || (regnum >= UNW_ARM_F0 && regnum <= UNW_ARM_F7) + || (regnum >= UNW_ARM_wCGR0 && regnum <= UNW_ARM_wCGR7) + || (regnum >= UNW_ARM_wR0 && regnum <= UNW_ARM_wR15) + || (regnum >= UNW_ARM_wC0 && regnum <= UNW_ARM_wC7) + || (regnum >= UNW_ARM_D0 && regnum <= UNW_ARM_D31)); +} diff --git a/src/arm/offsets.h b/src/arm/offsets.h new file mode 100644 index 0000000..0593685 --- /dev/null +++ b/src/arm/offsets.h @@ -0,0 +1,36 @@ +/* Linux-specific definitions: */ + +/* Define various structure offsets to simplify cross-compilation. */ + +/* Offsets for ARM Linux "ucontext_t": */ + +#define LINUX_UC_FLAGS_OFF 0x00 +#define LINUX_UC_LINK_OFF 0x04 +#define LINUX_UC_STACK_OFF 0x08 +#define LINUX_UC_MCONTEXT_OFF 0x14 +#define LINUX_UC_SIGMASK_OFF 0x68 +#define LINUX_UC_REGSPACE_OFF 0xE8 + +/* Offsets for ARM Linux "struct sigcontext": */ + +#define LINUX_SC_TRAPNO_OFF 0x00 +#define LINUX_SC_ERRORCODE_OFF 0x04 +#define LINUX_SC_OLDMASK_OFF 0x08 +#define LINUX_SC_R0_OFF 0x0C +#define LINUX_SC_R1_OFF 0x10 +#define LINUX_SC_R2_OFF 0x14 +#define LINUX_SC_R3_OFF 0x18 +#define LINUX_SC_R4_OFF 0x1C +#define LINUX_SC_R5_OFF 0x20 +#define LINUX_SC_R6_OFF 0x24 +#define LINUX_SC_R7_OFF 0x28 +#define LINUX_SC_R8_OFF 0x2C +#define LINUX_SC_R9_OFF 0x30 +#define LINUX_SC_R10_OFF 0x34 +#define LINUX_SC_FP_OFF 0x38 +#define LINUX_SC_IP_OFF 0x3C +#define LINUX_SC_SP_OFF 0x40 +#define LINUX_SC_LR_OFF 0x44 +#define LINUX_SC_PC_OFF 0x48 +#define LINUX_SC_CPSR_OFF 0x4C +#define LINUX_SC_FAULTADDR_OFF 0x50 diff --git a/src/arm/regname.c b/src/arm/regname.c new file mode 100644 index 0000000..474337a --- /dev/null +++ b/src/arm/regname.c @@ -0,0 +1,90 @@ +#include "unwind_i.h" + +static const char *regname[] = + { + /* 0. */ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + /* 8. */ + "r8", "r9", "r10", "fp", "ip", "sp", "lr", "pc", + /* 16. Obsolete FPA names. */ + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + /* 24. */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 32. */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 40. */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 48. */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 56. */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 64. */ + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + /* 72. */ + "s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15", + /* 80. */ + "s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23", + /* 88. */ + "s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31", + /* 96. */ + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + /* 104. */ + "wCGR0", "wCGR1", "wCGR2", "wCGR3", "wCGR4", "wCGR5", "wCGR6", "wCGR7", + /* 112. */ + "wR0", "wR1", "wR2", "wR3", "wR4", "wR5", "wR6", "wR7", + /* 128. */ + "spsr", "spsr_fiq", "spsr_irq", "spsr_abt", "spsr_und", "spsr_svc", 0, 0, + /* 136. */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 144. */ + "r8_usr", "r9_usr", "r10_usr", "r11_usr", "r12_usr", "r13_usr", "r14_usr", + /* 151. */ + "r8_fiq", "r9_fiq", "r10_fiq", "r11_fiq", "r12_fiq", "r13_fiq", "r14_fiq", + /* 158. */ + "r13_irq", "r14_irq", + /* 160. */ + "r13_abt", "r14_abt", + /* 162. */ + "r13_und", "r14_und", + /* 164. */ + "r13_svc", "r14_svc", 0, 0, + /* 168. */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 176. */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 184. */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 192. */ + "wC0", "wC1", "wC2", "wC3", "wC4", "wC5", "wC6", "wC7", + /* 200. */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 208. */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 216. */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 224. */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 232. */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 240. */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 248. */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 256. */ + "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", + /* 264. */ + "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", + /* 272. */ + "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", + /* 280. */ + "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31", + }; + +PROTECTED const char * +unw_regname (unw_regnum_t reg) +{ + if (reg < (unw_regnum_t) ARRAY_SIZE (regname)) + return regname[reg]; + else + return "???"; +} diff --git a/src/arm/siglongjmp.S b/src/arm/siglongjmp.S new file mode 100644 index 0000000..87f939e --- /dev/null +++ b/src/arm/siglongjmp.S @@ -0,0 +1,8 @@ + /* Dummy implementation for now. */ + + .globl _UI_siglongjmp_cont + .globl _UI_longjmp_cont + +_UI_siglongjmp_cont: +_UI_longjmp_cont: + bx lr diff --git a/src/arm/unwind_i.h b/src/arm/unwind_i.h new file mode 100644 index 0000000..f0949fa --- /dev/null +++ b/src/arm/unwind_i.h @@ -0,0 +1,39 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef unwind_i_h +#define unwind_i_h + +#include +#include + +#include + +#include "libunwind_i.h" + +#define arm_local_addr_space_init UNW_OBJ(local_addr_space_init) + +extern void arm_local_addr_space_init (void); + +#endif /* unwind_i_h */ diff --git a/src/dwarf/Gexpr.c b/src/dwarf/Gexpr.c index b62c4df..4a01215 100644 --- a/src/dwarf/Gexpr.c +++ b/src/dwarf/Gexpr.c @@ -47,10 +47,11 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define ULEB128 0x4 #define SLEB128 0x5 #define OFFSET 0x6 /* 32-bit offset for 32-bit DWARF, 64-bit otherwise */ +#define ADDR 0x7 /* Machine address. */ static uint8_t operands[256] = { - [DW_OP_addr] = OPND1 (sizeof (unw_word_t) == 4 ? VAL32 : VAL64), + [DW_OP_addr] = OPND1 (ADDR), [DW_OP_const1u] = OPND1 (VAL8), [DW_OP_const1s] = OPND1 (VAL8), [DW_OP_const2u] = OPND1 (VAL16), @@ -106,7 +107,18 @@ static uint8_t operands[256] = [DW_OP_call_ref] = OPND1 (OFFSET) }; -#define sword(X) ((unw_sword_t) (X)) +static inline unw_sword_t +sword (unw_addr_space_t as, unw_word_t val) +{ + switch (dwarf_addr_size (as)) + { + case 1: return (int8_t) val; + case 2: return (int16_t) val; + case 4: return (int32_t) val; + case 8: return (int64_t) val; + default: abort (); + } +} static inline unw_word_t read_operand (unw_addr_space_t as, unw_accessors_t *a, @@ -118,25 +130,43 @@ read_operand (unw_addr_space_t as, unw_accessors_t *a, uint64_t u64; int ret; + if (operand_type == ADDR) + switch (dwarf_addr_size (as)) + { + case 1: operand_type = VAL8; break; + case 2: operand_type = VAL16; break; + case 4: operand_type = VAL32; break; + case 8: operand_type = VAL64; break; + default: abort (); + } + switch (operand_type) { case VAL8: ret = dwarf_readu8 (as, a, addr, &u8, arg); + if (ret < 0) + return ret; *val = u8; break; case VAL16: ret = dwarf_readu16 (as, a, addr, &u16, arg); + if (ret < 0) + return ret; *val = u16; break; case VAL32: ret = dwarf_readu32 (as, a, addr, &u32, arg); + if (ret < 0) + return ret; *val = u32; break; case VAL64: ret = dwarf_readu64 (as, a, addr, &u64, arg); + if (ret < 0) + return ret; *val = u64; break; @@ -345,8 +375,10 @@ do { \ tmp1 = pop (); switch (operand1) { - case 0: - break; + default: + Debug (1, "Unexpected DW_OP_deref_size size %d\n", + (int) operand1); + return -UNW_EINVAL; case 1: if ((ret = dwarf_readu8 (as, a, &tmp1, &u8, arg)) < 0) @@ -379,7 +411,7 @@ do { \ case 8: if ((ret = dwarf_readu64 (as, a, &tmp1, &u64, arg)) < 0) return ret; - tmp2 = u16; + tmp2 = u64; if (operand1 != 8) { if (dwarf_is_big_endian (as)) @@ -433,7 +465,7 @@ do { \ case DW_OP_abs: Debug (15, "OP_abs\n"); tmp1 = pop (); - if (tmp1 & ((unw_word_t) 1 << (8 * sizeof (unw_word_t) - 1))) + if (tmp1 & ((unw_word_t) 1 << (8 * dwarf_addr_size (as) - 1))) tmp1 = -tmp1; push (tmp1); break; @@ -450,7 +482,7 @@ do { \ tmp1 = pop (); tmp2 = pop (); if (tmp1) - tmp1 = sword (tmp2) / sword (tmp1); + tmp1 = sword (as, tmp2) / sword (as, tmp1); push (tmp1); break; @@ -528,7 +560,7 @@ do { \ Debug (15, "OP_shra\n"); tmp1 = pop (); tmp2 = pop (); - push (sword (tmp2) >> tmp1); + push (sword (as, tmp2) >> tmp1); break; case DW_OP_xor: @@ -542,42 +574,42 @@ do { \ Debug (15, "OP_le\n"); tmp1 = pop (); tmp2 = pop (); - push (sword (tmp1) <= sword (tmp2)); + push (sword (as, tmp1) <= sword (as, tmp2)); break; case DW_OP_ge: Debug (15, "OP_ge\n"); tmp1 = pop (); tmp2 = pop (); - push (sword (tmp1) >= sword (tmp2)); + push (sword (as, tmp1) >= sword (as, tmp2)); break; case DW_OP_eq: Debug (15, "OP_eq\n"); tmp1 = pop (); tmp2 = pop (); - push (sword (tmp1) == sword (tmp2)); + push (sword (as, tmp1) == sword (as, tmp2)); break; case DW_OP_lt: Debug (15, "OP_lt\n"); tmp1 = pop (); tmp2 = pop (); - push (sword (tmp1) < sword (tmp2)); + push (sword (as, tmp1) < sword (as, tmp2)); break; case DW_OP_gt: Debug (15, "OP_gt\n"); tmp1 = pop (); tmp2 = pop (); - push (sword (tmp1) > sword (tmp2)); + push (sword (as, tmp1) > sword (as, tmp2)); break; case DW_OP_ne: Debug (15, "OP_ne\n"); tmp1 = pop (); tmp2 = pop (); - push (sword (tmp1) != sword (tmp2)); + push (sword (as, tmp1) != sword (as, tmp2)); break; case DW_OP_skip: diff --git a/src/dwarf/Gfde.c b/src/dwarf/Gfde.c index 8a85685..fb46a78 100644 --- a/src/dwarf/Gfde.c +++ b/src/dwarf/Gfde.c @@ -26,12 +26,15 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "dwarf_i.h" static inline int -is_cie_id (unw_word_t val) +is_cie_id (unw_word_t val, int is_debug_frame) { - /* DWARF spec says CIE_id is 0xffffffff (for 32-bit ELF) or - 0xffffffffffffffff (for 64-bit ELF). However, the GNU toolchain + /* The CIE ID is normally 0xffffffff (for 32-bit ELF) or + 0xffffffffffffffff (for 64-bit ELF). However, .eh_frame uses 0. */ - return (val == 0 || val == - (unw_word_t) 1); + if (is_debug_frame) + return (val == - (uint32_t) 1 || val == - (uint64_t) 1); + else + return (val == 0); } /* Note: we don't need to keep track of more than the first four @@ -41,7 +44,8 @@ is_cie_id (unw_word_t val) repeated. */ static inline int parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr, - const unw_proc_info_t *pi, struct dwarf_cie_info *dci, void *arg) + const unw_proc_info_t *pi, struct dwarf_cie_info *dci, + unw_word_t base, void *arg) { uint8_t version, ch, augstr[5], fde_encoding, handler_encoding; unw_word_t len, cie_end_addr, aug_size; @@ -57,7 +61,7 @@ parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr, "address-unit sized constants". The `R' augmentation can be used to override this, but by default, we pick an address-sized unit for fde_encoding. */ - switch (sizeof (unw_word_t)) + switch (dwarf_addr_size (as)) { case 4: fde_encoding = DW_EH_PE_udata4; break; case 8: fde_encoding = DW_EH_PE_udata8; break; @@ -74,13 +78,14 @@ parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr, { /* the CIE is in the 32-bit DWARF format */ uint32_t cie_id; + /* DWARF says CIE id should be 0xffffffff, but in .eh_frame, it's 0 */ + const uint32_t expected_id = (base) ? 0xffffffff : 0; len = u32val; cie_end_addr = addr + len; if ((ret = dwarf_readu32 (as, a, &addr, &cie_id, arg)) < 0) return ret; - /* DWARF says CIE id should be 0xffffffff, but in .eh_frame, it's 0 */ - if (cie_id != 0) + if (cie_id != expected_id) { Debug (1, "Unexpected CIE id %x\n", cie_id); return -UNW_EINVAL; @@ -90,6 +95,9 @@ parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr, { /* the CIE is in the 64-bit DWARF format */ uint64_t cie_id; + /* DWARF says CIE id should be 0xffffffffffffffff, but in + .eh_frame, it's 0 */ + const uint64_t expected_id = (base) ? 0xffffffffffffffffull : 0; if ((ret = dwarf_readu64 (as, a, &addr, &u64val, arg)) < 0) return ret; @@ -97,9 +105,7 @@ parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr, cie_end_addr = addr + len; if ((ret = dwarf_readu64 (as, a, &addr, &cie_id, arg)) < 0) return ret; - /* DWARF says CIE id should be 0xffffffffffffffff, but in - .eh_frame, it's 0 */ - if (cie_id != 0) + if (cie_id != expected_id) { Debug (1, "Unexpected CIE id %llx\n", (long long) cie_id); return -UNW_EINVAL; @@ -146,14 +152,16 @@ parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr, arg)) < 0) return ret; + i = 0; if (augstr[0] == 'z') { dci->sized_augmentation = 1; if ((ret = dwarf_read_uleb128 (as, a, &addr, &aug_size, arg)) < 0) return ret; + i++; } - for (i = 1; i < sizeof (augstr) && augstr[i]; ++i) + for (; i < sizeof (augstr) && augstr[i]; ++i) switch (augstr[i]) { case 'L': @@ -185,16 +193,15 @@ parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr, break; default: + Debug (1, "Unexpected augmentation string `%s'\n", augstr); if (dci->sized_augmentation) /* If we have the size of the augmentation body, we can skip over the parts that we don't understand, so we're OK. */ - return 0; + goto done; else - { - Debug (1, "Unexpected augmentation string `%s'\n", augstr); - return -UNW_EINVAL; - } + return -UNW_EINVAL; } + done: dci->fde_encoding = fde_encoding; dci->cie_instr_start = addr; Debug (15, "CIE parsed OK, augmentation = \"%s\", handler=0x%lx\n", @@ -202,12 +209,15 @@ parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr, return 0; } -/* Extract proc-info from the FDE starting at adress ADDR. */ +/* Extract proc-info from the FDE starting at adress ADDR. + + Pass BASE as zero for eh_frame behaviour, or a pointer to + debug_frame base for debug_frame behaviour. */ HIDDEN int dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addrp, unw_proc_info_t *pi, - int need_unwind_info, + int need_unwind_info, unw_word_t base, void *arg) { unw_word_t fde_end_addr, cie_addr, cie_offset_addr, aug_end_addr = 0; @@ -226,7 +236,7 @@ dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a, if (u32val != 0xffffffff) { - uint32_t cie_offset; + int32_t cie_offset; /* In some configurations, an FDE with a 0 length indicates the end of the FDE-table. */ @@ -241,19 +251,22 @@ dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a, if ((ret = dwarf_reads32 (as, a, &addr, &cie_offset, arg)) < 0) return ret; - if (is_cie_id (cie_offset)) + if (is_cie_id (cie_offset, base != 0)) /* ignore CIEs (happens during linear searches) */ return 0; - /* DWARF says that the CIE_pointer in the FDE is a - .debug_frame-relative offset, but the GCC-generated .eh_frame - sections instead store a "pcrelative" offset, which is just - as fine as it's self-contained. */ - cie_addr = cie_offset_addr - cie_offset; + if (base != 0) + cie_addr = base + cie_offset; + else + /* DWARF says that the CIE_pointer in the FDE is a + .debug_frame-relative offset, but the GCC-generated .eh_frame + sections instead store a "pcrelative" offset, which is just + as fine as it's self-contained. */ + cie_addr = cie_offset_addr - cie_offset; } else { - uint64_t cie_offset; + int64_t cie_offset; /* the FDE is in the 64-bit DWARF format */ @@ -266,18 +279,23 @@ dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a, if ((ret = dwarf_reads64 (as, a, &addr, &cie_offset, arg)) < 0) return ret; - if (is_cie_id (cie_offset)) + if (is_cie_id (cie_offset, base != 0)) /* ignore CIEs (happens during linear searches) */ return 0; - /* DWARF says that the CIE_pointer in the FDE is a - .debug_frame-relative offset, but the GCC-generated .eh_frame - sections instead store a "pcrelative" offset, which is just - as fine as it's self-contained. */ - cie_addr = (unw_word_t) ((uint64_t) cie_offset_addr - cie_offset); + if (base != 0) + cie_addr = base + cie_offset; + else + /* DWARF says that the CIE_pointer in the FDE is a + .debug_frame-relative offset, but the GCC-generated .eh_frame + sections instead store a "pcrelative" offset, which is just + as fine as it's self-contained. */ + cie_addr = (unw_word_t) ((uint64_t) cie_offset_addr - cie_offset); } - if ((ret = parse_cie (as, a, cie_addr, pi, &dci, arg)) < 0) + Debug (15, "looking for CIE at address %x\n", (int) cie_addr); + + if ((ret = parse_cie (as, a, cie_addr, pi, &dci, base, arg)) < 0) return ret; /* IP-range has same encoding as FDE pointers, except that it's diff --git a/src/dwarf/Gfind_proc_info-lsb.c b/src/dwarf/Gfind_proc_info-lsb.c index a8a3b69..d3d5fe5 100644 --- a/src/dwarf/Gfind_proc_info-lsb.c +++ b/src/dwarf/Gfind_proc_info-lsb.c @@ -28,6 +28,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include +#include +#include #include "dwarf_i.h" #include "dwarf-eh.h" @@ -41,6 +43,10 @@ struct table_entry #ifndef UNW_REMOTE_ONLY +#ifdef __linux +#include "os-linux.h" +#endif + struct callback_data { /* in: */ @@ -50,6 +56,7 @@ struct callback_data /* out: */ int single_fde; /* did we find a single FDE? (vs. a table) */ unw_dyn_info_t di; /* table info (if single_fde is false) */ + unw_dyn_info_t di_debug; /* additional table info for .debug_frame */ }; static int @@ -65,7 +72,7 @@ linear_search (unw_addr_space_t as, unw_word_t ip, while (i++ < fde_count && addr < eh_frame_end) { fde_addr = addr; - if ((ret = dwarf_extract_proc_info_from_fde (as, a, &addr, pi, 0, arg)) + if ((ret = dwarf_extract_proc_info_from_fde (as, a, &addr, pi, 0, 0, arg)) < 0) return ret; @@ -75,7 +82,8 @@ linear_search (unw_addr_space_t as, unw_word_t ip, return 1; addr = fde_addr; if ((ret = dwarf_extract_proc_info_from_fde (as, a, &addr, pi, - need_unwind_info, arg)) + need_unwind_info, 0, + arg)) < 0) return ret; return 1; @@ -84,8 +92,316 @@ linear_search (unw_addr_space_t as, unw_word_t ip, return -UNW_ENOINFO; } -/* Info is a pointer to a unw_dyn_info_t structure and, on entry, - member u.rti.segbase contains the instruction-pointer we're looking +/* Load .debug_frame section from FILE. Allocates and returns space + in *BUF, and sets *BUFSIZE to its size. IS_LOCAL is 1 if using the + local process, in which case we can search the system debug file + directory; 0 for other address spaces, in which case we do not; or + -1 for recursive calls following .gnu_debuglink. Returns 0 on + success, 1 on error. Succeeds even if the file contains no + .debug_frame. */ +/* XXX: Could use mmap; but elf_map_image keeps tons mapped in. */ + +static int +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; + unsigned int i; + size_t linksize = 0; + char *linkbuf = NULL; + + *buf = NULL; + *bufsize = 0; + + f = fopen (file, "r"); + + if (!f) + return 1; + + fread (&ehdr, sizeof (Elf_W (Ehdr)), 1, f); + + shstrndx = ehdr.e_shstrndx; + + Debug (4, "opened file '%s'. Section header at offset %d\n", + file, (int) ehdr.e_shoff); + + 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); + + Debug (4, "loading string table of size %d\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); + + for (i = 1; i < ehdr.e_shnum && *buf == NULL; i++) + { + char *secname = &stringtab[sec_hdrs[i].sh_name]; + + if (strcmp (secname, ".debug_frame") == 0) + { + *bufsize = sec_hdrs[i].sh_size; + *buf = malloc (*bufsize); + + fseek (f, sec_hdrs[i].sh_offset, SEEK_SET); + fread (*buf, 1, *bufsize, f); + + Debug (4, "read %d bytes of .debug_frame from offset %d\n", + *bufsize, sec_hdrs[i].sh_offset); + } + else if (is_local >= 0 && 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); + + Debug (4, "read %d bytes of .gnu_debuglink from offset %d\n", + *bufsize, sec_hdrs[i].sh_offset); + } + } + + free (stringtab); + free (sec_hdrs); + + fclose (f); + + if (*buf == NULL && linkbuf != NULL && memchr (linkbuf, 0, linksize) != NULL) + { + char *newname, *basedir, *p; + static const char *debugdir = "/usr/lib/debug"; + int ret; + + /* XXX: Don't bother with the checksum; just search for the file. */ + basedir = malloc (strlen (file) + 1); + newname = malloc (strlen (linkbuf) + strlen (debugdir) + + strlen (file) + 9); + + p = strrchr (file, '/'); + if (p != NULL) + { + memcpy (basedir, file, p - file); + basedir[p - file] = '\0'; + } + else + basedir[0] = 0; + + strcpy (newname, basedir); + strcat (newname, "/"); + strcat (newname, linkbuf); + ret = load_debug_frame (newname, buf, bufsize, -1); + + if (ret == 1) + { + strcpy (newname, basedir); + strcat (newname, "/.debug/"); + strcat (newname, linkbuf); + ret = load_debug_frame (newname, buf, bufsize, -1); + } + + if (ret == 1 && is_local == 1) + { + strcpy (newname, debugdir); + strcat (newname, basedir); + strcat (newname, "/"); + strcat (newname, linkbuf); + ret = load_debug_frame (newname, buf, bufsize, -1); + } + + free (basedir); + free (newname); + } + free (linkbuf); + + return 0; +} + +/* Locate the binary which originated the contents of address ADDR. Return + the name of the binary in *name (which is allocated on the heap, and must + be freed by the caller). Returns 0 if a binary is successfully found, or 1 + if an error occurs. */ + +static int +find_binary_for_address (unw_word_t ip, char **name) +{ +#ifdef __linux + struct map_iterator mi; + char path[PATH_MAX]; + int found = 0; + int pid = getpid (); + unsigned long segbase, mapoff, hi; + + maps_init (&mi, pid); + while (maps_next (&mi, &segbase, &hi, &mapoff, path, sizeof (path))) + if (ip >= segbase && ip < hi) + { + found = 1; + break; + } + maps_close (&mi); + + if (found) + { + *name = strdup (path); + return 0; + } +#endif + + return 1; +} + +/* Locate and/or try to load a debug_frame section for address ADDR. Return + pointer to debug frame descriptor, or zero if not found. */ + +static struct unw_debug_frame_list * +locate_debug_info (unw_addr_space_t as, struct dl_phdr_info *info, + unw_word_t addr, const char *dlname) +{ + struct unw_debug_frame_list *w, *fdesc = 0; + char *name = 0; + int err; + uint64_t start = 0, end = 0; + char *buf; + size_t bufsize; + unsigned int i; + + /* First, see if we loaded this frame already. */ + + for (w = as->debug_frames; w; w = w->next) + { + Debug (4, "checking %p: %x-%x\n", w, (int)w->start, (int)w->end); + if (addr >= w->start && addr < w->end) + return w; + } + + /* If the object name we receive is blank, there's still a chance of locating + the file by parsing /proc/self/maps. */ + + if (strcmp (dlname, "") == 0) + { + err = find_binary_for_address (addr, &name); + if (err) + { + Debug (15, "tried to locate binary for 0x%" PRIx64 ", but no luck\n", + (uint64_t) addr); + return 0; + } + } + else + name = (char*) dlname; + + /* Find the start/end of the described region by parsing the + dl_phdr_info structure. */ + + start = info->dlpi_addr + info->dlpi_phdr[0].p_vaddr; + end = start; + + for (i = 0; i < info->dlpi_phnum; i++) + { + Elf_W (Addr) hdrbase = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr; + Elf_W (Addr) hdrlimit = hdrbase + info->dlpi_phdr[i].p_memsz; + + if (info->dlpi_phdr[i].p_type != PT_LOAD) + continue; + + if (hdrbase < start) + start = hdrbase; + if (hdrlimit > end) + end = hdrlimit; + } + + Debug (4, "calculated bounds of %x-%x for '%s'\n", (int)start, (int)end, + name); + + err = load_debug_frame (name, &buf, &bufsize, as == unw_local_addr_space); + + if (!err) + { + fdesc = malloc (sizeof (struct unw_debug_frame_list)); + + fdesc->start = start; + fdesc->end = end; + fdesc->debug_frame = buf; + fdesc->debug_frame_size = bufsize; + fdesc->index = NULL; + fdesc->next = as->debug_frames; + + as->debug_frames = fdesc; + } + + if (name && name != dlname) + free (name); + + return fdesc; +} + +struct debug_frame_tab + { + struct table_entry *tab; + uint32_t length; + uint32_t size; + }; + +static struct debug_frame_tab * +debug_frame_tab_new (unsigned int base_size) +{ + struct debug_frame_tab *tab = malloc (sizeof (struct debug_frame_tab)); + + tab->tab = calloc (base_size, sizeof (struct table_entry)); + tab->length = 0; + tab->size = base_size; + + return tab; +} + +static void +debug_frame_tab_append (struct debug_frame_tab *tab, + unw_word_t fde_offset, unw_word_t start_ip) +{ + unsigned int length = tab->length; + + if (length == tab->size) + { + tab->size *= 2; + tab->tab = realloc (tab->tab, sizeof (struct table_entry) * tab->size); + } + + tab->tab[length].fde_offset = fde_offset; + tab->tab[length].start_ip_offset = start_ip; + + tab->length = length + 1; +} + +static void +debug_frame_tab_shrink (struct debug_frame_tab *tab) +{ + if (tab->size > tab->length) + { + tab->tab = realloc (tab->tab, sizeof (struct table_entry) * tab->length); + tab->size = tab->length; + } +} + +static int +debug_frame_tab_compare (const void *a, const void *b) +{ + const struct table_entry *fa = a, *fb = b; + + if (fa->start_ip_offset > fb->start_ip_offset) + return 1; + else if (fa->start_ip_offset < fb->start_ip_offset) + return -1; + else + return 0; +} + +/* ptr is a pointer to a callback_data structure and, on entry, + member ip contains the instruction-pointer we're looking for. */ static int callback (struct dl_phdr_info *info, size_t size, void *ptr) @@ -100,6 +416,8 @@ 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; @@ -136,117 +454,261 @@ callback (struct dl_phdr_info *info, size_t size, void *ptr) else if (phdr->p_type == PT_DYNAMIC) p_dynamic = phdr; } - if (!p_text || !p_eh_hdr) + + if (!p_text) return 0; - if (likely (p_eh_hdr->p_vaddr >= p_text->p_vaddr - && p_eh_hdr->p_vaddr < p_text->p_vaddr + p_text->p_memsz)) - /* normal case: eh-hdr is inside text segment */ - segbase = p_text->p_vaddr + load_base; - else + if (p_eh_hdr) { - /* Special case: eh-hdr is in some other segment; this may - happen, e.g., for the Linux kernel's gate DSO, for - example. */ - phdr = info->dlpi_phdr; - for (n = info->dlpi_phnum; --n >= 0; phdr++) + if (likely (p_eh_hdr->p_vaddr >= p_text->p_vaddr + && p_eh_hdr->p_vaddr < p_text->p_vaddr + p_text->p_memsz)) + /* normal case: eh-hdr is inside text segment */ + segbase = p_text->p_vaddr + load_base; + else { - if (phdr->p_type == PT_LOAD && p_eh_hdr->p_vaddr >= phdr->p_vaddr - && p_eh_hdr->p_vaddr < phdr->p_vaddr + phdr->p_memsz) + /* Special case: eh-hdr is in some other segment; this may + happen, e.g., for the Linux kernel's gate DSO, for + example. */ + phdr = info->dlpi_phdr; + for (n = info->dlpi_phnum; --n >= 0; phdr++) { - segbase = phdr->p_vaddr + load_base; - break; + if (phdr->p_type == PT_LOAD && p_eh_hdr->p_vaddr >= phdr->p_vaddr + && p_eh_hdr->p_vaddr < phdr->p_vaddr + phdr->p_memsz) + { + segbase = phdr->p_vaddr + load_base; + break; + } } } + + if (p_dynamic) + { + /* For dynamicly linked executables and shared libraries, + DT_PLTGOT is the value that data-relative addresses are + relative to for that object. We call this the "gp". */ + Elf_W(Dyn) *dyn = (Elf_W(Dyn) *)(p_dynamic->p_vaddr + load_base); + for (; dyn->d_tag != DT_NULL; ++dyn) + if (dyn->d_tag == DT_PLTGOT) + { + /* Assume that _DYNAMIC is writable and GLIBC has + relocated it (true for x86 at least). */ + di->gp = dyn->d_un.d_ptr; + break; + } + } + else + /* Otherwise this is a static executable with no _DYNAMIC. Assume + that data-relative addresses are relative to 0, i.e., + absolute. */ + di->gp = 0; + pi->gp = di->gp; + + hdr = (struct dwarf_eh_frame_hdr *) (p_eh_hdr->p_vaddr + load_base); + if (hdr->version != DW_EH_VERSION) + { + Debug (1, "table `%s' has unexpected version %d\n", + info->dlpi_name, hdr->version); + return 0; + } + + a = unw_get_accessors (unw_local_addr_space); + addr = (unw_word_t) (uintptr_t) (hdr + 1); + + /* (Optionally) read eh_frame_ptr: */ + if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a, + &addr, hdr->eh_frame_ptr_enc, pi, + &eh_frame_start, NULL)) < 0) + return ret; + + /* (Optionally) read fde_count: */ + if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a, + &addr, hdr->fde_count_enc, pi, + &fde_count, NULL)) < 0) + return ret; + + if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) + { + /* 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); + else + Debug (4, "table `%s' has encoding 0x%x; doing linear search\n", + info->dlpi_name, hdr->table_enc); + + eh_frame_end = max_load_addr; /* XXX can we do better? */ + + if (hdr->fde_count_enc == DW_EH_PE_omit) + fde_count = ~0UL; + if (hdr->eh_frame_ptr_enc == DW_EH_PE_omit) + abort (); + + /* XXX we know how to build a local binary search table for + .debug_frame, so we could do that here too. */ + cb_data->single_fde = 1; + found = linear_search (unw_local_addr_space, ip, + eh_frame_start, eh_frame_end, fde_count, + pi, need_unwind_info, NULL); + if (found != 1) + found = 0; + } + else + { + di->format = UNW_INFO_FORMAT_REMOTE_TABLE; + di->start_ip = p_text->p_vaddr + load_base; + di->end_ip = p_text->p_vaddr + load_base + p_text->p_memsz; + di->u.rti.name_ptr = (unw_word_t) (uintptr_t) info->dlpi_name; + di->u.rti.table_data = addr; + assert (sizeof (struct table_entry) % sizeof (unw_word_t) == 0); + di->u.rti.table_len = (fde_count * sizeof (struct table_entry) + / sizeof (unw_word_t)); + /* For the binary-search table in the eh_frame_hdr, data-relative + means relative to the start of that section... */ + di->u.rti.segbase = (unw_word_t) (uintptr_t) hdr; + + found = 1; + Debug (15, "found table `%s': segbase=0x%lx, len=%lu, gp=0x%lx, " + "table_data=0x%lx\n", (char *) (uintptr_t) di->u.rti.name_ptr, + (long) di->u.rti.segbase, (long) di->u.rti.table_len, + (long) di->gp, (long) di->u.rti.table_data); + } } - if (p_dynamic) + Debug (15, "Trying to find .debug_frame\n"); + di = &cb_data->di_debug; + fdesc = locate_debug_info (unw_local_addr_space, info, ip, info->dlpi_name); + + if (!fdesc) { - /* For dynamicly linked executables and shared libraries, - DT_PLTGOT is the value that data-relative addresses are - relative to for that object. We call this the "gp". */ - Elf_W(Dyn) *dyn = (Elf_W(Dyn) *)(p_dynamic->p_vaddr + load_base); - for (; dyn->d_tag != DT_NULL; ++dyn) - if (dyn->d_tag == DT_PLTGOT) - { - /* Assume that _DYNAMIC is writable and GLIBC has - relocated it (true for x86 at least). */ - di->gp = dyn->d_un.d_ptr; - break; - } + Debug (15, "couldn't load .debug_frame\n"); + return found; } else - /* Otherwise this is a static executable with no _DYNAMIC. Assume - that data-relative addresses are relative to 0, i.e., - absolute. */ - di->gp = 0; - pi->gp = di->gp; - - hdr = (struct dwarf_eh_frame_hdr *) (p_eh_hdr->p_vaddr + load_base); - if (hdr->version != DW_EH_VERSION) { - Debug (1, "table `%s' has unexpected version %d\n", - info->dlpi_name, hdr->version); - return 0; - } + char *buf; + size_t bufsize; + unw_word_t item_start, item_end = 0; + uint32_t u32val = 0; + uint64_t cie_id = 0; + struct debug_frame_tab *tab; - a = unw_get_accessors (unw_local_addr_space); - addr = (unw_word_t) (hdr + 1); + Debug (15, "loaded .debug_frame\n"); - /* (Optionally) read eh_frame_ptr: */ - if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a, - &addr, hdr->eh_frame_ptr_enc, pi, - &eh_frame_start, NULL)) < 0) - return ret; + buf = fdesc->debug_frame; + bufsize = fdesc->debug_frame_size; - /* (Optionally) read fde_count: */ - if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a, - &addr, hdr->fde_count_enc, pi, - &fde_count, NULL)) < 0) - return ret; + if (bufsize == 0) + { + Debug (15, "zero-length .debug_frame\n"); + return found; + } - if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) - { - /* 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); - else - Debug (4, "table `%s' has encoding 0x%x; doing linear search\n", - info->dlpi_name, hdr->table_enc); + /* 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. */ - eh_frame_end = max_load_addr; /* XXX can we do better? */ + tab = debug_frame_tab_new (16); - if (hdr->fde_count_enc == DW_EH_PE_omit) - fde_count = ~0UL; - if (hdr->eh_frame_ptr_enc == DW_EH_PE_omit) - abort (); + 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); + } - 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); + 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); } - cb_data->single_fde = 0; - di->format = UNW_INFO_FORMAT_REMOTE_TABLE; - di->start_ip = p_text->p_vaddr + load_base; - di->end_ip = p_text->p_vaddr + load_base + p_text->p_memsz; - di->u.rti.name_ptr = (unw_word_t) info->dlpi_name; - di->u.rti.table_data = addr; - assert (sizeof (struct table_entry) % sizeof (unw_word_t) == 0); - di->u.rti.table_len = (fde_count * sizeof (struct table_entry) - / sizeof (unw_word_t)); - /* For the binary-search table in the eh_frame_hdr, data-relative - means relative to the start of that section... */ - di->u.rti.segbase = (unw_word_t) hdr; - - Debug (15, "found table `%s': segbase=0x%lx, len=%lu, gp=0x%lx, " - "table_data=0x%lx\n", (char *) di->u.rti.name_ptr, - (long) di->u.rti.segbase, (long) di->u.rti.table_len, - (long) di->gp, (long) di->u.rti.table_data); - return 1; + return found; } HIDDEN int @@ -259,9 +721,12 @@ dwarf_find_proc_info (unw_addr_space_t as, unw_word_t ip, Debug (14, "looking for IP=0x%lx\n", (long) ip); + memset (&cb_data, 0, sizeof (cb_data)); cb_data.ip = ip; cb_data.pi = pi; cb_data.need_unwind_info = need_unwind_info; + cb_data.di.format = -1; + cb_data.di_debug.format = -1; sigprocmask (SIG_SETMASK, &unwi_full_mask, &saved_mask); ret = dl_iterate_phdr (callback, &cb_data); @@ -276,14 +741,22 @@ dwarf_find_proc_info (unw_addr_space_t as, unw_word_t ip, if (cb_data.single_fde) /* already got the result in *pi */ return 0; - else - /* search the table: */ - return dwarf_search_unwind_table (as, ip, &cb_data.di, + + /* search the table: */ + if (cb_data.di.format != -1) + ret = dwarf_search_unwind_table (as, ip, &cb_data.di, pi, need_unwind_info, arg); + else + ret = -UNW_ENOINFO; + + if (ret == -UNW_ENOINFO && cb_data.di_debug.format != -1) + ret = dwarf_search_unwind_table (as, ip, &cb_data.di_debug, pi, + need_unwind_info, arg); + return ret; } static inline const struct table_entry * -lookup (struct table_entry *table, size_t table_size, int32_t rel_ip) +lookup (const struct table_entry *table, size_t table_size, int32_t rel_ip) { unsigned long table_len = table_size / sizeof (struct table_entry); const struct table_entry *e = 0; @@ -294,6 +767,7 @@ lookup (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); if (rel_ip < e->start_ip_offset) hi = mid; else @@ -353,16 +827,47 @@ dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, unw_dyn_info_t *di, unw_proc_info_t *pi, int need_unwind_info, void *arg) { - const struct table_entry *e = NULL; + const struct table_entry *e = NULL, *table; unw_word_t segbase = 0, fde_addr; unw_accessors_t *a; #ifndef UNW_LOCAL_ONLY struct table_entry ent; #endif int ret; + unw_word_t debug_frame_base; + size_t table_len; +#ifdef UNW_REMOTE_ONLY + assert (di->format == UNW_INFO_FORMAT_REMOTE_TABLE); +#else assert (di->format == UNW_INFO_FORMAT_REMOTE_TABLE - && (ip >= di->start_ip && ip < di->end_ip)); + || di->format == UNW_INFO_FORMAT_TABLE); +#endif + assert (ip >= di->start_ip && ip < di->end_ip); + + if (di->format == UNW_INFO_FORMAT_REMOTE_TABLE) + { + table = (const struct table_entry *) (uintptr_t) di->u.rti.table_data; + table_len = di->u.rti.table_len * sizeof (unw_word_t); + debug_frame_base = 0; + } + else + { +#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); + table = fdesc->index; + table_len = fdesc->index_size * sizeof (struct table_entry); + debug_frame_base = (uintptr_t) fdesc->debug_frame; +#endif + } a = unw_get_accessors (as); @@ -370,16 +875,14 @@ dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, if (as == unw_local_addr_space) { segbase = di->u.rti.segbase; - e = lookup ((struct table_entry *) di->u.rti.table_data, - di->u.rti.table_len * sizeof (unw_word_t), ip - segbase); + e = lookup (table, table_len, ip - segbase); } else #endif { #ifndef UNW_LOCAL_ONLY segbase = di->u.rti.segbase; - if ((ret = remote_lookup (as, di->u.rti.table_data, - di->u.rti.table_len * sizeof (unw_word_t), + if ((ret = remote_lookup (as, (uintptr_t) table, table_len, ip - segbase, &ent, arg)) < 0) return ret; if (ret) @@ -390,17 +893,34 @@ 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); /* IP is inside this table's range, but there is no explicit unwind info. */ return -UNW_ENOINFO; } Debug (15, "ip=0x%lx, start_ip=0x%lx\n", - (long) ip, (long) (e->start_ip_offset + segbase)); - fde_addr = e->fde_offset + segbase; + (long) ip, (long) (e->start_ip_offset)); + if (debug_frame_base) + 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); if ((ret = dwarf_extract_proc_info_from_fde (as, a, &fde_addr, pi, - need_unwind_info, arg)) < 0) + need_unwind_info, + debug_frame_base, arg)) < 0) return ret; + /* .debug_frame uses an absolute encoding that does not know about any + shared library relocation. */ + if (di->format == UNW_INFO_FORMAT_TABLE) + { + pi->start_ip += segbase; + pi->end_ip += segbase; + } + if (ip < pi->start_ip || ip >= pi->end_ip) return -UNW_ENOINFO; diff --git a/src/dwarf/Gpe.c b/src/dwarf/Gpe.c index 4ca0d64..c271d76 100644 --- a/src/dwarf/Gpe.c +++ b/src/dwarf/Gpe.c @@ -26,6 +26,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "dwarf_i.h" #include "libunwind_i.h" +#include + HIDDEN int dwarf_read_encoded_pointer (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, unsigned char encoding, diff --git a/src/mi/Gfind_dynamic_proc_info.c b/src/mi/Gfind_dynamic_proc_info.c index 24c72ed..6cf3b42 100644 --- a/src/mi/Gfind_dynamic_proc_info.c +++ b/src/mi/Gfind_dynamic_proc_info.c @@ -49,7 +49,7 @@ local_find_proc_info (unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, return -UNW_ENOINFO; #endif - list = (unw_dyn_info_list_t *) _U_dyn_info_list_addr (); + list = (unw_dyn_info_list_t *) (uintptr_t) _U_dyn_info_list_addr (); for (di = list->first; di; di = di->next) if (ip >= di->start_ip && ip < di->end_ip) return unwi_extract_dynamic_proc_info (as, ip, pi, di, need_unwind_info, diff --git a/src/mi/backtrace.c b/src/mi/backtrace.c index 3a386ae..46d6c97 100644 --- a/src/mi/backtrace.c +++ b/src/mi/backtrace.c @@ -49,7 +49,7 @@ backtrace (void **buffer, int size) if (unw_get_reg (&cursor, UNW_REG_IP, &ip) < 0) return n; - buffer[n++] = (void *) ip; + buffer[n++] = (void *) (uintptr_t) ip; } return n; } diff --git a/src/mi/dyn-info-list.c b/src/mi/dyn-info-list.c index b06fee4..5a5e30e 100644 --- a/src/mi/dyn-info-list.c +++ b/src/mi/dyn-info-list.c @@ -30,5 +30,5 @@ HIDDEN unw_dyn_info_list_t _U_dyn_info_list; PROTECTED unw_word_t _U_dyn_info_list_addr (void) { - return (unw_word_t) &_U_dyn_info_list; + return (unw_word_t) (uintptr_t) &_U_dyn_info_list; } diff --git a/src/mi/flush_cache.c b/src/mi/flush_cache.c index 15e8950..c5650ba 100644 --- a/src/mi/flush_cache.c +++ b/src/mi/flush_cache.c @@ -28,9 +28,23 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ PROTECTED void unw_flush_cache (unw_addr_space_t as, unw_word_t lo, unw_word_t hi) { +#if !UNW_TARGET_IA64 + struct unw_debug_frame_list *w = as->debug_frames; +#endif + /* clear dyn_info_list_addr cache: */ as->dyn_info_list_addr = 0; +#if !UNW_TARGET_IA64 + for (; w; w = w->next) + { + if (w->index) + free (w->index); + free (w->debug_frame); + } + as->debug_frames = NULL; +#endif + /* This lets us flush caches lazily. The implementation currently ignores the flush range arguments (lo-hi). This is OK because unw_flush_cache() is allowed to flush more than the requested diff --git a/src/mips/Gcreate_addr_space.c b/src/mips/Gcreate_addr_space.c new file mode 100644 index 0000000..af084ef --- /dev/null +++ b/src/mips/Gcreate_addr_space.c @@ -0,0 +1,65 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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 + +#include "unwind_i.h" + +PROTECTED unw_addr_space_t +unw_create_addr_space (unw_accessors_t *a, int byte_order) +{ +#ifdef UNW_LOCAL_ONLY + return NULL; +#else + unw_addr_space_t as = malloc (sizeof (*as)); + + if (!as) + return NULL; + + memset (as, 0, sizeof (*as)); + + as->acc = *a; + + /* + * MIPS supports only big or little-endian, not weird stuff like + * PDP_ENDIAN. + */ + if (byte_order != 0 + && byte_order != __LITTLE_ENDIAN + && byte_order != __BIG_ENDIAN) + return NULL; + + if (byte_order == 0) + /* use host default: */ + as->big_endian = (__BYTE_ORDER == __BIG_ENDIAN); + else + as->big_endian = (byte_order == __BIG_ENDIAN); + + /* FIXME! There is no way to specify the ABI. */ + as->abi = UNW_MIPS_ABI_O32; + as->addr_size = 4; + + return as; +#endif +} diff --git a/src/mips/Gget_proc_info.c b/src/mips/Gget_proc_info.c new file mode 100644 index 0000000..973ddd9 --- /dev/null +++ b/src/mips/Gget_proc_info.c @@ -0,0 +1,41 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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_get_proc_info (unw_cursor_t *cursor, unw_proc_info_t *pi) +{ + struct cursor *c = (struct cursor *) cursor; + int ret; + + /* We can only unwind using Dwarf into on MIPS: return failure code + if it's not present. */ + ret = dwarf_make_proc_info (&c->dwarf); + if (ret < 0) + return ret; + + *pi = c->dwarf.pi; + return 0; +} diff --git a/src/mips/Gget_save_loc.c b/src/mips/Gget_save_loc.c new file mode 100644 index 0000000..dbccea8 --- /dev/null +++ b/src/mips/Gget_save_loc.c @@ -0,0 +1,99 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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" + +/* FIXME for MIPS. */ + +PROTECTED int +unw_get_save_loc (unw_cursor_t *cursor, int reg, unw_save_loc_t *sloc) +{ + struct cursor *c = (struct cursor *) cursor; + dwarf_loc_t loc; + + loc = DWARF_NULL_LOC; /* default to "not saved" */ + + switch (reg) + { + case UNW_MIPS_R0: + case UNW_MIPS_R1: + case UNW_MIPS_R2: + case UNW_MIPS_R3: + case UNW_MIPS_R4: + case UNW_MIPS_R5: + case UNW_MIPS_R6: + case UNW_MIPS_R7: + case UNW_MIPS_R8: + case UNW_MIPS_R9: + case UNW_MIPS_R10: + case UNW_MIPS_R11: + case UNW_MIPS_R12: + case UNW_MIPS_R13: + case UNW_MIPS_R14: + case UNW_MIPS_R15: + case UNW_MIPS_R16: + case UNW_MIPS_R17: + case UNW_MIPS_R18: + case UNW_MIPS_R19: + case UNW_MIPS_R20: + case UNW_MIPS_R21: + case UNW_MIPS_R22: + case UNW_MIPS_R23: + case UNW_MIPS_R24: + case UNW_MIPS_R25: + case UNW_MIPS_R26: + case UNW_MIPS_R27: + case UNW_MIPS_R28: + case UNW_MIPS_R29: + case UNW_MIPS_R30: + case UNW_MIPS_R31: + loc = c->dwarf.loc[reg - UNW_MIPS_R0]; + break; + + default: + break; + } + + memset (sloc, 0, sizeof (sloc)); + + if (DWARF_IS_NULL_LOC (loc)) + { + sloc->type = UNW_SLT_NONE; + return 0; + } + +#if !defined(UNW_LOCAL_ONLY) + if (DWARF_IS_REG_LOC (loc)) + { + sloc->type = UNW_SLT_REG; + sloc->u.regnum = DWARF_GET_LOC (loc); + } + else +#endif + { + sloc->type = UNW_SLT_MEMORY; + sloc->u.addr = DWARF_GET_LOC (loc); + } + return 0; +} diff --git a/src/mips/Gglobal.c b/src/mips/Gglobal.c new file mode 100644 index 0000000..1874a5b --- /dev/null +++ b/src/mips/Gglobal.c @@ -0,0 +1,66 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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 "dwarf_i.h" + +HIDDEN pthread_mutex_t mips_lock = PTHREAD_MUTEX_INITIALIZER; +HIDDEN int tdep_needs_initialization = 1; + +/* FIXME: I'm pretty sure we don't need this at all for MIPS, but "generic" + code (include/dwarf_i.h) seems to expect it to be here at present. */ + +HIDDEN uint8_t dwarf_to_unw_regnum_map[] = + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 + }; + +HIDDEN void +tdep_init (void) +{ + intrmask_t saved_mask; + + sigfillset (&unwi_full_mask); + + sigprocmask (SIG_SETMASK, &unwi_full_mask, &saved_mask); + mutex_lock (&mips_lock); + { + if (!tdep_needs_initialization) + /* another thread else beat us to it... */ + goto out; + + mi_init (); + + dwarf_init (); + +#ifndef UNW_REMOTE_ONLY + mips_local_addr_space_init (); +#endif + tdep_needs_initialization = 0; /* signal that we're initialized... */ + } + out: + mutex_unlock (&mips_lock); + sigprocmask (SIG_SETMASK, &saved_mask, NULL); +} diff --git a/src/mips/Ginit.c b/src/mips/Ginit.c new file mode 100644 index 0000000..6ffeabf --- /dev/null +++ b/src/mips/Ginit.c @@ -0,0 +1,208 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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 +#include + +#include "unwind_i.h" + +#ifdef UNW_REMOTE_ONLY + +/* unw_local_addr_space is a NULL pointer in this case. */ +PROTECTED unw_addr_space_t unw_local_addr_space; + +#else /* !UNW_REMOTE_ONLY */ + +static struct unw_addr_space local_addr_space; + +PROTECTED unw_addr_space_t unw_local_addr_space = &local_addr_space; + +/* Return the address of the 64-bit slot in UC for REG (even for o32, + where registers are 32-bit, the slots are still 64-bit). */ + +static inline void * +uc_addr (ucontext_t *uc, int reg) +{ + if (reg >= UNW_MIPS_R0 && reg < UNW_MIPS_R0 + 32) + return &uc->uc_mcontext.gregs[reg - UNW_MIPS_R0]; + else + return NULL; +} + +# ifdef UNW_LOCAL_ONLY + +HIDDEN void * +tdep_uc_addr (ucontext_t *uc, int reg) +{ + char *addr = uc_addr (uc, reg); + + if (reg >= UNW_MIPS_R0 && reg <= UNW_MIPS_R31 + && tdep_big_endian (unw_local_addr_space) + && unw_local_addr_space->abi == UNW_MIPS_ABI_O32) + addr += 4; + + return addr; +} + +# endif /* UNW_LOCAL_ONLY */ + +HIDDEN unw_dyn_info_list_t _U_dyn_info_list; + +/* XXX fix me: there is currently no way to locate the dyn-info list + by a remote unwinder. On ia64, this is done via a special + unwind-table entry. Perhaps something similar can be done with + DWARF2 unwind info. */ + +static void +put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg) +{ + /* it's a no-op */ +} + +static int +get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr, + void *arg) +{ + *dyn_info_list_addr = (unw_word_t) (intptr_t) &_U_dyn_info_list; + return 0; +} + +static int +access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, + void *arg) +{ + if (write) + { + Debug (16, "mem[%llx] <- %llx\n", (long long) addr, (long long) *val); + *(unw_word_t *) (intptr_t) addr = *val; + } + else + { + *val = *(unw_word_t *) (intptr_t) addr; + Debug (16, "mem[%llx] -> %llx\n", (long long) addr, (long long) *val); + } + return 0; +} + +static int +access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, + void *arg) +{ + unw_word_t *addr; + ucontext_t *uc = arg; + + if (unw_is_fpreg (reg)) + goto badreg; + + Debug (16, "reg = %s\n", unw_regname (reg)); + if (!(addr = uc_addr (uc, reg))) + goto badreg; + + if (write) + { + *(unw_word_t *) (intptr_t) addr = (mips_reg_t) *val; + Debug (12, "%s <- %llx\n", unw_regname (reg), (long long) *val); + } + else + { + *val = (mips_reg_t) *(unw_word_t *) (intptr_t) addr; + Debug (12, "%s -> %llx\n", unw_regname (reg), (long long) *val); + } + return 0; + + badreg: + Debug (1, "bad register number %u\n", reg); + return -UNW_EBADREG; +} + +static int +access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, + int write, void *arg) +{ + ucontext_t *uc = arg; + unw_fpreg_t *addr; + + if (!unw_is_fpreg (reg)) + goto badreg; + + if (!(addr = uc_addr (uc, reg))) + goto badreg; + + if (write) + { + Debug (12, "%s <- %08lx.%08lx.%08lx\n", unw_regname (reg), + ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]); + *(unw_fpreg_t *) (intptr_t) addr = *val; + } + else + { + *val = *(unw_fpreg_t *) (intptr_t) addr; + Debug (12, "%s -> %08lx.%08lx.%08lx\n", unw_regname (reg), + ((long *)val)[0], ((long *)val)[1], ((long *)val)[2]); + } + return 0; + + badreg: + Debug (1, "bad register number %u\n", reg); + /* attempt to access a non-preserved register */ + return -UNW_EBADREG; +} + +static int +get_static_proc_name (unw_addr_space_t as, unw_word_t ip, + char *buf, size_t buf_len, unw_word_t *offp, + void *arg) +{ + + return elf_w (get_proc_name) (as, getpid (), ip, buf, buf_len, offp); +} + +HIDDEN void +mips_local_addr_space_init (void) +{ + memset (&local_addr_space, 0, sizeof (local_addr_space)); + local_addr_space.big_endian = (__BYTE_ORDER == __BIG_ENDIAN); +#if _MIPS_SIM == _ABIO32 + local_addr_space.abi = UNW_MIPS_ABI_O32; +#elif _MIPS_SIM == _ABIN32 + local_addr_space.abi = UNW_MIPS_ABI_N32; +#elif _MIPS_SIM == _ABI64 + local_addr_space.abi = UNW_MIPS_ABI_N64; +#else +# error Unsupported ABI +#endif + local_addr_space.addr_size = sizeof (void *); + local_addr_space.caching_policy = UNW_CACHE_GLOBAL; + local_addr_space.acc.find_proc_info = dwarf_find_proc_info; + local_addr_space.acc.put_unwind_info = put_unwind_info; + local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr; + local_addr_space.acc.access_mem = access_mem; + local_addr_space.acc.access_reg = access_reg; + local_addr_space.acc.access_fpreg = access_fpreg; + local_addr_space.acc.resume = 0; /* mips_local_resume? FIXME! */ + local_addr_space.acc.get_proc_name = get_static_proc_name; + unw_flush_cache (&local_addr_space, 0, 0); +} + +#endif /* !UNW_REMOTE_ONLY */ diff --git a/src/mips/Ginit_local.c b/src/mips/Ginit_local.c new file mode 100644 index 0000000..7b2881e --- /dev/null +++ b/src/mips/Ginit_local.c @@ -0,0 +1,53 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "unwind_i.h" +#include "init.h" + +#ifdef UNW_REMOTE_ONLY + +PROTECTED int +unw_init_local (unw_cursor_t *cursor, ucontext_t *uc) +{ + return -UNW_EINVAL; +} + +#else /* !UNW_REMOTE_ONLY */ + +PROTECTED int +unw_init_local (unw_cursor_t *cursor, ucontext_t *uc) +{ + struct cursor *c = (struct cursor *) cursor; + + if (tdep_needs_initialization) + tdep_init (); + + Debug (1, "(cursor=%p)\n", c); + + c->dwarf.as = unw_local_addr_space; + c->dwarf.as_arg = uc; + return common_init (c); +} + +#endif /* !UNW_REMOTE_ONLY */ diff --git a/src/mips/Ginit_remote.c b/src/mips/Ginit_remote.c new file mode 100644 index 0000000..3baf3f6 --- /dev/null +++ b/src/mips/Ginit_remote.c @@ -0,0 +1,45 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "init.h" +#include "unwind_i.h" + +PROTECTED int +unw_init_remote (unw_cursor_t *cursor, unw_addr_space_t as, void *as_arg) +{ +#ifdef UNW_LOCAL_ONLY + return -UNW_EINVAL; +#else /* !UNW_LOCAL_ONLY */ + struct cursor *c = (struct cursor *) cursor; + + if (tdep_needs_initialization) + tdep_init (); + + Debug (1, "(cursor=%p)\n", c); + + c->dwarf.as = as; + c->dwarf.as_arg = as_arg; + return common_init (c); +#endif /* !UNW_LOCAL_ONLY */ +} diff --git a/src/mips/Gis_signal_frame.c b/src/mips/Gis_signal_frame.c new file mode 100644 index 0000000..fef542b --- /dev/null +++ b/src/mips/Gis_signal_frame.c @@ -0,0 +1,35 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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 + +/* FIXME for MIPS. */ + +PROTECTED int +unw_is_signal_frame (unw_cursor_t *cursor) +{ + printf ("%s: implement me\n", __FUNCTION__); + return -UNW_ENOINFO; +} diff --git a/src/mips/Gregs.c b/src/mips/Gregs.c new file mode 100644 index 0000000..9f37299 --- /dev/null +++ b/src/mips/Gregs.c @@ -0,0 +1,99 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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" + +/* FIXME: The following is probably unfinished and/or at least partly bogus. */ + +HIDDEN int +tdep_access_reg (struct cursor *c, unw_regnum_t reg, unw_word_t *valp, + int write) +{ + dwarf_loc_t loc = DWARF_NULL_LOC; + + switch (reg) + { + case UNW_MIPS_R0: + case UNW_MIPS_R1: + case UNW_MIPS_R2: + case UNW_MIPS_R3: + case UNW_MIPS_R4: + case UNW_MIPS_R5: + case UNW_MIPS_R6: + case UNW_MIPS_R7: + case UNW_MIPS_R8: + case UNW_MIPS_R9: + case UNW_MIPS_R10: + case UNW_MIPS_R11: + case UNW_MIPS_R12: + case UNW_MIPS_R13: + case UNW_MIPS_R14: + case UNW_MIPS_R15: + case UNW_MIPS_R16: + case UNW_MIPS_R17: + case UNW_MIPS_R18: + case UNW_MIPS_R19: + case UNW_MIPS_R20: + case UNW_MIPS_R21: + case UNW_MIPS_R22: + case UNW_MIPS_R23: + case UNW_MIPS_R24: + case UNW_MIPS_R25: + case UNW_MIPS_R26: + case UNW_MIPS_R27: + case UNW_MIPS_R28: + case UNW_MIPS_R29: + case UNW_MIPS_R30: + case UNW_MIPS_R31: + loc = c->dwarf.loc[reg - UNW_MIPS_R0]; + break; + + case UNW_MIPS_CFA: + if (write) + return -UNW_EREADONLYREG; + *valp = c->dwarf.cfa; + return 0; + + /* FIXME: IP? Copro & shadow registers? */ + + default: + Debug (1, "bad register number %u\n", reg); + return -UNW_EBADREG; + } + + if (write) + return dwarf_put (&c->dwarf, loc, *valp); + else + return dwarf_get (&c->dwarf, loc, valp); +} + +/* FIXME for MIPS. */ + +HIDDEN int +tdep_access_fpreg (struct cursor *c, unw_regnum_t reg, unw_fpreg_t *valp, + int write) +{ + Debug (1, "bad register number %u\n", reg); + return -UNW_EBADREG; +} diff --git a/src/mips/Gresume.c b/src/mips/Gresume.c new file mode 100644 index 0000000..b822e24 --- /dev/null +++ b/src/mips/Gresume.c @@ -0,0 +1,45 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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. */ + +/* FIXME for MIPS. */ + +#include + +#include "unwind_i.h" + +#ifndef UNW_REMOTE_ONLY + +HIDDEN inline int +mips_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg) +{ + return -UNW_EINVAL; +} + +#endif /* !UNW_REMOTE_ONLY */ + +PROTECTED int +unw_resume (unw_cursor_t *cursor) +{ + return -UNW_EINVAL; +} diff --git a/src/mips/Gstep.c b/src/mips/Gstep.c new file mode 100644 index 0000000..bafeef1 --- /dev/null +++ b/src/mips/Gstep.c @@ -0,0 +1,48 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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_step (unw_cursor_t *cursor) +{ + struct cursor *c = (struct cursor *) cursor; + int ret; + + Debug (1, "(cursor=%p)\n", c); + + /* Try DWARF-based unwinding... this is the only method likely to work for + MIPS. */ + ret = dwarf_step (&c->dwarf); + + if (unlikely (ret == -UNW_ESTOPUNWIND)) + return ret; + + /* Dwarf unwinding didn't work, stop. */ + if (unlikely (ret < 0)) + return 0; + + return (c->dwarf.ip == 0) ? 0 : 1; +} diff --git a/src/mips/Lcreate_addr_space.c b/src/mips/Lcreate_addr_space.c new file mode 100644 index 0000000..0f2dc6b --- /dev/null +++ b/src/mips/Lcreate_addr_space.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gcreate_addr_space.c" +#endif diff --git a/src/mips/Lget_proc_info.c b/src/mips/Lget_proc_info.c new file mode 100644 index 0000000..69028b0 --- /dev/null +++ b/src/mips/Lget_proc_info.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gget_proc_info.c" +#endif diff --git a/src/mips/Lget_save_loc.c b/src/mips/Lget_save_loc.c new file mode 100644 index 0000000..9ea048a --- /dev/null +++ b/src/mips/Lget_save_loc.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gget_save_loc.c" +#endif diff --git a/src/mips/Lglobal.c b/src/mips/Lglobal.c new file mode 100644 index 0000000..6d7b489 --- /dev/null +++ b/src/mips/Lglobal.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gglobal.c" +#endif diff --git a/src/mips/Linit.c b/src/mips/Linit.c new file mode 100644 index 0000000..e9abfdd --- /dev/null +++ b/src/mips/Linit.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Ginit.c" +#endif diff --git a/src/mips/Linit_local.c b/src/mips/Linit_local.c new file mode 100644 index 0000000..68a1687 --- /dev/null +++ b/src/mips/Linit_local.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Ginit_local.c" +#endif diff --git a/src/mips/Linit_remote.c b/src/mips/Linit_remote.c new file mode 100644 index 0000000..58cb04a --- /dev/null +++ b/src/mips/Linit_remote.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Ginit_remote.c" +#endif diff --git a/src/mips/Lis_signal_frame.c b/src/mips/Lis_signal_frame.c new file mode 100644 index 0000000..b9a7c4f --- /dev/null +++ b/src/mips/Lis_signal_frame.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gis_signal_frame.c" +#endif diff --git a/src/mips/Lregs.c b/src/mips/Lregs.c new file mode 100644 index 0000000..2c9c75c --- /dev/null +++ b/src/mips/Lregs.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gregs.c" +#endif diff --git a/src/mips/Lresume.c b/src/mips/Lresume.c new file mode 100644 index 0000000..41a8cf0 --- /dev/null +++ b/src/mips/Lresume.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gresume.c" +#endif diff --git a/src/mips/Lstep.c b/src/mips/Lstep.c new file mode 100644 index 0000000..c1ac3c7 --- /dev/null +++ b/src/mips/Lstep.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gstep.c" +#endif diff --git a/src/mips/elfxx.c b/src/mips/elfxx.c new file mode 100644 index 0000000..07d3d12 --- /dev/null +++ b/src/mips/elfxx.c @@ -0,0 +1,27 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "libunwind_i.h" + +#include "../src/elfxx.c" diff --git a/src/mips/gen-offsets.c b/src/mips/gen-offsets.c new file mode 100644 index 0000000..448f694 --- /dev/null +++ b/src/mips/gen-offsets.c @@ -0,0 +1,30 @@ +#include +#include +#include + +#define UC(N,X) \ + printf ("#define LINUX_UC_" N "_OFF\t0x%X\n", offsetof (ucontext_t, X)) + +#define SC(N,X) \ + printf ("#define LINUX_SC_" N "_OFF\t0x%X\n", offsetof (struct sigcontext, X)) + +int +main (void) +{ + printf ( +"/* Linux-specific definitions: */\n\n" + +"/* Define various structure offsets to simplify cross-compilation. */\n\n" + +"/* Offsets for MIPS Linux \"ucontext_t\": */\n\n"); + + UC ("FLAGS", uc_flags); + UC ("LINK", uc_link); + UC ("STACK", uc_stack); + UC ("MCONTEXT", uc_mcontext); + UC ("SIGMASK", uc_sigmask); + + UC ("MCONTEXT_GREGS", uc_mcontext.gregs); + + return 0; +} diff --git a/src/mips/getcontext.S b/src/mips/getcontext.S new file mode 100644 index 0000000..ad9e1f0 --- /dev/null +++ b/src/mips/getcontext.S @@ -0,0 +1,85 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +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" +#include + + .text + +#if _MIPS_SIM == _ABIO32 +# if __BYTE_ORDER == __BIG_ENDIAN +# define OFFSET 4 +# else +# define OFFSET 0 +# endif +# define SREG(X) \ + sw $X, (LINUX_UC_MCONTEXT_GREGS + 8 * X + OFFSET) ($4); \ + sra $1, $X, 31; \ + sw $1, (LINUX_UC_MCONTEXT_GREGS + 8 * X + 4 - OFFSET) ($4) +#else +# define SREG(X) sd $X, (LINUX_UC_MCONTEXT_GREGS + 8 * X) ($4) +#endif + + .global _Umips_getcontext + .type _Umips_getcontext, %function + # This is a stub version of getcontext() for MIPS which only stores core + # registers. +_Umips_getcontext: + .set noat + SREG (1) + SREG (0) + SREG (2) + SREG (3) + SREG (4) + SREG (5) + SREG (6) + SREG (7) + SREG (8) + SREG (9) + SREG (10) + SREG (11) + SREG (12) + SREG (13) + SREG (14) + SREG (15) + SREG (16) + SREG (17) + SREG (18) + SREG (19) + SREG (20) + SREG (21) + SREG (22) + SREG (23) + SREG (24) + SREG (25) + SREG (26) + SREG (27) + SREG (28) + SREG (29) + SREG (30) + SREG (31) + li $2, 0 + j $31 + + .size _Umips_getcontext, .-_Umips_getcontext diff --git a/src/mips/init.h b/src/mips/init.h new file mode 100644 index 0000000..98956d4 --- /dev/null +++ b/src/mips/init.h @@ -0,0 +1,56 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "unwind_i.h" + +static inline int +common_init (struct cursor *c) +{ + int ret, i; + + for (i = 0; i < 32; i++) + c->dwarf.loc[i] = DWARF_REG_LOC (&c->dwarf, UNW_MIPS_R0 + i); + for (i = 32; i < DWARF_NUM_PRESERVED_REGS; ++i) + c->dwarf.loc[i] = DWARF_NULL_LOC; + + ret = dwarf_get (&c->dwarf, c->dwarf.loc[31], &c->dwarf.ip); + if (ret < 0) + return ret; + + ret = dwarf_get (&c->dwarf, DWARF_REG_LOC (&c->dwarf, UNW_MIPS_R29), + &c->dwarf.cfa); + if (ret < 0) + return ret; + + /* FIXME: Initialisation for other registers. */ + + c->dwarf.args_size = 0; + c->dwarf.ret_addr_column = 0; + c->dwarf.pi_valid = 0; + c->dwarf.pi_is_dynamic = 0; + c->dwarf.hint = 0; + c->dwarf.prev_rs = 0; + + return 0; +} diff --git a/src/mips/is_fpreg.c b/src/mips/is_fpreg.c new file mode 100644 index 0000000..3acc696 --- /dev/null +++ b/src/mips/is_fpreg.c @@ -0,0 +1,35 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "libunwind_i.h" + +/* FIXME: I'm not sure if libunwind's GP/FP register distinction is very useful + on MIPS. */ + +PROTECTED int +unw_is_fpreg (int regnum) +{ + /* FIXME: Support FP. */ + return 0; +} diff --git a/src/mips/offsets.h b/src/mips/offsets.h new file mode 100644 index 0000000..8040f6a --- /dev/null +++ b/src/mips/offsets.h @@ -0,0 +1,39 @@ +/* Linux-specific definitions: */ + +/* Define various structure offsets to simplify cross-compilation. */ + +/* FIXME: Currently these are only used in getcontext.S, which is only used + for a local unwinder, so we can use the compile-time ABI. At a later date + we will want all three here, to use for signal handlers. Also, because + of the three ABIs, gen-offsets.c can not quite generate this file. */ + +/* Offsets for MIPS Linux "ucontext_t": */ + +#if _MIPS_SIM == _ABIO32 + +# define LINUX_UC_FLAGS_OFF 0x0 +# define LINUX_UC_LINK_OFF 0x4 +# define LINUX_UC_STACK_OFF 0x8 +# define LINUX_UC_MCONTEXT_OFF 0x18 +# define LINUX_UC_SIGMASK_OFF 0x268 +# define LINUX_UC_MCONTEXT_GREGS 0x28 + +#elif _MIPS_SIM == _ABIN32 + +# define LINUX_UC_FLAGS_OFF 0x0 +# define LINUX_UC_LINK_OFF 0x4 +# define LINUX_UC_STACK_OFF 0x8 +# define LINUX_UC_MCONTEXT_OFF 0x18 +# define LINUX_UC_SIGMASK_OFF 0x270 +# define LINUX_UC_MCONTEXT_GREGS 0x18 + +#elif _MIPS_SIM == _ABI64 + +# define LINUX_UC_FLAGS_OFF 0x0 +# define LINUX_UC_LINK_OFF 0x8 +# define LINUX_UC_STACK_OFF 0x10 +# define LINUX_UC_MCONTEXT_OFF 0x28 +# define LINUX_UC_SIGMASK_OFF 0x280 +# define LINUX_UC_MCONTEXT_GREGS 0x28 + +#endif diff --git a/src/mips/regname.c b/src/mips/regname.c new file mode 100644 index 0000000..94000d4 --- /dev/null +++ b/src/mips/regname.c @@ -0,0 +1,46 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "unwind_i.h" + +static const char *regname[] = + { + /* 0. */ + "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7", + /* 8. */ + "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", + /* 16. */ + "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23", + /* 24. */ + "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31", + }; + +PROTECTED const char * +unw_regname (unw_regnum_t reg) +{ + if (reg < (unw_regnum_t) ARRAY_SIZE (regname)) + return regname[reg]; + else + return "???"; +} diff --git a/src/mips/siglongjmp.S b/src/mips/siglongjmp.S new file mode 100644 index 0000000..9cbcf3d --- /dev/null +++ b/src/mips/siglongjmp.S @@ -0,0 +1,8 @@ + /* Dummy implementation for now. */ + + .globl _UI_siglongjmp_cont + .globl _UI_longjmp_cont + +_UI_siglongjmp_cont: +_UI_longjmp_cont: + j $31 diff --git a/src/mips/unwind_i.h b/src/mips/unwind_i.h new file mode 100644 index 0000000..faf3237 --- /dev/null +++ b/src/mips/unwind_i.h @@ -0,0 +1,48 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef unwind_i_h +#define unwind_i_h + +#include +#include + +#include + +#include "libunwind_i.h" + +/* +#define x86_lock UNW_OBJ(lock) +#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) + +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 void mips_local_addr_space_init (void); + +#endif /* unwind_i_h */ diff --git a/src/ptrace/_UPT_reg_offset.c b/src/ptrace/_UPT_reg_offset.c index ae54cd5..f586fb7 100644 --- a/src/ptrace/_UPT_reg_offset.c +++ b/src/ptrace/_UPT_reg_offset.c @@ -288,6 +288,8 @@ int _UPT_reg_offset[UNW_REG_LAST + 1] = // [UNW_X86_64_SS] = 0xa0 #elif defined(UNW_TARGET_PPC32) #elif defined(UNW_TARGET_PPC64) +#elif defined(UNW_TARGET_ARM) +#elif defined(UNW_TARGET_MIPS) #else # error Fix me. #endif diff --git a/src/setjmp/longjmp.c b/src/setjmp/longjmp.c index ac31b5b..ca5f1e4 100644 --- a/src/setjmp/longjmp.c +++ b/src/setjmp/longjmp.c @@ -63,7 +63,7 @@ _longjmp (jmp_buf env, int val) if (unw_set_reg (&c, UNW_REG_EH + 0, wp[JB_RP]) < 0 || unw_set_reg (&c, UNW_REG_EH + 1, val) < 0 || unw_set_reg (&c, UNW_REG_IP, - (unw_word_t) &_UI_longjmp_cont)) + (unw_word_t) (uintptr_t) &_UI_longjmp_cont)) abort (); unw_resume (&c); diff --git a/src/setjmp/siglongjmp.c b/src/setjmp/siglongjmp.c index 4257082..9a091fa 100644 --- a/src/setjmp/siglongjmp.c +++ b/src/setjmp/siglongjmp.c @@ -81,7 +81,7 @@ siglongjmp (sigjmp_buf env, int val) if (unw_set_reg (&c, UNW_REG_EH + 0, wp[JB_RP]) < 0 || unw_set_reg (&c, UNW_REG_EH + 1, val) < 0 - || unw_set_reg (&c, UNW_REG_IP, (unw_word_t) cont)) + || unw_set_reg (&c, UNW_REG_IP, (unw_word_t) (uintptr_t) cont)) abort (); unw_resume (&c); diff --git a/src/unwind/FindEnclosingFunction.c b/src/unwind/FindEnclosingFunction.c index 782aafe..e639894 100644 --- a/src/unwind/FindEnclosingFunction.c +++ b/src/unwind/FindEnclosingFunction.c @@ -30,11 +30,12 @@ _Unwind_FindEnclosingFunction (void *ip) { unw_proc_info_t pi; - if (unw_get_proc_info_by_ip (unw_local_addr_space, (unw_word_t) ip, &pi, 0) + if (unw_get_proc_info_by_ip (unw_local_addr_space, + (unw_word_t) (uintptr_t) ip, &pi, 0) < 0) return NULL; - return (void *) pi.start_ip; + return (void *) (uintptr_t) pi.start_ip; } void *__libunwind_Unwind_FindEnclosingFunction (void *) diff --git a/src/unwind/RaiseException.c b/src/unwind/RaiseException.c index a5e05f0..5533876 100644 --- a/src/unwind/RaiseException.c +++ b/src/unwind/RaiseException.c @@ -60,7 +60,7 @@ _Unwind_RaiseException (struct _Unwind_Exception *exception_object) if (unw_get_proc_info (&context.cursor, &pi) < 0) return _URC_FATAL_PHASE1_ERROR; - personality = (_Unwind_Personality_Fn) pi.handler; + personality = (_Unwind_Personality_Fn) (uintptr_t) pi.handler; if (personality) { reason = (*personality) (_U_VERSION, _UA_SEARCH_PHASE, diff --git a/src/unwind/unwind-internal.h b/src/unwind/unwind-internal.h index 4db2eda..169bad5 100644 --- a/src/unwind/unwind-internal.h +++ b/src/unwind/unwind-internal.h @@ -103,7 +103,7 @@ _Unwind_Phase2 (struct _Unwind_Exception *exception_object, || unw_get_proc_info (&context->cursor, &pi) < 0) return _URC_FATAL_PHASE2_ERROR; - personality = (_Unwind_Personality_Fn) pi.handler; + personality = (_Unwind_Personality_Fn) (uintptr_t) pi.handler; if (personality) { if (!stop) diff --git a/tests/Makefile.in b/tests/Makefile.in index 7ca5277..77d49bd 100644