Import LLVM libunwind snapshot revision 246528
From https://llvm.org/svn/llvm-project/libunwind/trunk/
This commit is contained in:
commit
dc24fbd60e
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/vendor/llvm-libunwind/dist/; revision=288149 svn path=/vendor/llvm-libunwind/libunwind-r246528/; revision=288150; tag=vendor/llvm-libunwind/libunwind-r246528
20
include/__libunwind_config.h
Normal file
20
include/__libunwind_config.h
Normal file
@ -0,0 +1,20 @@
|
||||
//===------------------------- __libunwind_config.h -----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef ____LIBUNWIND_CONFIG_H__
|
||||
#define ____LIBUNWIND_CONFIG_H__
|
||||
|
||||
#if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && \
|
||||
!defined(__ARM_DWARF_EH__)
|
||||
#define _LIBUNWIND_ARM_EHABI 1
|
||||
#else
|
||||
#define _LIBUNWIND_ARM_EHABI 0
|
||||
#endif
|
||||
|
||||
#endif // ____LIBUNWIND_CONFIG_H__
|
536
include/libunwind.h
Normal file
536
include/libunwind.h
Normal file
@ -0,0 +1,536 @@
|
||||
//===---------------------------- libunwind.h -----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//
|
||||
// Compatible with libuwind API documented at:
|
||||
// http://www.nongnu.org/libunwind/man/libunwind(3).html
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __LIBUNWIND__
|
||||
#define __LIBUNWIND__
|
||||
|
||||
#include <__libunwind_config.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <Availability.h>
|
||||
#ifdef __arm__
|
||||
#define LIBUNWIND_AVAIL __attribute__((unavailable))
|
||||
#else
|
||||
#define LIBUNWIND_AVAIL __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_5_0)
|
||||
#endif
|
||||
#else
|
||||
#define LIBUNWIND_AVAIL
|
||||
#endif
|
||||
|
||||
/* error codes */
|
||||
enum {
|
||||
UNW_ESUCCESS = 0, /* no error */
|
||||
UNW_EUNSPEC = -6540, /* unspecified (general) error */
|
||||
UNW_ENOMEM = -6541, /* out of memory */
|
||||
UNW_EBADREG = -6542, /* bad register number */
|
||||
UNW_EREADONLYREG = -6543, /* attempt to write read-only register */
|
||||
UNW_ESTOPUNWIND = -6544, /* stop unwinding */
|
||||
UNW_EINVALIDIP = -6545, /* invalid IP */
|
||||
UNW_EBADFRAME = -6546, /* bad frame */
|
||||
UNW_EINVAL = -6547, /* unsupported operation or bad value */
|
||||
UNW_EBADVERSION = -6548, /* unwind info has unsupported version */
|
||||
UNW_ENOINFO = -6549 /* no unwind info found */
|
||||
};
|
||||
|
||||
struct unw_context_t {
|
||||
uint64_t data[128];
|
||||
};
|
||||
typedef struct unw_context_t unw_context_t;
|
||||
|
||||
struct unw_cursor_t {
|
||||
uint64_t data[140];
|
||||
};
|
||||
typedef struct unw_cursor_t unw_cursor_t;
|
||||
|
||||
typedef struct unw_addr_space *unw_addr_space_t;
|
||||
|
||||
typedef int unw_regnum_t;
|
||||
#if _LIBUNWIND_ARM_EHABI
|
||||
typedef uint32_t unw_word_t;
|
||||
typedef uint64_t unw_fpreg_t;
|
||||
#else
|
||||
typedef uint64_t unw_word_t;
|
||||
typedef double unw_fpreg_t;
|
||||
#endif
|
||||
|
||||
struct unw_proc_info_t {
|
||||
unw_word_t start_ip; /* start address of function */
|
||||
unw_word_t end_ip; /* address after end of function */
|
||||
unw_word_t lsda; /* address of language specific data area, */
|
||||
/* or zero if not used */
|
||||
unw_word_t handler; /* personality routine, or zero if not used */
|
||||
unw_word_t gp; /* not used */
|
||||
unw_word_t flags; /* not used */
|
||||
uint32_t format; /* compact unwind encoding, or zero if none */
|
||||
uint32_t unwind_info_size; /* size of dwarf unwind info, or zero if none */
|
||||
unw_word_t unwind_info; /* address of dwarf unwind info, or zero */
|
||||
unw_word_t extra; /* mach_header of mach-o image containing func */
|
||||
};
|
||||
typedef struct unw_proc_info_t unw_proc_info_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern int unw_getcontext(unw_context_t *) LIBUNWIND_AVAIL;
|
||||
extern int unw_init_local(unw_cursor_t *, unw_context_t *) LIBUNWIND_AVAIL;
|
||||
extern int unw_step(unw_cursor_t *) LIBUNWIND_AVAIL;
|
||||
extern int unw_get_reg(unw_cursor_t *, unw_regnum_t, unw_word_t *) LIBUNWIND_AVAIL;
|
||||
extern int unw_get_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t *) LIBUNWIND_AVAIL;
|
||||
extern int unw_set_reg(unw_cursor_t *, unw_regnum_t, unw_word_t) LIBUNWIND_AVAIL;
|
||||
extern int unw_set_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t) LIBUNWIND_AVAIL;
|
||||
extern int unw_resume(unw_cursor_t *) LIBUNWIND_AVAIL;
|
||||
|
||||
#ifdef __arm__
|
||||
/* Save VFP registers in FSTMX format (instead of FSTMD). */
|
||||
extern void unw_save_vfp_as_X(unw_cursor_t *) LIBUNWIND_AVAIL;
|
||||
#endif
|
||||
|
||||
|
||||
extern const char *unw_regname(unw_cursor_t *, unw_regnum_t) LIBUNWIND_AVAIL;
|
||||
extern int unw_get_proc_info(unw_cursor_t *, unw_proc_info_t *) LIBUNWIND_AVAIL;
|
||||
extern int unw_is_fpreg(unw_cursor_t *, unw_regnum_t) LIBUNWIND_AVAIL;
|
||||
extern int unw_is_signal_frame(unw_cursor_t *) LIBUNWIND_AVAIL;
|
||||
extern int unw_get_proc_name(unw_cursor_t *, char *, size_t, unw_word_t *) LIBUNWIND_AVAIL;
|
||||
//extern int unw_get_save_loc(unw_cursor_t*, int, unw_save_loc_t*);
|
||||
|
||||
extern unw_addr_space_t unw_local_addr_space;
|
||||
|
||||
#ifdef UNW_REMOTE
|
||||
/*
|
||||
* Mac OS X "remote" API for unwinding other processes on same machine
|
||||
*
|
||||
*/
|
||||
extern unw_addr_space_t unw_create_addr_space_for_task(task_t);
|
||||
extern void unw_destroy_addr_space(unw_addr_space_t);
|
||||
extern int unw_init_remote_thread(unw_cursor_t *, unw_addr_space_t, thread_t *);
|
||||
#endif /* UNW_REMOTE */
|
||||
|
||||
/*
|
||||
* traditional libuwind "remote" API
|
||||
* NOT IMPLEMENTED on Mac OS X
|
||||
*
|
||||
* extern int unw_init_remote(unw_cursor_t*, unw_addr_space_t,
|
||||
* thread_t*);
|
||||
* extern unw_accessors_t unw_get_accessors(unw_addr_space_t);
|
||||
* extern unw_addr_space_t unw_create_addr_space(unw_accessors_t, int);
|
||||
* extern void unw_flush_cache(unw_addr_space_t, unw_word_t,
|
||||
* unw_word_t);
|
||||
* extern int unw_set_caching_policy(unw_addr_space_t,
|
||||
* unw_caching_policy_t);
|
||||
* extern void _U_dyn_register(unw_dyn_info_t*);
|
||||
* extern void _U_dyn_cancel(unw_dyn_info_t*);
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
// architecture independent register numbers
|
||||
enum {
|
||||
UNW_REG_IP = -1, // instruction pointer
|
||||
UNW_REG_SP = -2, // stack pointer
|
||||
};
|
||||
|
||||
// 32-bit x86 registers
|
||||
enum {
|
||||
UNW_X86_EAX = 0,
|
||||
UNW_X86_ECX = 1,
|
||||
UNW_X86_EDX = 2,
|
||||
UNW_X86_EBX = 3,
|
||||
UNW_X86_EBP = 4,
|
||||
UNW_X86_ESP = 5,
|
||||
UNW_X86_ESI = 6,
|
||||
UNW_X86_EDI = 7
|
||||
};
|
||||
|
||||
// 64-bit x86_64 registers
|
||||
enum {
|
||||
UNW_X86_64_RAX = 0,
|
||||
UNW_X86_64_RDX = 1,
|
||||
UNW_X86_64_RCX = 2,
|
||||
UNW_X86_64_RBX = 3,
|
||||
UNW_X86_64_RSI = 4,
|
||||
UNW_X86_64_RDI = 5,
|
||||
UNW_X86_64_RBP = 6,
|
||||
UNW_X86_64_RSP = 7,
|
||||
UNW_X86_64_R8 = 8,
|
||||
UNW_X86_64_R9 = 9,
|
||||
UNW_X86_64_R10 = 10,
|
||||
UNW_X86_64_R11 = 11,
|
||||
UNW_X86_64_R12 = 12,
|
||||
UNW_X86_64_R13 = 13,
|
||||
UNW_X86_64_R14 = 14,
|
||||
UNW_X86_64_R15 = 15
|
||||
};
|
||||
|
||||
|
||||
// 32-bit ppc register numbers
|
||||
enum {
|
||||
UNW_PPC_R0 = 0,
|
||||
UNW_PPC_R1 = 1,
|
||||
UNW_PPC_R2 = 2,
|
||||
UNW_PPC_R3 = 3,
|
||||
UNW_PPC_R4 = 4,
|
||||
UNW_PPC_R5 = 5,
|
||||
UNW_PPC_R6 = 6,
|
||||
UNW_PPC_R7 = 7,
|
||||
UNW_PPC_R8 = 8,
|
||||
UNW_PPC_R9 = 9,
|
||||
UNW_PPC_R10 = 10,
|
||||
UNW_PPC_R11 = 11,
|
||||
UNW_PPC_R12 = 12,
|
||||
UNW_PPC_R13 = 13,
|
||||
UNW_PPC_R14 = 14,
|
||||
UNW_PPC_R15 = 15,
|
||||
UNW_PPC_R16 = 16,
|
||||
UNW_PPC_R17 = 17,
|
||||
UNW_PPC_R18 = 18,
|
||||
UNW_PPC_R19 = 19,
|
||||
UNW_PPC_R20 = 20,
|
||||
UNW_PPC_R21 = 21,
|
||||
UNW_PPC_R22 = 22,
|
||||
UNW_PPC_R23 = 23,
|
||||
UNW_PPC_R24 = 24,
|
||||
UNW_PPC_R25 = 25,
|
||||
UNW_PPC_R26 = 26,
|
||||
UNW_PPC_R27 = 27,
|
||||
UNW_PPC_R28 = 28,
|
||||
UNW_PPC_R29 = 29,
|
||||
UNW_PPC_R30 = 30,
|
||||
UNW_PPC_R31 = 31,
|
||||
UNW_PPC_F0 = 32,
|
||||
UNW_PPC_F1 = 33,
|
||||
UNW_PPC_F2 = 34,
|
||||
UNW_PPC_F3 = 35,
|
||||
UNW_PPC_F4 = 36,
|
||||
UNW_PPC_F5 = 37,
|
||||
UNW_PPC_F6 = 38,
|
||||
UNW_PPC_F7 = 39,
|
||||
UNW_PPC_F8 = 40,
|
||||
UNW_PPC_F9 = 41,
|
||||
UNW_PPC_F10 = 42,
|
||||
UNW_PPC_F11 = 43,
|
||||
UNW_PPC_F12 = 44,
|
||||
UNW_PPC_F13 = 45,
|
||||
UNW_PPC_F14 = 46,
|
||||
UNW_PPC_F15 = 47,
|
||||
UNW_PPC_F16 = 48,
|
||||
UNW_PPC_F17 = 49,
|
||||
UNW_PPC_F18 = 50,
|
||||
UNW_PPC_F19 = 51,
|
||||
UNW_PPC_F20 = 52,
|
||||
UNW_PPC_F21 = 53,
|
||||
UNW_PPC_F22 = 54,
|
||||
UNW_PPC_F23 = 55,
|
||||
UNW_PPC_F24 = 56,
|
||||
UNW_PPC_F25 = 57,
|
||||
UNW_PPC_F26 = 58,
|
||||
UNW_PPC_F27 = 59,
|
||||
UNW_PPC_F28 = 60,
|
||||
UNW_PPC_F29 = 61,
|
||||
UNW_PPC_F30 = 62,
|
||||
UNW_PPC_F31 = 63,
|
||||
UNW_PPC_MQ = 64,
|
||||
UNW_PPC_LR = 65,
|
||||
UNW_PPC_CTR = 66,
|
||||
UNW_PPC_AP = 67,
|
||||
UNW_PPC_CR0 = 68,
|
||||
UNW_PPC_CR1 = 69,
|
||||
UNW_PPC_CR2 = 70,
|
||||
UNW_PPC_CR3 = 71,
|
||||
UNW_PPC_CR4 = 72,
|
||||
UNW_PPC_CR5 = 73,
|
||||
UNW_PPC_CR6 = 74,
|
||||
UNW_PPC_CR7 = 75,
|
||||
UNW_PPC_XER = 76,
|
||||
UNW_PPC_V0 = 77,
|
||||
UNW_PPC_V1 = 78,
|
||||
UNW_PPC_V2 = 79,
|
||||
UNW_PPC_V3 = 80,
|
||||
UNW_PPC_V4 = 81,
|
||||
UNW_PPC_V5 = 82,
|
||||
UNW_PPC_V6 = 83,
|
||||
UNW_PPC_V7 = 84,
|
||||
UNW_PPC_V8 = 85,
|
||||
UNW_PPC_V9 = 86,
|
||||
UNW_PPC_V10 = 87,
|
||||
UNW_PPC_V11 = 88,
|
||||
UNW_PPC_V12 = 89,
|
||||
UNW_PPC_V13 = 90,
|
||||
UNW_PPC_V14 = 91,
|
||||
UNW_PPC_V15 = 92,
|
||||
UNW_PPC_V16 = 93,
|
||||
UNW_PPC_V17 = 94,
|
||||
UNW_PPC_V18 = 95,
|
||||
UNW_PPC_V19 = 96,
|
||||
UNW_PPC_V20 = 97,
|
||||
UNW_PPC_V21 = 98,
|
||||
UNW_PPC_V22 = 99,
|
||||
UNW_PPC_V23 = 100,
|
||||
UNW_PPC_V24 = 101,
|
||||
UNW_PPC_V25 = 102,
|
||||
UNW_PPC_V26 = 103,
|
||||
UNW_PPC_V27 = 104,
|
||||
UNW_PPC_V28 = 105,
|
||||
UNW_PPC_V29 = 106,
|
||||
UNW_PPC_V30 = 107,
|
||||
UNW_PPC_V31 = 108,
|
||||
UNW_PPC_VRSAVE = 109,
|
||||
UNW_PPC_VSCR = 110,
|
||||
UNW_PPC_SPE_ACC = 111,
|
||||
UNW_PPC_SPEFSCR = 112
|
||||
};
|
||||
|
||||
// 64-bit ARM64 registers
|
||||
enum {
|
||||
UNW_ARM64_X0 = 0,
|
||||
UNW_ARM64_X1 = 1,
|
||||
UNW_ARM64_X2 = 2,
|
||||
UNW_ARM64_X3 = 3,
|
||||
UNW_ARM64_X4 = 4,
|
||||
UNW_ARM64_X5 = 5,
|
||||
UNW_ARM64_X6 = 6,
|
||||
UNW_ARM64_X7 = 7,
|
||||
UNW_ARM64_X8 = 8,
|
||||
UNW_ARM64_X9 = 9,
|
||||
UNW_ARM64_X10 = 10,
|
||||
UNW_ARM64_X11 = 11,
|
||||
UNW_ARM64_X12 = 12,
|
||||
UNW_ARM64_X13 = 13,
|
||||
UNW_ARM64_X14 = 14,
|
||||
UNW_ARM64_X15 = 15,
|
||||
UNW_ARM64_X16 = 16,
|
||||
UNW_ARM64_X17 = 17,
|
||||
UNW_ARM64_X18 = 18,
|
||||
UNW_ARM64_X19 = 19,
|
||||
UNW_ARM64_X20 = 20,
|
||||
UNW_ARM64_X21 = 21,
|
||||
UNW_ARM64_X22 = 22,
|
||||
UNW_ARM64_X23 = 23,
|
||||
UNW_ARM64_X24 = 24,
|
||||
UNW_ARM64_X25 = 25,
|
||||
UNW_ARM64_X26 = 26,
|
||||
UNW_ARM64_X27 = 27,
|
||||
UNW_ARM64_X28 = 28,
|
||||
UNW_ARM64_X29 = 29,
|
||||
UNW_ARM64_FP = 29,
|
||||
UNW_ARM64_X30 = 30,
|
||||
UNW_ARM64_LR = 30,
|
||||
UNW_ARM64_X31 = 31,
|
||||
UNW_ARM64_SP = 31,
|
||||
// reserved block
|
||||
UNW_ARM64_D0 = 64,
|
||||
UNW_ARM64_D1 = 65,
|
||||
UNW_ARM64_D2 = 66,
|
||||
UNW_ARM64_D3 = 67,
|
||||
UNW_ARM64_D4 = 68,
|
||||
UNW_ARM64_D5 = 69,
|
||||
UNW_ARM64_D6 = 70,
|
||||
UNW_ARM64_D7 = 71,
|
||||
UNW_ARM64_D8 = 72,
|
||||
UNW_ARM64_D9 = 73,
|
||||
UNW_ARM64_D10 = 74,
|
||||
UNW_ARM64_D11 = 75,
|
||||
UNW_ARM64_D12 = 76,
|
||||
UNW_ARM64_D13 = 77,
|
||||
UNW_ARM64_D14 = 78,
|
||||
UNW_ARM64_D15 = 79,
|
||||
UNW_ARM64_D16 = 80,
|
||||
UNW_ARM64_D17 = 81,
|
||||
UNW_ARM64_D18 = 82,
|
||||
UNW_ARM64_D19 = 83,
|
||||
UNW_ARM64_D20 = 84,
|
||||
UNW_ARM64_D21 = 85,
|
||||
UNW_ARM64_D22 = 86,
|
||||
UNW_ARM64_D23 = 87,
|
||||
UNW_ARM64_D24 = 88,
|
||||
UNW_ARM64_D25 = 89,
|
||||
UNW_ARM64_D26 = 90,
|
||||
UNW_ARM64_D27 = 91,
|
||||
UNW_ARM64_D28 = 92,
|
||||
UNW_ARM64_D29 = 93,
|
||||
UNW_ARM64_D30 = 94,
|
||||
UNW_ARM64_D31 = 95,
|
||||
};
|
||||
|
||||
// 32-bit ARM registers. Numbers match DWARF for ARM spec #3.1 Table 1.
|
||||
// Naming scheme uses recommendations given in Note 4 for VFP-v2 and VFP-v3.
|
||||
// In this scheme, even though the 64-bit floating point registers D0-D31
|
||||
// overlap physically with the 32-bit floating pointer registers S0-S31,
|
||||
// they are given a non-overlapping range of register numbers.
|
||||
//
|
||||
// Commented out ranges are not preserved during unwinding.
|
||||
enum {
|
||||
UNW_ARM_R0 = 0,
|
||||
UNW_ARM_R1 = 1,
|
||||
UNW_ARM_R2 = 2,
|
||||
UNW_ARM_R3 = 3,
|
||||
UNW_ARM_R4 = 4,
|
||||
UNW_ARM_R5 = 5,
|
||||
UNW_ARM_R6 = 6,
|
||||
UNW_ARM_R7 = 7,
|
||||
UNW_ARM_R8 = 8,
|
||||
UNW_ARM_R9 = 9,
|
||||
UNW_ARM_R10 = 10,
|
||||
UNW_ARM_R11 = 11,
|
||||
UNW_ARM_R12 = 12,
|
||||
UNW_ARM_SP = 13, // Logical alias for UNW_REG_SP
|
||||
UNW_ARM_R13 = 13,
|
||||
UNW_ARM_LR = 14,
|
||||
UNW_ARM_R14 = 14,
|
||||
UNW_ARM_IP = 15, // Logical alias for UNW_REG_IP
|
||||
UNW_ARM_R15 = 15,
|
||||
// 16-63 -- OBSOLETE. Used in VFP1 to represent both S0-S31 and D0-D31.
|
||||
UNW_ARM_S0 = 64,
|
||||
UNW_ARM_S1 = 65,
|
||||
UNW_ARM_S2 = 66,
|
||||
UNW_ARM_S3 = 67,
|
||||
UNW_ARM_S4 = 68,
|
||||
UNW_ARM_S5 = 69,
|
||||
UNW_ARM_S6 = 70,
|
||||
UNW_ARM_S7 = 71,
|
||||
UNW_ARM_S8 = 72,
|
||||
UNW_ARM_S9 = 73,
|
||||
UNW_ARM_S10 = 74,
|
||||
UNW_ARM_S11 = 75,
|
||||
UNW_ARM_S12 = 76,
|
||||
UNW_ARM_S13 = 77,
|
||||
UNW_ARM_S14 = 78,
|
||||
UNW_ARM_S15 = 79,
|
||||
UNW_ARM_S16 = 80,
|
||||
UNW_ARM_S17 = 81,
|
||||
UNW_ARM_S18 = 82,
|
||||
UNW_ARM_S19 = 83,
|
||||
UNW_ARM_S20 = 84,
|
||||
UNW_ARM_S21 = 85,
|
||||
UNW_ARM_S22 = 86,
|
||||
UNW_ARM_S23 = 87,
|
||||
UNW_ARM_S24 = 88,
|
||||
UNW_ARM_S25 = 89,
|
||||
UNW_ARM_S26 = 90,
|
||||
UNW_ARM_S27 = 91,
|
||||
UNW_ARM_S28 = 92,
|
||||
UNW_ARM_S29 = 93,
|
||||
UNW_ARM_S30 = 94,
|
||||
UNW_ARM_S31 = 95,
|
||||
// 96-103 -- OBSOLETE. F0-F7. Used by the FPA system. Superseded by VFP.
|
||||
// 104-111 -- wCGR0-wCGR7, ACC0-ACC7 (Intel wireless MMX)
|
||||
UNW_ARM_WR0 = 112,
|
||||
UNW_ARM_WR1 = 113,
|
||||
UNW_ARM_WR2 = 114,
|
||||
UNW_ARM_WR3 = 115,
|
||||
UNW_ARM_WR4 = 116,
|
||||
UNW_ARM_WR5 = 117,
|
||||
UNW_ARM_WR6 = 118,
|
||||
UNW_ARM_WR7 = 119,
|
||||
UNW_ARM_WR8 = 120,
|
||||
UNW_ARM_WR9 = 121,
|
||||
UNW_ARM_WR10 = 122,
|
||||
UNW_ARM_WR11 = 123,
|
||||
UNW_ARM_WR12 = 124,
|
||||
UNW_ARM_WR13 = 125,
|
||||
UNW_ARM_WR14 = 126,
|
||||
UNW_ARM_WR15 = 127,
|
||||
// 128-133 -- SPSR, SPSR_{FIQ|IRQ|ABT|UND|SVC}
|
||||
// 134-143 -- Reserved
|
||||
// 144-150 -- R8_USR-R14_USR
|
||||
// 151-157 -- R8_FIQ-R14_FIQ
|
||||
// 158-159 -- R13_IRQ-R14_IRQ
|
||||
// 160-161 -- R13_ABT-R14_ABT
|
||||
// 162-163 -- R13_UND-R14_UND
|
||||
// 164-165 -- R13_SVC-R14_SVC
|
||||
// 166-191 -- Reserved
|
||||
UNW_ARM_WC0 = 192,
|
||||
UNW_ARM_WC1 = 193,
|
||||
UNW_ARM_WC2 = 194,
|
||||
UNW_ARM_WC3 = 195,
|
||||
// 196-199 -- wC4-wC7 (Intel wireless MMX control)
|
||||
// 200-255 -- Reserved
|
||||
UNW_ARM_D0 = 256,
|
||||
UNW_ARM_D1 = 257,
|
||||
UNW_ARM_D2 = 258,
|
||||
UNW_ARM_D3 = 259,
|
||||
UNW_ARM_D4 = 260,
|
||||
UNW_ARM_D5 = 261,
|
||||
UNW_ARM_D6 = 262,
|
||||
UNW_ARM_D7 = 263,
|
||||
UNW_ARM_D8 = 264,
|
||||
UNW_ARM_D9 = 265,
|
||||
UNW_ARM_D10 = 266,
|
||||
UNW_ARM_D11 = 267,
|
||||
UNW_ARM_D12 = 268,
|
||||
UNW_ARM_D13 = 269,
|
||||
UNW_ARM_D14 = 270,
|
||||
UNW_ARM_D15 = 271,
|
||||
UNW_ARM_D16 = 272,
|
||||
UNW_ARM_D17 = 273,
|
||||
UNW_ARM_D18 = 274,
|
||||
UNW_ARM_D19 = 275,
|
||||
UNW_ARM_D20 = 276,
|
||||
UNW_ARM_D21 = 277,
|
||||
UNW_ARM_D22 = 278,
|
||||
UNW_ARM_D23 = 279,
|
||||
UNW_ARM_D24 = 280,
|
||||
UNW_ARM_D25 = 281,
|
||||
UNW_ARM_D26 = 282,
|
||||
UNW_ARM_D27 = 283,
|
||||
UNW_ARM_D28 = 284,
|
||||
UNW_ARM_D29 = 285,
|
||||
UNW_ARM_D30 = 286,
|
||||
UNW_ARM_D31 = 287,
|
||||
// 288-319 -- Reserved for VFP/Neon
|
||||
// 320-8191 -- Reserved
|
||||
// 8192-16383 -- Unspecified vendor co-processor register.
|
||||
};
|
||||
|
||||
// OpenRISC1000 register numbers
|
||||
enum {
|
||||
UNW_OR1K_R0 = 0,
|
||||
UNW_OR1K_R1 = 1,
|
||||
UNW_OR1K_R2 = 2,
|
||||
UNW_OR1K_R3 = 3,
|
||||
UNW_OR1K_R4 = 4,
|
||||
UNW_OR1K_R5 = 5,
|
||||
UNW_OR1K_R6 = 6,
|
||||
UNW_OR1K_R7 = 7,
|
||||
UNW_OR1K_R8 = 8,
|
||||
UNW_OR1K_R9 = 9,
|
||||
UNW_OR1K_R10 = 10,
|
||||
UNW_OR1K_R11 = 11,
|
||||
UNW_OR1K_R12 = 12,
|
||||
UNW_OR1K_R13 = 13,
|
||||
UNW_OR1K_R14 = 14,
|
||||
UNW_OR1K_R15 = 15,
|
||||
UNW_OR1K_R16 = 16,
|
||||
UNW_OR1K_R17 = 17,
|
||||
UNW_OR1K_R18 = 18,
|
||||
UNW_OR1K_R19 = 19,
|
||||
UNW_OR1K_R20 = 20,
|
||||
UNW_OR1K_R21 = 21,
|
||||
UNW_OR1K_R22 = 22,
|
||||
UNW_OR1K_R23 = 23,
|
||||
UNW_OR1K_R24 = 24,
|
||||
UNW_OR1K_R25 = 25,
|
||||
UNW_OR1K_R26 = 26,
|
||||
UNW_OR1K_R27 = 27,
|
||||
UNW_OR1K_R28 = 28,
|
||||
UNW_OR1K_R29 = 29,
|
||||
UNW_OR1K_R30 = 30,
|
||||
UNW_OR1K_R31 = 31,
|
||||
};
|
||||
|
||||
#endif
|
478
include/mach-o/compact_unwind_encoding.h
Normal file
478
include/mach-o/compact_unwind_encoding.h
Normal file
@ -0,0 +1,478 @@
|
||||
//===------------------ mach-o/compact_unwind_encoding.h ------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//
|
||||
// Darwin's alternative to dwarf based unwind encodings.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
||||
#ifndef __COMPACT_UNWIND_ENCODING__
|
||||
#define __COMPACT_UNWIND_ENCODING__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
//
|
||||
// Compilers can emit standard Dwarf FDEs in the __TEXT,__eh_frame section
|
||||
// of object files. Or compilers can emit compact unwind information in
|
||||
// the __LD,__compact_unwind section.
|
||||
//
|
||||
// When the linker creates a final linked image, it will create a
|
||||
// __TEXT,__unwind_info section. This section is a small and fast way for the
|
||||
// runtime to access unwind info for any given function. If the compiler
|
||||
// emitted compact unwind info for the function, that compact unwind info will
|
||||
// be encoded in the __TEXT,__unwind_info section. If the compiler emitted
|
||||
// dwarf unwind info, the __TEXT,__unwind_info section will contain the offset
|
||||
// of the FDE in the __TEXT,__eh_frame section in the final linked image.
|
||||
//
|
||||
// Note: Previously, the linker would transform some dwarf unwind infos into
|
||||
// compact unwind info. But that is fragile and no longer done.
|
||||
|
||||
|
||||
//
|
||||
// The compact unwind endoding is a 32-bit value which encoded in an
|
||||
// architecture specific way, which registers to restore from where, and how
|
||||
// to unwind out of the function.
|
||||
//
|
||||
typedef uint32_t compact_unwind_encoding_t;
|
||||
|
||||
|
||||
// architecture independent bits
|
||||
enum {
|
||||
UNWIND_IS_NOT_FUNCTION_START = 0x80000000,
|
||||
UNWIND_HAS_LSDA = 0x40000000,
|
||||
UNWIND_PERSONALITY_MASK = 0x30000000,
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// x86
|
||||
//
|
||||
// 1-bit: start
|
||||
// 1-bit: has lsda
|
||||
// 2-bit: personality index
|
||||
//
|
||||
// 4-bits: 0=old, 1=ebp based, 2=stack-imm, 3=stack-ind, 4=dwarf
|
||||
// ebp based:
|
||||
// 15-bits (5*3-bits per reg) register permutation
|
||||
// 8-bits for stack offset
|
||||
// frameless:
|
||||
// 8-bits stack size
|
||||
// 3-bits stack adjust
|
||||
// 3-bits register count
|
||||
// 10-bits register permutation
|
||||
//
|
||||
enum {
|
||||
UNWIND_X86_MODE_MASK = 0x0F000000,
|
||||
UNWIND_X86_MODE_EBP_FRAME = 0x01000000,
|
||||
UNWIND_X86_MODE_STACK_IMMD = 0x02000000,
|
||||
UNWIND_X86_MODE_STACK_IND = 0x03000000,
|
||||
UNWIND_X86_MODE_DWARF = 0x04000000,
|
||||
|
||||
UNWIND_X86_EBP_FRAME_REGISTERS = 0x00007FFF,
|
||||
UNWIND_X86_EBP_FRAME_OFFSET = 0x00FF0000,
|
||||
|
||||
UNWIND_X86_FRAMELESS_STACK_SIZE = 0x00FF0000,
|
||||
UNWIND_X86_FRAMELESS_STACK_ADJUST = 0x0000E000,
|
||||
UNWIND_X86_FRAMELESS_STACK_REG_COUNT = 0x00001C00,
|
||||
UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF,
|
||||
|
||||
UNWIND_X86_DWARF_SECTION_OFFSET = 0x00FFFFFF,
|
||||
};
|
||||
|
||||
enum {
|
||||
UNWIND_X86_REG_NONE = 0,
|
||||
UNWIND_X86_REG_EBX = 1,
|
||||
UNWIND_X86_REG_ECX = 2,
|
||||
UNWIND_X86_REG_EDX = 3,
|
||||
UNWIND_X86_REG_EDI = 4,
|
||||
UNWIND_X86_REG_ESI = 5,
|
||||
UNWIND_X86_REG_EBP = 6,
|
||||
};
|
||||
|
||||
//
|
||||
// For x86 there are four modes for the compact unwind encoding:
|
||||
// UNWIND_X86_MODE_EBP_FRAME:
|
||||
// EBP based frame where EBP is push on stack immediately after return address,
|
||||
// then ESP is moved to EBP. Thus, to unwind ESP is restored with the current
|
||||
// EPB value, then EBP is restored by popping off the stack, and the return
|
||||
// is done by popping the stack once more into the pc.
|
||||
// All non-volatile registers that need to be restored must have been saved
|
||||
// in a small range in the stack that starts EBP-4 to EBP-1020. The offset/4
|
||||
// is encoded in the UNWIND_X86_EBP_FRAME_OFFSET bits. The registers saved
|
||||
// are encoded in the UNWIND_X86_EBP_FRAME_REGISTERS bits as five 3-bit entries.
|
||||
// Each entry contains which register to restore.
|
||||
// UNWIND_X86_MODE_STACK_IMMD:
|
||||
// A "frameless" (EBP not used as frame pointer) function with a small
|
||||
// constant stack size. To return, a constant (encoded in the compact
|
||||
// unwind encoding) is added to the ESP. Then the return is done by
|
||||
// popping the stack into the pc.
|
||||
// All non-volatile registers that need to be restored must have been saved
|
||||
// on the stack immediately after the return address. The stack_size/4 is
|
||||
// encoded in the UNWIND_X86_FRAMELESS_STACK_SIZE (max stack size is 1024).
|
||||
// The number of registers saved is encoded in UNWIND_X86_FRAMELESS_STACK_REG_COUNT.
|
||||
// UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION constains which registers were
|
||||
// saved and their order.
|
||||
// UNWIND_X86_MODE_STACK_IND:
|
||||
// A "frameless" (EBP not used as frame pointer) function large constant
|
||||
// stack size. This case is like the previous, except the stack size is too
|
||||
// large to encode in the compact unwind encoding. Instead it requires that
|
||||
// the function contains "subl $nnnnnnnn,ESP" in its prolog. The compact
|
||||
// encoding contains the offset to the nnnnnnnn value in the function in
|
||||
// UNWIND_X86_FRAMELESS_STACK_SIZE.
|
||||
// UNWIND_X86_MODE_DWARF:
|
||||
// No compact unwind encoding is available. Instead the low 24-bits of the
|
||||
// compact encoding is the offset of the dwarf FDE in the __eh_frame section.
|
||||
// This mode is never used in object files. It is only generated by the
|
||||
// linker in final linked images which have only dwarf unwind info for a
|
||||
// function.
|
||||
//
|
||||
// The permutation encoding is a Lehmer code sequence encoded into a
|
||||
// single variable-base number so we can encode the ordering of up to
|
||||
// six registers in a 10-bit space.
|
||||
//
|
||||
// The following is the algorithm used to create the permutation encoding used
|
||||
// with frameless stacks. It is passed the number of registers to be saved and
|
||||
// an array of the register numbers saved.
|
||||
//
|
||||
//uint32_t permute_encode(uint32_t registerCount, const uint32_t registers[6])
|
||||
//{
|
||||
// uint32_t renumregs[6];
|
||||
// for (int i=6-registerCount; i < 6; ++i) {
|
||||
// int countless = 0;
|
||||
// for (int j=6-registerCount; j < i; ++j) {
|
||||
// if ( registers[j] < registers[i] )
|
||||
// ++countless;
|
||||
// }
|
||||
// renumregs[i] = registers[i] - countless -1;
|
||||
// }
|
||||
// uint32_t permutationEncoding = 0;
|
||||
// switch ( registerCount ) {
|
||||
// case 6:
|
||||
// permutationEncoding |= (120*renumregs[0] + 24*renumregs[1]
|
||||
// + 6*renumregs[2] + 2*renumregs[3]
|
||||
// + renumregs[4]);
|
||||
// break;
|
||||
// case 5:
|
||||
// permutationEncoding |= (120*renumregs[1] + 24*renumregs[2]
|
||||
// + 6*renumregs[3] + 2*renumregs[4]
|
||||
// + renumregs[5]);
|
||||
// break;
|
||||
// case 4:
|
||||
// permutationEncoding |= (60*renumregs[2] + 12*renumregs[3]
|
||||
// + 3*renumregs[4] + renumregs[5]);
|
||||
// break;
|
||||
// case 3:
|
||||
// permutationEncoding |= (20*renumregs[3] + 4*renumregs[4]
|
||||
// + renumregs[5]);
|
||||
// break;
|
||||
// case 2:
|
||||
// permutationEncoding |= (5*renumregs[4] + renumregs[5]);
|
||||
// break;
|
||||
// case 1:
|
||||
// permutationEncoding |= (renumregs[5]);
|
||||
// break;
|
||||
// }
|
||||
// return permutationEncoding;
|
||||
//}
|
||||
//
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// x86_64
|
||||
//
|
||||
// 1-bit: start
|
||||
// 1-bit: has lsda
|
||||
// 2-bit: personality index
|
||||
//
|
||||
// 4-bits: 0=old, 1=rbp based, 2=stack-imm, 3=stack-ind, 4=dwarf
|
||||
// rbp based:
|
||||
// 15-bits (5*3-bits per reg) register permutation
|
||||
// 8-bits for stack offset
|
||||
// frameless:
|
||||
// 8-bits stack size
|
||||
// 3-bits stack adjust
|
||||
// 3-bits register count
|
||||
// 10-bits register permutation
|
||||
//
|
||||
enum {
|
||||
UNWIND_X86_64_MODE_MASK = 0x0F000000,
|
||||
UNWIND_X86_64_MODE_RBP_FRAME = 0x01000000,
|
||||
UNWIND_X86_64_MODE_STACK_IMMD = 0x02000000,
|
||||
UNWIND_X86_64_MODE_STACK_IND = 0x03000000,
|
||||
UNWIND_X86_64_MODE_DWARF = 0x04000000,
|
||||
|
||||
UNWIND_X86_64_RBP_FRAME_REGISTERS = 0x00007FFF,
|
||||
UNWIND_X86_64_RBP_FRAME_OFFSET = 0x00FF0000,
|
||||
|
||||
UNWIND_X86_64_FRAMELESS_STACK_SIZE = 0x00FF0000,
|
||||
UNWIND_X86_64_FRAMELESS_STACK_ADJUST = 0x0000E000,
|
||||
UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT = 0x00001C00,
|
||||
UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF,
|
||||
|
||||
UNWIND_X86_64_DWARF_SECTION_OFFSET = 0x00FFFFFF,
|
||||
};
|
||||
|
||||
enum {
|
||||
UNWIND_X86_64_REG_NONE = 0,
|
||||
UNWIND_X86_64_REG_RBX = 1,
|
||||
UNWIND_X86_64_REG_R12 = 2,
|
||||
UNWIND_X86_64_REG_R13 = 3,
|
||||
UNWIND_X86_64_REG_R14 = 4,
|
||||
UNWIND_X86_64_REG_R15 = 5,
|
||||
UNWIND_X86_64_REG_RBP = 6,
|
||||
};
|
||||
//
|
||||
// For x86_64 there are four modes for the compact unwind encoding:
|
||||
// UNWIND_X86_64_MODE_RBP_FRAME:
|
||||
// RBP based frame where RBP is push on stack immediately after return address,
|
||||
// then RSP is moved to RBP. Thus, to unwind RSP is restored with the current
|
||||
// EPB value, then RBP is restored by popping off the stack, and the return
|
||||
// is done by popping the stack once more into the pc.
|
||||
// All non-volatile registers that need to be restored must have been saved
|
||||
// in a small range in the stack that starts RBP-8 to RBP-2040. The offset/8
|
||||
// is encoded in the UNWIND_X86_64_RBP_FRAME_OFFSET bits. The registers saved
|
||||
// are encoded in the UNWIND_X86_64_RBP_FRAME_REGISTERS bits as five 3-bit entries.
|
||||
// Each entry contains which register to restore.
|
||||
// UNWIND_X86_64_MODE_STACK_IMMD:
|
||||
// A "frameless" (RBP not used as frame pointer) function with a small
|
||||
// constant stack size. To return, a constant (encoded in the compact
|
||||
// unwind encoding) is added to the RSP. Then the return is done by
|
||||
// popping the stack into the pc.
|
||||
// All non-volatile registers that need to be restored must have been saved
|
||||
// on the stack immediately after the return address. The stack_size/8 is
|
||||
// encoded in the UNWIND_X86_64_FRAMELESS_STACK_SIZE (max stack size is 2048).
|
||||
// The number of registers saved is encoded in UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT.
|
||||
// UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION constains which registers were
|
||||
// saved and their order.
|
||||
// UNWIND_X86_64_MODE_STACK_IND:
|
||||
// A "frameless" (RBP not used as frame pointer) function large constant
|
||||
// stack size. This case is like the previous, except the stack size is too
|
||||
// large to encode in the compact unwind encoding. Instead it requires that
|
||||
// the function contains "subq $nnnnnnnn,RSP" in its prolog. The compact
|
||||
// encoding contains the offset to the nnnnnnnn value in the function in
|
||||
// UNWIND_X86_64_FRAMELESS_STACK_SIZE.
|
||||
// UNWIND_X86_64_MODE_DWARF:
|
||||
// No compact unwind encoding is available. Instead the low 24-bits of the
|
||||
// compact encoding is the offset of the dwarf FDE in the __eh_frame section.
|
||||
// This mode is never used in object files. It is only generated by the
|
||||
// linker in final linked images which have only dwarf unwind info for a
|
||||
// function.
|
||||
//
|
||||
|
||||
|
||||
// ARM64
|
||||
//
|
||||
// 1-bit: start
|
||||
// 1-bit: has lsda
|
||||
// 2-bit: personality index
|
||||
//
|
||||
// 4-bits: 4=frame-based, 3=dwarf, 2=frameless
|
||||
// frameless:
|
||||
// 12-bits of stack size
|
||||
// frame-based:
|
||||
// 4-bits D reg pairs saved
|
||||
// 5-bits X reg pairs saved
|
||||
// dwarf:
|
||||
// 24-bits offset of dwarf FDE in __eh_frame section
|
||||
//
|
||||
enum {
|
||||
UNWIND_ARM64_MODE_MASK = 0x0F000000,
|
||||
UNWIND_ARM64_MODE_FRAMELESS = 0x02000000,
|
||||
UNWIND_ARM64_MODE_DWARF = 0x03000000,
|
||||
UNWIND_ARM64_MODE_FRAME = 0x04000000,
|
||||
|
||||
UNWIND_ARM64_FRAME_X19_X20_PAIR = 0x00000001,
|
||||
UNWIND_ARM64_FRAME_X21_X22_PAIR = 0x00000002,
|
||||
UNWIND_ARM64_FRAME_X23_X24_PAIR = 0x00000004,
|
||||
UNWIND_ARM64_FRAME_X25_X26_PAIR = 0x00000008,
|
||||
UNWIND_ARM64_FRAME_X27_X28_PAIR = 0x00000010,
|
||||
UNWIND_ARM64_FRAME_D8_D9_PAIR = 0x00000100,
|
||||
UNWIND_ARM64_FRAME_D10_D11_PAIR = 0x00000200,
|
||||
UNWIND_ARM64_FRAME_D12_D13_PAIR = 0x00000400,
|
||||
UNWIND_ARM64_FRAME_D14_D15_PAIR = 0x00000800,
|
||||
|
||||
UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK = 0x00FFF000,
|
||||
UNWIND_ARM64_DWARF_SECTION_OFFSET = 0x00FFFFFF,
|
||||
};
|
||||
// For arm64 there are three modes for the compact unwind encoding:
|
||||
// UNWIND_ARM64_MODE_FRAME:
|
||||
// This is a standard arm64 prolog where FP/LR are immediately pushed on the
|
||||
// stack, then SP is copied to FP. If there are any non-volatile registers
|
||||
// saved, then are copied into the stack frame in pairs in a contiguous
|
||||
// range right below the saved FP/LR pair. Any subset of the five X pairs
|
||||
// and four D pairs can be saved, but the memory layout must be in register
|
||||
// number order.
|
||||
// UNWIND_ARM64_MODE_FRAMELESS:
|
||||
// A "frameless" leaf function, where FP/LR are not saved. The return address
|
||||
// remains in LR throughout the function. If any non-volatile registers
|
||||
// are saved, they must be pushed onto the stack before any stack space is
|
||||
// allocated for local variables. The stack sized (including any saved
|
||||
// non-volatile registers) divided by 16 is encoded in the bits
|
||||
// UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK.
|
||||
// UNWIND_ARM64_MODE_DWARF:
|
||||
// No compact unwind encoding is available. Instead the low 24-bits of the
|
||||
// compact encoding is the offset of the dwarf FDE in the __eh_frame section.
|
||||
// This mode is never used in object files. It is only generated by the
|
||||
// linker in final linked images which have only dwarf unwind info for a
|
||||
// function.
|
||||
//
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Relocatable Object Files: __LD,__compact_unwind
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// A compiler can generated compact unwind information for a function by adding
|
||||
// a "row" to the __LD,__compact_unwind section. This section has the
|
||||
// S_ATTR_DEBUG bit set, so the section will be ignored by older linkers.
|
||||
// It is removed by the new linker, so never ends up in final executables.
|
||||
// This section is a table, initially with one row per function (that needs
|
||||
// unwind info). The table columns and some conceptual entries are:
|
||||
//
|
||||
// range-start pointer to start of function/range
|
||||
// range-length
|
||||
// compact-unwind-encoding 32-bit encoding
|
||||
// personality-function or zero if no personality function
|
||||
// lsda or zero if no LSDA data
|
||||
//
|
||||
// The length and encoding fields are 32-bits. The other are all pointer sized.
|
||||
//
|
||||
// In x86_64 assembly, these entry would look like:
|
||||
//
|
||||
// .section __LD,__compact_unwind,regular,debug
|
||||
//
|
||||
// #compact unwind for _foo
|
||||
// .quad _foo
|
||||
// .set L1,LfooEnd-_foo
|
||||
// .long L1
|
||||
// .long 0x01010001
|
||||
// .quad 0
|
||||
// .quad 0
|
||||
//
|
||||
// #compact unwind for _bar
|
||||
// .quad _bar
|
||||
// .set L2,LbarEnd-_bar
|
||||
// .long L2
|
||||
// .long 0x01020011
|
||||
// .quad __gxx_personality
|
||||
// .quad except_tab1
|
||||
//
|
||||
//
|
||||
// Notes: There is no need for any labels in the the __compact_unwind section.
|
||||
// The use of the .set directive is to force the evaluation of the
|
||||
// range-length at assembly time, instead of generating relocations.
|
||||
//
|
||||
// To support future compiler optimizations where which non-volatile registers
|
||||
// are saved changes within a function (e.g. delay saving non-volatiles until
|
||||
// necessary), there can by multiple lines in the __compact_unwind table for one
|
||||
// function, each with a different (non-overlapping) range and each with
|
||||
// different compact unwind encodings that correspond to the non-volatiles
|
||||
// saved at that range of the function.
|
||||
//
|
||||
// If a particular function is so wacky that there is no compact unwind way
|
||||
// to encode it, then the compiler can emit traditional dwarf unwind info.
|
||||
// The runtime will use which ever is available.
|
||||
//
|
||||
// Runtime support for compact unwind encodings are only available on 10.6
|
||||
// and later. So, the compiler should not generate it when targeting pre-10.6.
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Final Linked Images: __TEXT,__unwind_info
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// The __TEXT,__unwind_info section is laid out for an efficient two level lookup.
|
||||
// The header of the section contains a coarse index that maps function address
|
||||
// to the page (4096 byte block) containing the unwind info for that function.
|
||||
//
|
||||
|
||||
#define UNWIND_SECTION_VERSION 1
|
||||
struct unwind_info_section_header
|
||||
{
|
||||
uint32_t version; // UNWIND_SECTION_VERSION
|
||||
uint32_t commonEncodingsArraySectionOffset;
|
||||
uint32_t commonEncodingsArrayCount;
|
||||
uint32_t personalityArraySectionOffset;
|
||||
uint32_t personalityArrayCount;
|
||||
uint32_t indexSectionOffset;
|
||||
uint32_t indexCount;
|
||||
// compact_unwind_encoding_t[]
|
||||
// uint32_t personalities[]
|
||||
// unwind_info_section_header_index_entry[]
|
||||
// unwind_info_section_header_lsda_index_entry[]
|
||||
};
|
||||
|
||||
struct unwind_info_section_header_index_entry
|
||||
{
|
||||
uint32_t functionOffset;
|
||||
uint32_t secondLevelPagesSectionOffset; // section offset to start of regular or compress page
|
||||
uint32_t lsdaIndexArraySectionOffset; // section offset to start of lsda_index array for this range
|
||||
};
|
||||
|
||||
struct unwind_info_section_header_lsda_index_entry
|
||||
{
|
||||
uint32_t functionOffset;
|
||||
uint32_t lsdaOffset;
|
||||
};
|
||||
|
||||
//
|
||||
// There are two kinds of second level index pages: regular and compressed.
|
||||
// A compressed page can hold up to 1021 entries, but it cannot be used
|
||||
// if too many different encoding types are used. The regular page holds
|
||||
// 511 entries.
|
||||
//
|
||||
|
||||
struct unwind_info_regular_second_level_entry
|
||||
{
|
||||
uint32_t functionOffset;
|
||||
compact_unwind_encoding_t encoding;
|
||||
};
|
||||
|
||||
#define UNWIND_SECOND_LEVEL_REGULAR 2
|
||||
struct unwind_info_regular_second_level_page_header
|
||||
{
|
||||
uint32_t kind; // UNWIND_SECOND_LEVEL_REGULAR
|
||||
uint16_t entryPageOffset;
|
||||
uint16_t entryCount;
|
||||
// entry array
|
||||
};
|
||||
|
||||
#define UNWIND_SECOND_LEVEL_COMPRESSED 3
|
||||
struct unwind_info_compressed_second_level_page_header
|
||||
{
|
||||
uint32_t kind; // UNWIND_SECOND_LEVEL_COMPRESSED
|
||||
uint16_t entryPageOffset;
|
||||
uint16_t entryCount;
|
||||
uint16_t encodingsPageOffset;
|
||||
uint16_t encodingsCount;
|
||||
// 32-bit entry array
|
||||
// encodings array
|
||||
};
|
||||
|
||||
#define UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry) (entry & 0x00FFFFFF)
|
||||
#define UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry) ((entry >> 24) & 0xFF)
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
372
include/unwind.h
Normal file
372
include/unwind.h
Normal file
@ -0,0 +1,372 @@
|
||||
//===------------------------------- unwind.h -----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//
|
||||
// C++ ABI Level 1 ABI documented at:
|
||||
// http://mentorembedded.github.io/cxx-abi/abi-eh.html
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __UNWIND_H__
|
||||
#define __UNWIND_H__
|
||||
|
||||
#include <__libunwind_config.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#define LIBUNWIND_UNAVAIL __attribute__ (( unavailable ))
|
||||
#else
|
||||
#define LIBUNWIND_UNAVAIL
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
_URC_NO_REASON = 0,
|
||||
_URC_OK = 0,
|
||||
_URC_FOREIGN_EXCEPTION_CAUGHT = 1,
|
||||
_URC_FATAL_PHASE2_ERROR = 2,
|
||||
_URC_FATAL_PHASE1_ERROR = 3,
|
||||
_URC_NORMAL_STOP = 4,
|
||||
_URC_END_OF_STACK = 5,
|
||||
_URC_HANDLER_FOUND = 6,
|
||||
_URC_INSTALL_CONTEXT = 7,
|
||||
_URC_CONTINUE_UNWIND = 8,
|
||||
#if _LIBUNWIND_ARM_EHABI
|
||||
_URC_FAILURE = 9
|
||||
#endif
|
||||
} _Unwind_Reason_Code;
|
||||
|
||||
typedef enum {
|
||||
_UA_SEARCH_PHASE = 1,
|
||||
_UA_CLEANUP_PHASE = 2,
|
||||
_UA_HANDLER_FRAME = 4,
|
||||
_UA_FORCE_UNWIND = 8,
|
||||
_UA_END_OF_STACK = 16 // gcc extension to C++ ABI
|
||||
} _Unwind_Action;
|
||||
|
||||
typedef struct _Unwind_Context _Unwind_Context; // opaque
|
||||
|
||||
#if _LIBUNWIND_ARM_EHABI
|
||||
typedef uint32_t _Unwind_State;
|
||||
|
||||
static const _Unwind_State _US_VIRTUAL_UNWIND_FRAME = 0;
|
||||
static const _Unwind_State _US_UNWIND_FRAME_STARTING = 1;
|
||||
static const _Unwind_State _US_UNWIND_FRAME_RESUME = 2;
|
||||
/* Undocumented flag for force unwinding. */
|
||||
static const _Unwind_State _US_FORCE_UNWIND = 8;
|
||||
|
||||
typedef uint32_t _Unwind_EHT_Header;
|
||||
|
||||
struct _Unwind_Control_Block;
|
||||
typedef struct _Unwind_Control_Block _Unwind_Control_Block;
|
||||
typedef struct _Unwind_Control_Block _Unwind_Exception; /* Alias */
|
||||
|
||||
struct _Unwind_Control_Block {
|
||||
uint64_t exception_class;
|
||||
void (*exception_cleanup)(_Unwind_Reason_Code, _Unwind_Control_Block*);
|
||||
|
||||
/* Unwinder cache, private fields for the unwinder's use */
|
||||
struct {
|
||||
uint32_t reserved1; /* init reserved1 to 0, then don't touch */
|
||||
uint32_t reserved2;
|
||||
uint32_t reserved3;
|
||||
uint32_t reserved4;
|
||||
uint32_t reserved5;
|
||||
} unwinder_cache;
|
||||
|
||||
/* Propagation barrier cache (valid after phase 1): */
|
||||
struct {
|
||||
uint32_t sp;
|
||||
uint32_t bitpattern[5];
|
||||
} barrier_cache;
|
||||
|
||||
/* Cleanup cache (preserved over cleanup): */
|
||||
struct {
|
||||
uint32_t bitpattern[4];
|
||||
} cleanup_cache;
|
||||
|
||||
/* Pr cache (for pr's benefit): */
|
||||
struct {
|
||||
uint32_t fnstart; /* function start address */
|
||||
_Unwind_EHT_Header* ehtp; /* pointer to EHT entry header word */
|
||||
uint32_t additional;
|
||||
uint32_t reserved1;
|
||||
} pr_cache;
|
||||
|
||||
long long int :0; /* Enforce the 8-byte alignment */
|
||||
};
|
||||
|
||||
typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)
|
||||
(_Unwind_State state,
|
||||
_Unwind_Exception* exceptionObject,
|
||||
struct _Unwind_Context* context);
|
||||
|
||||
typedef _Unwind_Reason_Code (*__personality_routine)
|
||||
(_Unwind_State state,
|
||||
_Unwind_Exception* exceptionObject,
|
||||
struct _Unwind_Context* context);
|
||||
#else
|
||||
struct _Unwind_Context; // opaque
|
||||
struct _Unwind_Exception; // forward declaration
|
||||
typedef struct _Unwind_Exception _Unwind_Exception;
|
||||
|
||||
struct _Unwind_Exception {
|
||||
uint64_t exception_class;
|
||||
void (*exception_cleanup)(_Unwind_Reason_Code reason,
|
||||
_Unwind_Exception *exc);
|
||||
uintptr_t private_1; // non-zero means forced unwind
|
||||
uintptr_t private_2; // holds sp that phase1 found for phase2 to use
|
||||
#ifndef __LP64__
|
||||
// The gcc implementation of _Unwind_Exception used attribute mode on the
|
||||
// above fields which had the side effect of causing this whole struct to
|
||||
// round up to 32 bytes in size. To be more explicit, we add pad fields
|
||||
// added for binary compatibility.
|
||||
uint32_t reserved[3];
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)
|
||||
(int version,
|
||||
_Unwind_Action actions,
|
||||
uint64_t exceptionClass,
|
||||
_Unwind_Exception* exceptionObject,
|
||||
struct _Unwind_Context* context,
|
||||
void* stop_parameter );
|
||||
|
||||
typedef _Unwind_Reason_Code (*__personality_routine)
|
||||
(int version,
|
||||
_Unwind_Action actions,
|
||||
uint64_t exceptionClass,
|
||||
_Unwind_Exception* exceptionObject,
|
||||
struct _Unwind_Context* context);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//
|
||||
// The following are the base functions documented by the C++ ABI
|
||||
//
|
||||
#ifdef __USING_SJLJ_EXCEPTIONS__
|
||||
extern _Unwind_Reason_Code
|
||||
_Unwind_SjLj_RaiseException(_Unwind_Exception *exception_object);
|
||||
extern void _Unwind_SjLj_Resume(_Unwind_Exception *exception_object);
|
||||
#else
|
||||
extern _Unwind_Reason_Code
|
||||
_Unwind_RaiseException(_Unwind_Exception *exception_object);
|
||||
extern void _Unwind_Resume(_Unwind_Exception *exception_object);
|
||||
#endif
|
||||
extern void _Unwind_DeleteException(_Unwind_Exception *exception_object);
|
||||
|
||||
#if _LIBUNWIND_ARM_EHABI
|
||||
typedef enum {
|
||||
_UVRSC_CORE = 0, /* integer register */
|
||||
_UVRSC_VFP = 1, /* vfp */
|
||||
_UVRSC_WMMXD = 3, /* Intel WMMX data register */
|
||||
_UVRSC_WMMXC = 4 /* Intel WMMX control register */
|
||||
} _Unwind_VRS_RegClass;
|
||||
|
||||
typedef enum {
|
||||
_UVRSD_UINT32 = 0,
|
||||
_UVRSD_VFPX = 1,
|
||||
_UVRSD_UINT64 = 3,
|
||||
_UVRSD_FLOAT = 4,
|
||||
_UVRSD_DOUBLE = 5
|
||||
} _Unwind_VRS_DataRepresentation;
|
||||
|
||||
typedef enum {
|
||||
_UVRSR_OK = 0,
|
||||
_UVRSR_NOT_IMPLEMENTED = 1,
|
||||
_UVRSR_FAILED = 2
|
||||
} _Unwind_VRS_Result;
|
||||
|
||||
extern void _Unwind_Complete(_Unwind_Exception* exception_object);
|
||||
|
||||
extern _Unwind_VRS_Result
|
||||
_Unwind_VRS_Get(_Unwind_Context *context, _Unwind_VRS_RegClass regclass,
|
||||
uint32_t regno, _Unwind_VRS_DataRepresentation representation,
|
||||
void *valuep);
|
||||
|
||||
extern _Unwind_VRS_Result
|
||||
_Unwind_VRS_Set(_Unwind_Context *context, _Unwind_VRS_RegClass regclass,
|
||||
uint32_t regno, _Unwind_VRS_DataRepresentation representation,
|
||||
void *valuep);
|
||||
|
||||
extern _Unwind_VRS_Result
|
||||
_Unwind_VRS_Pop(_Unwind_Context *context, _Unwind_VRS_RegClass regclass,
|
||||
uint32_t discriminator,
|
||||
_Unwind_VRS_DataRepresentation representation);
|
||||
#endif
|
||||
|
||||
#if !_LIBUNWIND_ARM_EHABI
|
||||
|
||||
extern uintptr_t _Unwind_GetGR(struct _Unwind_Context *context, int index);
|
||||
extern void _Unwind_SetGR(struct _Unwind_Context *context, int index,
|
||||
uintptr_t new_value);
|
||||
extern uintptr_t _Unwind_GetIP(struct _Unwind_Context *context);
|
||||
extern void _Unwind_SetIP(struct _Unwind_Context *, uintptr_t new_value);
|
||||
|
||||
#else // _LIBUNWIND_ARM_EHABI
|
||||
|
||||
#if defined(_LIBUNWIND_UNWIND_LEVEL1_EXTERNAL_LINKAGE)
|
||||
#define _LIBUNWIND_EXPORT_UNWIND_LEVEL1 extern
|
||||
#else
|
||||
#define _LIBUNWIND_EXPORT_UNWIND_LEVEL1 static __inline__
|
||||
#endif
|
||||
|
||||
// These are de facto helper functions for ARM, which delegate the function
|
||||
// calls to _Unwind_VRS_Get/Set(). These are not a part of ARM EHABI
|
||||
// specification, thus these function MUST be inlined. Please don't replace
|
||||
// these with the "extern" function declaration; otherwise, the program
|
||||
// including this <unwind.h> header won't be ABI compatible and will result in
|
||||
// link error when we are linking the program with libgcc.
|
||||
|
||||
_LIBUNWIND_EXPORT_UNWIND_LEVEL1
|
||||
uintptr_t _Unwind_GetGR(struct _Unwind_Context *context, int index) {
|
||||
uintptr_t value = 0;
|
||||
_Unwind_VRS_Get(context, _UVRSC_CORE, (uint32_t)index, _UVRSD_UINT32, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
_LIBUNWIND_EXPORT_UNWIND_LEVEL1
|
||||
void _Unwind_SetGR(struct _Unwind_Context *context, int index,
|
||||
uintptr_t value) {
|
||||
_Unwind_VRS_Set(context, _UVRSC_CORE, (uint32_t)index, _UVRSD_UINT32, &value);
|
||||
}
|
||||
|
||||
_LIBUNWIND_EXPORT_UNWIND_LEVEL1
|
||||
uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) {
|
||||
// remove the thumb-bit before returning
|
||||
return _Unwind_GetGR(context, 15) & (~(uintptr_t)0x1);
|
||||
}
|
||||
|
||||
_LIBUNWIND_EXPORT_UNWIND_LEVEL1
|
||||
void _Unwind_SetIP(struct _Unwind_Context *context, uintptr_t value) {
|
||||
uintptr_t thumb_bit = _Unwind_GetGR(context, 15) & ((uintptr_t)0x1);
|
||||
_Unwind_SetGR(context, 15, value | thumb_bit);
|
||||
}
|
||||
#endif // _LIBUNWIND_ARM_EHABI
|
||||
|
||||
extern uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context *context);
|
||||
extern uintptr_t
|
||||
_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context);
|
||||
#ifdef __USING_SJLJ_EXCEPTIONS__
|
||||
extern _Unwind_Reason_Code
|
||||
_Unwind_SjLj_ForcedUnwind(_Unwind_Exception *exception_object,
|
||||
_Unwind_Stop_Fn stop, void *stop_parameter);
|
||||
#else
|
||||
extern _Unwind_Reason_Code
|
||||
_Unwind_ForcedUnwind(_Unwind_Exception *exception_object,
|
||||
_Unwind_Stop_Fn stop, void *stop_parameter);
|
||||
#endif
|
||||
|
||||
#ifdef __USING_SJLJ_EXCEPTIONS__
|
||||
typedef struct _Unwind_FunctionContext *_Unwind_FunctionContext_t;
|
||||
extern void _Unwind_SjLj_Register(_Unwind_FunctionContext_t fc);
|
||||
extern void _Unwind_SjLj_Unregister(_Unwind_FunctionContext_t fc);
|
||||
#endif
|
||||
|
||||
//
|
||||
// The following are semi-suppoted extensions to the C++ ABI
|
||||
//
|
||||
|
||||
//
|
||||
// called by __cxa_rethrow().
|
||||
//
|
||||
#ifdef __USING_SJLJ_EXCEPTIONS__
|
||||
extern _Unwind_Reason_Code
|
||||
_Unwind_SjLj_Resume_or_Rethrow(_Unwind_Exception *exception_object);
|
||||
#else
|
||||
extern _Unwind_Reason_Code
|
||||
_Unwind_Resume_or_Rethrow(_Unwind_Exception *exception_object);
|
||||
#endif
|
||||
|
||||
// _Unwind_Backtrace() is a gcc extension that walks the stack and calls the
|
||||
// _Unwind_Trace_Fn once per frame until it reaches the bottom of the stack
|
||||
// or the _Unwind_Trace_Fn function returns something other than _URC_NO_REASON.
|
||||
typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn)(struct _Unwind_Context *,
|
||||
void *);
|
||||
extern _Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn, void *);
|
||||
|
||||
// _Unwind_GetCFA is a gcc extension that can be called from within a
|
||||
// personality handler to get the CFA (stack pointer before call) of
|
||||
// current frame.
|
||||
extern uintptr_t _Unwind_GetCFA(struct _Unwind_Context *);
|
||||
|
||||
|
||||
// _Unwind_GetIPInfo is a gcc extension that can be called from within a
|
||||
// personality handler. Similar to _Unwind_GetIP() but also returns in
|
||||
// *ipBefore a non-zero value if the instruction pointer is at or before the
|
||||
// instruction causing the unwind. Normally, in a function call, the IP returned
|
||||
// is the return address which is after the call instruction and may be past the
|
||||
// end of the function containing the call instruction.
|
||||
extern uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context,
|
||||
int *ipBefore);
|
||||
|
||||
|
||||
// __register_frame() is used with dynamically generated code to register the
|
||||
// FDE for a generated (JIT) code. The FDE must use pc-rel addressing to point
|
||||
// to its function and optional LSDA.
|
||||
// __register_frame() has existed in all versions of Mac OS X, but in 10.4 and
|
||||
// 10.5 it was buggy and did not actually register the FDE with the unwinder.
|
||||
// In 10.6 and later it does register properly.
|
||||
extern void __register_frame(const void *fde);
|
||||
extern void __deregister_frame(const void *fde);
|
||||
|
||||
// _Unwind_Find_FDE() will locate the FDE if the pc is in some function that has
|
||||
// an associated FDE. Note, Mac OS X 10.6 and later, introduces "compact unwind
|
||||
// info" which the runtime uses in preference to dwarf unwind info. This
|
||||
// function will only work if the target function has an FDE but no compact
|
||||
// unwind info.
|
||||
struct dwarf_eh_bases {
|
||||
uintptr_t tbase;
|
||||
uintptr_t dbase;
|
||||
uintptr_t func;
|
||||
};
|
||||
extern const void *_Unwind_Find_FDE(const void *pc, struct dwarf_eh_bases *);
|
||||
|
||||
|
||||
// This function attempts to find the start (address of first instruction) of
|
||||
// a function given an address inside the function. It only works if the
|
||||
// function has an FDE (dwarf unwind info).
|
||||
// This function is unimplemented on Mac OS X 10.6 and later. Instead, use
|
||||
// _Unwind_Find_FDE() and look at the dwarf_eh_bases.func result.
|
||||
extern void *_Unwind_FindEnclosingFunction(void *pc);
|
||||
|
||||
// Mac OS X does not support text-rel and data-rel addressing so these functions
|
||||
// are unimplemented
|
||||
extern uintptr_t _Unwind_GetDataRelBase(struct _Unwind_Context *context)
|
||||
LIBUNWIND_UNAVAIL;
|
||||
extern uintptr_t _Unwind_GetTextRelBase(struct _Unwind_Context *context)
|
||||
LIBUNWIND_UNAVAIL;
|
||||
|
||||
// Mac OS X 10.4 and 10.5 had implementations of these functions in
|
||||
// libgcc_s.dylib, but they never worked.
|
||||
/// These functions are no longer available on Mac OS X.
|
||||
extern void __register_frame_info_bases(const void *fde, void *ob, void *tb,
|
||||
void *db) LIBUNWIND_UNAVAIL;
|
||||
extern void __register_frame_info(const void *fde, void *ob)
|
||||
LIBUNWIND_UNAVAIL;
|
||||
extern void __register_frame_info_table_bases(const void *fde, void *ob,
|
||||
void *tb, void *db)
|
||||
LIBUNWIND_UNAVAIL;
|
||||
extern void __register_frame_info_table(const void *fde, void *ob)
|
||||
LIBUNWIND_UNAVAIL;
|
||||
extern void __register_frame_table(const void *fde)
|
||||
LIBUNWIND_UNAVAIL;
|
||||
extern void *__deregister_frame_info(const void *fde)
|
||||
LIBUNWIND_UNAVAIL;
|
||||
extern void *__deregister_frame_info_bases(const void *fde)
|
||||
LIBUNWIND_UNAVAIL;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __UNWIND_H__
|
593
src/AddressSpace.hpp
Normal file
593
src/AddressSpace.hpp
Normal file
@ -0,0 +1,593 @@
|
||||
//===------------------------- AddressSpace.hpp ---------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//
|
||||
// Abstracts accessing local vs remote address spaces.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __ADDRESSSPACE_HPP__
|
||||
#define __ADDRESSSPACE_HPP__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef _LIBUNWIND_IS_BAREMETAL
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <mach-o/getsect.h>
|
||||
namespace libunwind {
|
||||
bool checkKeyMgrRegisteredFDEs(uintptr_t targetAddr, void *&fde);
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "libunwind.h"
|
||||
#include "config.h"
|
||||
#include "dwarf2.h"
|
||||
#include "Registers.hpp"
|
||||
|
||||
#if _LIBUNWIND_ARM_EHABI
|
||||
#ifdef __linux__
|
||||
|
||||
typedef long unsigned int *_Unwind_Ptr;
|
||||
extern "C" _Unwind_Ptr __gnu_Unwind_Find_exidx(_Unwind_Ptr addr, int *len);
|
||||
|
||||
// Emulate the BSD dl_unwind_find_exidx API when on a GNU libdl system.
|
||||
#define dl_unwind_find_exidx __gnu_Unwind_Find_exidx
|
||||
|
||||
#elif !defined(_LIBUNWIND_IS_BAREMETAL)
|
||||
#include <link.h>
|
||||
#else // !defined(_LIBUNWIND_IS_BAREMETAL)
|
||||
// When statically linked on bare-metal, the symbols for the EH table are looked
|
||||
// up without going through the dynamic loader.
|
||||
struct EHTEntry {
|
||||
uint32_t functionOffset;
|
||||
uint32_t unwindOpcodes;
|
||||
};
|
||||
extern EHTEntry __exidx_start;
|
||||
extern EHTEntry __exidx_end;
|
||||
#endif // !defined(_LIBUNWIND_IS_BAREMETAL)
|
||||
#endif // _LIBUNWIND_ARM_EHABI
|
||||
|
||||
#if defined(__CloudABI__) || defined(__FreeBSD__) || defined(__linux__)
|
||||
#if _LIBUNWIND_SUPPORT_DWARF_UNWIND && _LIBUNWIND_SUPPORT_DWARF_INDEX
|
||||
#include <link.h>
|
||||
// Macro for machine-independent access to the ELF program headers. This
|
||||
// macro is not available on some systems (e.g., FreeBSD). On these
|
||||
// systems the data structures are just called Elf_XXX. Define ElfW()
|
||||
// locally.
|
||||
#if !defined(ElfW)
|
||||
#define ElfW(type) Elf_##type
|
||||
#endif
|
||||
#include "EHHeaderParser.hpp"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace libunwind {
|
||||
|
||||
/// Used by findUnwindSections() to return info about needed sections.
|
||||
struct UnwindInfoSections {
|
||||
#if _LIBUNWIND_SUPPORT_DWARF_UNWIND || _LIBUNWIND_SUPPORT_DWARF_INDEX || \
|
||||
_LIBUNWIND_SUPPORT_COMPACT_UNWIND
|
||||
// No dso_base for ARM EHABI.
|
||||
uintptr_t dso_base;
|
||||
#endif
|
||||
#if _LIBUNWIND_SUPPORT_DWARF_UNWIND
|
||||
uintptr_t dwarf_section;
|
||||
uintptr_t dwarf_section_length;
|
||||
#endif
|
||||
#if _LIBUNWIND_SUPPORT_DWARF_INDEX
|
||||
uintptr_t dwarf_index_section;
|
||||
uintptr_t dwarf_index_section_length;
|
||||
#endif
|
||||
#if _LIBUNWIND_SUPPORT_COMPACT_UNWIND
|
||||
uintptr_t compact_unwind_section;
|
||||
uintptr_t compact_unwind_section_length;
|
||||
#endif
|
||||
#if _LIBUNWIND_ARM_EHABI
|
||||
uintptr_t arm_section;
|
||||
uintptr_t arm_section_length;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/// LocalAddressSpace is used as a template parameter to UnwindCursor when
|
||||
/// unwinding a thread in the same process. The wrappers compile away,
|
||||
/// making local unwinds fast.
|
||||
class __attribute__((visibility("hidden"))) LocalAddressSpace {
|
||||
public:
|
||||
#ifdef __LP64__
|
||||
typedef uint64_t pint_t;
|
||||
typedef int64_t sint_t;
|
||||
#else
|
||||
typedef uint32_t pint_t;
|
||||
typedef int32_t sint_t;
|
||||
#endif
|
||||
uint8_t get8(pint_t addr) {
|
||||
uint8_t val;
|
||||
memcpy(&val, (void *)addr, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
uint16_t get16(pint_t addr) {
|
||||
uint16_t val;
|
||||
memcpy(&val, (void *)addr, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
uint32_t get32(pint_t addr) {
|
||||
uint32_t val;
|
||||
memcpy(&val, (void *)addr, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
uint64_t get64(pint_t addr) {
|
||||
uint64_t val;
|
||||
memcpy(&val, (void *)addr, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
double getDouble(pint_t addr) {
|
||||
double val;
|
||||
memcpy(&val, (void *)addr, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
v128 getVector(pint_t addr) {
|
||||
v128 val;
|
||||
memcpy(&val, (void *)addr, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
uintptr_t getP(pint_t addr);
|
||||
static uint64_t getULEB128(pint_t &addr, pint_t end);
|
||||
static int64_t getSLEB128(pint_t &addr, pint_t end);
|
||||
|
||||
pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
|
||||
pint_t datarelBase = 0);
|
||||
bool findFunctionName(pint_t addr, char *buf, size_t bufLen,
|
||||
unw_word_t *offset);
|
||||
bool findUnwindSections(pint_t targetAddr, UnwindInfoSections &info);
|
||||
bool findOtherFDE(pint_t targetAddr, pint_t &fde);
|
||||
|
||||
static LocalAddressSpace sThisAddressSpace;
|
||||
};
|
||||
|
||||
inline uintptr_t LocalAddressSpace::getP(pint_t addr) {
|
||||
#ifdef __LP64__
|
||||
return get64(addr);
|
||||
#else
|
||||
return get32(addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Read a ULEB128 into a 64-bit word.
|
||||
inline uint64_t LocalAddressSpace::getULEB128(pint_t &addr, pint_t end) {
|
||||
const uint8_t *p = (uint8_t *)addr;
|
||||
const uint8_t *pend = (uint8_t *)end;
|
||||
uint64_t result = 0;
|
||||
int bit = 0;
|
||||
do {
|
||||
uint64_t b;
|
||||
|
||||
if (p == pend)
|
||||
_LIBUNWIND_ABORT("truncated uleb128 expression");
|
||||
|
||||
b = *p & 0x7f;
|
||||
|
||||
if (bit >= 64 || b << bit >> bit != b) {
|
||||
_LIBUNWIND_ABORT("malformed uleb128 expression");
|
||||
} else {
|
||||
result |= b << bit;
|
||||
bit += 7;
|
||||
}
|
||||
} while (*p++ >= 0x80);
|
||||
addr = (pint_t) p;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Read a SLEB128 into a 64-bit word.
|
||||
inline int64_t LocalAddressSpace::getSLEB128(pint_t &addr, pint_t end) {
|
||||
const uint8_t *p = (uint8_t *)addr;
|
||||
const uint8_t *pend = (uint8_t *)end;
|
||||
int64_t result = 0;
|
||||
int bit = 0;
|
||||
uint8_t byte;
|
||||
do {
|
||||
if (p == pend)
|
||||
_LIBUNWIND_ABORT("truncated sleb128 expression");
|
||||
byte = *p++;
|
||||
result |= ((byte & 0x7f) << bit);
|
||||
bit += 7;
|
||||
} while (byte & 0x80);
|
||||
// sign extend negative numbers
|
||||
if ((byte & 0x40) != 0)
|
||||
result |= (-1LL) << bit;
|
||||
addr = (pint_t) p;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline LocalAddressSpace::pint_t
|
||||
LocalAddressSpace::getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
|
||||
pint_t datarelBase) {
|
||||
pint_t startAddr = addr;
|
||||
const uint8_t *p = (uint8_t *)addr;
|
||||
pint_t result;
|
||||
|
||||
// first get value
|
||||
switch (encoding & 0x0F) {
|
||||
case DW_EH_PE_ptr:
|
||||
result = getP(addr);
|
||||
p += sizeof(pint_t);
|
||||
addr = (pint_t) p;
|
||||
break;
|
||||
case DW_EH_PE_uleb128:
|
||||
result = (pint_t)getULEB128(addr, end);
|
||||
break;
|
||||
case DW_EH_PE_udata2:
|
||||
result = get16(addr);
|
||||
p += 2;
|
||||
addr = (pint_t) p;
|
||||
break;
|
||||
case DW_EH_PE_udata4:
|
||||
result = get32(addr);
|
||||
p += 4;
|
||||
addr = (pint_t) p;
|
||||
break;
|
||||
case DW_EH_PE_udata8:
|
||||
result = (pint_t)get64(addr);
|
||||
p += 8;
|
||||
addr = (pint_t) p;
|
||||
break;
|
||||
case DW_EH_PE_sleb128:
|
||||
result = (pint_t)getSLEB128(addr, end);
|
||||
break;
|
||||
case DW_EH_PE_sdata2:
|
||||
// Sign extend from signed 16-bit value.
|
||||
result = (pint_t)(int16_t)get16(addr);
|
||||
p += 2;
|
||||
addr = (pint_t) p;
|
||||
break;
|
||||
case DW_EH_PE_sdata4:
|
||||
// Sign extend from signed 32-bit value.
|
||||
result = (pint_t)(int32_t)get32(addr);
|
||||
p += 4;
|
||||
addr = (pint_t) p;
|
||||
break;
|
||||
case DW_EH_PE_sdata8:
|
||||
result = (pint_t)get64(addr);
|
||||
p += 8;
|
||||
addr = (pint_t) p;
|
||||
break;
|
||||
default:
|
||||
_LIBUNWIND_ABORT("unknown pointer encoding");
|
||||
}
|
||||
|
||||
// then add relative offset
|
||||
switch (encoding & 0x70) {
|
||||
case DW_EH_PE_absptr:
|
||||
// do nothing
|
||||
break;
|
||||
case DW_EH_PE_pcrel:
|
||||
result += startAddr;
|
||||
break;
|
||||
case DW_EH_PE_textrel:
|
||||
_LIBUNWIND_ABORT("DW_EH_PE_textrel pointer encoding not supported");
|
||||
break;
|
||||
case DW_EH_PE_datarel:
|
||||
// DW_EH_PE_datarel is only valid in a few places, so the parameter has a
|
||||
// default value of 0, and we abort in the event that someone calls this
|
||||
// function with a datarelBase of 0 and DW_EH_PE_datarel encoding.
|
||||
if (datarelBase == 0)
|
||||
_LIBUNWIND_ABORT("DW_EH_PE_datarel is invalid with a datarelBase of 0");
|
||||
result += datarelBase;
|
||||
break;
|
||||
case DW_EH_PE_funcrel:
|
||||
_LIBUNWIND_ABORT("DW_EH_PE_funcrel pointer encoding not supported");
|
||||
break;
|
||||
case DW_EH_PE_aligned:
|
||||
_LIBUNWIND_ABORT("DW_EH_PE_aligned pointer encoding not supported");
|
||||
break;
|
||||
default:
|
||||
_LIBUNWIND_ABORT("unknown pointer encoding");
|
||||
break;
|
||||
}
|
||||
|
||||
if (encoding & DW_EH_PE_indirect)
|
||||
result = getP(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
struct dyld_unwind_sections
|
||||
{
|
||||
const struct mach_header* mh;
|
||||
const void* dwarf_section;
|
||||
uintptr_t dwarf_section_length;
|
||||
const void* compact_unwind_section;
|
||||
uintptr_t compact_unwind_section_length;
|
||||
};
|
||||
#if (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) \
|
||||
&& (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)) \
|
||||
|| defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
|
||||
// In 10.7.0 or later, libSystem.dylib implements this function.
|
||||
extern "C" bool _dyld_find_unwind_sections(void *, dyld_unwind_sections *);
|
||||
#else
|
||||
// In 10.6.x and earlier, we need to implement this functionality.
|
||||
static inline bool _dyld_find_unwind_sections(void* addr,
|
||||
dyld_unwind_sections* info) {
|
||||
// Find mach-o image containing address.
|
||||
Dl_info dlinfo;
|
||||
if (!dladdr(addr, &dlinfo))
|
||||
return false;
|
||||
const mach_header *mh = (const mach_header *)dlinfo.dli_saddr;
|
||||
|
||||
// Find dwarf unwind section in that image.
|
||||
unsigned long size;
|
||||
const uint8_t *p = getsectiondata(mh, "__TEXT", "__eh_frame", &size);
|
||||
if (!p)
|
||||
return false;
|
||||
|
||||
// Fill in return struct.
|
||||
info->mh = mh;
|
||||
info->dwarf_section = p;
|
||||
info->dwarf_section_length = size;
|
||||
info->compact_unwind_section = 0;
|
||||
info->compact_unwind_section_length = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr,
|
||||
UnwindInfoSections &info) {
|
||||
#ifdef __APPLE__
|
||||
dyld_unwind_sections dyldInfo;
|
||||
if (_dyld_find_unwind_sections((void *)targetAddr, &dyldInfo)) {
|
||||
info.dso_base = (uintptr_t)dyldInfo.mh;
|
||||
#if _LIBUNWIND_SUPPORT_DWARF_UNWIND
|
||||
info.dwarf_section = (uintptr_t)dyldInfo.dwarf_section;
|
||||
info.dwarf_section_length = dyldInfo.dwarf_section_length;
|
||||
#endif
|
||||
info.compact_unwind_section = (uintptr_t)dyldInfo.compact_unwind_section;
|
||||
info.compact_unwind_section_length = dyldInfo.compact_unwind_section_length;
|
||||
return true;
|
||||
}
|
||||
#elif _LIBUNWIND_ARM_EHABI
|
||||
#ifdef _LIBUNWIND_IS_BAREMETAL
|
||||
// Bare metal is statically linked, so no need to ask the dynamic loader
|
||||
info.arm_section = (uintptr_t)(&__exidx_start);
|
||||
info.arm_section_length = (uintptr_t)(&__exidx_end - &__exidx_start);
|
||||
#else
|
||||
int length = 0;
|
||||
info.arm_section = (uintptr_t) dl_unwind_find_exidx(
|
||||
(_Unwind_Ptr) targetAddr, &length);
|
||||
info.arm_section_length = (uintptr_t)length;
|
||||
#endif
|
||||
_LIBUNWIND_TRACE_UNWINDING("findUnwindSections: section %X length %x\n",
|
||||
info.arm_section, info.arm_section_length);
|
||||
if (info.arm_section && info.arm_section_length)
|
||||
return true;
|
||||
#elif _LIBUNWIND_SUPPORT_DWARF_UNWIND
|
||||
#if _LIBUNWIND_SUPPORT_DWARF_INDEX
|
||||
struct dl_iterate_cb_data {
|
||||
LocalAddressSpace *addressSpace;
|
||||
UnwindInfoSections *sects;
|
||||
uintptr_t targetAddr;
|
||||
};
|
||||
|
||||
dl_iterate_cb_data cb_data = {this, &info, targetAddr};
|
||||
int found = dl_iterate_phdr(
|
||||
[](struct dl_phdr_info *pinfo, size_t, void *data) -> int {
|
||||
auto cbdata = static_cast<dl_iterate_cb_data *>(data);
|
||||
size_t object_length;
|
||||
bool found_obj = false;
|
||||
bool found_hdr = false;
|
||||
|
||||
assert(cbdata);
|
||||
assert(cbdata->sects);
|
||||
|
||||
if (cbdata->targetAddr < pinfo->dlpi_addr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !defined(Elf_Half)
|
||||
typedef ElfW(Half) Elf_Half;
|
||||
#endif
|
||||
#if !defined(Elf_Phdr)
|
||||
typedef ElfW(Phdr) Elf_Phdr;
|
||||
#endif
|
||||
|
||||
for (Elf_Half i = 0; i < pinfo->dlpi_phnum; i++) {
|
||||
const Elf_Phdr *phdr = &pinfo->dlpi_phdr[i];
|
||||
if (phdr->p_type == PT_LOAD) {
|
||||
uintptr_t begin = pinfo->dlpi_addr + phdr->p_vaddr;
|
||||
uintptr_t end = begin + phdr->p_memsz;
|
||||
if (cbdata->targetAddr >= begin && cbdata->targetAddr < end) {
|
||||
cbdata->sects->dso_base = begin;
|
||||
object_length = phdr->p_memsz;
|
||||
found_obj = true;
|
||||
}
|
||||
} else if (phdr->p_type == PT_GNU_EH_FRAME) {
|
||||
EHHeaderParser<LocalAddressSpace>::EHHeaderInfo hdrInfo;
|
||||
uintptr_t eh_frame_hdr_start = pinfo->dlpi_addr + phdr->p_vaddr;
|
||||
cbdata->sects->dwarf_index_section = eh_frame_hdr_start;
|
||||
cbdata->sects->dwarf_index_section_length = phdr->p_memsz;
|
||||
EHHeaderParser<LocalAddressSpace>::decodeEHHdr(
|
||||
*cbdata->addressSpace, eh_frame_hdr_start, phdr->p_memsz,
|
||||
hdrInfo);
|
||||
cbdata->sects->dwarf_section = hdrInfo.eh_frame_ptr;
|
||||
found_hdr = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_obj && found_hdr) {
|
||||
cbdata->sects->dwarf_section_length = object_length;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
&cb_data);
|
||||
return static_cast<bool>(found);
|
||||
#else
|
||||
#error "_LIBUNWIND_SUPPORT_DWARF_UNWIND requires _LIBUNWIND_SUPPORT_DWARF_INDEX on this platform."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
inline bool LocalAddressSpace::findOtherFDE(pint_t targetAddr, pint_t &fde) {
|
||||
#ifdef __APPLE__
|
||||
return checkKeyMgrRegisteredFDEs(targetAddr, *((void**)&fde));
|
||||
#else
|
||||
// TO DO: if OS has way to dynamically register FDEs, check that.
|
||||
(void)targetAddr;
|
||||
(void)fde;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool LocalAddressSpace::findFunctionName(pint_t addr, char *buf,
|
||||
size_t bufLen,
|
||||
unw_word_t *offset) {
|
||||
#ifndef _LIBUNWIND_IS_BAREMETAL
|
||||
Dl_info dyldInfo;
|
||||
if (dladdr((void *)addr, &dyldInfo)) {
|
||||
if (dyldInfo.dli_sname != NULL) {
|
||||
snprintf(buf, bufLen, "%s", dyldInfo.dli_sname);
|
||||
*offset = (addr - (pint_t) dyldInfo.dli_saddr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef UNW_REMOTE
|
||||
|
||||
/// OtherAddressSpace is used as a template parameter to UnwindCursor when
|
||||
/// unwinding a thread in the another process. The other process can be a
|
||||
/// different endianness and a different pointer size which is handled by
|
||||
/// the P template parameter.
|
||||
template <typename P>
|
||||
class OtherAddressSpace {
|
||||
public:
|
||||
OtherAddressSpace(task_t task) : fTask(task) {}
|
||||
|
||||
typedef typename P::uint_t pint_t;
|
||||
|
||||
uint8_t get8(pint_t addr);
|
||||
uint16_t get16(pint_t addr);
|
||||
uint32_t get32(pint_t addr);
|
||||
uint64_t get64(pint_t addr);
|
||||
pint_t getP(pint_t addr);
|
||||
uint64_t getULEB128(pint_t &addr, pint_t end);
|
||||
int64_t getSLEB128(pint_t &addr, pint_t end);
|
||||
pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
|
||||
pint_t datarelBase = 0);
|
||||
bool findFunctionName(pint_t addr, char *buf, size_t bufLen,
|
||||
unw_word_t *offset);
|
||||
bool findUnwindSections(pint_t targetAddr, UnwindInfoSections &info);
|
||||
bool findOtherFDE(pint_t targetAddr, pint_t &fde);
|
||||
private:
|
||||
void *localCopy(pint_t addr);
|
||||
|
||||
task_t fTask;
|
||||
};
|
||||
|
||||
template <typename P> uint8_t OtherAddressSpace<P>::get8(pint_t addr) {
|
||||
return *((uint8_t *)localCopy(addr));
|
||||
}
|
||||
|
||||
template <typename P> uint16_t OtherAddressSpace<P>::get16(pint_t addr) {
|
||||
return P::E::get16(*(uint16_t *)localCopy(addr));
|
||||
}
|
||||
|
||||
template <typename P> uint32_t OtherAddressSpace<P>::get32(pint_t addr) {
|
||||
return P::E::get32(*(uint32_t *)localCopy(addr));
|
||||
}
|
||||
|
||||
template <typename P> uint64_t OtherAddressSpace<P>::get64(pint_t addr) {
|
||||
return P::E::get64(*(uint64_t *)localCopy(addr));
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
typename P::uint_t OtherAddressSpace<P>::getP(pint_t addr) {
|
||||
return P::getP(*(uint64_t *)localCopy(addr));
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
uint64_t OtherAddressSpace<P>::getULEB128(pint_t &addr, pint_t end) {
|
||||
uintptr_t size = (end - addr);
|
||||
LocalAddressSpace::pint_t laddr = (LocalAddressSpace::pint_t) localCopy(addr);
|
||||
LocalAddressSpace::pint_t sladdr = laddr;
|
||||
uint64_t result = LocalAddressSpace::getULEB128(laddr, laddr + size);
|
||||
addr += (laddr - sladdr);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
int64_t OtherAddressSpace<P>::getSLEB128(pint_t &addr, pint_t end) {
|
||||
uintptr_t size = (end - addr);
|
||||
LocalAddressSpace::pint_t laddr = (LocalAddressSpace::pint_t) localCopy(addr);
|
||||
LocalAddressSpace::pint_t sladdr = laddr;
|
||||
uint64_t result = LocalAddressSpace::getSLEB128(laddr, laddr + size);
|
||||
addr += (laddr - sladdr);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename P> void *OtherAddressSpace<P>::localCopy(pint_t addr) {
|
||||
// FIX ME
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
bool OtherAddressSpace<P>::findFunctionName(pint_t addr, char *buf,
|
||||
size_t bufLen, unw_word_t *offset) {
|
||||
// FIX ME
|
||||
}
|
||||
|
||||
/// unw_addr_space is the base class that abstract unw_addr_space_t type in
|
||||
/// libunwind.h points to.
|
||||
struct unw_addr_space {
|
||||
cpu_type_t cpuType;
|
||||
task_t taskPort;
|
||||
};
|
||||
|
||||
/// unw_addr_space_i386 is the concrete instance that a unw_addr_space_t points
|
||||
/// to when examining
|
||||
/// a 32-bit intel process.
|
||||
struct unw_addr_space_i386 : public unw_addr_space {
|
||||
unw_addr_space_i386(task_t task) : oas(task) {}
|
||||
OtherAddressSpace<Pointer32<LittleEndian> > oas;
|
||||
};
|
||||
|
||||
/// unw_addr_space_x86_64 is the concrete instance that a unw_addr_space_t
|
||||
/// points to when examining
|
||||
/// a 64-bit intel process.
|
||||
struct unw_addr_space_x86_64 : public unw_addr_space {
|
||||
unw_addr_space_x86_64(task_t task) : oas(task) {}
|
||||
OtherAddressSpace<Pointer64<LittleEndian> > oas;
|
||||
};
|
||||
|
||||
/// unw_addr_space_ppc is the concrete instance that a unw_addr_space_t points
|
||||
/// to when examining
|
||||
/// a 32-bit PowerPC process.
|
||||
struct unw_addr_space_ppc : public unw_addr_space {
|
||||
unw_addr_space_ppc(task_t task) : oas(task) {}
|
||||
OtherAddressSpace<Pointer32<BigEndian> > oas;
|
||||
};
|
||||
|
||||
#endif // UNW_REMOTE
|
||||
|
||||
} // namespace libunwind
|
||||
|
||||
#endif // __ADDRESSSPACE_HPP__
|
693
src/CompactUnwinder.hpp
Normal file
693
src/CompactUnwinder.hpp
Normal file
@ -0,0 +1,693 @@
|
||||
//===-------------------------- CompactUnwinder.hpp -----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//
|
||||
// Does runtime stack unwinding using compact unwind encodings.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __COMPACT_UNWINDER_HPP__
|
||||
#define __COMPACT_UNWINDER_HPP__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <libunwind.h>
|
||||
#include <mach-o/compact_unwind_encoding.h>
|
||||
|
||||
#include "AddressSpace.hpp"
|
||||
#include "Registers.hpp"
|
||||
|
||||
#define EXTRACT_BITS(value, mask) \
|
||||
((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1))
|
||||
|
||||
namespace libunwind {
|
||||
|
||||
/// CompactUnwinder_x86 uses a compact unwind info to virtually "step" (aka
|
||||
/// unwind) by modifying a Registers_x86 register set
|
||||
template <typename A>
|
||||
class CompactUnwinder_x86 {
|
||||
public:
|
||||
|
||||
static int stepWithCompactEncoding(compact_unwind_encoding_t info,
|
||||
uint32_t functionStart, A &addressSpace,
|
||||
Registers_x86 ®isters);
|
||||
|
||||
private:
|
||||
typename A::pint_t pint_t;
|
||||
|
||||
static void frameUnwind(A &addressSpace, Registers_x86 ®isters);
|
||||
static void framelessUnwind(A &addressSpace,
|
||||
typename A::pint_t returnAddressLocation,
|
||||
Registers_x86 ®isters);
|
||||
static int
|
||||
stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding,
|
||||
uint32_t functionStart, A &addressSpace,
|
||||
Registers_x86 ®isters);
|
||||
static int stepWithCompactEncodingFrameless(
|
||||
compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
|
||||
A &addressSpace, Registers_x86 ®isters, bool indirectStackSize);
|
||||
};
|
||||
|
||||
template <typename A>
|
||||
int CompactUnwinder_x86<A>::stepWithCompactEncoding(
|
||||
compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
|
||||
A &addressSpace, Registers_x86 ®isters) {
|
||||
switch (compactEncoding & UNWIND_X86_MODE_MASK) {
|
||||
case UNWIND_X86_MODE_EBP_FRAME:
|
||||
return stepWithCompactEncodingEBPFrame(compactEncoding, functionStart,
|
||||
addressSpace, registers);
|
||||
case UNWIND_X86_MODE_STACK_IMMD:
|
||||
return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
|
||||
addressSpace, registers, false);
|
||||
case UNWIND_X86_MODE_STACK_IND:
|
||||
return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
|
||||
addressSpace, registers, true);
|
||||
}
|
||||
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
int CompactUnwinder_x86<A>::stepWithCompactEncodingEBPFrame(
|
||||
compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
|
||||
A &addressSpace, Registers_x86 ®isters) {
|
||||
uint32_t savedRegistersOffset =
|
||||
EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_OFFSET);
|
||||
uint32_t savedRegistersLocations =
|
||||
EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_REGISTERS);
|
||||
|
||||
uint32_t savedRegisters = registers.getEBP() - 4 * savedRegistersOffset;
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
switch (savedRegistersLocations & 0x7) {
|
||||
case UNWIND_X86_REG_NONE:
|
||||
// no register saved in this slot
|
||||
break;
|
||||
case UNWIND_X86_REG_EBX:
|
||||
registers.setEBX(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_REG_ECX:
|
||||
registers.setECX(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_REG_EDX:
|
||||
registers.setEDX(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_REG_EDI:
|
||||
registers.setEDI(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_REG_ESI:
|
||||
registers.setESI(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
default:
|
||||
(void)functionStart;
|
||||
_LIBUNWIND_DEBUG_LOG("bad register for EBP frame, encoding=%08X for "
|
||||
"function starting at 0x%X\n",
|
||||
compactEncoding, functionStart);
|
||||
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
||||
}
|
||||
savedRegisters += 4;
|
||||
savedRegistersLocations = (savedRegistersLocations >> 3);
|
||||
}
|
||||
frameUnwind(addressSpace, registers);
|
||||
return UNW_STEP_SUCCESS;
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
int CompactUnwinder_x86<A>::stepWithCompactEncodingFrameless(
|
||||
compact_unwind_encoding_t encoding, uint32_t functionStart,
|
||||
A &addressSpace, Registers_x86 ®isters, bool indirectStackSize) {
|
||||
uint32_t stackSizeEncoded =
|
||||
EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);
|
||||
uint32_t stackAdjust =
|
||||
EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST);
|
||||
uint32_t regCount =
|
||||
EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT);
|
||||
uint32_t permutation =
|
||||
EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION);
|
||||
uint32_t stackSize = stackSizeEncoded * 4;
|
||||
if (indirectStackSize) {
|
||||
// stack size is encoded in subl $xxx,%esp instruction
|
||||
uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded);
|
||||
stackSize = subl + 4 * stackAdjust;
|
||||
}
|
||||
// decompress permutation
|
||||
uint32_t permunreg[6];
|
||||
switch (regCount) {
|
||||
case 6:
|
||||
permunreg[0] = permutation / 120;
|
||||
permutation -= (permunreg[0] * 120);
|
||||
permunreg[1] = permutation / 24;
|
||||
permutation -= (permunreg[1] * 24);
|
||||
permunreg[2] = permutation / 6;
|
||||
permutation -= (permunreg[2] * 6);
|
||||
permunreg[3] = permutation / 2;
|
||||
permutation -= (permunreg[3] * 2);
|
||||
permunreg[4] = permutation;
|
||||
permunreg[5] = 0;
|
||||
break;
|
||||
case 5:
|
||||
permunreg[0] = permutation / 120;
|
||||
permutation -= (permunreg[0] * 120);
|
||||
permunreg[1] = permutation / 24;
|
||||
permutation -= (permunreg[1] * 24);
|
||||
permunreg[2] = permutation / 6;
|
||||
permutation -= (permunreg[2] * 6);
|
||||
permunreg[3] = permutation / 2;
|
||||
permutation -= (permunreg[3] * 2);
|
||||
permunreg[4] = permutation;
|
||||
break;
|
||||
case 4:
|
||||
permunreg[0] = permutation / 60;
|
||||
permutation -= (permunreg[0] * 60);
|
||||
permunreg[1] = permutation / 12;
|
||||
permutation -= (permunreg[1] * 12);
|
||||
permunreg[2] = permutation / 3;
|
||||
permutation -= (permunreg[2] * 3);
|
||||
permunreg[3] = permutation;
|
||||
break;
|
||||
case 3:
|
||||
permunreg[0] = permutation / 20;
|
||||
permutation -= (permunreg[0] * 20);
|
||||
permunreg[1] = permutation / 4;
|
||||
permutation -= (permunreg[1] * 4);
|
||||
permunreg[2] = permutation;
|
||||
break;
|
||||
case 2:
|
||||
permunreg[0] = permutation / 5;
|
||||
permutation -= (permunreg[0] * 5);
|
||||
permunreg[1] = permutation;
|
||||
break;
|
||||
case 1:
|
||||
permunreg[0] = permutation;
|
||||
break;
|
||||
}
|
||||
// re-number registers back to standard numbers
|
||||
int registersSaved[6];
|
||||
bool used[7] = { false, false, false, false, false, false, false };
|
||||
for (uint32_t i = 0; i < regCount; ++i) {
|
||||
uint32_t renum = 0;
|
||||
for (int u = 1; u < 7; ++u) {
|
||||
if (!used[u]) {
|
||||
if (renum == permunreg[i]) {
|
||||
registersSaved[i] = u;
|
||||
used[u] = true;
|
||||
break;
|
||||
}
|
||||
++renum;
|
||||
}
|
||||
}
|
||||
}
|
||||
uint32_t savedRegisters = registers.getSP() + stackSize - 4 - 4 * regCount;
|
||||
for (uint32_t i = 0; i < regCount; ++i) {
|
||||
switch (registersSaved[i]) {
|
||||
case UNWIND_X86_REG_EBX:
|
||||
registers.setEBX(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_REG_ECX:
|
||||
registers.setECX(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_REG_EDX:
|
||||
registers.setEDX(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_REG_EDI:
|
||||
registers.setEDI(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_REG_ESI:
|
||||
registers.setESI(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_REG_EBP:
|
||||
registers.setEBP(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
default:
|
||||
_LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for "
|
||||
"function starting at 0x%X\n",
|
||||
encoding, functionStart);
|
||||
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
||||
}
|
||||
savedRegisters += 4;
|
||||
}
|
||||
framelessUnwind(addressSpace, savedRegisters, registers);
|
||||
return UNW_STEP_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
template <typename A>
|
||||
void CompactUnwinder_x86<A>::frameUnwind(A &addressSpace,
|
||||
Registers_x86 ®isters) {
|
||||
typename A::pint_t bp = registers.getEBP();
|
||||
// ebp points to old ebp
|
||||
registers.setEBP(addressSpace.get32(bp));
|
||||
// old esp is ebp less saved ebp and return address
|
||||
registers.setSP((uint32_t)bp + 8);
|
||||
// pop return address into eip
|
||||
registers.setIP(addressSpace.get32(bp + 4));
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
void CompactUnwinder_x86<A>::framelessUnwind(
|
||||
A &addressSpace, typename A::pint_t returnAddressLocation,
|
||||
Registers_x86 ®isters) {
|
||||
// return address is on stack after last saved register
|
||||
registers.setIP(addressSpace.get32(returnAddressLocation));
|
||||
// old esp is before return address
|
||||
registers.setSP((uint32_t)returnAddressLocation + 4);
|
||||
}
|
||||
|
||||
|
||||
/// CompactUnwinder_x86_64 uses a compact unwind info to virtually "step" (aka
|
||||
/// unwind) by modifying a Registers_x86_64 register set
|
||||
template <typename A>
|
||||
class CompactUnwinder_x86_64 {
|
||||
public:
|
||||
|
||||
static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
|
||||
uint64_t functionStart, A &addressSpace,
|
||||
Registers_x86_64 ®isters);
|
||||
|
||||
private:
|
||||
typename A::pint_t pint_t;
|
||||
|
||||
static void frameUnwind(A &addressSpace, Registers_x86_64 ®isters);
|
||||
static void framelessUnwind(A &addressSpace, uint64_t returnAddressLocation,
|
||||
Registers_x86_64 ®isters);
|
||||
static int
|
||||
stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding,
|
||||
uint64_t functionStart, A &addressSpace,
|
||||
Registers_x86_64 ®isters);
|
||||
static int stepWithCompactEncodingFrameless(
|
||||
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
|
||||
A &addressSpace, Registers_x86_64 ®isters, bool indirectStackSize);
|
||||
};
|
||||
|
||||
template <typename A>
|
||||
int CompactUnwinder_x86_64<A>::stepWithCompactEncoding(
|
||||
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
|
||||
A &addressSpace, Registers_x86_64 ®isters) {
|
||||
switch (compactEncoding & UNWIND_X86_64_MODE_MASK) {
|
||||
case UNWIND_X86_64_MODE_RBP_FRAME:
|
||||
return stepWithCompactEncodingRBPFrame(compactEncoding, functionStart,
|
||||
addressSpace, registers);
|
||||
case UNWIND_X86_64_MODE_STACK_IMMD:
|
||||
return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
|
||||
addressSpace, registers, false);
|
||||
case UNWIND_X86_64_MODE_STACK_IND:
|
||||
return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
|
||||
addressSpace, registers, true);
|
||||
}
|
||||
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
int CompactUnwinder_x86_64<A>::stepWithCompactEncodingRBPFrame(
|
||||
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
|
||||
A &addressSpace, Registers_x86_64 ®isters) {
|
||||
uint32_t savedRegistersOffset =
|
||||
EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_OFFSET);
|
||||
uint32_t savedRegistersLocations =
|
||||
EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);
|
||||
|
||||
uint64_t savedRegisters = registers.getRBP() - 8 * savedRegistersOffset;
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
switch (savedRegistersLocations & 0x7) {
|
||||
case UNWIND_X86_64_REG_NONE:
|
||||
// no register saved in this slot
|
||||
break;
|
||||
case UNWIND_X86_64_REG_RBX:
|
||||
registers.setRBX(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_64_REG_R12:
|
||||
registers.setR12(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_64_REG_R13:
|
||||
registers.setR13(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_64_REG_R14:
|
||||
registers.setR14(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_64_REG_R15:
|
||||
registers.setR15(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
default:
|
||||
(void)functionStart;
|
||||
_LIBUNWIND_DEBUG_LOG("bad register for RBP frame, encoding=%08X for "
|
||||
"function starting at 0x%llX\n",
|
||||
compactEncoding, functionStart);
|
||||
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
||||
}
|
||||
savedRegisters += 8;
|
||||
savedRegistersLocations = (savedRegistersLocations >> 3);
|
||||
}
|
||||
frameUnwind(addressSpace, registers);
|
||||
return UNW_STEP_SUCCESS;
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
int CompactUnwinder_x86_64<A>::stepWithCompactEncodingFrameless(
|
||||
compact_unwind_encoding_t encoding, uint64_t functionStart, A &addressSpace,
|
||||
Registers_x86_64 ®isters, bool indirectStackSize) {
|
||||
uint32_t stackSizeEncoded =
|
||||
EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
|
||||
uint32_t stackAdjust =
|
||||
EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST);
|
||||
uint32_t regCount =
|
||||
EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT);
|
||||
uint32_t permutation =
|
||||
EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION);
|
||||
uint32_t stackSize = stackSizeEncoded * 8;
|
||||
if (indirectStackSize) {
|
||||
// stack size is encoded in subl $xxx,%esp instruction
|
||||
uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded);
|
||||
stackSize = subl + 8 * stackAdjust;
|
||||
}
|
||||
// decompress permutation
|
||||
uint32_t permunreg[6];
|
||||
switch (regCount) {
|
||||
case 6:
|
||||
permunreg[0] = permutation / 120;
|
||||
permutation -= (permunreg[0] * 120);
|
||||
permunreg[1] = permutation / 24;
|
||||
permutation -= (permunreg[1] * 24);
|
||||
permunreg[2] = permutation / 6;
|
||||
permutation -= (permunreg[2] * 6);
|
||||
permunreg[3] = permutation / 2;
|
||||
permutation -= (permunreg[3] * 2);
|
||||
permunreg[4] = permutation;
|
||||
permunreg[5] = 0;
|
||||
break;
|
||||
case 5:
|
||||
permunreg[0] = permutation / 120;
|
||||
permutation -= (permunreg[0] * 120);
|
||||
permunreg[1] = permutation / 24;
|
||||
permutation -= (permunreg[1] * 24);
|
||||
permunreg[2] = permutation / 6;
|
||||
permutation -= (permunreg[2] * 6);
|
||||
permunreg[3] = permutation / 2;
|
||||
permutation -= (permunreg[3] * 2);
|
||||
permunreg[4] = permutation;
|
||||
break;
|
||||
case 4:
|
||||
permunreg[0] = permutation / 60;
|
||||
permutation -= (permunreg[0] * 60);
|
||||
permunreg[1] = permutation / 12;
|
||||
permutation -= (permunreg[1] * 12);
|
||||
permunreg[2] = permutation / 3;
|
||||
permutation -= (permunreg[2] * 3);
|
||||
permunreg[3] = permutation;
|
||||
break;
|
||||
case 3:
|
||||
permunreg[0] = permutation / 20;
|
||||
permutation -= (permunreg[0] * 20);
|
||||
permunreg[1] = permutation / 4;
|
||||
permutation -= (permunreg[1] * 4);
|
||||
permunreg[2] = permutation;
|
||||
break;
|
||||
case 2:
|
||||
permunreg[0] = permutation / 5;
|
||||
permutation -= (permunreg[0] * 5);
|
||||
permunreg[1] = permutation;
|
||||
break;
|
||||
case 1:
|
||||
permunreg[0] = permutation;
|
||||
break;
|
||||
}
|
||||
// re-number registers back to standard numbers
|
||||
int registersSaved[6];
|
||||
bool used[7] = { false, false, false, false, false, false, false };
|
||||
for (uint32_t i = 0; i < regCount; ++i) {
|
||||
uint32_t renum = 0;
|
||||
for (int u = 1; u < 7; ++u) {
|
||||
if (!used[u]) {
|
||||
if (renum == permunreg[i]) {
|
||||
registersSaved[i] = u;
|
||||
used[u] = true;
|
||||
break;
|
||||
}
|
||||
++renum;
|
||||
}
|
||||
}
|
||||
}
|
||||
uint64_t savedRegisters = registers.getSP() + stackSize - 8 - 8 * regCount;
|
||||
for (uint32_t i = 0; i < regCount; ++i) {
|
||||
switch (registersSaved[i]) {
|
||||
case UNWIND_X86_64_REG_RBX:
|
||||
registers.setRBX(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_64_REG_R12:
|
||||
registers.setR12(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_64_REG_R13:
|
||||
registers.setR13(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_64_REG_R14:
|
||||
registers.setR14(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_64_REG_R15:
|
||||
registers.setR15(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_64_REG_RBP:
|
||||
registers.setRBP(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
default:
|
||||
_LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for "
|
||||
"function starting at 0x%llX\n",
|
||||
encoding, functionStart);
|
||||
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
||||
}
|
||||
savedRegisters += 8;
|
||||
}
|
||||
framelessUnwind(addressSpace, savedRegisters, registers);
|
||||
return UNW_STEP_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
template <typename A>
|
||||
void CompactUnwinder_x86_64<A>::frameUnwind(A &addressSpace,
|
||||
Registers_x86_64 ®isters) {
|
||||
uint64_t rbp = registers.getRBP();
|
||||
// ebp points to old ebp
|
||||
registers.setRBP(addressSpace.get64(rbp));
|
||||
// old esp is ebp less saved ebp and return address
|
||||
registers.setSP(rbp + 16);
|
||||
// pop return address into eip
|
||||
registers.setIP(addressSpace.get64(rbp + 8));
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
void CompactUnwinder_x86_64<A>::framelessUnwind(A &addressSpace,
|
||||
uint64_t returnAddressLocation,
|
||||
Registers_x86_64 ®isters) {
|
||||
// return address is on stack after last saved register
|
||||
registers.setIP(addressSpace.get64(returnAddressLocation));
|
||||
// old esp is before return address
|
||||
registers.setSP(returnAddressLocation + 8);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// CompactUnwinder_arm64 uses a compact unwind info to virtually "step" (aka
|
||||
/// unwind) by modifying a Registers_arm64 register set
|
||||
template <typename A>
|
||||
class CompactUnwinder_arm64 {
|
||||
public:
|
||||
|
||||
static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
|
||||
uint64_t functionStart, A &addressSpace,
|
||||
Registers_arm64 ®isters);
|
||||
|
||||
private:
|
||||
typename A::pint_t pint_t;
|
||||
|
||||
static int
|
||||
stepWithCompactEncodingFrame(compact_unwind_encoding_t compactEncoding,
|
||||
uint64_t functionStart, A &addressSpace,
|
||||
Registers_arm64 ®isters);
|
||||
static int stepWithCompactEncodingFrameless(
|
||||
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
|
||||
A &addressSpace, Registers_arm64 ®isters);
|
||||
};
|
||||
|
||||
template <typename A>
|
||||
int CompactUnwinder_arm64<A>::stepWithCompactEncoding(
|
||||
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
|
||||
A &addressSpace, Registers_arm64 ®isters) {
|
||||
switch (compactEncoding & UNWIND_ARM64_MODE_MASK) {
|
||||
case UNWIND_ARM64_MODE_FRAME:
|
||||
return stepWithCompactEncodingFrame(compactEncoding, functionStart,
|
||||
addressSpace, registers);
|
||||
case UNWIND_ARM64_MODE_FRAMELESS:
|
||||
return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
|
||||
addressSpace, registers);
|
||||
}
|
||||
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrameless(
|
||||
compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
|
||||
Registers_arm64 ®isters) {
|
||||
uint32_t stackSize =
|
||||
16 * EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK);
|
||||
|
||||
uint64_t savedRegisterLoc = registers.getSP() + stackSize;
|
||||
|
||||
if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
|
||||
registers.setRegister(UNW_ARM64_X19, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setRegister(UNW_ARM64_X20, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
|
||||
registers.setRegister(UNW_ARM64_X21, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setRegister(UNW_ARM64_X22, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
|
||||
registers.setRegister(UNW_ARM64_X23, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setRegister(UNW_ARM64_X24, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
|
||||
registers.setRegister(UNW_ARM64_X25, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setRegister(UNW_ARM64_X26, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
|
||||
registers.setRegister(UNW_ARM64_X27, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setRegister(UNW_ARM64_X28, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
|
||||
if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
|
||||
registers.setFloatRegister(UNW_ARM64_D8,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setFloatRegister(UNW_ARM64_D9,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
|
||||
registers.setFloatRegister(UNW_ARM64_D10,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setFloatRegister(UNW_ARM64_D11,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
|
||||
registers.setFloatRegister(UNW_ARM64_D12,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setFloatRegister(UNW_ARM64_D13,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
|
||||
registers.setFloatRegister(UNW_ARM64_D14,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setFloatRegister(UNW_ARM64_D15,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
|
||||
// subtract stack size off of sp
|
||||
registers.setSP(savedRegisterLoc);
|
||||
|
||||
// set pc to be value in lr
|
||||
registers.setIP(registers.getRegister(UNW_ARM64_LR));
|
||||
|
||||
return UNW_STEP_SUCCESS;
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrame(
|
||||
compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
|
||||
Registers_arm64 ®isters) {
|
||||
uint64_t savedRegisterLoc = registers.getFP() - 8;
|
||||
|
||||
if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
|
||||
registers.setRegister(UNW_ARM64_X19, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setRegister(UNW_ARM64_X20, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
|
||||
registers.setRegister(UNW_ARM64_X21, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setRegister(UNW_ARM64_X22, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
|
||||
registers.setRegister(UNW_ARM64_X23, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setRegister(UNW_ARM64_X24, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
|
||||
registers.setRegister(UNW_ARM64_X25, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setRegister(UNW_ARM64_X26, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
|
||||
registers.setRegister(UNW_ARM64_X27, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setRegister(UNW_ARM64_X28, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
|
||||
if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
|
||||
registers.setFloatRegister(UNW_ARM64_D8,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setFloatRegister(UNW_ARM64_D9,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
|
||||
registers.setFloatRegister(UNW_ARM64_D10,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setFloatRegister(UNW_ARM64_D11,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
|
||||
registers.setFloatRegister(UNW_ARM64_D12,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setFloatRegister(UNW_ARM64_D13,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
|
||||
registers.setFloatRegister(UNW_ARM64_D14,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setFloatRegister(UNW_ARM64_D15,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
|
||||
uint64_t fp = registers.getFP();
|
||||
// fp points to old fp
|
||||
registers.setFP(addressSpace.get64(fp));
|
||||
// old sp is fp less saved fp and lr
|
||||
registers.setSP(fp + 16);
|
||||
// pop return address into pc
|
||||
registers.setIP(addressSpace.get64(fp + 8));
|
||||
|
||||
return UNW_STEP_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
} // namespace libunwind
|
||||
|
||||
#endif // __COMPACT_UNWINDER_HPP__
|
760
src/DwarfInstructions.hpp
Normal file
760
src/DwarfInstructions.hpp
Normal file
@ -0,0 +1,760 @@
|
||||
//===-------------------------- DwarfInstructions.hpp ---------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//
|
||||
// Processor specific interpretation of dwarf unwind info.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __DWARF_INSTRUCTIONS_HPP__
|
||||
#define __DWARF_INSTRUCTIONS_HPP__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "dwarf2.h"
|
||||
#include "AddressSpace.hpp"
|
||||
#include "Registers.hpp"
|
||||
#include "DwarfParser.hpp"
|
||||
#include "config.h"
|
||||
|
||||
|
||||
namespace libunwind {
|
||||
|
||||
|
||||
/// DwarfInstructions maps abtract dwarf unwind instructions to a particular
|
||||
/// architecture
|
||||
template <typename A, typename R>
|
||||
class DwarfInstructions {
|
||||
public:
|
||||
typedef typename A::pint_t pint_t;
|
||||
typedef typename A::sint_t sint_t;
|
||||
|
||||
static int stepWithDwarf(A &addressSpace, pint_t pc, pint_t fdeStart,
|
||||
R ®isters);
|
||||
|
||||
private:
|
||||
|
||||
enum {
|
||||
DW_X86_64_RET_ADDR = 16
|
||||
};
|
||||
|
||||
enum {
|
||||
DW_X86_RET_ADDR = 8
|
||||
};
|
||||
|
||||
typedef typename CFI_Parser<A>::RegisterLocation RegisterLocation;
|
||||
typedef typename CFI_Parser<A>::PrologInfo PrologInfo;
|
||||
typedef typename CFI_Parser<A>::FDE_Info FDE_Info;
|
||||
typedef typename CFI_Parser<A>::CIE_Info CIE_Info;
|
||||
|
||||
static pint_t evaluateExpression(pint_t expression, A &addressSpace,
|
||||
const R ®isters,
|
||||
pint_t initialStackValue);
|
||||
static pint_t getSavedRegister(A &addressSpace, const R ®isters,
|
||||
pint_t cfa, const RegisterLocation &savedReg);
|
||||
static double getSavedFloatRegister(A &addressSpace, const R ®isters,
|
||||
pint_t cfa, const RegisterLocation &savedReg);
|
||||
static v128 getSavedVectorRegister(A &addressSpace, const R ®isters,
|
||||
pint_t cfa, const RegisterLocation &savedReg);
|
||||
|
||||
static pint_t getCFA(A &addressSpace, const PrologInfo &prolog,
|
||||
const R ®isters) {
|
||||
if (prolog.cfaRegister != 0)
|
||||
return (pint_t)((sint_t)registers.getRegister((int)prolog.cfaRegister) +
|
||||
prolog.cfaRegisterOffset);
|
||||
if (prolog.cfaExpression != 0)
|
||||
return evaluateExpression((pint_t)prolog.cfaExpression, addressSpace,
|
||||
registers, 0);
|
||||
assert(0 && "getCFA(): unknown location");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename A, typename R>
|
||||
typename A::pint_t DwarfInstructions<A, R>::getSavedRegister(
|
||||
A &addressSpace, const R ®isters, pint_t cfa,
|
||||
const RegisterLocation &savedReg) {
|
||||
switch (savedReg.location) {
|
||||
case CFI_Parser<A>::kRegisterInCFA:
|
||||
return addressSpace.getP(cfa + (pint_t)savedReg.value);
|
||||
|
||||
case CFI_Parser<A>::kRegisterAtExpression:
|
||||
return addressSpace.getP(
|
||||
evaluateExpression((pint_t)savedReg.value, addressSpace,
|
||||
registers, cfa));
|
||||
|
||||
case CFI_Parser<A>::kRegisterIsExpression:
|
||||
return evaluateExpression((pint_t)savedReg.value, addressSpace,
|
||||
registers, cfa);
|
||||
|
||||
case CFI_Parser<A>::kRegisterInRegister:
|
||||
return registers.getRegister((int)savedReg.value);
|
||||
|
||||
case CFI_Parser<A>::kRegisterUnused:
|
||||
case CFI_Parser<A>::kRegisterOffsetFromCFA:
|
||||
// FIX ME
|
||||
break;
|
||||
}
|
||||
_LIBUNWIND_ABORT("unsupported restore location for register");
|
||||
}
|
||||
|
||||
template <typename A, typename R>
|
||||
double DwarfInstructions<A, R>::getSavedFloatRegister(
|
||||
A &addressSpace, const R ®isters, pint_t cfa,
|
||||
const RegisterLocation &savedReg) {
|
||||
switch (savedReg.location) {
|
||||
case CFI_Parser<A>::kRegisterInCFA:
|
||||
return addressSpace.getDouble(cfa + (pint_t)savedReg.value);
|
||||
|
||||
case CFI_Parser<A>::kRegisterAtExpression:
|
||||
return addressSpace.getDouble(
|
||||
evaluateExpression((pint_t)savedReg.value, addressSpace,
|
||||
registers, cfa));
|
||||
|
||||
case CFI_Parser<A>::kRegisterIsExpression:
|
||||
case CFI_Parser<A>::kRegisterUnused:
|
||||
case CFI_Parser<A>::kRegisterOffsetFromCFA:
|
||||
case CFI_Parser<A>::kRegisterInRegister:
|
||||
// FIX ME
|
||||
break;
|
||||
}
|
||||
_LIBUNWIND_ABORT("unsupported restore location for float register");
|
||||
}
|
||||
|
||||
template <typename A, typename R>
|
||||
v128 DwarfInstructions<A, R>::getSavedVectorRegister(
|
||||
A &addressSpace, const R ®isters, pint_t cfa,
|
||||
const RegisterLocation &savedReg) {
|
||||
switch (savedReg.location) {
|
||||
case CFI_Parser<A>::kRegisterInCFA:
|
||||
return addressSpace.getVector(cfa + (pint_t)savedReg.value);
|
||||
|
||||
case CFI_Parser<A>::kRegisterAtExpression:
|
||||
return addressSpace.getVector(
|
||||
evaluateExpression((pint_t)savedReg.value, addressSpace,
|
||||
registers, cfa));
|
||||
|
||||
case CFI_Parser<A>::kRegisterIsExpression:
|
||||
case CFI_Parser<A>::kRegisterUnused:
|
||||
case CFI_Parser<A>::kRegisterOffsetFromCFA:
|
||||
case CFI_Parser<A>::kRegisterInRegister:
|
||||
// FIX ME
|
||||
break;
|
||||
}
|
||||
_LIBUNWIND_ABORT("unsupported restore location for vector register");
|
||||
}
|
||||
|
||||
template <typename A, typename R>
|
||||
int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pint_t pc,
|
||||
pint_t fdeStart, R ®isters) {
|
||||
FDE_Info fdeInfo;
|
||||
CIE_Info cieInfo;
|
||||
if (CFI_Parser<A>::decodeFDE(addressSpace, fdeStart, &fdeInfo,
|
||||
&cieInfo) == NULL) {
|
||||
PrologInfo prolog;
|
||||
if (CFI_Parser<A>::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, pc,
|
||||
&prolog)) {
|
||||
// get pointer to cfa (architecture specific)
|
||||
pint_t cfa = getCFA(addressSpace, prolog, registers);
|
||||
|
||||
// restore registers that dwarf says were saved
|
||||
R newRegisters = registers;
|
||||
pint_t returnAddress = 0;
|
||||
const int lastReg = R::lastDwarfRegNum();
|
||||
assert((int)CFI_Parser<A>::kMaxRegisterNumber > lastReg &&
|
||||
"register range too large");
|
||||
assert(lastReg >= (int)cieInfo.returnAddressRegister &&
|
||||
"register range does not contain return address register");
|
||||
for (int i = 0; i <= lastReg; ++i) {
|
||||
if (prolog.savedRegisters[i].location !=
|
||||
CFI_Parser<A>::kRegisterUnused) {
|
||||
if (registers.validFloatRegister(i))
|
||||
newRegisters.setFloatRegister(
|
||||
i, getSavedFloatRegister(addressSpace, registers, cfa,
|
||||
prolog.savedRegisters[i]));
|
||||
else if (registers.validVectorRegister(i))
|
||||
newRegisters.setVectorRegister(
|
||||
i, getSavedVectorRegister(addressSpace, registers, cfa,
|
||||
prolog.savedRegisters[i]));
|
||||
else if (i == (int)cieInfo.returnAddressRegister)
|
||||
returnAddress = getSavedRegister(addressSpace, registers, cfa,
|
||||
prolog.savedRegisters[i]);
|
||||
else if (registers.validRegister(i))
|
||||
newRegisters.setRegister(
|
||||
i, getSavedRegister(addressSpace, registers, cfa,
|
||||
prolog.savedRegisters[i]));
|
||||
else
|
||||
return UNW_EBADREG;
|
||||
}
|
||||
}
|
||||
|
||||
// By definition, the CFA is the stack pointer at the call site, so
|
||||
// restoring SP means setting it to CFA.
|
||||
newRegisters.setSP(cfa);
|
||||
|
||||
// Return address is address after call site instruction, so setting IP to
|
||||
// that does simualates a return.
|
||||
newRegisters.setIP(returnAddress);
|
||||
|
||||
// Simulate the step by replacing the register set with the new ones.
|
||||
registers = newRegisters;
|
||||
|
||||
return UNW_STEP_SUCCESS;
|
||||
}
|
||||
}
|
||||
return UNW_EBADFRAME;
|
||||
}
|
||||
|
||||
template <typename A, typename R>
|
||||
typename A::pint_t
|
||||
DwarfInstructions<A, R>::evaluateExpression(pint_t expression, A &addressSpace,
|
||||
const R ®isters,
|
||||
pint_t initialStackValue) {
|
||||
const bool log = false;
|
||||
pint_t p = expression;
|
||||
pint_t expressionEnd = expression + 20; // temp, until len read
|
||||
pint_t length = (pint_t)addressSpace.getULEB128(p, expressionEnd);
|
||||
expressionEnd = p + length;
|
||||
if (log)
|
||||
fprintf(stderr, "evaluateExpression(): length=%" PRIu64 "\n",
|
||||
(uint64_t)length);
|
||||
pint_t stack[100];
|
||||
pint_t *sp = stack;
|
||||
*(++sp) = initialStackValue;
|
||||
|
||||
while (p < expressionEnd) {
|
||||
if (log) {
|
||||
for (pint_t *t = sp; t > stack; --t) {
|
||||
fprintf(stderr, "sp[] = 0x%" PRIx64 "\n", (uint64_t)(*t));
|
||||
}
|
||||
}
|
||||
uint8_t opcode = addressSpace.get8(p++);
|
||||
sint_t svalue, svalue2;
|
||||
pint_t value;
|
||||
uint32_t reg;
|
||||
switch (opcode) {
|
||||
case DW_OP_addr:
|
||||
// push immediate address sized value
|
||||
value = addressSpace.getP(p);
|
||||
p += sizeof(pint_t);
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_deref:
|
||||
// pop stack, dereference, push result
|
||||
value = *sp--;
|
||||
*(++sp) = addressSpace.getP(value);
|
||||
if (log)
|
||||
fprintf(stderr, "dereference 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_const1u:
|
||||
// push immediate 1 byte value
|
||||
value = addressSpace.get8(p);
|
||||
p += 1;
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_const1s:
|
||||
// push immediate 1 byte signed value
|
||||
svalue = (int8_t) addressSpace.get8(p);
|
||||
p += 1;
|
||||
*(++sp) = (pint_t)svalue;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue);
|
||||
break;
|
||||
|
||||
case DW_OP_const2u:
|
||||
// push immediate 2 byte value
|
||||
value = addressSpace.get16(p);
|
||||
p += 2;
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_const2s:
|
||||
// push immediate 2 byte signed value
|
||||
svalue = (int16_t) addressSpace.get16(p);
|
||||
p += 2;
|
||||
*(++sp) = (pint_t)svalue;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue);
|
||||
break;
|
||||
|
||||
case DW_OP_const4u:
|
||||
// push immediate 4 byte value
|
||||
value = addressSpace.get32(p);
|
||||
p += 4;
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_const4s:
|
||||
// push immediate 4 byte signed value
|
||||
svalue = (int32_t)addressSpace.get32(p);
|
||||
p += 4;
|
||||
*(++sp) = (pint_t)svalue;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue);
|
||||
break;
|
||||
|
||||
case DW_OP_const8u:
|
||||
// push immediate 8 byte value
|
||||
value = (pint_t)addressSpace.get64(p);
|
||||
p += 8;
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_const8s:
|
||||
// push immediate 8 byte signed value
|
||||
value = (pint_t)addressSpace.get64(p);
|
||||
p += 8;
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_constu:
|
||||
// push immediate ULEB128 value
|
||||
value = (pint_t)addressSpace.getULEB128(p, expressionEnd);
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_consts:
|
||||
// push immediate SLEB128 value
|
||||
svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd);
|
||||
*(++sp) = (pint_t)svalue;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue);
|
||||
break;
|
||||
|
||||
case DW_OP_dup:
|
||||
// push top of stack
|
||||
value = *sp;
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "duplicate top of stack\n");
|
||||
break;
|
||||
|
||||
case DW_OP_drop:
|
||||
// pop
|
||||
--sp;
|
||||
if (log)
|
||||
fprintf(stderr, "pop top of stack\n");
|
||||
break;
|
||||
|
||||
case DW_OP_over:
|
||||
// dup second
|
||||
value = sp[-1];
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "duplicate second in stack\n");
|
||||
break;
|
||||
|
||||
case DW_OP_pick:
|
||||
// pick from
|
||||
reg = addressSpace.get8(p);
|
||||
p += 1;
|
||||
value = sp[-reg];
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "duplicate %d in stack\n", reg);
|
||||
break;
|
||||
|
||||
case DW_OP_swap:
|
||||
// swap top two
|
||||
value = sp[0];
|
||||
sp[0] = sp[-1];
|
||||
sp[-1] = value;
|
||||
if (log)
|
||||
fprintf(stderr, "swap top of stack\n");
|
||||
break;
|
||||
|
||||
case DW_OP_rot:
|
||||
// rotate top three
|
||||
value = sp[0];
|
||||
sp[0] = sp[-1];
|
||||
sp[-1] = sp[-2];
|
||||
sp[-2] = value;
|
||||
if (log)
|
||||
fprintf(stderr, "rotate top three of stack\n");
|
||||
break;
|
||||
|
||||
case DW_OP_xderef:
|
||||
// pop stack, dereference, push result
|
||||
value = *sp--;
|
||||
*sp = *((pint_t*)value);
|
||||
if (log)
|
||||
fprintf(stderr, "x-dereference 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_abs:
|
||||
svalue = (sint_t)*sp;
|
||||
if (svalue < 0)
|
||||
*sp = (pint_t)(-svalue);
|
||||
if (log)
|
||||
fprintf(stderr, "abs\n");
|
||||
break;
|
||||
|
||||
case DW_OP_and:
|
||||
value = *sp--;
|
||||
*sp &= value;
|
||||
if (log)
|
||||
fprintf(stderr, "and\n");
|
||||
break;
|
||||
|
||||
case DW_OP_div:
|
||||
svalue = (sint_t)(*sp--);
|
||||
svalue2 = (sint_t)*sp;
|
||||
*sp = (pint_t)(svalue2 / svalue);
|
||||
if (log)
|
||||
fprintf(stderr, "div\n");
|
||||
break;
|
||||
|
||||
case DW_OP_minus:
|
||||
value = *sp--;
|
||||
*sp = *sp - value;
|
||||
if (log)
|
||||
fprintf(stderr, "minus\n");
|
||||
break;
|
||||
|
||||
case DW_OP_mod:
|
||||
svalue = (sint_t)(*sp--);
|
||||
svalue2 = (sint_t)*sp;
|
||||
*sp = (pint_t)(svalue2 % svalue);
|
||||
if (log)
|
||||
fprintf(stderr, "module\n");
|
||||
break;
|
||||
|
||||
case DW_OP_mul:
|
||||
svalue = (sint_t)(*sp--);
|
||||
svalue2 = (sint_t)*sp;
|
||||
*sp = (pint_t)(svalue2 * svalue);
|
||||
if (log)
|
||||
fprintf(stderr, "mul\n");
|
||||
break;
|
||||
|
||||
case DW_OP_neg:
|
||||
*sp = 0 - *sp;
|
||||
if (log)
|
||||
fprintf(stderr, "neg\n");
|
||||
break;
|
||||
|
||||
case DW_OP_not:
|
||||
svalue = (sint_t)(*sp);
|
||||
*sp = (pint_t)(~svalue);
|
||||
if (log)
|
||||
fprintf(stderr, "not\n");
|
||||
break;
|
||||
|
||||
case DW_OP_or:
|
||||
value = *sp--;
|
||||
*sp |= value;
|
||||
if (log)
|
||||
fprintf(stderr, "or\n");
|
||||
break;
|
||||
|
||||
case DW_OP_plus:
|
||||
value = *sp--;
|
||||
*sp += value;
|
||||
if (log)
|
||||
fprintf(stderr, "plus\n");
|
||||
break;
|
||||
|
||||
case DW_OP_plus_uconst:
|
||||
// pop stack, add uelb128 constant, push result
|
||||
*sp += addressSpace.getULEB128(p, expressionEnd);
|
||||
if (log)
|
||||
fprintf(stderr, "add constant\n");
|
||||
break;
|
||||
|
||||
case DW_OP_shl:
|
||||
value = *sp--;
|
||||
*sp = *sp << value;
|
||||
if (log)
|
||||
fprintf(stderr, "shift left\n");
|
||||
break;
|
||||
|
||||
case DW_OP_shr:
|
||||
value = *sp--;
|
||||
*sp = *sp >> value;
|
||||
if (log)
|
||||
fprintf(stderr, "shift left\n");
|
||||
break;
|
||||
|
||||
case DW_OP_shra:
|
||||
value = *sp--;
|
||||
svalue = (sint_t)*sp;
|
||||
*sp = (pint_t)(svalue >> value);
|
||||
if (log)
|
||||
fprintf(stderr, "shift left arithmetric\n");
|
||||
break;
|
||||
|
||||
case DW_OP_xor:
|
||||
value = *sp--;
|
||||
*sp ^= value;
|
||||
if (log)
|
||||
fprintf(stderr, "xor\n");
|
||||
break;
|
||||
|
||||
case DW_OP_skip:
|
||||
svalue = (int16_t) addressSpace.get16(p);
|
||||
p += 2;
|
||||
p = (pint_t)((sint_t)p + svalue);
|
||||
if (log)
|
||||
fprintf(stderr, "skip %" PRIu64 "\n", (uint64_t)svalue);
|
||||
break;
|
||||
|
||||
case DW_OP_bra:
|
||||
svalue = (int16_t) addressSpace.get16(p);
|
||||
p += 2;
|
||||
if (*sp--)
|
||||
p = (pint_t)((sint_t)p + svalue);
|
||||
if (log)
|
||||
fprintf(stderr, "bra %" PRIu64 "\n", (uint64_t)svalue);
|
||||
break;
|
||||
|
||||
case DW_OP_eq:
|
||||
value = *sp--;
|
||||
*sp = (*sp == value);
|
||||
if (log)
|
||||
fprintf(stderr, "eq\n");
|
||||
break;
|
||||
|
||||
case DW_OP_ge:
|
||||
value = *sp--;
|
||||
*sp = (*sp >= value);
|
||||
if (log)
|
||||
fprintf(stderr, "ge\n");
|
||||
break;
|
||||
|
||||
case DW_OP_gt:
|
||||
value = *sp--;
|
||||
*sp = (*sp > value);
|
||||
if (log)
|
||||
fprintf(stderr, "gt\n");
|
||||
break;
|
||||
|
||||
case DW_OP_le:
|
||||
value = *sp--;
|
||||
*sp = (*sp <= value);
|
||||
if (log)
|
||||
fprintf(stderr, "le\n");
|
||||
break;
|
||||
|
||||
case DW_OP_lt:
|
||||
value = *sp--;
|
||||
*sp = (*sp < value);
|
||||
if (log)
|
||||
fprintf(stderr, "lt\n");
|
||||
break;
|
||||
|
||||
case DW_OP_ne:
|
||||
value = *sp--;
|
||||
*sp = (*sp != value);
|
||||
if (log)
|
||||
fprintf(stderr, "ne\n");
|
||||
break;
|
||||
|
||||
case DW_OP_lit0:
|
||||
case DW_OP_lit1:
|
||||
case DW_OP_lit2:
|
||||
case DW_OP_lit3:
|
||||
case DW_OP_lit4:
|
||||
case DW_OP_lit5:
|
||||
case DW_OP_lit6:
|
||||
case DW_OP_lit7:
|
||||
case DW_OP_lit8:
|
||||
case DW_OP_lit9:
|
||||
case DW_OP_lit10:
|
||||
case DW_OP_lit11:
|
||||
case DW_OP_lit12:
|
||||
case DW_OP_lit13:
|
||||
case DW_OP_lit14:
|
||||
case DW_OP_lit15:
|
||||
case DW_OP_lit16:
|
||||
case DW_OP_lit17:
|
||||
case DW_OP_lit18:
|
||||
case DW_OP_lit19:
|
||||
case DW_OP_lit20:
|
||||
case DW_OP_lit21:
|
||||
case DW_OP_lit22:
|
||||
case DW_OP_lit23:
|
||||
case DW_OP_lit24:
|
||||
case DW_OP_lit25:
|
||||
case DW_OP_lit26:
|
||||
case DW_OP_lit27:
|
||||
case DW_OP_lit28:
|
||||
case DW_OP_lit29:
|
||||
case DW_OP_lit30:
|
||||
case DW_OP_lit31:
|
||||
value = static_cast<pint_t>(opcode - DW_OP_lit0);
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "push literal 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_reg0:
|
||||
case DW_OP_reg1:
|
||||
case DW_OP_reg2:
|
||||
case DW_OP_reg3:
|
||||
case DW_OP_reg4:
|
||||
case DW_OP_reg5:
|
||||
case DW_OP_reg6:
|
||||
case DW_OP_reg7:
|
||||
case DW_OP_reg8:
|
||||
case DW_OP_reg9:
|
||||
case DW_OP_reg10:
|
||||
case DW_OP_reg11:
|
||||
case DW_OP_reg12:
|
||||
case DW_OP_reg13:
|
||||
case DW_OP_reg14:
|
||||
case DW_OP_reg15:
|
||||
case DW_OP_reg16:
|
||||
case DW_OP_reg17:
|
||||
case DW_OP_reg18:
|
||||
case DW_OP_reg19:
|
||||
case DW_OP_reg20:
|
||||
case DW_OP_reg21:
|
||||
case DW_OP_reg22:
|
||||
case DW_OP_reg23:
|
||||
case DW_OP_reg24:
|
||||
case DW_OP_reg25:
|
||||
case DW_OP_reg26:
|
||||
case DW_OP_reg27:
|
||||
case DW_OP_reg28:
|
||||
case DW_OP_reg29:
|
||||
case DW_OP_reg30:
|
||||
case DW_OP_reg31:
|
||||
reg = static_cast<uint32_t>(opcode - DW_OP_reg0);
|
||||
*(++sp) = registers.getRegister((int)reg);
|
||||
if (log)
|
||||
fprintf(stderr, "push reg %d\n", reg);
|
||||
break;
|
||||
|
||||
case DW_OP_regx:
|
||||
reg = static_cast<uint32_t>(addressSpace.getULEB128(p, expressionEnd));
|
||||
*(++sp) = registers.getRegister((int)reg);
|
||||
if (log)
|
||||
fprintf(stderr, "push reg %d + 0x%" PRIx64 "\n", reg, (uint64_t)svalue);
|
||||
break;
|
||||
|
||||
case DW_OP_breg0:
|
||||
case DW_OP_breg1:
|
||||
case DW_OP_breg2:
|
||||
case DW_OP_breg3:
|
||||
case DW_OP_breg4:
|
||||
case DW_OP_breg5:
|
||||
case DW_OP_breg6:
|
||||
case DW_OP_breg7:
|
||||
case DW_OP_breg8:
|
||||
case DW_OP_breg9:
|
||||
case DW_OP_breg10:
|
||||
case DW_OP_breg11:
|
||||
case DW_OP_breg12:
|
||||
case DW_OP_breg13:
|
||||
case DW_OP_breg14:
|
||||
case DW_OP_breg15:
|
||||
case DW_OP_breg16:
|
||||
case DW_OP_breg17:
|
||||
case DW_OP_breg18:
|
||||
case DW_OP_breg19:
|
||||
case DW_OP_breg20:
|
||||
case DW_OP_breg21:
|
||||
case DW_OP_breg22:
|
||||
case DW_OP_breg23:
|
||||
case DW_OP_breg24:
|
||||
case DW_OP_breg25:
|
||||
case DW_OP_breg26:
|
||||
case DW_OP_breg27:
|
||||
case DW_OP_breg28:
|
||||
case DW_OP_breg29:
|
||||
case DW_OP_breg30:
|
||||
case DW_OP_breg31:
|
||||
reg = static_cast<uint32_t>(opcode - DW_OP_breg0);
|
||||
svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd);
|
||||
svalue += static_cast<sint_t>(registers.getRegister((int)reg));
|
||||
*(++sp) = (pint_t)(svalue);
|
||||
if (log)
|
||||
fprintf(stderr, "push reg %d + 0x%" PRIx64 "\n", reg, (uint64_t)svalue);
|
||||
break;
|
||||
|
||||
case DW_OP_bregx:
|
||||
reg = static_cast<uint32_t>(addressSpace.getULEB128(p, expressionEnd));
|
||||
svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd);
|
||||
svalue += static_cast<sint_t>(registers.getRegister((int)reg));
|
||||
*(++sp) = (pint_t)(svalue);
|
||||
if (log)
|
||||
fprintf(stderr, "push reg %d + 0x%" PRIx64 "\n", reg, (uint64_t)svalue);
|
||||
break;
|
||||
|
||||
case DW_OP_fbreg:
|
||||
_LIBUNWIND_ABORT("DW_OP_fbreg not implemented");
|
||||
break;
|
||||
|
||||
case DW_OP_piece:
|
||||
_LIBUNWIND_ABORT("DW_OP_piece not implemented");
|
||||
break;
|
||||
|
||||
case DW_OP_deref_size:
|
||||
// pop stack, dereference, push result
|
||||
value = *sp--;
|
||||
switch (addressSpace.get8(p++)) {
|
||||
case 1:
|
||||
value = addressSpace.get8(value);
|
||||
break;
|
||||
case 2:
|
||||
value = addressSpace.get16(value);
|
||||
break;
|
||||
case 4:
|
||||
value = addressSpace.get32(value);
|
||||
break;
|
||||
case 8:
|
||||
value = (pint_t)addressSpace.get64(value);
|
||||
break;
|
||||
default:
|
||||
_LIBUNWIND_ABORT("DW_OP_deref_size with bad size");
|
||||
}
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "sized dereference 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_xderef_size:
|
||||
case DW_OP_nop:
|
||||
case DW_OP_push_object_addres:
|
||||
case DW_OP_call2:
|
||||
case DW_OP_call4:
|
||||
case DW_OP_call_ref:
|
||||
default:
|
||||
_LIBUNWIND_ABORT("dwarf opcode not implemented");
|
||||
}
|
||||
|
||||
}
|
||||
if (log)
|
||||
fprintf(stderr, "expression evaluates to 0x%" PRIx64 "\n", (uint64_t)*sp);
|
||||
return *sp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace libunwind
|
||||
|
||||
#endif // __DWARF_INSTRUCTIONS_HPP__
|
722
src/DwarfParser.hpp
Normal file
722
src/DwarfParser.hpp
Normal file
@ -0,0 +1,722 @@
|
||||
//===--------------------------- DwarfParser.hpp --------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//
|
||||
// Parses DWARF CFIs (FDEs and CIEs).
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __DWARF_PARSER_HPP__
|
||||
#define __DWARF_PARSER_HPP__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "libunwind.h"
|
||||
#include "dwarf2.h"
|
||||
|
||||
#include "AddressSpace.hpp"
|
||||
|
||||
namespace libunwind {
|
||||
|
||||
/// CFI_Parser does basic parsing of a CFI (Call Frame Information) records.
|
||||
/// See Dwarf Spec for details:
|
||||
/// http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
|
||||
///
|
||||
template <typename A>
|
||||
class CFI_Parser {
|
||||
public:
|
||||
typedef typename A::pint_t pint_t;
|
||||
|
||||
/// Information encoded in a CIE (Common Information Entry)
|
||||
struct CIE_Info {
|
||||
pint_t cieStart;
|
||||
pint_t cieLength;
|
||||
pint_t cieInstructions;
|
||||
uint8_t pointerEncoding;
|
||||
uint8_t lsdaEncoding;
|
||||
uint8_t personalityEncoding;
|
||||
uint8_t personalityOffsetInCIE;
|
||||
pint_t personality;
|
||||
uint32_t codeAlignFactor;
|
||||
int dataAlignFactor;
|
||||
bool isSignalFrame;
|
||||
bool fdesHaveAugmentationData;
|
||||
uint8_t returnAddressRegister;
|
||||
};
|
||||
|
||||
/// Information about an FDE (Frame Description Entry)
|
||||
struct FDE_Info {
|
||||
pint_t fdeStart;
|
||||
pint_t fdeLength;
|
||||
pint_t fdeInstructions;
|
||||
pint_t pcStart;
|
||||
pint_t pcEnd;
|
||||
pint_t lsda;
|
||||
};
|
||||
|
||||
enum {
|
||||
kMaxRegisterNumber = 120
|
||||
};
|
||||
enum RegisterSavedWhere {
|
||||
kRegisterUnused,
|
||||
kRegisterInCFA,
|
||||
kRegisterOffsetFromCFA,
|
||||
kRegisterInRegister,
|
||||
kRegisterAtExpression,
|
||||
kRegisterIsExpression
|
||||
};
|
||||
struct RegisterLocation {
|
||||
RegisterSavedWhere location;
|
||||
int64_t value;
|
||||
};
|
||||
/// Information about a frame layout and registers saved determined
|
||||
/// by "running" the dwarf FDE "instructions"
|
||||
struct PrologInfo {
|
||||
uint32_t cfaRegister;
|
||||
int32_t cfaRegisterOffset; // CFA = (cfaRegister)+cfaRegisterOffset
|
||||
int64_t cfaExpression; // CFA = expression
|
||||
uint32_t spExtraArgSize;
|
||||
uint32_t codeOffsetAtStackDecrement;
|
||||
bool registersInOtherRegisters;
|
||||
bool sameValueUsed;
|
||||
RegisterLocation savedRegisters[kMaxRegisterNumber];
|
||||
};
|
||||
|
||||
struct PrologInfoStackEntry {
|
||||
PrologInfoStackEntry(PrologInfoStackEntry *n, const PrologInfo &i)
|
||||
: next(n), info(i) {}
|
||||
PrologInfoStackEntry *next;
|
||||
PrologInfo info;
|
||||
};
|
||||
|
||||
static bool findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart,
|
||||
uint32_t sectionLength, pint_t fdeHint, FDE_Info *fdeInfo,
|
||||
CIE_Info *cieInfo);
|
||||
static const char *decodeFDE(A &addressSpace, pint_t fdeStart,
|
||||
FDE_Info *fdeInfo, CIE_Info *cieInfo);
|
||||
static bool parseFDEInstructions(A &addressSpace, const FDE_Info &fdeInfo,
|
||||
const CIE_Info &cieInfo, pint_t upToPC,
|
||||
PrologInfo *results);
|
||||
|
||||
static const char *parseCIE(A &addressSpace, pint_t cie, CIE_Info *cieInfo);
|
||||
|
||||
private:
|
||||
static bool parseInstructions(A &addressSpace, pint_t instructions,
|
||||
pint_t instructionsEnd, const CIE_Info &cieInfo,
|
||||
pint_t pcoffset,
|
||||
PrologInfoStackEntry *&rememberStack,
|
||||
PrologInfo *results);
|
||||
};
|
||||
|
||||
/// Parse a FDE into a CIE_Info and an FDE_Info
|
||||
template <typename A>
|
||||
const char *CFI_Parser<A>::decodeFDE(A &addressSpace, pint_t fdeStart,
|
||||
FDE_Info *fdeInfo, CIE_Info *cieInfo) {
|
||||
pint_t p = fdeStart;
|
||||
pint_t cfiLength = (pint_t)addressSpace.get32(p);
|
||||
p += 4;
|
||||
if (cfiLength == 0xffffffff) {
|
||||
// 0xffffffff means length is really next 8 bytes
|
||||
cfiLength = (pint_t)addressSpace.get64(p);
|
||||
p += 8;
|
||||
}
|
||||
if (cfiLength == 0)
|
||||
return "FDE has zero length"; // end marker
|
||||
uint32_t ciePointer = addressSpace.get32(p);
|
||||
if (ciePointer == 0)
|
||||
return "FDE is really a CIE"; // this is a CIE not an FDE
|
||||
pint_t nextCFI = p + cfiLength;
|
||||
pint_t cieStart = p - ciePointer;
|
||||
const char *err = parseCIE(addressSpace, cieStart, cieInfo);
|
||||
if (err != NULL)
|
||||
return err;
|
||||
p += 4;
|
||||
// parse pc begin and range
|
||||
pint_t pcStart =
|
||||
addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding);
|
||||
pint_t pcRange =
|
||||
addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F);
|
||||
// parse rest of info
|
||||
fdeInfo->lsda = 0;
|
||||
// check for augmentation length
|
||||
if (cieInfo->fdesHaveAugmentationData) {
|
||||
pint_t augLen = (pint_t)addressSpace.getULEB128(p, nextCFI);
|
||||
pint_t endOfAug = p + augLen;
|
||||
if (cieInfo->lsdaEncoding != DW_EH_PE_omit) {
|
||||
// peek at value (without indirection). Zero means no lsda
|
||||
pint_t lsdaStart = p;
|
||||
if (addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) !=
|
||||
0) {
|
||||
// reset pointer and re-parse lsda address
|
||||
p = lsdaStart;
|
||||
fdeInfo->lsda =
|
||||
addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding);
|
||||
}
|
||||
}
|
||||
p = endOfAug;
|
||||
}
|
||||
fdeInfo->fdeStart = fdeStart;
|
||||
fdeInfo->fdeLength = nextCFI - fdeStart;
|
||||
fdeInfo->fdeInstructions = p;
|
||||
fdeInfo->pcStart = pcStart;
|
||||
fdeInfo->pcEnd = pcStart + pcRange;
|
||||
return NULL; // success
|
||||
}
|
||||
|
||||
/// Scan an eh_frame section to find an FDE for a pc
|
||||
template <typename A>
|
||||
bool CFI_Parser<A>::findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart,
|
||||
uint32_t sectionLength, pint_t fdeHint,
|
||||
FDE_Info *fdeInfo, CIE_Info *cieInfo) {
|
||||
//fprintf(stderr, "findFDE(0x%llX)\n", (long long)pc);
|
||||
pint_t p = (fdeHint != 0) ? fdeHint : ehSectionStart;
|
||||
const pint_t ehSectionEnd = p + sectionLength;
|
||||
while (p < ehSectionEnd) {
|
||||
pint_t currentCFI = p;
|
||||
//fprintf(stderr, "findFDE() CFI at 0x%llX\n", (long long)p);
|
||||
pint_t cfiLength = addressSpace.get32(p);
|
||||
p += 4;
|
||||
if (cfiLength == 0xffffffff) {
|
||||
// 0xffffffff means length is really next 8 bytes
|
||||
cfiLength = (pint_t)addressSpace.get64(p);
|
||||
p += 8;
|
||||
}
|
||||
if (cfiLength == 0)
|
||||
return false; // end marker
|
||||
uint32_t id = addressSpace.get32(p);
|
||||
if (id == 0) {
|
||||
// skip over CIEs
|
||||
p += cfiLength;
|
||||
} else {
|
||||
// process FDE to see if it covers pc
|
||||
pint_t nextCFI = p + cfiLength;
|
||||
uint32_t ciePointer = addressSpace.get32(p);
|
||||
pint_t cieStart = p - ciePointer;
|
||||
// validate pointer to CIE is within section
|
||||
if ((ehSectionStart <= cieStart) && (cieStart < ehSectionEnd)) {
|
||||
if (parseCIE(addressSpace, cieStart, cieInfo) == NULL) {
|
||||
p += 4;
|
||||
// parse pc begin and range
|
||||
pint_t pcStart =
|
||||
addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding);
|
||||
pint_t pcRange = addressSpace.getEncodedP(
|
||||
p, nextCFI, cieInfo->pointerEncoding & 0x0F);
|
||||
// test if pc is within the function this FDE covers
|
||||
if ((pcStart < pc) && (pc <= pcStart + pcRange)) {
|
||||
// parse rest of info
|
||||
fdeInfo->lsda = 0;
|
||||
// check for augmentation length
|
||||
if (cieInfo->fdesHaveAugmentationData) {
|
||||
pint_t augLen = (pint_t)addressSpace.getULEB128(p, nextCFI);
|
||||
pint_t endOfAug = p + augLen;
|
||||
if (cieInfo->lsdaEncoding != DW_EH_PE_omit) {
|
||||
// peek at value (without indirection). Zero means no lsda
|
||||
pint_t lsdaStart = p;
|
||||
if (addressSpace.getEncodedP(
|
||||
p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0) {
|
||||
// reset pointer and re-parse lsda address
|
||||
p = lsdaStart;
|
||||
fdeInfo->lsda = addressSpace
|
||||
.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding);
|
||||
}
|
||||
}
|
||||
p = endOfAug;
|
||||
}
|
||||
fdeInfo->fdeStart = currentCFI;
|
||||
fdeInfo->fdeLength = nextCFI - currentCFI;
|
||||
fdeInfo->fdeInstructions = p;
|
||||
fdeInfo->pcStart = pcStart;
|
||||
fdeInfo->pcEnd = pcStart + pcRange;
|
||||
return true;
|
||||
} else {
|
||||
// pc is not in begin/range, skip this FDE
|
||||
}
|
||||
} else {
|
||||
// malformed CIE, now augmentation describing pc range encoding
|
||||
}
|
||||
} else {
|
||||
// malformed FDE. CIE is bad
|
||||
}
|
||||
p = nextCFI;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Extract info from a CIE
|
||||
template <typename A>
|
||||
const char *CFI_Parser<A>::parseCIE(A &addressSpace, pint_t cie,
|
||||
CIE_Info *cieInfo) {
|
||||
cieInfo->pointerEncoding = 0;
|
||||
cieInfo->lsdaEncoding = DW_EH_PE_omit;
|
||||
cieInfo->personalityEncoding = 0;
|
||||
cieInfo->personalityOffsetInCIE = 0;
|
||||
cieInfo->personality = 0;
|
||||
cieInfo->codeAlignFactor = 0;
|
||||
cieInfo->dataAlignFactor = 0;
|
||||
cieInfo->isSignalFrame = false;
|
||||
cieInfo->fdesHaveAugmentationData = false;
|
||||
cieInfo->cieStart = cie;
|
||||
pint_t p = cie;
|
||||
pint_t cieLength = (pint_t)addressSpace.get32(p);
|
||||
p += 4;
|
||||
pint_t cieContentEnd = p + cieLength;
|
||||
if (cieLength == 0xffffffff) {
|
||||
// 0xffffffff means length is really next 8 bytes
|
||||
cieLength = (pint_t)addressSpace.get64(p);
|
||||
p += 8;
|
||||
cieContentEnd = p + cieLength;
|
||||
}
|
||||
if (cieLength == 0)
|
||||
return NULL;
|
||||
// CIE ID is always 0
|
||||
if (addressSpace.get32(p) != 0)
|
||||
return "CIE ID is not zero";
|
||||
p += 4;
|
||||
// Version is always 1 or 3
|
||||
uint8_t version = addressSpace.get8(p);
|
||||
if ((version != 1) && (version != 3))
|
||||
return "CIE version is not 1 or 3";
|
||||
++p;
|
||||
// save start of augmentation string and find end
|
||||
pint_t strStart = p;
|
||||
while (addressSpace.get8(p) != 0)
|
||||
++p;
|
||||
++p;
|
||||
// parse code aligment factor
|
||||
cieInfo->codeAlignFactor = (uint32_t)addressSpace.getULEB128(p, cieContentEnd);
|
||||
// parse data alignment factor
|
||||
cieInfo->dataAlignFactor = (int)addressSpace.getSLEB128(p, cieContentEnd);
|
||||
// parse return address register
|
||||
uint64_t raReg = addressSpace.getULEB128(p, cieContentEnd);
|
||||
assert(raReg < 255 && "return address register too large");
|
||||
cieInfo->returnAddressRegister = (uint8_t)raReg;
|
||||
// parse augmentation data based on augmentation string
|
||||
const char *result = NULL;
|
||||
if (addressSpace.get8(strStart) == 'z') {
|
||||
// parse augmentation data length
|
||||
addressSpace.getULEB128(p, cieContentEnd);
|
||||
for (pint_t s = strStart; addressSpace.get8(s) != '\0'; ++s) {
|
||||
switch (addressSpace.get8(s)) {
|
||||
case 'z':
|
||||
cieInfo->fdesHaveAugmentationData = true;
|
||||
break;
|
||||
case 'P':
|
||||
cieInfo->personalityEncoding = addressSpace.get8(p);
|
||||
++p;
|
||||
cieInfo->personalityOffsetInCIE = (uint8_t)(p - cie);
|
||||
cieInfo->personality = addressSpace
|
||||
.getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding);
|
||||
break;
|
||||
case 'L':
|
||||
cieInfo->lsdaEncoding = addressSpace.get8(p);
|
||||
++p;
|
||||
break;
|
||||
case 'R':
|
||||
cieInfo->pointerEncoding = addressSpace.get8(p);
|
||||
++p;
|
||||
break;
|
||||
case 'S':
|
||||
cieInfo->isSignalFrame = true;
|
||||
break;
|
||||
default:
|
||||
// ignore unknown letters
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
cieInfo->cieLength = cieContentEnd - cieInfo->cieStart;
|
||||
cieInfo->cieInstructions = p;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// "run" the dwarf instructions and create the abstact PrologInfo for an FDE
|
||||
template <typename A>
|
||||
bool CFI_Parser<A>::parseFDEInstructions(A &addressSpace,
|
||||
const FDE_Info &fdeInfo,
|
||||
const CIE_Info &cieInfo, pint_t upToPC,
|
||||
PrologInfo *results) {
|
||||
// clear results
|
||||
memset(results, '\0', sizeof(PrologInfo));
|
||||
PrologInfoStackEntry *rememberStack = NULL;
|
||||
|
||||
// parse CIE then FDE instructions
|
||||
return parseInstructions(addressSpace, cieInfo.cieInstructions,
|
||||
cieInfo.cieStart + cieInfo.cieLength, cieInfo,
|
||||
(pint_t)(-1), rememberStack, results) &&
|
||||
parseInstructions(addressSpace, fdeInfo.fdeInstructions,
|
||||
fdeInfo.fdeStart + fdeInfo.fdeLength, cieInfo,
|
||||
upToPC - fdeInfo.pcStart, rememberStack, results);
|
||||
}
|
||||
|
||||
/// "run" the dwarf instructions
|
||||
template <typename A>
|
||||
bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions,
|
||||
pint_t instructionsEnd,
|
||||
const CIE_Info &cieInfo, pint_t pcoffset,
|
||||
PrologInfoStackEntry *&rememberStack,
|
||||
PrologInfo *results) {
|
||||
const bool logDwarf = false;
|
||||
pint_t p = instructions;
|
||||
pint_t codeOffset = 0;
|
||||
PrologInfo initialState = *results;
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "parseInstructions(instructions=0x%0" PRIx64 ")\n",
|
||||
(uint64_t)instructionsEnd);
|
||||
|
||||
// see Dwarf Spec, section 6.4.2 for details on unwind opcodes
|
||||
while ((p < instructionsEnd) && (codeOffset < pcoffset)) {
|
||||
uint64_t reg;
|
||||
uint64_t reg2;
|
||||
int64_t offset;
|
||||
uint64_t length;
|
||||
uint8_t opcode = addressSpace.get8(p);
|
||||
uint8_t operand;
|
||||
PrologInfoStackEntry *entry;
|
||||
++p;
|
||||
switch (opcode) {
|
||||
case DW_CFA_nop:
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_nop\n");
|
||||
break;
|
||||
case DW_CFA_set_loc:
|
||||
codeOffset =
|
||||
addressSpace.getEncodedP(p, instructionsEnd, cieInfo.pointerEncoding);
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_set_loc\n");
|
||||
break;
|
||||
case DW_CFA_advance_loc1:
|
||||
codeOffset += (addressSpace.get8(p) * cieInfo.codeAlignFactor);
|
||||
p += 1;
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_advance_loc1: new offset=%" PRIu64 "\n",
|
||||
(uint64_t)codeOffset);
|
||||
break;
|
||||
case DW_CFA_advance_loc2:
|
||||
codeOffset += (addressSpace.get16(p) * cieInfo.codeAlignFactor);
|
||||
p += 2;
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_advance_loc2: new offset=%" PRIu64 "\n",
|
||||
(uint64_t)codeOffset);
|
||||
break;
|
||||
case DW_CFA_advance_loc4:
|
||||
codeOffset += (addressSpace.get32(p) * cieInfo.codeAlignFactor);
|
||||
p += 4;
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_advance_loc4: new offset=%" PRIu64 "\n",
|
||||
(uint64_t)codeOffset);
|
||||
break;
|
||||
case DW_CFA_offset_extended:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd)
|
||||
* cieInfo.dataAlignFactor;
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
fprintf(stderr,
|
||||
"malformed DW_CFA_offset_extended dwarf unwind, reg too big\n");
|
||||
return false;
|
||||
}
|
||||
results->savedRegisters[reg].location = kRegisterInCFA;
|
||||
results->savedRegisters[reg].value = offset;
|
||||
if (logDwarf)
|
||||
fprintf(stderr,
|
||||
"DW_CFA_offset_extended(reg=%" PRIu64 ", offset=%" PRId64 ")\n",
|
||||
reg, offset);
|
||||
break;
|
||||
case DW_CFA_restore_extended:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
;
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"malformed DW_CFA_restore_extended dwarf unwind, reg too big\n");
|
||||
return false;
|
||||
}
|
||||
results->savedRegisters[reg] = initialState.savedRegisters[reg];
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_restore_extended(reg=%" PRIu64 ")\n", reg);
|
||||
break;
|
||||
case DW_CFA_undefined:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
fprintf(stderr,
|
||||
"malformed DW_CFA_undefined dwarf unwind, reg too big\n");
|
||||
return false;
|
||||
}
|
||||
results->savedRegisters[reg].location = kRegisterUnused;
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_undefined(reg=%" PRIu64 ")\n", reg);
|
||||
break;
|
||||
case DW_CFA_same_value:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
fprintf(stderr,
|
||||
"malformed DW_CFA_same_value dwarf unwind, reg too big\n");
|
||||
return false;
|
||||
}
|
||||
// <rdar://problem/8456377> DW_CFA_same_value unsupported
|
||||
// "same value" means register was stored in frame, but its current
|
||||
// value has not changed, so no need to restore from frame.
|
||||
// We model this as if the register was never saved.
|
||||
results->savedRegisters[reg].location = kRegisterUnused;
|
||||
// set flag to disable conversion to compact unwind
|
||||
results->sameValueUsed = true;
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_same_value(reg=%" PRIu64 ")\n", reg);
|
||||
break;
|
||||
case DW_CFA_register:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
reg2 = addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
fprintf(stderr,
|
||||
"malformed DW_CFA_register dwarf unwind, reg too big\n");
|
||||
return false;
|
||||
}
|
||||
if (reg2 > kMaxRegisterNumber) {
|
||||
fprintf(stderr,
|
||||
"malformed DW_CFA_register dwarf unwind, reg2 too big\n");
|
||||
return false;
|
||||
}
|
||||
results->savedRegisters[reg].location = kRegisterInRegister;
|
||||
results->savedRegisters[reg].value = (int64_t)reg2;
|
||||
// set flag to disable conversion to compact unwind
|
||||
results->registersInOtherRegisters = true;
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_register(reg=%" PRIu64 ", reg2=%" PRIu64 ")\n",
|
||||
reg, reg2);
|
||||
break;
|
||||
case DW_CFA_remember_state:
|
||||
// avoid operator new, because that would be an upward dependency
|
||||
entry = (PrologInfoStackEntry *)malloc(sizeof(PrologInfoStackEntry));
|
||||
if (entry != NULL) {
|
||||
entry->next = rememberStack;
|
||||
entry->info = *results;
|
||||
rememberStack = entry;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_remember_state\n");
|
||||
break;
|
||||
case DW_CFA_restore_state:
|
||||
if (rememberStack != NULL) {
|
||||
PrologInfoStackEntry *top = rememberStack;
|
||||
*results = top->info;
|
||||
rememberStack = top->next;
|
||||
free((char *)top);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_restore_state\n");
|
||||
break;
|
||||
case DW_CFA_def_cfa:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
fprintf(stderr, "malformed DW_CFA_def_cfa dwarf unwind, reg too big\n");
|
||||
return false;
|
||||
}
|
||||
results->cfaRegister = (uint32_t)reg;
|
||||
results->cfaRegisterOffset = (int32_t)offset;
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_def_cfa(reg=%" PRIu64 ", offset=%" PRIu64 ")\n",
|
||||
reg, offset);
|
||||
break;
|
||||
case DW_CFA_def_cfa_register:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"malformed DW_CFA_def_cfa_register dwarf unwind, reg too big\n");
|
||||
return false;
|
||||
}
|
||||
results->cfaRegister = (uint32_t)reg;
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_def_cfa_register(%" PRIu64 ")\n", reg);
|
||||
break;
|
||||
case DW_CFA_def_cfa_offset:
|
||||
results->cfaRegisterOffset = (int32_t)
|
||||
addressSpace.getULEB128(p, instructionsEnd);
|
||||
results->codeOffsetAtStackDecrement = (uint32_t)codeOffset;
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_def_cfa_offset(%d)\n",
|
||||
results->cfaRegisterOffset);
|
||||
break;
|
||||
case DW_CFA_def_cfa_expression:
|
||||
results->cfaRegister = 0;
|
||||
results->cfaExpression = (int64_t)p;
|
||||
length = addressSpace.getULEB128(p, instructionsEnd);
|
||||
p += length;
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_def_cfa_expression(expression=0x%" PRIx64
|
||||
", length=%" PRIu64 ")\n",
|
||||
results->cfaExpression, length);
|
||||
break;
|
||||
case DW_CFA_expression:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
fprintf(stderr,
|
||||
"malformed DW_CFA_expression dwarf unwind, reg too big\n");
|
||||
return false;
|
||||
}
|
||||
results->savedRegisters[reg].location = kRegisterAtExpression;
|
||||
results->savedRegisters[reg].value = (int64_t)p;
|
||||
length = addressSpace.getULEB128(p, instructionsEnd);
|
||||
p += length;
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_expression(reg=%" PRIu64
|
||||
", expression=0x%" PRIx64 ", length=%" PRIu64 ")\n",
|
||||
reg, results->savedRegisters[reg].value, length);
|
||||
break;
|
||||
case DW_CFA_offset_extended_sf:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"malformed DW_CFA_offset_extended_sf dwarf unwind, reg too big\n");
|
||||
return false;
|
||||
}
|
||||
offset =
|
||||
addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
|
||||
results->savedRegisters[reg].location = kRegisterInCFA;
|
||||
results->savedRegisters[reg].value = offset;
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_offset_extended_sf(reg=%" PRIu64
|
||||
", offset=%" PRId64 ")\n",
|
||||
reg, offset);
|
||||
break;
|
||||
case DW_CFA_def_cfa_sf:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
offset =
|
||||
addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
fprintf(stderr,
|
||||
"malformed DW_CFA_def_cfa_sf dwarf unwind, reg too big\n");
|
||||
return false;
|
||||
}
|
||||
results->cfaRegister = (uint32_t)reg;
|
||||
results->cfaRegisterOffset = (int32_t)offset;
|
||||
if (logDwarf)
|
||||
fprintf(stderr,
|
||||
"DW_CFA_def_cfa_sf(reg=%" PRIu64 ", offset=%" PRId64 ")\n", reg,
|
||||
offset);
|
||||
break;
|
||||
case DW_CFA_def_cfa_offset_sf:
|
||||
results->cfaRegisterOffset = (int32_t)
|
||||
(addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor);
|
||||
results->codeOffsetAtStackDecrement = (uint32_t)codeOffset;
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_def_cfa_offset_sf(%d)\n",
|
||||
results->cfaRegisterOffset);
|
||||
break;
|
||||
case DW_CFA_val_offset:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd)
|
||||
* cieInfo.dataAlignFactor;
|
||||
results->savedRegisters[reg].location = kRegisterOffsetFromCFA;
|
||||
results->savedRegisters[reg].value = offset;
|
||||
if (logDwarf)
|
||||
fprintf(stderr,
|
||||
"DW_CFA_val_offset(reg=%" PRIu64 ", offset=%" PRId64 "\n", reg,
|
||||
offset);
|
||||
break;
|
||||
case DW_CFA_val_offset_sf:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
fprintf(stderr,
|
||||
"malformed DW_CFA_val_offset_sf dwarf unwind, reg too big\n");
|
||||
return false;
|
||||
}
|
||||
offset =
|
||||
addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
|
||||
results->savedRegisters[reg].location = kRegisterOffsetFromCFA;
|
||||
results->savedRegisters[reg].value = offset;
|
||||
if (logDwarf)
|
||||
fprintf(stderr,
|
||||
"DW_CFA_val_offset_sf(reg=%" PRIu64 ", offset=%" PRId64 "\n",
|
||||
reg, offset);
|
||||
break;
|
||||
case DW_CFA_val_expression:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
fprintf(stderr,
|
||||
"malformed DW_CFA_val_expression dwarf unwind, reg too big\n");
|
||||
return false;
|
||||
}
|
||||
results->savedRegisters[reg].location = kRegisterIsExpression;
|
||||
results->savedRegisters[reg].value = (int64_t)p;
|
||||
length = addressSpace.getULEB128(p, instructionsEnd);
|
||||
p += length;
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_val_expression(reg=%" PRIu64
|
||||
", expression=0x%" PRIx64 ", length=%" PRIu64 ")\n",
|
||||
reg, results->savedRegisters[reg].value, length);
|
||||
break;
|
||||
case DW_CFA_GNU_args_size:
|
||||
length = addressSpace.getULEB128(p, instructionsEnd);
|
||||
results->spExtraArgSize = (uint32_t)length;
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_GNU_args_size(%" PRIu64 ")\n", length);
|
||||
break;
|
||||
case DW_CFA_GNU_negative_offset_extended:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
fprintf(stderr, "malformed DW_CFA_GNU_negative_offset_extended dwarf "
|
||||
"unwind, reg too big\n");
|
||||
return false;
|
||||
}
|
||||
offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd)
|
||||
* cieInfo.dataAlignFactor;
|
||||
results->savedRegisters[reg].location = kRegisterInCFA;
|
||||
results->savedRegisters[reg].value = -offset;
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_GNU_negative_offset_extended(%" PRId64 ")\n",
|
||||
offset);
|
||||
break;
|
||||
default:
|
||||
operand = opcode & 0x3F;
|
||||
switch (opcode & 0xC0) {
|
||||
case DW_CFA_offset:
|
||||
reg = operand;
|
||||
offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd)
|
||||
* cieInfo.dataAlignFactor;
|
||||
results->savedRegisters[reg].location = kRegisterInCFA;
|
||||
results->savedRegisters[reg].value = offset;
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_offset(reg=%d, offset=%" PRId64 ")\n",
|
||||
operand, offset);
|
||||
break;
|
||||
case DW_CFA_advance_loc:
|
||||
codeOffset += operand * cieInfo.codeAlignFactor;
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_advance_loc: new offset=%" PRIu64 "\n",
|
||||
(uint64_t)codeOffset);
|
||||
break;
|
||||
case DW_CFA_restore:
|
||||
reg = operand;
|
||||
results->savedRegisters[reg] = initialState.savedRegisters[reg];
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "DW_CFA_restore(reg=%" PRIu64 ")\n", reg);
|
||||
break;
|
||||
default:
|
||||
if (logDwarf)
|
||||
fprintf(stderr, "unknown CFA opcode 0x%02X\n", opcode);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace libunwind
|
||||
|
||||
#endif // __DWARF_PARSER_HPP__
|
161
src/EHHeaderParser.hpp
Normal file
161
src/EHHeaderParser.hpp
Normal file
@ -0,0 +1,161 @@
|
||||
//===------------------------- EHHeaderParser.hpp -------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//
|
||||
// Parses ELF .eh_frame_hdr sections.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __EHHEADERPARSER_HPP__
|
||||
#define __EHHEADERPARSER_HPP__
|
||||
|
||||
#include "libunwind.h"
|
||||
|
||||
#include "AddressSpace.hpp"
|
||||
#include "DwarfParser.hpp"
|
||||
|
||||
namespace libunwind {
|
||||
|
||||
/// \brief EHHeaderParser does basic parsing of an ELF .eh_frame_hdr section.
|
||||
///
|
||||
/// See DWARF spec for details:
|
||||
/// http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
|
||||
///
|
||||
template <typename A> class EHHeaderParser {
|
||||
public:
|
||||
typedef typename A::pint_t pint_t;
|
||||
|
||||
/// Information encoded in the EH frame header.
|
||||
struct EHHeaderInfo {
|
||||
pint_t eh_frame_ptr;
|
||||
size_t fde_count;
|
||||
pint_t table;
|
||||
uint8_t table_enc;
|
||||
};
|
||||
|
||||
static void decodeEHHdr(A &addressSpace, pint_t ehHdrStart, pint_t ehHdrEnd,
|
||||
EHHeaderInfo &ehHdrInfo);
|
||||
static bool findFDE(A &addressSpace, pint_t pc, pint_t ehHdrStart,
|
||||
uint32_t sectionLength,
|
||||
typename CFI_Parser<A>::FDE_Info *fdeInfo,
|
||||
typename CFI_Parser<A>::CIE_Info *cieInfo);
|
||||
|
||||
private:
|
||||
static bool decodeTableEntry(A &addressSpace, pint_t &tableEntry,
|
||||
pint_t ehHdrStart, pint_t ehHdrEnd,
|
||||
uint8_t tableEnc,
|
||||
typename CFI_Parser<A>::FDE_Info *fdeInfo,
|
||||
typename CFI_Parser<A>::CIE_Info *cieInfo);
|
||||
static size_t getTableEntrySize(uint8_t tableEnc);
|
||||
};
|
||||
|
||||
template <typename A>
|
||||
void EHHeaderParser<A>::decodeEHHdr(A &addressSpace, pint_t ehHdrStart,
|
||||
pint_t ehHdrEnd, EHHeaderInfo &ehHdrInfo) {
|
||||
pint_t p = ehHdrStart;
|
||||
uint8_t version = addressSpace.get8(p++);
|
||||
if (version != 1)
|
||||
_LIBUNWIND_ABORT("Unsupported .eh_frame_hdr version");
|
||||
|
||||
uint8_t eh_frame_ptr_enc = addressSpace.get8(p++);
|
||||
uint8_t fde_count_enc = addressSpace.get8(p++);
|
||||
ehHdrInfo.table_enc = addressSpace.get8(p++);
|
||||
|
||||
ehHdrInfo.eh_frame_ptr =
|
||||
addressSpace.getEncodedP(p, ehHdrEnd, eh_frame_ptr_enc, ehHdrStart);
|
||||
ehHdrInfo.fde_count =
|
||||
addressSpace.getEncodedP(p, ehHdrEnd, fde_count_enc, ehHdrStart);
|
||||
ehHdrInfo.table = p;
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
bool EHHeaderParser<A>::decodeTableEntry(
|
||||
A &addressSpace, pint_t &tableEntry, pint_t ehHdrStart, pint_t ehHdrEnd,
|
||||
uint8_t tableEnc, typename CFI_Parser<A>::FDE_Info *fdeInfo,
|
||||
typename CFI_Parser<A>::CIE_Info *cieInfo) {
|
||||
// Have to decode the whole FDE for the PC range anyway, so just throw away
|
||||
// the PC start.
|
||||
addressSpace.getEncodedP(tableEntry, ehHdrEnd, tableEnc, ehHdrStart);
|
||||
pint_t fde =
|
||||
addressSpace.getEncodedP(tableEntry, ehHdrEnd, tableEnc, ehHdrStart);
|
||||
const char *message =
|
||||
CFI_Parser<A>::decodeFDE(addressSpace, fde, fdeInfo, cieInfo);
|
||||
if (message != NULL) {
|
||||
_LIBUNWIND_DEBUG_LOG("EHHeaderParser::decodeTableEntry: bad fde: %s\n",
|
||||
message);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
bool EHHeaderParser<A>::findFDE(A &addressSpace, pint_t pc, pint_t ehHdrStart,
|
||||
uint32_t sectionLength,
|
||||
typename CFI_Parser<A>::FDE_Info *fdeInfo,
|
||||
typename CFI_Parser<A>::CIE_Info *cieInfo) {
|
||||
pint_t ehHdrEnd = ehHdrStart + sectionLength;
|
||||
|
||||
EHHeaderParser<A>::EHHeaderInfo hdrInfo;
|
||||
EHHeaderParser<A>::decodeEHHdr(addressSpace, ehHdrStart, ehHdrEnd, hdrInfo);
|
||||
|
||||
size_t tableEntrySize = getTableEntrySize(hdrInfo.table_enc);
|
||||
pint_t tableEntry;
|
||||
|
||||
size_t low = 0;
|
||||
for (size_t len = hdrInfo.fde_count; len > 1;) {
|
||||
size_t mid = low + (len / 2);
|
||||
tableEntry = hdrInfo.table + mid * tableEntrySize;
|
||||
pint_t start = addressSpace.getEncodedP(tableEntry, ehHdrEnd,
|
||||
hdrInfo.table_enc, ehHdrStart);
|
||||
|
||||
if (start == pc) {
|
||||
low = mid;
|
||||
break;
|
||||
} else if (start < pc) {
|
||||
low = mid;
|
||||
len -= (len / 2);
|
||||
} else {
|
||||
len /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
tableEntry = hdrInfo.table + low * tableEntrySize;
|
||||
if (decodeTableEntry(addressSpace, tableEntry, ehHdrStart, ehHdrEnd,
|
||||
hdrInfo.table_enc, fdeInfo, cieInfo)) {
|
||||
if (pc >= fdeInfo->pcStart && pc < fdeInfo->pcEnd)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
size_t EHHeaderParser<A>::getTableEntrySize(uint8_t tableEnc) {
|
||||
switch (tableEnc & 0x0f) {
|
||||
case DW_EH_PE_sdata2:
|
||||
case DW_EH_PE_udata2:
|
||||
return 4;
|
||||
case DW_EH_PE_sdata4:
|
||||
case DW_EH_PE_udata4:
|
||||
return 8;
|
||||
case DW_EH_PE_sdata8:
|
||||
case DW_EH_PE_udata8:
|
||||
return 16;
|
||||
case DW_EH_PE_sleb128:
|
||||
case DW_EH_PE_uleb128:
|
||||
_LIBUNWIND_ABORT("Can't binary search on variable length encoded data.");
|
||||
case DW_EH_PE_omit:
|
||||
return 0;
|
||||
default:
|
||||
_LIBUNWIND_ABORT("Unknown DWARF encoding for search table.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
1898
src/Registers.hpp
Normal file
1898
src/Registers.hpp
Normal file
File diff suppressed because it is too large
Load Diff
1009
src/Unwind-EHABI.cpp
Normal file
1009
src/Unwind-EHABI.cpp
Normal file
File diff suppressed because it is too large
Load Diff
51
src/Unwind-EHABI.h
Normal file
51
src/Unwind-EHABI.h
Normal file
@ -0,0 +1,51 @@
|
||||
//===------------------------- Unwind-EHABI.hpp ---------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __UNWIND_EHABI_H__
|
||||
#define __UNWIND_EHABI_H__
|
||||
|
||||
#include <__libunwind_config.h>
|
||||
|
||||
#if _LIBUNWIND_ARM_EHABI
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unwind.h>
|
||||
|
||||
// Unable to unwind in the ARM index table (section 5 EHABI).
|
||||
#define UNW_EXIDX_CANTUNWIND 0x1
|
||||
|
||||
static inline uint32_t signExtendPrel31(uint32_t data) {
|
||||
return data | ((data & 0x40000000u) << 1);
|
||||
}
|
||||
|
||||
static inline uint32_t readPrel31(const uint32_t *data) {
|
||||
return (((uint32_t)(uintptr_t)data) + signExtendPrel31(*data));
|
||||
}
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern _Unwind_Reason_Code __aeabi_unwind_cpp_pr0(
|
||||
_Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context);
|
||||
|
||||
extern _Unwind_Reason_Code __aeabi_unwind_cpp_pr1(
|
||||
_Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context);
|
||||
|
||||
extern _Unwind_Reason_Code __aeabi_unwind_cpp_pr2(
|
||||
_Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // _LIBUNWIND_ARM_EHABI
|
||||
|
||||
#endif // __UNWIND_EHABI_H__
|
468
src/Unwind-sjlj.c
Normal file
468
src/Unwind-sjlj.c
Normal file
@ -0,0 +1,468 @@
|
||||
//===--------------------------- Unwind-sjlj.c ----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//
|
||||
// Implements setjump-longjump based C++ exceptions
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <unwind.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "unwind_ext.h"
|
||||
|
||||
//
|
||||
// 32-bit iOS uses setjump/longjump based C++ exceptions.
|
||||
// Other architectures use "zero cost" exceptions.
|
||||
//
|
||||
// With SJLJ based exceptions, any function that has a catch clause or needs to
|
||||
// do any clean up when an exception propagates through it, needs to call
|
||||
// _Unwind_SjLj_Register() at the start of the function and
|
||||
// _Unwind_SjLj_Unregister() at the end. The register function is called with
|
||||
// the address of a block of memory in the function's stack frame. The runtime
|
||||
// keeps a linked list (stack) of these blocks - one per thread. The calling
|
||||
// function also sets the personality and lsda fields of the block.
|
||||
//
|
||||
|
||||
#if _LIBUNWIND_BUILD_SJLJ_APIS
|
||||
|
||||
struct _Unwind_FunctionContext {
|
||||
// next function in stack of handlers
|
||||
struct _Unwind_FunctionContext *prev;
|
||||
|
||||
// set by calling function before registering to be the landing pad
|
||||
uintptr_t resumeLocation;
|
||||
|
||||
// set by personality handler to be parameters passed to landing pad function
|
||||
uintptr_t resumeParameters[4];
|
||||
|
||||
// set by calling function before registering
|
||||
__personality_routine personality; // arm offset=24
|
||||
uintptr_t lsda; // arm offset=28
|
||||
|
||||
// variable length array, contains registers to restore
|
||||
// 0 = r7, 1 = pc, 2 = sp
|
||||
void *jbuf[];
|
||||
};
|
||||
|
||||
|
||||
/// Called at start of each function that catches exceptions
|
||||
_LIBUNWIND_EXPORT void
|
||||
_Unwind_SjLj_Register(struct _Unwind_FunctionContext *fc) {
|
||||
fc->prev = __Unwind_SjLj_GetTopOfFunctionStack();
|
||||
__Unwind_SjLj_SetTopOfFunctionStack(fc);
|
||||
}
|
||||
|
||||
|
||||
/// Called at end of each function that catches exceptions
|
||||
_LIBUNWIND_EXPORT void
|
||||
_Unwind_SjLj_Unregister(struct _Unwind_FunctionContext *fc) {
|
||||
__Unwind_SjLj_SetTopOfFunctionStack(fc->prev);
|
||||
}
|
||||
|
||||
|
||||
static _Unwind_Reason_Code
|
||||
unwind_phase1(struct _Unwind_Exception *exception_object) {
|
||||
_Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack();
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase1: initial function-context=%p\n", c);
|
||||
|
||||
// walk each frame looking for a place to stop
|
||||
for (bool handlerNotFound = true; handlerNotFound; c = c->prev) {
|
||||
|
||||
// check for no more frames
|
||||
if (c == NULL) {
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): reached "
|
||||
"bottom => _URC_END_OF_STACK\n",
|
||||
exception_object);
|
||||
return _URC_END_OF_STACK;
|
||||
}
|
||||
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase1: function-context=%p\n", c);
|
||||
// if there is a personality routine, ask it if it will want to stop at this
|
||||
// frame
|
||||
if (c->personality != NULL) {
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): calling "
|
||||
"personality function %p\n",
|
||||
exception_object, c->personality);
|
||||
_Unwind_Reason_Code personalityResult = (*c->personality)(
|
||||
1, _UA_SEARCH_PHASE, exception_object->exception_class,
|
||||
exception_object, (struct _Unwind_Context *)c);
|
||||
switch (personalityResult) {
|
||||
case _URC_HANDLER_FOUND:
|
||||
// found a catch clause or locals that need destructing in this frame
|
||||
// stop search and remember function context
|
||||
handlerNotFound = false;
|
||||
exception_object->private_2 = (uintptr_t) c;
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): "
|
||||
"_URC_HANDLER_FOUND\n", exception_object);
|
||||
return _URC_NO_REASON;
|
||||
|
||||
case _URC_CONTINUE_UNWIND:
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): "
|
||||
"_URC_CONTINUE_UNWIND\n", exception_object);
|
||||
// continue unwinding
|
||||
break;
|
||||
|
||||
default:
|
||||
// something went wrong
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR\n",
|
||||
exception_object);
|
||||
return _URC_FATAL_PHASE1_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
|
||||
|
||||
static _Unwind_Reason_Code
|
||||
unwind_phase2(struct _Unwind_Exception *exception_object) {
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p)\n", exception_object);
|
||||
|
||||
// walk each frame until we reach where search phase said to stop
|
||||
_Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack();
|
||||
while (true) {
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2s(ex_ojb=%p): context=%p\n",
|
||||
exception_object, c);
|
||||
|
||||
// check for no more frames
|
||||
if (c == NULL) {
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step() reached "
|
||||
"bottom => _URC_END_OF_STACK\n",
|
||||
exception_object);
|
||||
return _URC_END_OF_STACK;
|
||||
}
|
||||
|
||||
// if there is a personality routine, tell it we are unwinding
|
||||
if (c->personality != NULL) {
|
||||
_Unwind_Action action = _UA_CLEANUP_PHASE;
|
||||
if ((uintptr_t) c == exception_object->private_2)
|
||||
action = (_Unwind_Action)(
|
||||
_UA_CLEANUP_PHASE |
|
||||
_UA_HANDLER_FRAME); // tell personality this was the frame it marked
|
||||
// in phase 1
|
||||
_Unwind_Reason_Code personalityResult =
|
||||
(*c->personality)(1, action, exception_object->exception_class,
|
||||
exception_object, (struct _Unwind_Context *)c);
|
||||
switch (personalityResult) {
|
||||
case _URC_CONTINUE_UNWIND:
|
||||
// continue unwinding
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND\n",
|
||||
exception_object);
|
||||
if ((uintptr_t) c == exception_object->private_2) {
|
||||
// phase 1 said we would stop at this frame, but we did not...
|
||||
_LIBUNWIND_ABORT("during phase1 personality function said it would "
|
||||
"stop here, but now if phase2 it did not stop here");
|
||||
}
|
||||
break;
|
||||
case _URC_INSTALL_CONTEXT:
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): "
|
||||
"_URC_INSTALL_CONTEXT, will resume at "
|
||||
"landing pad %p\n",
|
||||
exception_object, c->jbuf[1]);
|
||||
// personality routine says to transfer control to landing pad
|
||||
// we may get control back if landing pad calls _Unwind_Resume()
|
||||
__Unwind_SjLj_SetTopOfFunctionStack(c);
|
||||
__builtin_longjmp(c->jbuf, 1);
|
||||
// unw_resume() only returns if there was an error
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
default:
|
||||
// something went wrong
|
||||
_LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d",
|
||||
personalityResult);
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
}
|
||||
c = c->prev;
|
||||
}
|
||||
|
||||
// clean up phase did not resume at the frame that the search phase said it
|
||||
// would
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static _Unwind_Reason_Code
|
||||
unwind_phase2_forced(struct _Unwind_Exception *exception_object,
|
||||
_Unwind_Stop_Fn stop, void *stop_parameter) {
|
||||
// walk each frame until we reach where search phase said to stop
|
||||
_Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack();
|
||||
while (true) {
|
||||
|
||||
// get next frame (skip over first which is _Unwind_RaiseException)
|
||||
if (c == NULL) {
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step() reached "
|
||||
"bottom => _URC_END_OF_STACK\n",
|
||||
exception_object);
|
||||
return _URC_END_OF_STACK;
|
||||
}
|
||||
|
||||
// call stop function at each frame
|
||||
_Unwind_Action action =
|
||||
(_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE);
|
||||
_Unwind_Reason_Code stopResult =
|
||||
(*stop)(1, action, exception_object->exception_class, exception_object,
|
||||
(struct _Unwind_Context *)c, stop_parameter);
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||
"stop function returned %d\n",
|
||||
exception_object, stopResult);
|
||||
if (stopResult != _URC_NO_REASON) {
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||
"stopped by stop function\n",
|
||||
exception_object);
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
|
||||
// if there is a personality routine, tell it we are unwinding
|
||||
if (c->personality != NULL) {
|
||||
__personality_routine p = (__personality_routine) c->personality;
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||
"calling personality function %p\n",
|
||||
exception_object, p);
|
||||
_Unwind_Reason_Code personalityResult =
|
||||
(*p)(1, action, exception_object->exception_class, exception_object,
|
||||
(struct _Unwind_Context *)c);
|
||||
switch (personalityResult) {
|
||||
case _URC_CONTINUE_UNWIND:
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||
"personality returned _URC_CONTINUE_UNWIND\n",
|
||||
exception_object);
|
||||
// destructors called, continue unwinding
|
||||
break;
|
||||
case _URC_INSTALL_CONTEXT:
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||
"personality returned _URC_INSTALL_CONTEXT\n",
|
||||
exception_object);
|
||||
// we may get control back if landing pad calls _Unwind_Resume()
|
||||
__Unwind_SjLj_SetTopOfFunctionStack(c);
|
||||
__builtin_longjmp(c->jbuf, 1);
|
||||
break;
|
||||
default:
|
||||
// something went wrong
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||
"personality returned %d, "
|
||||
"_URC_FATAL_PHASE2_ERROR\n",
|
||||
exception_object, personalityResult);
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
}
|
||||
c = c->prev;
|
||||
}
|
||||
|
||||
// call stop function one last time and tell it we've reached the end of the
|
||||
// stack
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop "
|
||||
"function with _UA_END_OF_STACK\n",
|
||||
exception_object);
|
||||
_Unwind_Action lastAction =
|
||||
(_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK);
|
||||
(*stop)(1, lastAction, exception_object->exception_class, exception_object,
|
||||
(struct _Unwind_Context *)c, stop_parameter);
|
||||
|
||||
// clean up phase did not resume at the frame that the search phase said it
|
||||
// would
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/// Called by __cxa_throw. Only returns if there is a fatal error
|
||||
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||
_Unwind_SjLj_RaiseException(struct _Unwind_Exception *exception_object) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_SjLj_RaiseException(ex_obj=%p)\n", exception_object);
|
||||
|
||||
// mark that this is a non-forced unwind, so _Unwind_Resume() can do the right
|
||||
// thing
|
||||
exception_object->private_1 = 0;
|
||||
exception_object->private_2 = 0;
|
||||
|
||||
// phase 1: the search phase
|
||||
_Unwind_Reason_Code phase1 = unwind_phase1(exception_object);
|
||||
if (phase1 != _URC_NO_REASON)
|
||||
return phase1;
|
||||
|
||||
// phase 2: the clean up phase
|
||||
return unwind_phase2(exception_object);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// When _Unwind_RaiseException() is in phase2, it hands control
|
||||
/// to the personality function at each frame. The personality
|
||||
/// may force a jump to a landing pad in that function, the landing
|
||||
/// pad code may then call _Unwind_Resume() to continue with the
|
||||
/// unwinding. Note: the call to _Unwind_Resume() is from compiler
|
||||
/// geneated user code. All other _Unwind_* routines are called
|
||||
/// by the C++ runtime __cxa_* routines.
|
||||
///
|
||||
/// Re-throwing an exception is implemented by having the code call
|
||||
/// __cxa_rethrow() which in turn calls _Unwind_Resume_or_Rethrow()
|
||||
_LIBUNWIND_EXPORT void
|
||||
_Unwind_SjLj_Resume(struct _Unwind_Exception *exception_object) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_SjLj_Resume(ex_obj=%p)\n", exception_object);
|
||||
|
||||
if (exception_object->private_1 != 0)
|
||||
unwind_phase2_forced(exception_object,
|
||||
(_Unwind_Stop_Fn) exception_object->private_1,
|
||||
(void *)exception_object->private_2);
|
||||
else
|
||||
unwind_phase2(exception_object);
|
||||
|
||||
// clients assume _Unwind_Resume() does not return, so all we can do is abort.
|
||||
_LIBUNWIND_ABORT("_Unwind_SjLj_Resume() can't return");
|
||||
}
|
||||
|
||||
|
||||
/// Called by __cxa_rethrow().
|
||||
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||
_Unwind_SjLj_Resume_or_Rethrow(struct _Unwind_Exception *exception_object) {
|
||||
_LIBUNWIND_TRACE_API("__Unwind_SjLj_Resume_or_Rethrow(ex_obj=%p), "
|
||||
"private_1=%ld\n",
|
||||
exception_object, exception_object->private_1);
|
||||
// If this is non-forced and a stopping place was found, then this is a
|
||||
// re-throw.
|
||||
// Call _Unwind_RaiseException() as if this was a new exception.
|
||||
if (exception_object->private_1 == 0) {
|
||||
return _Unwind_SjLj_RaiseException(exception_object);
|
||||
// should return if there is no catch clause, so that __cxa_rethrow can call
|
||||
// std::terminate()
|
||||
}
|
||||
|
||||
// Call through to _Unwind_Resume() which distiguishes between forced and
|
||||
// regular exceptions.
|
||||
_Unwind_SjLj_Resume(exception_object);
|
||||
_LIBUNWIND_ABORT("__Unwind_SjLj_Resume_or_Rethrow() called "
|
||||
"_Unwind_SjLj_Resume() which unexpectedly returned");
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to get LSDA for current frame.
|
||||
_LIBUNWIND_EXPORT uintptr_t
|
||||
_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) {
|
||||
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetLanguageSpecificData(context=%p) "
|
||||
"=> 0x%0lX\n", context, ufc->lsda);
|
||||
return ufc->lsda;
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to get register values.
|
||||
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetGR(struct _Unwind_Context *context,
|
||||
int index) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetGR(context=%p, reg=%d)\n",
|
||||
context, index);
|
||||
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
|
||||
return ufc->resumeParameters[index];
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to alter register values.
|
||||
_LIBUNWIND_EXPORT void _Unwind_SetGR(struct _Unwind_Context *context, int index,
|
||||
uintptr_t new_value) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_SetGR(context=%p, reg=%d, value=0x%0lX)\n"
|
||||
, context, index, new_value);
|
||||
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
|
||||
ufc->resumeParameters[index] = new_value;
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to get instruction pointer.
|
||||
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) {
|
||||
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => 0x%lX\n", context,
|
||||
ufc->resumeLocation + 1);
|
||||
return ufc->resumeLocation + 1;
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to get instruction pointer.
|
||||
/// ipBefore is a boolean that says if IP is already adjusted to be the call
|
||||
/// site address. Normally IP is the return address.
|
||||
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context,
|
||||
int *ipBefore) {
|
||||
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
|
||||
*ipBefore = 0;
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetIPInfo(context=%p, %p) => 0x%lX\n",
|
||||
context, ipBefore, ufc->resumeLocation + 1);
|
||||
return ufc->resumeLocation + 1;
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to alter instruction pointer.
|
||||
_LIBUNWIND_EXPORT void _Unwind_SetIP(struct _Unwind_Context *context,
|
||||
uintptr_t new_value) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_SetIP(context=%p, value=0x%0lX)\n",
|
||||
context, new_value);
|
||||
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
|
||||
ufc->resumeLocation = new_value - 1;
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to find the start of the
|
||||
/// function.
|
||||
_LIBUNWIND_EXPORT uintptr_t
|
||||
_Unwind_GetRegionStart(struct _Unwind_Context *context) {
|
||||
// Not supported or needed for sjlj based unwinding
|
||||
(void)context;
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p)\n", context);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 if a foreign exception
|
||||
/// is caught.
|
||||
_LIBUNWIND_EXPORT void
|
||||
_Unwind_DeleteException(struct _Unwind_Exception *exception_object) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_DeleteException(ex_obj=%p)\n",
|
||||
exception_object);
|
||||
if (exception_object->exception_cleanup != NULL)
|
||||
(*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT,
|
||||
exception_object);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to get base address for data
|
||||
/// relative encodings.
|
||||
_LIBUNWIND_EXPORT uintptr_t
|
||||
_Unwind_GetDataRelBase(struct _Unwind_Context *context) {
|
||||
// Not supported or needed for sjlj based unwinding
|
||||
(void)context;
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetDataRelBase(context=%p)\n", context);
|
||||
_LIBUNWIND_ABORT("_Unwind_GetDataRelBase() not implemented");
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to get base address for text
|
||||
/// relative encodings.
|
||||
_LIBUNWIND_EXPORT uintptr_t
|
||||
_Unwind_GetTextRelBase(struct _Unwind_Context *context) {
|
||||
// Not supported or needed for sjlj based unwinding
|
||||
(void)context;
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetTextRelBase(context=%p)\n", context);
|
||||
_LIBUNWIND_ABORT("_Unwind_GetTextRelBase() not implemented");
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler to get "Call Frame Area" for current frame.
|
||||
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetCFA(struct _Unwind_Context *context) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetCFA(context=%p)\n", context);
|
||||
if (context != NULL) {
|
||||
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
|
||||
// Setjmp/longjmp based exceptions don't have a true CFA.
|
||||
// Instead, the SP in the jmpbuf is the closest approximation.
|
||||
return (uintptr_t) ufc->jbuf[2];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // _LIBUNWIND_BUILD_SJLJ_APIS
|
1338
src/UnwindCursor.hpp
Normal file
1338
src/UnwindCursor.hpp
Normal file
File diff suppressed because it is too large
Load Diff
316
src/UnwindLevel1-gcc-ext.c
Normal file
316
src/UnwindLevel1-gcc-ext.c
Normal file
@ -0,0 +1,316 @@
|
||||
//===--------------------- UnwindLevel1-gcc-ext.c -------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//
|
||||
// Implements gcc extensions to the C++ ABI Exception Handling Level 1.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "libunwind_ext.h"
|
||||
#include "libunwind.h"
|
||||
#include "Unwind-EHABI.h"
|
||||
#include "unwind.h"
|
||||
|
||||
#if _LIBUNWIND_BUILD_ZERO_COST_APIS
|
||||
|
||||
/// Called by __cxa_rethrow().
|
||||
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||
_Unwind_Resume_or_Rethrow(_Unwind_Exception *exception_object) {
|
||||
#if _LIBUNWIND_ARM_EHABI
|
||||
_LIBUNWIND_TRACE_API("_Unwind_Resume_or_Rethrow(ex_obj=%p), private_1=%ld\n",
|
||||
(void *)exception_object,
|
||||
(long)exception_object->unwinder_cache.reserved1);
|
||||
#else
|
||||
_LIBUNWIND_TRACE_API("_Unwind_Resume_or_Rethrow(ex_obj=%p), private_1=%ld\n",
|
||||
(void *)exception_object,
|
||||
(long)exception_object->private_1);
|
||||
#endif
|
||||
|
||||
#if _LIBUNWIND_ARM_EHABI
|
||||
// _Unwind_RaiseException on EHABI will always set the reserved1 field to 0,
|
||||
// which is in the same position as private_1 below.
|
||||
return _Unwind_RaiseException(exception_object);
|
||||
#else
|
||||
// If this is non-forced and a stopping place was found, then this is a
|
||||
// re-throw.
|
||||
// Call _Unwind_RaiseException() as if this was a new exception
|
||||
if (exception_object->private_1 == 0) {
|
||||
return _Unwind_RaiseException(exception_object);
|
||||
// Will return if there is no catch clause, so that __cxa_rethrow can call
|
||||
// std::terminate().
|
||||
}
|
||||
|
||||
// Call through to _Unwind_Resume() which distiguishes between forced and
|
||||
// regular exceptions.
|
||||
_Unwind_Resume(exception_object);
|
||||
_LIBUNWIND_ABORT("_Unwind_Resume_or_Rethrow() called _Unwind_RaiseException()"
|
||||
" which unexpectedly returned");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to get base address for data
|
||||
/// relative encodings.
|
||||
_LIBUNWIND_EXPORT uintptr_t
|
||||
_Unwind_GetDataRelBase(struct _Unwind_Context *context) {
|
||||
(void)context;
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetDataRelBase(context=%p)\n", (void *)context);
|
||||
_LIBUNWIND_ABORT("_Unwind_GetDataRelBase() not implemented");
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to get base address for text
|
||||
/// relative encodings.
|
||||
_LIBUNWIND_EXPORT uintptr_t
|
||||
_Unwind_GetTextRelBase(struct _Unwind_Context *context) {
|
||||
(void)context;
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetTextRelBase(context=%p)\n", (void *)context);
|
||||
_LIBUNWIND_ABORT("_Unwind_GetTextRelBase() not implemented");
|
||||
}
|
||||
|
||||
|
||||
/// Scans unwind information to find the function that contains the
|
||||
/// specified code address "pc".
|
||||
_LIBUNWIND_EXPORT void *_Unwind_FindEnclosingFunction(void *pc) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_FindEnclosingFunction(pc=%p)\n", pc);
|
||||
// This is slow, but works.
|
||||
// We create an unwind cursor then alter the IP to be pc
|
||||
unw_cursor_t cursor;
|
||||
unw_context_t uc;
|
||||
unw_proc_info_t info;
|
||||
unw_getcontext(&uc);
|
||||
unw_init_local(&cursor, &uc);
|
||||
unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)(long) pc);
|
||||
if (unw_get_proc_info(&cursor, &info) == UNW_ESUCCESS)
|
||||
return (void *)(long) info.start_ip;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// Walk every frame and call trace function at each one. If trace function
|
||||
/// returns anything other than _URC_NO_REASON, then walk is terminated.
|
||||
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||
_Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) {
|
||||
unw_cursor_t cursor;
|
||||
unw_context_t uc;
|
||||
unw_getcontext(&uc);
|
||||
unw_init_local(&cursor, &uc);
|
||||
|
||||
_LIBUNWIND_TRACE_API("_Unwind_Backtrace(callback=%p)\n",
|
||||
(void *)(uintptr_t)callback);
|
||||
|
||||
#if _LIBUNWIND_ARM_EHABI
|
||||
// Create a mock exception object for force unwinding.
|
||||
_Unwind_Exception ex;
|
||||
memset(&ex, '\0', sizeof(ex));
|
||||
ex.exception_class = 0x434C4E47554E5700; // CLNGUNW\0
|
||||
#endif
|
||||
|
||||
// walk each frame
|
||||
while (true) {
|
||||
_Unwind_Reason_Code result;
|
||||
|
||||
#if !_LIBUNWIND_ARM_EHABI
|
||||
// ask libuwind to get next frame (skip over first frame which is
|
||||
// _Unwind_Backtrace())
|
||||
if (unw_step(&cursor) <= 0) {
|
||||
_LIBUNWIND_TRACE_UNWINDING(" _backtrace: ended because cursor reached "
|
||||
"bottom of stack, returning %d\n",
|
||||
_URC_END_OF_STACK);
|
||||
return _URC_END_OF_STACK;
|
||||
}
|
||||
#else
|
||||
// Get the information for this frame.
|
||||
unw_proc_info_t frameInfo;
|
||||
if (unw_get_proc_info(&cursor, &frameInfo) != UNW_ESUCCESS) {
|
||||
return _URC_END_OF_STACK;
|
||||
}
|
||||
|
||||
// Update the pr_cache in the mock exception object.
|
||||
const uint32_t* unwindInfo = (uint32_t *) frameInfo.unwind_info;
|
||||
ex.pr_cache.fnstart = frameInfo.start_ip;
|
||||
ex.pr_cache.ehtp = (_Unwind_EHT_Header *) unwindInfo;
|
||||
ex.pr_cache.additional= frameInfo.flags;
|
||||
|
||||
struct _Unwind_Context *context = (struct _Unwind_Context *)&cursor;
|
||||
// Get and call the personality function to unwind the frame.
|
||||
__personality_routine handler = (__personality_routine) frameInfo.handler;
|
||||
if (handler == NULL) {
|
||||
return _URC_END_OF_STACK;
|
||||
}
|
||||
if (handler(_US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND, &ex, context) !=
|
||||
_URC_CONTINUE_UNWIND) {
|
||||
return _URC_END_OF_STACK;
|
||||
}
|
||||
#endif // _LIBUNWIND_ARM_EHABI
|
||||
|
||||
// debugging
|
||||
if (_LIBUNWIND_TRACING_UNWINDING) {
|
||||
char functionName[512];
|
||||
unw_proc_info_t frame;
|
||||
unw_word_t offset;
|
||||
unw_get_proc_name(&cursor, functionName, 512, &offset);
|
||||
unw_get_proc_info(&cursor, &frame);
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
" _backtrace: start_ip=0x%llX, func=%s, lsda=0x%llX, context=%p\n",
|
||||
(long long)frame.start_ip, functionName, (long long)frame.lsda,
|
||||
(void *)&cursor);
|
||||
}
|
||||
|
||||
// call trace function with this frame
|
||||
result = (*callback)((struct _Unwind_Context *)(&cursor), ref);
|
||||
if (result != _URC_NO_REASON) {
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
" _backtrace: ended because callback returned %d\n", result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Find dwarf unwind info for an address 'pc' in some function.
|
||||
_LIBUNWIND_EXPORT const void *_Unwind_Find_FDE(const void *pc,
|
||||
struct dwarf_eh_bases *bases) {
|
||||
// This is slow, but works.
|
||||
// We create an unwind cursor then alter the IP to be pc
|
||||
unw_cursor_t cursor;
|
||||
unw_context_t uc;
|
||||
unw_proc_info_t info;
|
||||
unw_getcontext(&uc);
|
||||
unw_init_local(&cursor, &uc);
|
||||
unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)(long) pc);
|
||||
unw_get_proc_info(&cursor, &info);
|
||||
bases->tbase = (uintptr_t)info.extra;
|
||||
bases->dbase = 0; // dbase not used on Mac OS X
|
||||
bases->func = (uintptr_t)info.start_ip;
|
||||
_LIBUNWIND_TRACE_API("_Unwind_Find_FDE(pc=%p) => %p\n", pc,
|
||||
(void *)(long) info.unwind_info);
|
||||
return (void *)(long) info.unwind_info;
|
||||
}
|
||||
|
||||
/// Returns the CFA (call frame area, or stack pointer at start of function)
|
||||
/// for the current context.
|
||||
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetCFA(struct _Unwind_Context *context) {
|
||||
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||
unw_word_t result;
|
||||
unw_get_reg(cursor, UNW_REG_SP, &result);
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetCFA(context=%p) => 0x%" PRIx64 "\n",
|
||||
(void *)context, (uint64_t)result);
|
||||
return (uintptr_t)result;
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to get instruction pointer.
|
||||
/// ipBefore is a boolean that says if IP is already adjusted to be the call
|
||||
/// site address. Normally IP is the return address.
|
||||
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context,
|
||||
int *ipBefore) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetIPInfo(context=%p)\n", (void *)context);
|
||||
*ipBefore = 0;
|
||||
return _Unwind_GetIP(context);
|
||||
}
|
||||
|
||||
#if _LIBUNWIND_SUPPORT_DWARF_UNWIND
|
||||
|
||||
/// Called by programs with dynamic code generators that want
|
||||
/// to register a dynamically generated FDE.
|
||||
/// This function has existed on Mac OS X since 10.4, but
|
||||
/// was broken until 10.6.
|
||||
_LIBUNWIND_EXPORT void __register_frame(const void *fde) {
|
||||
_LIBUNWIND_TRACE_API("__register_frame(%p)\n", fde);
|
||||
_unw_add_dynamic_fde((unw_word_t)(uintptr_t) fde);
|
||||
}
|
||||
|
||||
|
||||
/// Called by programs with dynamic code generators that want
|
||||
/// to unregister a dynamically generated FDE.
|
||||
/// This function has existed on Mac OS X since 10.4, but
|
||||
/// was broken until 10.6.
|
||||
_LIBUNWIND_EXPORT void __deregister_frame(const void *fde) {
|
||||
_LIBUNWIND_TRACE_API("__deregister_frame(%p)\n", fde);
|
||||
_unw_remove_dynamic_fde((unw_word_t)(uintptr_t) fde);
|
||||
}
|
||||
|
||||
|
||||
// The following register/deregister functions are gcc extensions.
|
||||
// They have existed on Mac OS X, but have never worked because Mac OS X
|
||||
// before 10.6 used keymgr to track known FDEs, but these functions
|
||||
// never got updated to use keymgr.
|
||||
// For now, we implement these as do-nothing functions to keep any existing
|
||||
// applications working. We also add the not in 10.6 symbol so that nwe
|
||||
// application won't be able to use them.
|
||||
|
||||
#if _LIBUNWIND_SUPPORT_FRAME_APIS
|
||||
_LIBUNWIND_EXPORT void __register_frame_info_bases(const void *fde, void *ob,
|
||||
void *tb, void *db) {
|
||||
(void)fde;
|
||||
(void)ob;
|
||||
(void)tb;
|
||||
(void)db;
|
||||
_LIBUNWIND_TRACE_API("__register_frame_info_bases(%p,%p, %p, %p)\n",
|
||||
fde, ob, tb, db);
|
||||
// do nothing, this function never worked in Mac OS X
|
||||
}
|
||||
|
||||
_LIBUNWIND_EXPORT void __register_frame_info(const void *fde, void *ob) {
|
||||
(void)fde;
|
||||
(void)ob;
|
||||
_LIBUNWIND_TRACE_API("__register_frame_info(%p, %p)\n", fde, ob);
|
||||
// do nothing, this function never worked in Mac OS X
|
||||
}
|
||||
|
||||
_LIBUNWIND_EXPORT void __register_frame_info_table_bases(const void *fde,
|
||||
void *ob, void *tb,
|
||||
void *db) {
|
||||
(void)fde;
|
||||
(void)ob;
|
||||
(void)tb;
|
||||
(void)db;
|
||||
_LIBUNWIND_TRACE_API("__register_frame_info_table_bases"
|
||||
"(%p,%p, %p, %p)\n", fde, ob, tb, db);
|
||||
// do nothing, this function never worked in Mac OS X
|
||||
}
|
||||
|
||||
_LIBUNWIND_EXPORT void __register_frame_info_table(const void *fde, void *ob) {
|
||||
(void)fde;
|
||||
(void)ob;
|
||||
_LIBUNWIND_TRACE_API("__register_frame_info_table(%p, %p)\n", fde, ob);
|
||||
// do nothing, this function never worked in Mac OS X
|
||||
}
|
||||
|
||||
_LIBUNWIND_EXPORT void __register_frame_table(const void *fde) {
|
||||
(void)fde;
|
||||
_LIBUNWIND_TRACE_API("__register_frame_table(%p)\n", fde);
|
||||
// do nothing, this function never worked in Mac OS X
|
||||
}
|
||||
|
||||
_LIBUNWIND_EXPORT void *__deregister_frame_info(const void *fde) {
|
||||
(void)fde;
|
||||
_LIBUNWIND_TRACE_API("__deregister_frame_info(%p)\n", fde);
|
||||
// do nothing, this function never worked in Mac OS X
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_LIBUNWIND_EXPORT void *__deregister_frame_info_bases(const void *fde) {
|
||||
(void)fde;
|
||||
_LIBUNWIND_TRACE_API("__deregister_frame_info_bases(%p)\n", fde);
|
||||
// do nothing, this function never worked in Mac OS X
|
||||
return NULL;
|
||||
}
|
||||
#endif // _LIBUNWIND_SUPPORT_FRAME_APIS
|
||||
|
||||
#endif // _LIBUNWIND_SUPPORT_DWARF_UNWIND
|
||||
|
||||
#endif // _LIBUNWIND_BUILD_ZERO_COST_APIS
|
506
src/UnwindLevel1.c
Normal file
506
src/UnwindLevel1.c
Normal file
@ -0,0 +1,506 @@
|
||||
//===------------------------- UnwindLevel1.c -----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//
|
||||
// Implements C++ ABI Exception Handling Level 1 as documented at:
|
||||
// http://mentorembedded.github.io/cxx-abi/abi-eh.html
|
||||
// using libunwind
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// ARM EHABI does not specify _Unwind_{Get,Set}{GR,IP}(). Thus, we are
|
||||
// defining inline functions to delegate the function calls to
|
||||
// _Unwind_VRS_{Get,Set}(). However, some applications might declare the
|
||||
// function protetype directly (instead of including <unwind.h>), thus we need
|
||||
// to export these functions from libunwind.so as well.
|
||||
#define _LIBUNWIND_UNWIND_LEVEL1_EXTERNAL_LINKAGE 1
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libunwind.h"
|
||||
#include "unwind.h"
|
||||
#include "config.h"
|
||||
|
||||
#if !_LIBUNWIND_ARM_EHABI
|
||||
|
||||
static _Unwind_Reason_Code
|
||||
unwind_phase1(unw_context_t *uc, _Unwind_Exception *exception_object) {
|
||||
unw_cursor_t cursor1;
|
||||
unw_init_local(&cursor1, uc);
|
||||
|
||||
// Walk each frame looking for a place to stop.
|
||||
bool handlerNotFound = true;
|
||||
while (handlerNotFound) {
|
||||
// Ask libuwind to get next frame (skip over first which is
|
||||
// _Unwind_RaiseException).
|
||||
int stepResult = unw_step(&cursor1);
|
||||
if (stepResult == 0) {
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): unw_step() reached "
|
||||
"bottom => _URC_END_OF_STACK\n",
|
||||
(void *)exception_object);
|
||||
return _URC_END_OF_STACK;
|
||||
} else if (stepResult < 0) {
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): unw_step failed => "
|
||||
"_URC_FATAL_PHASE1_ERROR\n",
|
||||
(void *)exception_object);
|
||||
return _URC_FATAL_PHASE1_ERROR;
|
||||
}
|
||||
|
||||
// See if frame has code to run (has personality routine).
|
||||
unw_proc_info_t frameInfo;
|
||||
unw_word_t sp;
|
||||
if (unw_get_proc_info(&cursor1, &frameInfo) != UNW_ESUCCESS) {
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): unw_get_proc_info "
|
||||
"failed => _URC_FATAL_PHASE1_ERROR\n",
|
||||
(void *)exception_object);
|
||||
return _URC_FATAL_PHASE1_ERROR;
|
||||
}
|
||||
|
||||
// When tracing, print state information.
|
||||
if (_LIBUNWIND_TRACING_UNWINDING) {
|
||||
char functionBuf[512];
|
||||
const char *functionName = functionBuf;
|
||||
unw_word_t offset;
|
||||
if ((unw_get_proc_name(&cursor1, functionBuf, sizeof(functionBuf),
|
||||
&offset) != UNW_ESUCCESS) ||
|
||||
(frameInfo.start_ip + offset > frameInfo.end_ip))
|
||||
functionName = ".anonymous.";
|
||||
unw_word_t pc;
|
||||
unw_get_reg(&cursor1, UNW_REG_IP, &pc);
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase1(ex_ojb=%p): pc=0x%" PRIx64 ", start_ip=0x%" PRIx64
|
||||
", func=%s, lsda=0x%" PRIx64 ", personality=0x%" PRIx64 "\n",
|
||||
(void *)exception_object, pc, frameInfo.start_ip, functionName,
|
||||
frameInfo.lsda, frameInfo.handler);
|
||||
}
|
||||
|
||||
// If there is a personality routine, ask it if it will want to stop at
|
||||
// this frame.
|
||||
if (frameInfo.handler != 0) {
|
||||
__personality_routine p =
|
||||
(__personality_routine)(long)(frameInfo.handler);
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase1(ex_ojb=%p): calling personality function %p\n",
|
||||
(void *)exception_object, (void *)(uintptr_t)p);
|
||||
_Unwind_Reason_Code personalityResult =
|
||||
(*p)(1, _UA_SEARCH_PHASE, exception_object->exception_class,
|
||||
exception_object, (struct _Unwind_Context *)(&cursor1));
|
||||
switch (personalityResult) {
|
||||
case _URC_HANDLER_FOUND:
|
||||
// found a catch clause or locals that need destructing in this frame
|
||||
// stop search and remember stack pointer at the frame
|
||||
handlerNotFound = false;
|
||||
unw_get_reg(&cursor1, UNW_REG_SP, &sp);
|
||||
exception_object->private_2 = (uintptr_t)sp;
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase1(ex_ojb=%p): _URC_HANDLER_FOUND \n",
|
||||
(void *)exception_object);
|
||||
return _URC_NO_REASON;
|
||||
|
||||
case _URC_CONTINUE_UNWIND:
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase1(ex_ojb=%p): _URC_CONTINUE_UNWIND\n",
|
||||
(void *)exception_object);
|
||||
// continue unwinding
|
||||
break;
|
||||
|
||||
default:
|
||||
// something went wrong
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR\n",
|
||||
(void *)exception_object);
|
||||
return _URC_FATAL_PHASE1_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
|
||||
|
||||
static _Unwind_Reason_Code
|
||||
unwind_phase2(unw_context_t *uc, _Unwind_Exception *exception_object) {
|
||||
unw_cursor_t cursor2;
|
||||
unw_init_local(&cursor2, uc);
|
||||
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p)\n",
|
||||
(void *)exception_object);
|
||||
|
||||
// Walk each frame until we reach where search phase said to stop.
|
||||
while (true) {
|
||||
|
||||
// Ask libuwind to get next frame (skip over first which is
|
||||
// _Unwind_RaiseException).
|
||||
int stepResult = unw_step(&cursor2);
|
||||
if (stepResult == 0) {
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step() reached "
|
||||
"bottom => _URC_END_OF_STACK\n",
|
||||
(void *)exception_object);
|
||||
return _URC_END_OF_STACK;
|
||||
} else if (stepResult < 0) {
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step failed => "
|
||||
"_URC_FATAL_PHASE1_ERROR\n",
|
||||
(void *)exception_object);
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
|
||||
// Get info about this frame.
|
||||
unw_word_t sp;
|
||||
unw_proc_info_t frameInfo;
|
||||
unw_get_reg(&cursor2, UNW_REG_SP, &sp);
|
||||
if (unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS) {
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): unw_get_proc_info "
|
||||
"failed => _URC_FATAL_PHASE1_ERROR\n",
|
||||
(void *)exception_object);
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
|
||||
// When tracing, print state information.
|
||||
if (_LIBUNWIND_TRACING_UNWINDING) {
|
||||
char functionBuf[512];
|
||||
const char *functionName = functionBuf;
|
||||
unw_word_t offset;
|
||||
if ((unw_get_proc_name(&cursor2, functionBuf, sizeof(functionBuf),
|
||||
&offset) != UNW_ESUCCESS) ||
|
||||
(frameInfo.start_ip + offset > frameInfo.end_ip))
|
||||
functionName = ".anonymous.";
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): start_ip=0x%" PRIx64
|
||||
", func=%s, sp=0x%" PRIx64 ", lsda=0x%" PRIx64
|
||||
", personality=0x%" PRIx64 "\n",
|
||||
(void *)exception_object, frameInfo.start_ip,
|
||||
functionName, sp, frameInfo.lsda,
|
||||
frameInfo.handler);
|
||||
}
|
||||
|
||||
// If there is a personality routine, tell it we are unwinding.
|
||||
if (frameInfo.handler != 0) {
|
||||
__personality_routine p =
|
||||
(__personality_routine)(long)(frameInfo.handler);
|
||||
_Unwind_Action action = _UA_CLEANUP_PHASE;
|
||||
if (sp == exception_object->private_2) {
|
||||
// Tell personality this was the frame it marked in phase 1.
|
||||
action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME);
|
||||
}
|
||||
_Unwind_Reason_Code personalityResult =
|
||||
(*p)(1, action, exception_object->exception_class, exception_object,
|
||||
(struct _Unwind_Context *)(&cursor2));
|
||||
switch (personalityResult) {
|
||||
case _URC_CONTINUE_UNWIND:
|
||||
// Continue unwinding
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND\n",
|
||||
(void *)exception_object);
|
||||
if (sp == exception_object->private_2) {
|
||||
// Phase 1 said we would stop at this frame, but we did not...
|
||||
_LIBUNWIND_ABORT("during phase1 personality function said it would "
|
||||
"stop here, but now in phase2 it did not stop here");
|
||||
}
|
||||
break;
|
||||
case _URC_INSTALL_CONTEXT:
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase2(ex_ojb=%p): _URC_INSTALL_CONTEXT\n",
|
||||
(void *)exception_object);
|
||||
// Personality routine says to transfer control to landing pad.
|
||||
// We may get control back if landing pad calls _Unwind_Resume().
|
||||
if (_LIBUNWIND_TRACING_UNWINDING) {
|
||||
unw_word_t pc;
|
||||
unw_get_reg(&cursor2, UNW_REG_IP, &pc);
|
||||
unw_get_reg(&cursor2, UNW_REG_SP, &sp);
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): re-entering "
|
||||
"user code with ip=0x%" PRIx64
|
||||
", sp=0x%" PRIx64 "\n",
|
||||
(void *)exception_object, pc, sp);
|
||||
}
|
||||
unw_resume(&cursor2);
|
||||
// unw_resume() only returns if there was an error.
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
default:
|
||||
// Personality routine returned an unknown result code.
|
||||
_LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d",
|
||||
personalityResult);
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up phase did not resume at the frame that the search phase
|
||||
// said it would...
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
|
||||
static _Unwind_Reason_Code
|
||||
unwind_phase2_forced(unw_context_t *uc,
|
||||
_Unwind_Exception *exception_object,
|
||||
_Unwind_Stop_Fn stop, void *stop_parameter) {
|
||||
unw_cursor_t cursor2;
|
||||
unw_init_local(&cursor2, uc);
|
||||
|
||||
// Walk each frame until we reach where search phase said to stop
|
||||
while (unw_step(&cursor2) > 0) {
|
||||
|
||||
// Update info about this frame.
|
||||
unw_proc_info_t frameInfo;
|
||||
if (unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS) {
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): unw_step "
|
||||
"failed => _URC_END_OF_STACK\n",
|
||||
(void *)exception_object);
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
|
||||
// When tracing, print state information.
|
||||
if (_LIBUNWIND_TRACING_UNWINDING) {
|
||||
char functionBuf[512];
|
||||
const char *functionName = functionBuf;
|
||||
unw_word_t offset;
|
||||
if ((unw_get_proc_name(&cursor2, functionBuf, sizeof(functionBuf),
|
||||
&offset) != UNW_ESUCCESS) ||
|
||||
(frameInfo.start_ip + offset > frameInfo.end_ip))
|
||||
functionName = ".anonymous.";
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase2_forced(ex_ojb=%p): start_ip=0x%" PRIx64
|
||||
", func=%s, lsda=0x%" PRIx64 ", personality=0x%" PRIx64 "\n",
|
||||
(void *)exception_object, frameInfo.start_ip, functionName,
|
||||
frameInfo.lsda, frameInfo.handler);
|
||||
}
|
||||
|
||||
// Call stop function at each frame.
|
||||
_Unwind_Action action =
|
||||
(_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE);
|
||||
_Unwind_Reason_Code stopResult =
|
||||
(*stop)(1, action, exception_object->exception_class, exception_object,
|
||||
(struct _Unwind_Context *)(&cursor2), stop_parameter);
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase2_forced(ex_ojb=%p): stop function returned %d\n",
|
||||
(void *)exception_object, stopResult);
|
||||
if (stopResult != _URC_NO_REASON) {
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase2_forced(ex_ojb=%p): stopped by stop function\n",
|
||||
(void *)exception_object);
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
|
||||
// If there is a personality routine, tell it we are unwinding.
|
||||
if (frameInfo.handler != 0) {
|
||||
__personality_routine p =
|
||||
(__personality_routine)(long)(frameInfo.handler);
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase2_forced(ex_ojb=%p): calling personality function %p\n",
|
||||
(void *)exception_object, (void *)(uintptr_t)p);
|
||||
_Unwind_Reason_Code personalityResult =
|
||||
(*p)(1, action, exception_object->exception_class, exception_object,
|
||||
(struct _Unwind_Context *)(&cursor2));
|
||||
switch (personalityResult) {
|
||||
case _URC_CONTINUE_UNWIND:
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||
"personality returned "
|
||||
"_URC_CONTINUE_UNWIND\n",
|
||||
(void *)exception_object);
|
||||
// Destructors called, continue unwinding
|
||||
break;
|
||||
case _URC_INSTALL_CONTEXT:
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||
"personality returned "
|
||||
"_URC_INSTALL_CONTEXT\n",
|
||||
(void *)exception_object);
|
||||
// We may get control back if landing pad calls _Unwind_Resume().
|
||||
unw_resume(&cursor2);
|
||||
break;
|
||||
default:
|
||||
// Personality routine returned an unknown result code.
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||
"personality returned %d, "
|
||||
"_URC_FATAL_PHASE2_ERROR\n",
|
||||
(void *)exception_object, personalityResult);
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call stop function one last time and tell it we've reached the end
|
||||
// of the stack.
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop "
|
||||
"function with _UA_END_OF_STACK\n",
|
||||
(void *)exception_object);
|
||||
_Unwind_Action lastAction =
|
||||
(_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK);
|
||||
(*stop)(1, lastAction, exception_object->exception_class, exception_object,
|
||||
(struct _Unwind_Context *)(&cursor2), stop_parameter);
|
||||
|
||||
// Clean up phase did not resume at the frame that the search phase said it
|
||||
// would.
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/// Called by __cxa_throw. Only returns if there is a fatal error.
|
||||
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||
_Unwind_RaiseException(_Unwind_Exception *exception_object) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_RaiseException(ex_obj=%p)\n",
|
||||
(void *)exception_object);
|
||||
unw_context_t uc;
|
||||
unw_getcontext(&uc);
|
||||
|
||||
// Mark that this is a non-forced unwind, so _Unwind_Resume()
|
||||
// can do the right thing.
|
||||
exception_object->private_1 = 0;
|
||||
exception_object->private_2 = 0;
|
||||
|
||||
// phase 1: the search phase
|
||||
_Unwind_Reason_Code phase1 = unwind_phase1(&uc, exception_object);
|
||||
if (phase1 != _URC_NO_REASON)
|
||||
return phase1;
|
||||
|
||||
// phase 2: the clean up phase
|
||||
return unwind_phase2(&uc, exception_object);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// When _Unwind_RaiseException() is in phase2, it hands control
|
||||
/// to the personality function at each frame. The personality
|
||||
/// may force a jump to a landing pad in that function, the landing
|
||||
/// pad code may then call _Unwind_Resume() to continue with the
|
||||
/// unwinding. Note: the call to _Unwind_Resume() is from compiler
|
||||
/// geneated user code. All other _Unwind_* routines are called
|
||||
/// by the C++ runtime __cxa_* routines.
|
||||
///
|
||||
/// Note: re-throwing an exception (as opposed to continuing the unwind)
|
||||
/// is implemented by having the code call __cxa_rethrow() which
|
||||
/// in turn calls _Unwind_Resume_or_Rethrow().
|
||||
_LIBUNWIND_EXPORT void
|
||||
_Unwind_Resume(_Unwind_Exception *exception_object) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_Resume(ex_obj=%p)\n", (void *)exception_object);
|
||||
unw_context_t uc;
|
||||
unw_getcontext(&uc);
|
||||
|
||||
if (exception_object->private_1 != 0)
|
||||
unwind_phase2_forced(&uc, exception_object,
|
||||
(_Unwind_Stop_Fn) exception_object->private_1,
|
||||
(void *)exception_object->private_2);
|
||||
else
|
||||
unwind_phase2(&uc, exception_object);
|
||||
|
||||
// Clients assume _Unwind_Resume() does not return, so all we can do is abort.
|
||||
_LIBUNWIND_ABORT("_Unwind_Resume() can't return");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Not used by C++.
|
||||
/// Unwinds stack, calling "stop" function at each frame.
|
||||
/// Could be used to implement longjmp().
|
||||
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||
_Unwind_ForcedUnwind(_Unwind_Exception *exception_object,
|
||||
_Unwind_Stop_Fn stop, void *stop_parameter) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)\n",
|
||||
(void *)exception_object, (void *)(uintptr_t)stop);
|
||||
unw_context_t uc;
|
||||
unw_getcontext(&uc);
|
||||
|
||||
// Mark that this is a forced unwind, so _Unwind_Resume() can do
|
||||
// the right thing.
|
||||
exception_object->private_1 = (uintptr_t) stop;
|
||||
exception_object->private_2 = (uintptr_t) stop_parameter;
|
||||
|
||||
// do it
|
||||
return unwind_phase2_forced(&uc, exception_object, stop, stop_parameter);
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to get LSDA for current frame.
|
||||
_LIBUNWIND_EXPORT uintptr_t
|
||||
_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) {
|
||||
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||
unw_proc_info_t frameInfo;
|
||||
uintptr_t result = 0;
|
||||
if (unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS)
|
||||
result = (uintptr_t)frameInfo.lsda;
|
||||
_LIBUNWIND_TRACE_API(
|
||||
"_Unwind_GetLanguageSpecificData(context=%p) => 0x%" PRIxPTR "\n",
|
||||
(void *)context, result);
|
||||
if (result != 0) {
|
||||
if (*((uint8_t *)result) != 0xFF)
|
||||
_LIBUNWIND_DEBUG_LOG("lsda at 0x%" PRIxPTR " does not start with 0xFF\n",
|
||||
result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to find the start of the
|
||||
/// function.
|
||||
_LIBUNWIND_EXPORT uintptr_t
|
||||
_Unwind_GetRegionStart(struct _Unwind_Context *context) {
|
||||
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||
unw_proc_info_t frameInfo;
|
||||
uintptr_t result = 0;
|
||||
if (unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS)
|
||||
result = (uintptr_t)frameInfo.start_ip;
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p) => 0x%" PRIxPTR "\n",
|
||||
(void *)context, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 if a foreign exception
|
||||
// is caught.
|
||||
_LIBUNWIND_EXPORT void
|
||||
_Unwind_DeleteException(_Unwind_Exception *exception_object) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_DeleteException(ex_obj=%p)\n",
|
||||
(void *)exception_object);
|
||||
if (exception_object->exception_cleanup != NULL)
|
||||
(*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT,
|
||||
exception_object);
|
||||
}
|
||||
|
||||
/// Called by personality handler during phase 2 to get register values.
|
||||
_LIBUNWIND_EXPORT uintptr_t
|
||||
_Unwind_GetGR(struct _Unwind_Context *context, int index) {
|
||||
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||
unw_word_t result;
|
||||
unw_get_reg(cursor, index, &result);
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetGR(context=%p, reg=%d) => 0x%" PRIx64 "\n",
|
||||
(void *)context, index, (uint64_t)result);
|
||||
return (uintptr_t)result;
|
||||
}
|
||||
|
||||
/// Called by personality handler during phase 2 to alter register values.
|
||||
_LIBUNWIND_EXPORT void _Unwind_SetGR(struct _Unwind_Context *context, int index,
|
||||
uintptr_t value) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_SetGR(context=%p, reg=%d, value=0x%0" PRIx64
|
||||
")\n",
|
||||
(void *)context, index, (uint64_t)value);
|
||||
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||
unw_set_reg(cursor, index, value);
|
||||
}
|
||||
|
||||
/// Called by personality handler during phase 2 to get instruction pointer.
|
||||
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) {
|
||||
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||
unw_word_t result;
|
||||
unw_get_reg(cursor, UNW_REG_IP, &result);
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => 0x%" PRIx64 "\n",
|
||||
(void *)context, (uint64_t)result);
|
||||
return (uintptr_t)result;
|
||||
}
|
||||
|
||||
/// Called by personality handler during phase 2 to alter instruction pointer,
|
||||
/// such as setting where the landing pad is, so _Unwind_Resume() will
|
||||
/// start executing in the landing pad.
|
||||
_LIBUNWIND_EXPORT void _Unwind_SetIP(struct _Unwind_Context *context,
|
||||
uintptr_t value) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_SetIP(context=%p, value=0x%0" PRIx64 ")\n",
|
||||
(void *)context, (uint64_t)value);
|
||||
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||
unw_set_reg(cursor, UNW_REG_IP, value);
|
||||
}
|
||||
|
||||
#endif // !_LIBUNWIND_ARM_EHABI
|
481
src/UnwindRegistersRestore.S
Normal file
481
src/UnwindRegistersRestore.S
Normal file
@ -0,0 +1,481 @@
|
||||
//===-------------------- UnwindRegistersRestore.S ------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "assembly.h"
|
||||
|
||||
.text
|
||||
|
||||
#if defined(__i386__)
|
||||
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_x866jumptoEv)
|
||||
#
|
||||
# void libunwind::Registers_x86::jumpto()
|
||||
#
|
||||
# On entry:
|
||||
# + +
|
||||
# +-----------------------+
|
||||
# + thread_state pointer +
|
||||
# +-----------------------+
|
||||
# + return address +
|
||||
# +-----------------------+ <-- SP
|
||||
# + +
|
||||
movl 4(%esp), %eax
|
||||
# set up eax and ret on new stack location
|
||||
movl 28(%eax), %edx # edx holds new stack pointer
|
||||
subl $8,%edx
|
||||
movl %edx, 28(%eax)
|
||||
movl 0(%eax), %ebx
|
||||
movl %ebx, 0(%edx)
|
||||
movl 40(%eax), %ebx
|
||||
movl %ebx, 4(%edx)
|
||||
# we now have ret and eax pushed onto where new stack will be
|
||||
# restore all registers
|
||||
movl 4(%eax), %ebx
|
||||
movl 8(%eax), %ecx
|
||||
movl 12(%eax), %edx
|
||||
movl 16(%eax), %edi
|
||||
movl 20(%eax), %esi
|
||||
movl 24(%eax), %ebp
|
||||
movl 28(%eax), %esp
|
||||
# skip ss
|
||||
# skip eflags
|
||||
pop %eax # eax was already pushed on new stack
|
||||
ret # eip was already pushed on new stack
|
||||
# skip cs
|
||||
# skip ds
|
||||
# skip es
|
||||
# skip fs
|
||||
# skip gs
|
||||
|
||||
#elif defined(__x86_64__)
|
||||
|
||||
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind16Registers_x86_646jumptoEv)
|
||||
#
|
||||
# void libunwind::Registers_x86_64::jumpto()
|
||||
#
|
||||
# On entry, thread_state pointer is in rdi
|
||||
|
||||
movq 56(%rdi), %rax # rax holds new stack pointer
|
||||
subq $16, %rax
|
||||
movq %rax, 56(%rdi)
|
||||
movq 32(%rdi), %rbx # store new rdi on new stack
|
||||
movq %rbx, 0(%rax)
|
||||
movq 128(%rdi), %rbx # store new rip on new stack
|
||||
movq %rbx, 8(%rax)
|
||||
# restore all registers
|
||||
movq 0(%rdi), %rax
|
||||
movq 8(%rdi), %rbx
|
||||
movq 16(%rdi), %rcx
|
||||
movq 24(%rdi), %rdx
|
||||
# restore rdi later
|
||||
movq 40(%rdi), %rsi
|
||||
movq 48(%rdi), %rbp
|
||||
# restore rsp later
|
||||
movq 64(%rdi), %r8
|
||||
movq 72(%rdi), %r9
|
||||
movq 80(%rdi), %r10
|
||||
movq 88(%rdi), %r11
|
||||
movq 96(%rdi), %r12
|
||||
movq 104(%rdi), %r13
|
||||
movq 112(%rdi), %r14
|
||||
movq 120(%rdi), %r15
|
||||
# skip rflags
|
||||
# skip cs
|
||||
# skip fs
|
||||
# skip gs
|
||||
movq 56(%rdi), %rsp # cut back rsp to new location
|
||||
pop %rdi # rdi was saved here earlier
|
||||
ret # rip was saved here
|
||||
|
||||
|
||||
#elif defined(__ppc__)
|
||||
|
||||
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_ppc6jumptoEv)
|
||||
;
|
||||
; void libunwind::Registers_ppc::jumpto()
|
||||
;
|
||||
; On entry:
|
||||
; thread_state pointer is in r3
|
||||
;
|
||||
|
||||
; restore integral registerrs
|
||||
; skip r0 for now
|
||||
; skip r1 for now
|
||||
lwz r2, 16(r3)
|
||||
; skip r3 for now
|
||||
; skip r4 for now
|
||||
; skip r5 for now
|
||||
lwz r6, 32(r3)
|
||||
lwz r7, 36(r3)
|
||||
lwz r8, 40(r3)
|
||||
lwz r9, 44(r3)
|
||||
lwz r10, 48(r3)
|
||||
lwz r11, 52(r3)
|
||||
lwz r12, 56(r3)
|
||||
lwz r13, 60(r3)
|
||||
lwz r14, 64(r3)
|
||||
lwz r15, 68(r3)
|
||||
lwz r16, 72(r3)
|
||||
lwz r17, 76(r3)
|
||||
lwz r18, 80(r3)
|
||||
lwz r19, 84(r3)
|
||||
lwz r20, 88(r3)
|
||||
lwz r21, 92(r3)
|
||||
lwz r22, 96(r3)
|
||||
lwz r23,100(r3)
|
||||
lwz r24,104(r3)
|
||||
lwz r25,108(r3)
|
||||
lwz r26,112(r3)
|
||||
lwz r27,116(r3)
|
||||
lwz r28,120(r3)
|
||||
lwz r29,124(r3)
|
||||
lwz r30,128(r3)
|
||||
lwz r31,132(r3)
|
||||
|
||||
; restore float registers
|
||||
lfd f0, 160(r3)
|
||||
lfd f1, 168(r3)
|
||||
lfd f2, 176(r3)
|
||||
lfd f3, 184(r3)
|
||||
lfd f4, 192(r3)
|
||||
lfd f5, 200(r3)
|
||||
lfd f6, 208(r3)
|
||||
lfd f7, 216(r3)
|
||||
lfd f8, 224(r3)
|
||||
lfd f9, 232(r3)
|
||||
lfd f10,240(r3)
|
||||
lfd f11,248(r3)
|
||||
lfd f12,256(r3)
|
||||
lfd f13,264(r3)
|
||||
lfd f14,272(r3)
|
||||
lfd f15,280(r3)
|
||||
lfd f16,288(r3)
|
||||
lfd f17,296(r3)
|
||||
lfd f18,304(r3)
|
||||
lfd f19,312(r3)
|
||||
lfd f20,320(r3)
|
||||
lfd f21,328(r3)
|
||||
lfd f22,336(r3)
|
||||
lfd f23,344(r3)
|
||||
lfd f24,352(r3)
|
||||
lfd f25,360(r3)
|
||||
lfd f26,368(r3)
|
||||
lfd f27,376(r3)
|
||||
lfd f28,384(r3)
|
||||
lfd f29,392(r3)
|
||||
lfd f30,400(r3)
|
||||
lfd f31,408(r3)
|
||||
|
||||
; restore vector registers if any are in use
|
||||
lwz r5,156(r3) ; test VRsave
|
||||
cmpwi r5,0
|
||||
beq Lnovec
|
||||
|
||||
subi r4,r1,16
|
||||
rlwinm r4,r4,0,0,27 ; mask low 4-bits
|
||||
; r4 is now a 16-byte aligned pointer into the red zone
|
||||
; the _vectorRegisters may not be 16-byte aligned so copy via red zone temp buffer
|
||||
|
||||
|
||||
#define LOAD_VECTOR_UNALIGNEDl(_index) \
|
||||
andis. r0,r5,(1<<(15-_index)) @\
|
||||
beq Ldone ## _index @\
|
||||
lwz r0, 424+_index*16(r3) @\
|
||||
stw r0, 0(r4) @\
|
||||
lwz r0, 424+_index*16+4(r3) @\
|
||||
stw r0, 4(r4) @\
|
||||
lwz r0, 424+_index*16+8(r3) @\
|
||||
stw r0, 8(r4) @\
|
||||
lwz r0, 424+_index*16+12(r3)@\
|
||||
stw r0, 12(r4) @\
|
||||
lvx v ## _index,0,r4 @\
|
||||
Ldone ## _index:
|
||||
|
||||
#define LOAD_VECTOR_UNALIGNEDh(_index) \
|
||||
andi. r0,r5,(1<<(31-_index)) @\
|
||||
beq Ldone ## _index @\
|
||||
lwz r0, 424+_index*16(r3) @\
|
||||
stw r0, 0(r4) @\
|
||||
lwz r0, 424+_index*16+4(r3) @\
|
||||
stw r0, 4(r4) @\
|
||||
lwz r0, 424+_index*16+8(r3) @\
|
||||
stw r0, 8(r4) @\
|
||||
lwz r0, 424+_index*16+12(r3)@\
|
||||
stw r0, 12(r4) @\
|
||||
lvx v ## _index,0,r4 @\
|
||||
Ldone ## _index:
|
||||
|
||||
|
||||
LOAD_VECTOR_UNALIGNEDl(0)
|
||||
LOAD_VECTOR_UNALIGNEDl(1)
|
||||
LOAD_VECTOR_UNALIGNEDl(2)
|
||||
LOAD_VECTOR_UNALIGNEDl(3)
|
||||
LOAD_VECTOR_UNALIGNEDl(4)
|
||||
LOAD_VECTOR_UNALIGNEDl(5)
|
||||
LOAD_VECTOR_UNALIGNEDl(6)
|
||||
LOAD_VECTOR_UNALIGNEDl(7)
|
||||
LOAD_VECTOR_UNALIGNEDl(8)
|
||||
LOAD_VECTOR_UNALIGNEDl(9)
|
||||
LOAD_VECTOR_UNALIGNEDl(10)
|
||||
LOAD_VECTOR_UNALIGNEDl(11)
|
||||
LOAD_VECTOR_UNALIGNEDl(12)
|
||||
LOAD_VECTOR_UNALIGNEDl(13)
|
||||
LOAD_VECTOR_UNALIGNEDl(14)
|
||||
LOAD_VECTOR_UNALIGNEDl(15)
|
||||
LOAD_VECTOR_UNALIGNEDh(16)
|
||||
LOAD_VECTOR_UNALIGNEDh(17)
|
||||
LOAD_VECTOR_UNALIGNEDh(18)
|
||||
LOAD_VECTOR_UNALIGNEDh(19)
|
||||
LOAD_VECTOR_UNALIGNEDh(20)
|
||||
LOAD_VECTOR_UNALIGNEDh(21)
|
||||
LOAD_VECTOR_UNALIGNEDh(22)
|
||||
LOAD_VECTOR_UNALIGNEDh(23)
|
||||
LOAD_VECTOR_UNALIGNEDh(24)
|
||||
LOAD_VECTOR_UNALIGNEDh(25)
|
||||
LOAD_VECTOR_UNALIGNEDh(26)
|
||||
LOAD_VECTOR_UNALIGNEDh(27)
|
||||
LOAD_VECTOR_UNALIGNEDh(28)
|
||||
LOAD_VECTOR_UNALIGNEDh(29)
|
||||
LOAD_VECTOR_UNALIGNEDh(30)
|
||||
LOAD_VECTOR_UNALIGNEDh(31)
|
||||
|
||||
Lnovec:
|
||||
lwz r0, 136(r3) ; __cr
|
||||
mtocrf 255,r0
|
||||
lwz r0, 148(r3) ; __ctr
|
||||
mtctr r0
|
||||
lwz r0, 0(r3) ; __ssr0
|
||||
mtctr r0
|
||||
lwz r0, 8(r3) ; do r0 now
|
||||
lwz r5,28(r3) ; do r5 now
|
||||
lwz r4,24(r3) ; do r4 now
|
||||
lwz r1,12(r3) ; do sp now
|
||||
lwz r3,20(r3) ; do r3 last
|
||||
bctr
|
||||
|
||||
#elif defined(__arm64__) || defined(__aarch64__)
|
||||
|
||||
//
|
||||
// void libunwind::Registers_arm64::jumpto()
|
||||
//
|
||||
// On entry:
|
||||
// thread_state pointer is in x0
|
||||
//
|
||||
.p2align 2
|
||||
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind15Registers_arm646jumptoEv)
|
||||
// skip restore of x0,x1 for now
|
||||
ldp x2, x3, [x0, #0x010]
|
||||
ldp x4, x5, [x0, #0x020]
|
||||
ldp x6, x7, [x0, #0x030]
|
||||
ldp x8, x9, [x0, #0x040]
|
||||
ldp x10,x11, [x0, #0x050]
|
||||
ldp x12,x13, [x0, #0x060]
|
||||
ldp x14,x15, [x0, #0x070]
|
||||
ldp x16,x17, [x0, #0x080]
|
||||
ldp x18,x19, [x0, #0x090]
|
||||
ldp x20,x21, [x0, #0x0A0]
|
||||
ldp x22,x23, [x0, #0x0B0]
|
||||
ldp x24,x25, [x0, #0x0C0]
|
||||
ldp x26,x27, [x0, #0x0D0]
|
||||
ldp x28,fp, [x0, #0x0E0]
|
||||
ldr lr, [x0, #0x100] // restore pc into lr
|
||||
ldr x1, [x0, #0x0F8]
|
||||
mov sp,x1 // restore sp
|
||||
|
||||
ldp d0, d1, [x0, #0x110]
|
||||
ldp d2, d3, [x0, #0x120]
|
||||
ldp d4, d5, [x0, #0x130]
|
||||
ldp d6, d7, [x0, #0x140]
|
||||
ldp d8, d9, [x0, #0x150]
|
||||
ldp d10,d11, [x0, #0x160]
|
||||
ldp d12,d13, [x0, #0x170]
|
||||
ldp d14,d15, [x0, #0x180]
|
||||
ldp d16,d17, [x0, #0x190]
|
||||
ldp d18,d19, [x0, #0x1A0]
|
||||
ldp d20,d21, [x0, #0x1B0]
|
||||
ldp d22,d23, [x0, #0x1C0]
|
||||
ldp d24,d25, [x0, #0x1D0]
|
||||
ldp d26,d27, [x0, #0x1E0]
|
||||
ldp d28,d29, [x0, #0x1F0]
|
||||
ldr d30, [x0, #0x200]
|
||||
ldr d31, [x0, #0x208]
|
||||
|
||||
ldp x0, x1, [x0, #0x000] // restore x0,x1
|
||||
ret lr // jump to pc
|
||||
|
||||
#elif defined(__arm__) && !defined(__APPLE__)
|
||||
|
||||
#if !defined(__ARM_ARCH_ISA_ARM)
|
||||
.thumb
|
||||
#endif
|
||||
|
||||
@
|
||||
@ void libunwind::Registers_arm::restoreCoreAndJumpTo()
|
||||
@
|
||||
@ On entry:
|
||||
@ thread_state pointer is in r0
|
||||
@
|
||||
.p2align 2
|
||||
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm20restoreCoreAndJumpToEv)
|
||||
#if !defined(__ARM_ARCH_ISA_ARM)
|
||||
ldr r2, [r0, #52]
|
||||
ldr r3, [r0, #60]
|
||||
mov sp, r2
|
||||
mov lr, r3 @ restore pc into lr
|
||||
ldm r0, {r0-r7}
|
||||
#else
|
||||
@ Use lr as base so that r0 can be restored.
|
||||
mov lr, r0
|
||||
@ 32bit thumb-2 restrictions for ldm:
|
||||
@ . the sp (r13) cannot be in the list
|
||||
@ . the pc (r15) and lr (r14) cannot both be in the list in an LDM instruction
|
||||
ldm lr, {r0-r12}
|
||||
ldr sp, [lr, #52]
|
||||
ldr lr, [lr, #60] @ restore pc into lr
|
||||
#endif
|
||||
JMP(lr)
|
||||
|
||||
@
|
||||
@ static void libunwind::Registers_arm::restoreVFPWithFLDMD(unw_fpreg_t* values)
|
||||
@
|
||||
@ On entry:
|
||||
@ values pointer is in r0
|
||||
@
|
||||
.p2align 2
|
||||
.fpu vfpv3-d16
|
||||
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm19restoreVFPWithFLDMDEPy)
|
||||
@ VFP and iwMMX instructions are only available when compiling with the flags
|
||||
@ that enable them. We do not want to do that in the library (because we do not
|
||||
@ want the compiler to generate instructions that access those) but this is
|
||||
@ only accessed if the personality routine needs these registers. Use of
|
||||
@ these registers implies they are, actually, available on the target, so
|
||||
@ it's ok to execute.
|
||||
@ So, generate the instruction using the corresponding coprocessor mnemonic.
|
||||
vldmia r0, {d0-d15}
|
||||
JMP(lr)
|
||||
|
||||
@
|
||||
@ static void libunwind::Registers_arm::restoreVFPWithFLDMX(unw_fpreg_t* values)
|
||||
@
|
||||
@ On entry:
|
||||
@ values pointer is in r0
|
||||
@
|
||||
.p2align 2
|
||||
.fpu vfpv3-d16
|
||||
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm19restoreVFPWithFLDMXEPy)
|
||||
vldmia r0, {d0-d15} @ fldmiax is deprecated in ARMv7+ and now behaves like vldmia
|
||||
JMP(lr)
|
||||
|
||||
@
|
||||
@ static void libunwind::Registers_arm::restoreVFPv3(unw_fpreg_t* values)
|
||||
@
|
||||
@ On entry:
|
||||
@ values pointer is in r0
|
||||
@
|
||||
.p2align 2
|
||||
.fpu vfpv3
|
||||
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm12restoreVFPv3EPy)
|
||||
vldmia r0, {d16-d31}
|
||||
JMP(lr)
|
||||
|
||||
@
|
||||
@ static void libunwind::Registers_arm::restoreiWMMX(unw_fpreg_t* values)
|
||||
@
|
||||
@ On entry:
|
||||
@ values pointer is in r0
|
||||
@
|
||||
.p2align 2
|
||||
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm12restoreiWMMXEPy)
|
||||
#if (!defined(__ARM_ARCH_6M__) && !defined(__ARM_ARCH_6SM__)) || defined(__ARM_WMMX)
|
||||
ldcl p1, cr0, [r0], #8 @ wldrd wR0, [r0], #8
|
||||
ldcl p1, cr1, [r0], #8 @ wldrd wR1, [r0], #8
|
||||
ldcl p1, cr2, [r0], #8 @ wldrd wR2, [r0], #8
|
||||
ldcl p1, cr3, [r0], #8 @ wldrd wR3, [r0], #8
|
||||
ldcl p1, cr4, [r0], #8 @ wldrd wR4, [r0], #8
|
||||
ldcl p1, cr5, [r0], #8 @ wldrd wR5, [r0], #8
|
||||
ldcl p1, cr6, [r0], #8 @ wldrd wR6, [r0], #8
|
||||
ldcl p1, cr7, [r0], #8 @ wldrd wR7, [r0], #8
|
||||
ldcl p1, cr8, [r0], #8 @ wldrd wR8, [r0], #8
|
||||
ldcl p1, cr9, [r0], #8 @ wldrd wR9, [r0], #8
|
||||
ldcl p1, cr10, [r0], #8 @ wldrd wR10, [r0], #8
|
||||
ldcl p1, cr11, [r0], #8 @ wldrd wR11, [r0], #8
|
||||
ldcl p1, cr12, [r0], #8 @ wldrd wR12, [r0], #8
|
||||
ldcl p1, cr13, [r0], #8 @ wldrd wR13, [r0], #8
|
||||
ldcl p1, cr14, [r0], #8 @ wldrd wR14, [r0], #8
|
||||
ldcl p1, cr15, [r0], #8 @ wldrd wR15, [r0], #8
|
||||
#endif
|
||||
JMP(lr)
|
||||
|
||||
@
|
||||
@ static void libunwind::Registers_arm::restoreiWMMXControl(unw_uint32_t* values)
|
||||
@
|
||||
@ On entry:
|
||||
@ values pointer is in r0
|
||||
@
|
||||
.p2align 2
|
||||
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm19restoreiWMMXControlEPj)
|
||||
#if (!defined(__ARM_ARCH_6M__) && !defined(__ARM_ARCH_6SM__)) || defined(__ARM_WMMX)
|
||||
ldc2 p1, cr8, [r0], #4 @ wldrw wCGR0, [r0], #4
|
||||
ldc2 p1, cr9, [r0], #4 @ wldrw wCGR1, [r0], #4
|
||||
ldc2 p1, cr10, [r0], #4 @ wldrw wCGR2, [r0], #4
|
||||
ldc2 p1, cr11, [r0], #4 @ wldrw wCGR3, [r0], #4
|
||||
#endif
|
||||
JMP(lr)
|
||||
|
||||
#elif defined(__or1k__)
|
||||
|
||||
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind14Registers_or1k6jumptoEv)
|
||||
#
|
||||
# void libunwind::Registers_or1k::jumpto()
|
||||
#
|
||||
# On entry:
|
||||
# thread_state pointer is in r3
|
||||
#
|
||||
|
||||
# restore integral registerrs
|
||||
l.lwz r0, 0(r3)
|
||||
l.lwz r1, 4(r3)
|
||||
l.lwz r2, 8(r3)
|
||||
# skip r3 for now
|
||||
l.lwz r4, 16(r3)
|
||||
l.lwz r5, 20(r3)
|
||||
l.lwz r6, 24(r3)
|
||||
l.lwz r7, 28(r3)
|
||||
l.lwz r8, 32(r3)
|
||||
l.lwz r9, 36(r3)
|
||||
l.lwz r10, 40(r3)
|
||||
l.lwz r11, 44(r3)
|
||||
l.lwz r12, 48(r3)
|
||||
l.lwz r13, 52(r3)
|
||||
l.lwz r14, 56(r3)
|
||||
l.lwz r15, 60(r3)
|
||||
l.lwz r16, 64(r3)
|
||||
l.lwz r17, 68(r3)
|
||||
l.lwz r18, 72(r3)
|
||||
l.lwz r19, 76(r3)
|
||||
l.lwz r20, 80(r3)
|
||||
l.lwz r21, 84(r3)
|
||||
l.lwz r22, 88(r3)
|
||||
l.lwz r23, 92(r3)
|
||||
l.lwz r24, 96(r3)
|
||||
l.lwz r25,100(r3)
|
||||
l.lwz r26,104(r3)
|
||||
l.lwz r27,108(r3)
|
||||
l.lwz r28,112(r3)
|
||||
l.lwz r29,116(r3)
|
||||
l.lwz r30,120(r3)
|
||||
l.lwz r31,124(r3)
|
||||
|
||||
# at last, restore r3
|
||||
l.lwz r3, 12(r3)
|
||||
|
||||
# jump to pc
|
||||
l.jr r9
|
||||
l.nop
|
||||
|
||||
#endif
|
457
src/UnwindRegistersSave.S
Normal file
457
src/UnwindRegistersSave.S
Normal file
@ -0,0 +1,457 @@
|
||||
//===------------------------ UnwindRegistersSave.S -----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "assembly.h"
|
||||
|
||||
.text
|
||||
|
||||
#if defined(__i386__)
|
||||
|
||||
#
|
||||
# extern int unw_getcontext(unw_context_t* thread_state)
|
||||
#
|
||||
# On entry:
|
||||
# + +
|
||||
# +-----------------------+
|
||||
# + thread_state pointer +
|
||||
# +-----------------------+
|
||||
# + return address +
|
||||
# +-----------------------+ <-- SP
|
||||
# + +
|
||||
#
|
||||
DEFINE_LIBUNWIND_FUNCTION(unw_getcontext)
|
||||
push %eax
|
||||
movl 8(%esp), %eax
|
||||
movl %ebx, 4(%eax)
|
||||
movl %ecx, 8(%eax)
|
||||
movl %edx, 12(%eax)
|
||||
movl %edi, 16(%eax)
|
||||
movl %esi, 20(%eax)
|
||||
movl %ebp, 24(%eax)
|
||||
movl %esp, %edx
|
||||
addl $8, %edx
|
||||
movl %edx, 28(%eax) # store what sp was at call site as esp
|
||||
# skip ss
|
||||
# skip eflags
|
||||
movl 4(%esp), %edx
|
||||
movl %edx, 40(%eax) # store return address as eip
|
||||
# skip cs
|
||||
# skip ds
|
||||
# skip es
|
||||
# skip fs
|
||||
# skip gs
|
||||
movl (%esp), %edx
|
||||
movl %edx, (%eax) # store original eax
|
||||
popl %eax
|
||||
xorl %eax, %eax # return UNW_ESUCCESS
|
||||
ret
|
||||
|
||||
#elif defined(__x86_64__)
|
||||
|
||||
#
|
||||
# extern int unw_getcontext(unw_context_t* thread_state)
|
||||
#
|
||||
# On entry:
|
||||
# thread_state pointer is in rdi
|
||||
#
|
||||
DEFINE_LIBUNWIND_FUNCTION(unw_getcontext)
|
||||
movq %rax, (%rdi)
|
||||
movq %rbx, 8(%rdi)
|
||||
movq %rcx, 16(%rdi)
|
||||
movq %rdx, 24(%rdi)
|
||||
movq %rdi, 32(%rdi)
|
||||
movq %rsi, 40(%rdi)
|
||||
movq %rbp, 48(%rdi)
|
||||
movq %rsp, 56(%rdi)
|
||||
addq $8, 56(%rdi)
|
||||
movq %r8, 64(%rdi)
|
||||
movq %r9, 72(%rdi)
|
||||
movq %r10, 80(%rdi)
|
||||
movq %r11, 88(%rdi)
|
||||
movq %r12, 96(%rdi)
|
||||
movq %r13,104(%rdi)
|
||||
movq %r14,112(%rdi)
|
||||
movq %r15,120(%rdi)
|
||||
movq (%rsp),%rsi
|
||||
movq %rsi,128(%rdi) # store return address as rip
|
||||
# skip rflags
|
||||
# skip cs
|
||||
# skip fs
|
||||
# skip gs
|
||||
xorl %eax, %eax # return UNW_ESUCCESS
|
||||
ret
|
||||
|
||||
#elif defined(__ppc__)
|
||||
|
||||
;
|
||||
; extern int unw_getcontext(unw_context_t* thread_state)
|
||||
;
|
||||
; On entry:
|
||||
; thread_state pointer is in r3
|
||||
;
|
||||
DEFINE_LIBUNWIND_FUNCTION(unw_getcontext)
|
||||
stw r0, 8(r3)
|
||||
mflr r0
|
||||
stw r0, 0(r3) ; store lr as ssr0
|
||||
stw r1, 12(r3)
|
||||
stw r2, 16(r3)
|
||||
stw r3, 20(r3)
|
||||
stw r4, 24(r3)
|
||||
stw r5, 28(r3)
|
||||
stw r6, 32(r3)
|
||||
stw r7, 36(r3)
|
||||
stw r8, 40(r3)
|
||||
stw r9, 44(r3)
|
||||
stw r10, 48(r3)
|
||||
stw r11, 52(r3)
|
||||
stw r12, 56(r3)
|
||||
stw r13, 60(r3)
|
||||
stw r14, 64(r3)
|
||||
stw r15, 68(r3)
|
||||
stw r16, 72(r3)
|
||||
stw r17, 76(r3)
|
||||
stw r18, 80(r3)
|
||||
stw r19, 84(r3)
|
||||
stw r20, 88(r3)
|
||||
stw r21, 92(r3)
|
||||
stw r22, 96(r3)
|
||||
stw r23,100(r3)
|
||||
stw r24,104(r3)
|
||||
stw r25,108(r3)
|
||||
stw r26,112(r3)
|
||||
stw r27,116(r3)
|
||||
stw r28,120(r3)
|
||||
stw r29,124(r3)
|
||||
stw r30,128(r3)
|
||||
stw r31,132(r3)
|
||||
|
||||
; save VRSave register
|
||||
mfspr r0,256
|
||||
stw r0,156(r3)
|
||||
; save CR registers
|
||||
mfcr r0
|
||||
stw r0,136(r3)
|
||||
; save CTR register
|
||||
mfctr r0
|
||||
stw r0,148(r3)
|
||||
|
||||
; save float registers
|
||||
stfd f0, 160(r3)
|
||||
stfd f1, 168(r3)
|
||||
stfd f2, 176(r3)
|
||||
stfd f3, 184(r3)
|
||||
stfd f4, 192(r3)
|
||||
stfd f5, 200(r3)
|
||||
stfd f6, 208(r3)
|
||||
stfd f7, 216(r3)
|
||||
stfd f8, 224(r3)
|
||||
stfd f9, 232(r3)
|
||||
stfd f10,240(r3)
|
||||
stfd f11,248(r3)
|
||||
stfd f12,256(r3)
|
||||
stfd f13,264(r3)
|
||||
stfd f14,272(r3)
|
||||
stfd f15,280(r3)
|
||||
stfd f16,288(r3)
|
||||
stfd f17,296(r3)
|
||||
stfd f18,304(r3)
|
||||
stfd f19,312(r3)
|
||||
stfd f20,320(r3)
|
||||
stfd f21,328(r3)
|
||||
stfd f22,336(r3)
|
||||
stfd f23,344(r3)
|
||||
stfd f24,352(r3)
|
||||
stfd f25,360(r3)
|
||||
stfd f26,368(r3)
|
||||
stfd f27,376(r3)
|
||||
stfd f28,384(r3)
|
||||
stfd f29,392(r3)
|
||||
stfd f30,400(r3)
|
||||
stfd f31,408(r3)
|
||||
|
||||
|
||||
; save vector registers
|
||||
|
||||
subi r4,r1,16
|
||||
rlwinm r4,r4,0,0,27 ; mask low 4-bits
|
||||
; r4 is now a 16-byte aligned pointer into the red zone
|
||||
|
||||
#define SAVE_VECTOR_UNALIGNED(_vec, _offset) \
|
||||
stvx _vec,0,r4 @\
|
||||
lwz r5, 0(r4) @\
|
||||
stw r5, _offset(r3) @\
|
||||
lwz r5, 4(r4) @\
|
||||
stw r5, _offset+4(r3) @\
|
||||
lwz r5, 8(r4) @\
|
||||
stw r5, _offset+8(r3) @\
|
||||
lwz r5, 12(r4) @\
|
||||
stw r5, _offset+12(r3)
|
||||
|
||||
SAVE_VECTOR_UNALIGNED( v0, 424+0x000)
|
||||
SAVE_VECTOR_UNALIGNED( v1, 424+0x010)
|
||||
SAVE_VECTOR_UNALIGNED( v2, 424+0x020)
|
||||
SAVE_VECTOR_UNALIGNED( v3, 424+0x030)
|
||||
SAVE_VECTOR_UNALIGNED( v4, 424+0x040)
|
||||
SAVE_VECTOR_UNALIGNED( v5, 424+0x050)
|
||||
SAVE_VECTOR_UNALIGNED( v6, 424+0x060)
|
||||
SAVE_VECTOR_UNALIGNED( v7, 424+0x070)
|
||||
SAVE_VECTOR_UNALIGNED( v8, 424+0x080)
|
||||
SAVE_VECTOR_UNALIGNED( v9, 424+0x090)
|
||||
SAVE_VECTOR_UNALIGNED(v10, 424+0x0A0)
|
||||
SAVE_VECTOR_UNALIGNED(v11, 424+0x0B0)
|
||||
SAVE_VECTOR_UNALIGNED(v12, 424+0x0C0)
|
||||
SAVE_VECTOR_UNALIGNED(v13, 424+0x0D0)
|
||||
SAVE_VECTOR_UNALIGNED(v14, 424+0x0E0)
|
||||
SAVE_VECTOR_UNALIGNED(v15, 424+0x0F0)
|
||||
SAVE_VECTOR_UNALIGNED(v16, 424+0x100)
|
||||
SAVE_VECTOR_UNALIGNED(v17, 424+0x110)
|
||||
SAVE_VECTOR_UNALIGNED(v18, 424+0x120)
|
||||
SAVE_VECTOR_UNALIGNED(v19, 424+0x130)
|
||||
SAVE_VECTOR_UNALIGNED(v20, 424+0x140)
|
||||
SAVE_VECTOR_UNALIGNED(v21, 424+0x150)
|
||||
SAVE_VECTOR_UNALIGNED(v22, 424+0x160)
|
||||
SAVE_VECTOR_UNALIGNED(v23, 424+0x170)
|
||||
SAVE_VECTOR_UNALIGNED(v24, 424+0x180)
|
||||
SAVE_VECTOR_UNALIGNED(v25, 424+0x190)
|
||||
SAVE_VECTOR_UNALIGNED(v26, 424+0x1A0)
|
||||
SAVE_VECTOR_UNALIGNED(v27, 424+0x1B0)
|
||||
SAVE_VECTOR_UNALIGNED(v28, 424+0x1C0)
|
||||
SAVE_VECTOR_UNALIGNED(v29, 424+0x1D0)
|
||||
SAVE_VECTOR_UNALIGNED(v30, 424+0x1E0)
|
||||
SAVE_VECTOR_UNALIGNED(v31, 424+0x1F0)
|
||||
|
||||
li r3, 0 ; return UNW_ESUCCESS
|
||||
blr
|
||||
|
||||
|
||||
#elif defined(__arm64__) || defined(__aarch64__)
|
||||
|
||||
//
|
||||
// extern int unw_getcontext(unw_context_t* thread_state)
|
||||
//
|
||||
// On entry:
|
||||
// thread_state pointer is in x0
|
||||
//
|
||||
.p2align 2
|
||||
DEFINE_LIBUNWIND_FUNCTION(unw_getcontext)
|
||||
stp x0, x1, [x0, #0x000]
|
||||
stp x2, x3, [x0, #0x010]
|
||||
stp x4, x5, [x0, #0x020]
|
||||
stp x6, x7, [x0, #0x030]
|
||||
stp x8, x9, [x0, #0x040]
|
||||
stp x10,x11, [x0, #0x050]
|
||||
stp x12,x13, [x0, #0x060]
|
||||
stp x14,x15, [x0, #0x070]
|
||||
stp x16,x17, [x0, #0x080]
|
||||
stp x18,x19, [x0, #0x090]
|
||||
stp x20,x21, [x0, #0x0A0]
|
||||
stp x22,x23, [x0, #0x0B0]
|
||||
stp x24,x25, [x0, #0x0C0]
|
||||
stp x26,x27, [x0, #0x0D0]
|
||||
stp x28,fp, [x0, #0x0E0]
|
||||
str lr, [x0, #0x0F0]
|
||||
mov x1,sp
|
||||
str x1, [x0, #0x0F8]
|
||||
str lr, [x0, #0x100] // store return address as pc
|
||||
// skip cpsr
|
||||
stp d0, d1, [x0, #0x110]
|
||||
stp d2, d3, [x0, #0x120]
|
||||
stp d4, d5, [x0, #0x130]
|
||||
stp d6, d7, [x0, #0x140]
|
||||
stp d8, d9, [x0, #0x150]
|
||||
stp d10,d11, [x0, #0x160]
|
||||
stp d12,d13, [x0, #0x170]
|
||||
stp d14,d15, [x0, #0x180]
|
||||
stp d16,d17, [x0, #0x190]
|
||||
stp d18,d19, [x0, #0x1A0]
|
||||
stp d20,d21, [x0, #0x1B0]
|
||||
stp d22,d23, [x0, #0x1C0]
|
||||
stp d24,d25, [x0, #0x1D0]
|
||||
stp d26,d27, [x0, #0x1E0]
|
||||
stp d28,d29, [x0, #0x1F0]
|
||||
str d30, [x0, #0x200]
|
||||
str d31, [x0, #0x208]
|
||||
mov x0, #0 // return UNW_ESUCCESS
|
||||
ret
|
||||
|
||||
#elif defined(__arm__) && !defined(__APPLE__)
|
||||
|
||||
#if !defined(__ARM_ARCH_ISA_ARM)
|
||||
.thumb
|
||||
#endif
|
||||
|
||||
@
|
||||
@ extern int unw_getcontext(unw_context_t* thread_state)
|
||||
@
|
||||
@ On entry:
|
||||
@ thread_state pointer is in r0
|
||||
@
|
||||
@ Per EHABI #4.7 this only saves the core integer registers.
|
||||
@ EHABI #7.4.5 notes that in general all VRS registers should be restored
|
||||
@ however this is very hard to do for VFP registers because it is unknown
|
||||
@ to the library how many registers are implemented by the architecture.
|
||||
@ Instead, VFP registers are demand saved by logic external to unw_getcontext.
|
||||
@
|
||||
.p2align 2
|
||||
DEFINE_LIBUNWIND_FUNCTION(unw_getcontext)
|
||||
#if !defined(__ARM_ARCH_ISA_ARM)
|
||||
stm r0, {r0-r7}
|
||||
mov r2, sp
|
||||
mov r3, lr
|
||||
str r2, [r0, #52]
|
||||
str r3, [r0, #56]
|
||||
str r3, [r0, #60] @ store return address as pc
|
||||
#else
|
||||
@ 32bit thumb-2 restrictions for stm:
|
||||
@ . the sp (r13) cannot be in the list
|
||||
@ . the pc (r15) cannot be in the list in an STM instruction
|
||||
stm r0, {r0-r12}
|
||||
str sp, [r0, #52]
|
||||
str lr, [r0, #56]
|
||||
str lr, [r0, #60] @ store return address as pc
|
||||
#endif
|
||||
#if __ARM_ARCH_ISA_THUMB == 1
|
||||
@ T1 does not have a non-cpsr-clobbering register-zeroing instruction.
|
||||
@ It is safe to use here though because we are about to return, and cpsr is
|
||||
@ not expected to be preserved.
|
||||
movs r0, #0 @ return UNW_ESUCCESS
|
||||
#else
|
||||
mov r0, #0 @ return UNW_ESUCCESS
|
||||
#endif
|
||||
JMP(lr)
|
||||
|
||||
@
|
||||
@ static void libunwind::Registers_arm::saveVFPWithFSTMD(unw_fpreg_t* values)
|
||||
@
|
||||
@ On entry:
|
||||
@ values pointer is in r0
|
||||
@
|
||||
.p2align 2
|
||||
.fpu vfpv3-d16
|
||||
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm16saveVFPWithFSTMDEPy)
|
||||
vstmia r0, {d0-d15}
|
||||
JMP(lr)
|
||||
|
||||
@
|
||||
@ static void libunwind::Registers_arm::saveVFPWithFSTMX(unw_fpreg_t* values)
|
||||
@
|
||||
@ On entry:
|
||||
@ values pointer is in r0
|
||||
@
|
||||
.p2align 2
|
||||
.fpu vfpv3-d16
|
||||
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm16saveVFPWithFSTMXEPy)
|
||||
vstmia r0, {d0-d15} @ fstmiax is deprecated in ARMv7+ and now behaves like vstmia
|
||||
JMP(lr)
|
||||
|
||||
@
|
||||
@ static void libunwind::Registers_arm::saveVFPv3(unw_fpreg_t* values)
|
||||
@
|
||||
@ On entry:
|
||||
@ values pointer is in r0
|
||||
@
|
||||
.p2align 2
|
||||
.fpu vfpv3
|
||||
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm9saveVFPv3EPy)
|
||||
@ VFP and iwMMX instructions are only available when compiling with the flags
|
||||
@ that enable them. We do not want to do that in the library (because we do not
|
||||
@ want the compiler to generate instructions that access those) but this is
|
||||
@ only accessed if the personality routine needs these registers. Use of
|
||||
@ these registers implies they are, actually, available on the target, so
|
||||
@ it's ok to execute.
|
||||
@ So, generate the instructions using the corresponding coprocessor mnemonic.
|
||||
vstmia r0, {d16-d31}
|
||||
JMP(lr)
|
||||
|
||||
@
|
||||
@ static void libunwind::Registers_arm::saveiWMMX(unw_fpreg_t* values)
|
||||
@
|
||||
@ On entry:
|
||||
@ values pointer is in r0
|
||||
@
|
||||
.p2align 2
|
||||
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm9saveiWMMXEPy)
|
||||
#if (!defined(__ARM_ARCH_6M__) && !defined(__ARM_ARCH_6SM__)) || defined(__ARM_WMMX)
|
||||
stcl p1, cr0, [r0], #8 @ wstrd wR0, [r0], #8
|
||||
stcl p1, cr1, [r0], #8 @ wstrd wR1, [r0], #8
|
||||
stcl p1, cr2, [r0], #8 @ wstrd wR2, [r0], #8
|
||||
stcl p1, cr3, [r0], #8 @ wstrd wR3, [r0], #8
|
||||
stcl p1, cr4, [r0], #8 @ wstrd wR4, [r0], #8
|
||||
stcl p1, cr5, [r0], #8 @ wstrd wR5, [r0], #8
|
||||
stcl p1, cr6, [r0], #8 @ wstrd wR6, [r0], #8
|
||||
stcl p1, cr7, [r0], #8 @ wstrd wR7, [r0], #8
|
||||
stcl p1, cr8, [r0], #8 @ wstrd wR8, [r0], #8
|
||||
stcl p1, cr9, [r0], #8 @ wstrd wR9, [r0], #8
|
||||
stcl p1, cr10, [r0], #8 @ wstrd wR10, [r0], #8
|
||||
stcl p1, cr11, [r0], #8 @ wstrd wR11, [r0], #8
|
||||
stcl p1, cr12, [r0], #8 @ wstrd wR12, [r0], #8
|
||||
stcl p1, cr13, [r0], #8 @ wstrd wR13, [r0], #8
|
||||
stcl p1, cr14, [r0], #8 @ wstrd wR14, [r0], #8
|
||||
stcl p1, cr15, [r0], #8 @ wstrd wR15, [r0], #8
|
||||
#endif
|
||||
JMP(lr)
|
||||
|
||||
@
|
||||
@ static void libunwind::Registers_arm::saveiWMMXControl(unw_uint32_t* values)
|
||||
@
|
||||
@ On entry:
|
||||
@ values pointer is in r0
|
||||
@
|
||||
.p2align 2
|
||||
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm16saveiWMMXControlEPj)
|
||||
#if (!defined(__ARM_ARCH_6M__) && !defined(__ARM_ARCH_6SM__)) || defined(__ARM_WMMX)
|
||||
stc2 p1, cr8, [r0], #4 @ wstrw wCGR0, [r0], #4
|
||||
stc2 p1, cr9, [r0], #4 @ wstrw wCGR1, [r0], #4
|
||||
stc2 p1, cr10, [r0], #4 @ wstrw wCGR2, [r0], #4
|
||||
stc2 p1, cr11, [r0], #4 @ wstrw wCGR3, [r0], #4
|
||||
#endif
|
||||
JMP(lr)
|
||||
|
||||
#elif defined(__or1k__)
|
||||
|
||||
#
|
||||
# extern int unw_getcontext(unw_context_t* thread_state)
|
||||
#
|
||||
# On entry:
|
||||
# thread_state pointer is in r3
|
||||
#
|
||||
DEFINE_LIBUNWIND_FUNCTION(unw_getcontext)
|
||||
l.sw 0(r3), r0
|
||||
l.sw 4(r3), r1
|
||||
l.sw 8(r3), r2
|
||||
l.sw 12(r3), r3
|
||||
l.sw 16(r3), r4
|
||||
l.sw 20(r3), r5
|
||||
l.sw 24(r3), r6
|
||||
l.sw 28(r3), r7
|
||||
l.sw 32(r3), r8
|
||||
l.sw 36(r3), r9
|
||||
l.sw 40(r3), r10
|
||||
l.sw 44(r3), r11
|
||||
l.sw 48(r3), r12
|
||||
l.sw 52(r3), r13
|
||||
l.sw 56(r3), r14
|
||||
l.sw 60(r3), r15
|
||||
l.sw 64(r3), r16
|
||||
l.sw 68(r3), r17
|
||||
l.sw 72(r3), r18
|
||||
l.sw 76(r3), r19
|
||||
l.sw 80(r3), r20
|
||||
l.sw 84(r3), r21
|
||||
l.sw 88(r3), r22
|
||||
l.sw 92(r3), r23
|
||||
l.sw 96(r3), r24
|
||||
l.sw 100(r3), r25
|
||||
l.sw 104(r3), r26
|
||||
l.sw 108(r3), r27
|
||||
l.sw 112(r3), r28
|
||||
l.sw 116(r3), r29
|
||||
l.sw 120(r3), r30
|
||||
l.sw 124(r3), r31
|
||||
#endif
|
205
src/Unwind_AppleExtras.cpp
Normal file
205
src/Unwind_AppleExtras.cpp
Normal file
@ -0,0 +1,205 @@
|
||||
//===--------------------- Unwind_AppleExtras.cpp -------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "config.h"
|
||||
#include "DwarfParser.hpp"
|
||||
#include "unwind_ext.h"
|
||||
|
||||
|
||||
// private keymgr stuff
|
||||
#define KEYMGR_GCC3_DW2_OBJ_LIST 302
|
||||
extern "C" {
|
||||
extern void _keymgr_set_and_unlock_processwide_ptr(int key, void *ptr);
|
||||
extern void *_keymgr_get_and_lock_processwide_ptr(int key);
|
||||
}
|
||||
|
||||
// undocumented libgcc "struct object"
|
||||
struct libgcc_object {
|
||||
void *start;
|
||||
void *unused1;
|
||||
void *unused2;
|
||||
void *fde;
|
||||
unsigned long encoding;
|
||||
void *fde_end;
|
||||
libgcc_object *next;
|
||||
};
|
||||
|
||||
// undocumented libgcc "struct km_object_info" referenced by
|
||||
// KEYMGR_GCC3_DW2_OBJ_LIST
|
||||
struct libgcc_object_info {
|
||||
libgcc_object *seen_objects;
|
||||
libgcc_object *unseen_objects;
|
||||
unsigned spare[2];
|
||||
};
|
||||
|
||||
|
||||
// static linker symbols to prevent wrong two level namespace for _Unwind symbols
|
||||
#if defined(__arm__)
|
||||
#define NOT_HERE_BEFORE_5_0(sym) \
|
||||
extern const char sym##_tmp30 __asm("$ld$hide$os3.0$_" #sym ); \
|
||||
__attribute__((visibility("default"))) const char sym##_tmp30 = 0; \
|
||||
extern const char sym##_tmp31 __asm("$ld$hide$os3.1$_" #sym ); \
|
||||
__attribute__((visibility("default"))) const char sym##_tmp31 = 0; \
|
||||
extern const char sym##_tmp32 __asm("$ld$hide$os3.2$_" #sym );\
|
||||
__attribute__((visibility("default"))) const char sym##_tmp32 = 0; \
|
||||
extern const char sym##_tmp40 __asm("$ld$hide$os4.0$_" #sym ); \
|
||||
__attribute__((visibility("default"))) const char sym##_tmp40 = 0; \
|
||||
extern const char sym##_tmp41 __asm("$ld$hide$os4.1$_" #sym ); \
|
||||
__attribute__((visibility("default"))) const char sym##_tmp41 = 0; \
|
||||
extern const char sym##_tmp42 __asm("$ld$hide$os4.2$_" #sym ); \
|
||||
__attribute__((visibility("default"))) const char sym##_tmp42 = 0; \
|
||||
extern const char sym##_tmp43 __asm("$ld$hide$os4.3$_" #sym ); \
|
||||
__attribute__((visibility("default"))) const char sym##_tmp43 = 0;
|
||||
#elif defined(__arm64__)
|
||||
#define NOT_HERE_BEFORE_10_6(sym)
|
||||
#define NEVER_HERE(sym)
|
||||
#else
|
||||
#define NOT_HERE_BEFORE_10_6(sym) \
|
||||
extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); \
|
||||
__attribute__((visibility("default"))) const char sym##_tmp4 = 0; \
|
||||
extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); \
|
||||
__attribute__((visibility("default"))) const char sym##_tmp5 = 0;
|
||||
#define NEVER_HERE(sym) \
|
||||
extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); \
|
||||
__attribute__((visibility("default"))) const char sym##_tmp4 = 0; \
|
||||
extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); \
|
||||
__attribute__((visibility("default"))) const char sym##_tmp5 = 0; \
|
||||
extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); \
|
||||
__attribute__((visibility("default"))) const char sym##_tmp6 = 0;
|
||||
#endif
|
||||
|
||||
|
||||
#if _LIBUNWIND_BUILD_ZERO_COST_APIS
|
||||
|
||||
//
|
||||
// symbols in libSystem.dylib in 10.6 and later, but are in libgcc_s.dylib in
|
||||
// earlier versions
|
||||
//
|
||||
NOT_HERE_BEFORE_10_6(_Unwind_DeleteException)
|
||||
NOT_HERE_BEFORE_10_6(_Unwind_Find_FDE)
|
||||
NOT_HERE_BEFORE_10_6(_Unwind_ForcedUnwind)
|
||||
NOT_HERE_BEFORE_10_6(_Unwind_GetGR)
|
||||
NOT_HERE_BEFORE_10_6(_Unwind_GetIP)
|
||||
NOT_HERE_BEFORE_10_6(_Unwind_GetLanguageSpecificData)
|
||||
NOT_HERE_BEFORE_10_6(_Unwind_GetRegionStart)
|
||||
NOT_HERE_BEFORE_10_6(_Unwind_RaiseException)
|
||||
NOT_HERE_BEFORE_10_6(_Unwind_Resume)
|
||||
NOT_HERE_BEFORE_10_6(_Unwind_SetGR)
|
||||
NOT_HERE_BEFORE_10_6(_Unwind_SetIP)
|
||||
NOT_HERE_BEFORE_10_6(_Unwind_Backtrace)
|
||||
NOT_HERE_BEFORE_10_6(_Unwind_FindEnclosingFunction)
|
||||
NOT_HERE_BEFORE_10_6(_Unwind_GetCFA)
|
||||
NOT_HERE_BEFORE_10_6(_Unwind_GetDataRelBase)
|
||||
NOT_HERE_BEFORE_10_6(_Unwind_GetTextRelBase)
|
||||
NOT_HERE_BEFORE_10_6(_Unwind_Resume_or_Rethrow)
|
||||
NOT_HERE_BEFORE_10_6(_Unwind_GetIPInfo)
|
||||
NOT_HERE_BEFORE_10_6(__register_frame)
|
||||
NOT_HERE_BEFORE_10_6(__deregister_frame)
|
||||
|
||||
//
|
||||
// symbols in libSystem.dylib for compatibility, but we don't want any new code
|
||||
// using them
|
||||
//
|
||||
NEVER_HERE(__register_frame_info_bases)
|
||||
NEVER_HERE(__register_frame_info)
|
||||
NEVER_HERE(__register_frame_info_table_bases)
|
||||
NEVER_HERE(__register_frame_info_table)
|
||||
NEVER_HERE(__register_frame_table)
|
||||
NEVER_HERE(__deregister_frame_info)
|
||||
NEVER_HERE(__deregister_frame_info_bases)
|
||||
|
||||
#endif // _LIBUNWIND_BUILD_ZERO_COST_APIS
|
||||
|
||||
|
||||
|
||||
|
||||
#if _LIBUNWIND_BUILD_SJLJ_APIS
|
||||
//
|
||||
// symbols in libSystem.dylib in iOS 5.0 and later, but are in libgcc_s.dylib in
|
||||
// earlier versions
|
||||
//
|
||||
NOT_HERE_BEFORE_5_0(_Unwind_GetLanguageSpecificData)
|
||||
NOT_HERE_BEFORE_5_0(_Unwind_GetRegionStart)
|
||||
NOT_HERE_BEFORE_5_0(_Unwind_GetIP)
|
||||
NOT_HERE_BEFORE_5_0(_Unwind_SetGR)
|
||||
NOT_HERE_BEFORE_5_0(_Unwind_SetIP)
|
||||
NOT_HERE_BEFORE_5_0(_Unwind_DeleteException)
|
||||
NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Register)
|
||||
NOT_HERE_BEFORE_5_0(_Unwind_GetGR)
|
||||
NOT_HERE_BEFORE_5_0(_Unwind_GetIPInfo)
|
||||
NOT_HERE_BEFORE_5_0(_Unwind_GetCFA)
|
||||
NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Resume)
|
||||
NOT_HERE_BEFORE_5_0(_Unwind_SjLj_RaiseException)
|
||||
NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Resume_or_Rethrow)
|
||||
NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Unregister)
|
||||
|
||||
#endif // _LIBUNWIND_BUILD_SJLJ_APIS
|
||||
|
||||
|
||||
namespace libunwind {
|
||||
|
||||
_LIBUNWIND_HIDDEN
|
||||
bool checkKeyMgrRegisteredFDEs(uintptr_t pc, void *&fde) {
|
||||
#if __MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
// lastly check for old style keymgr registration of dynamically generated
|
||||
// FDEs acquire exclusive access to libgcc_object_info
|
||||
libgcc_object_info *head = (libgcc_object_info *)
|
||||
_keymgr_get_and_lock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST);
|
||||
if (head != NULL) {
|
||||
// look at each FDE in keymgr
|
||||
for (libgcc_object *ob = head->unseen_objects; ob != NULL; ob = ob->next) {
|
||||
CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo;
|
||||
CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo;
|
||||
const char *msg = CFI_Parser<LocalAddressSpace>::decodeFDE(
|
||||
LocalAddressSpace::sThisAddressSpace,
|
||||
(uintptr_t)ob->fde, &fdeInfo, &cieInfo);
|
||||
if (msg == NULL) {
|
||||
// Check if this FDE is for a function that includes the pc
|
||||
if ((fdeInfo.pcStart <= pc) && (pc < fdeInfo.pcEnd)) {
|
||||
fde = (void*)fdeInfo.pcStart;
|
||||
_keymgr_set_and_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST,
|
||||
head);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// release libgcc_object_info
|
||||
_keymgr_set_and_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST, head);
|
||||
#else
|
||||
(void)pc;
|
||||
(void)fde;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#if !defined(FOR_DYLD) && _LIBUNWIND_BUILD_SJLJ_APIS
|
||||
|
||||
#include <System/pthread_machdep.h>
|
||||
|
||||
// Accessors to get get/set linked list of frames for sjlj based execeptions.
|
||||
_LIBUNWIND_HIDDEN
|
||||
struct _Unwind_FunctionContext *__Unwind_SjLj_GetTopOfFunctionStack() {
|
||||
return (struct _Unwind_FunctionContext *)
|
||||
_pthread_getspecific_direct(__PTK_LIBC_DYLD_Unwind_SjLj_Key);
|
||||
}
|
||||
|
||||
_LIBUNWIND_HIDDEN
|
||||
void __Unwind_SjLj_SetTopOfFunctionStack(struct _Unwind_FunctionContext *fc) {
|
||||
_pthread_setspecific_direct(__PTK_LIBC_DYLD_Unwind_SjLj_Key, fc);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
80
src/assembly.h
Normal file
80
src/assembly.h
Normal file
@ -0,0 +1,80 @@
|
||||
/* ===-- assembly.h - libUnwind assembler support macros -------------------===
|
||||
*
|
||||
* The LLVM Compiler Infrastructure
|
||||
*
|
||||
* This file is dual licensed under the MIT and the University of Illinois Open
|
||||
* Source Licenses. See LICENSE.TXT for details.
|
||||
*
|
||||
* ===----------------------------------------------------------------------===
|
||||
*
|
||||
* This file defines macros for use in libUnwind assembler source.
|
||||
* This file is not part of the interface of this library.
|
||||
*
|
||||
* ===----------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#ifndef UNWIND_ASSEMBLY_H
|
||||
#define UNWIND_ASSEMBLY_H
|
||||
|
||||
#if defined(__POWERPC__) || defined(__powerpc__) || defined(__ppc__)
|
||||
#define SEPARATOR @
|
||||
#elif defined(__arm64__)
|
||||
#define SEPARATOR %%
|
||||
#else
|
||||
#define SEPARATOR ;
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#define HIDDEN_DIRECTIVE .private_extern
|
||||
#else
|
||||
#define HIDDEN_DIRECTIVE .hidden
|
||||
#endif
|
||||
|
||||
#define GLUE2(a, b) a ## b
|
||||
#define GLUE(a, b) GLUE2(a, b)
|
||||
#define SYMBOL_NAME(name) GLUE(__USER_LABEL_PREFIX__, name)
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#define SYMBOL_IS_FUNC(name)
|
||||
#elif defined(__ELF__)
|
||||
#if defined(__arm__)
|
||||
#define SYMBOL_IS_FUNC(name) .type name,%function
|
||||
#else
|
||||
#define SYMBOL_IS_FUNC(name) .type name,@function
|
||||
#endif
|
||||
#else
|
||||
#define SYMBOL_IS_FUNC(name) \
|
||||
.def name SEPARATOR \
|
||||
.scl 2 SEPARATOR \
|
||||
.type 32 SEPARATOR \
|
||||
.endef
|
||||
#endif
|
||||
|
||||
#define DEFINE_LIBUNWIND_FUNCTION(name) \
|
||||
.globl SYMBOL_NAME(name) SEPARATOR \
|
||||
SYMBOL_IS_FUNC(SYMBOL_NAME(name)) SEPARATOR \
|
||||
SYMBOL_NAME(name):
|
||||
|
||||
#define DEFINE_LIBUNWIND_PRIVATE_FUNCTION(name) \
|
||||
.globl SYMBOL_NAME(name) SEPARATOR \
|
||||
HIDDEN_DIRECTIVE SYMBOL_NAME(name) SEPARATOR \
|
||||
SYMBOL_IS_FUNC(SYMBOL_NAME(name)) SEPARATOR \
|
||||
SYMBOL_NAME(name):
|
||||
|
||||
#if defined(__arm__)
|
||||
#if !defined(__ARM_ARCH)
|
||||
#define __ARM_ARCH 4
|
||||
#endif
|
||||
|
||||
#if defined(__ARM_ARCH_4T__) || __ARM_ARCH >= 5
|
||||
#define ARM_HAS_BX
|
||||
#endif
|
||||
|
||||
#ifdef ARM_HAS_BX
|
||||
#define JMP(r) bx r
|
||||
#else
|
||||
#define JMP(r) mov pc, r
|
||||
#endif
|
||||
#endif /* __arm__ */
|
||||
|
||||
#endif /* UNWIND_ASSEMBLY_H */
|
127
src/config.h
Normal file
127
src/config.h
Normal file
@ -0,0 +1,127 @@
|
||||
//===----------------------------- config.h -------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//
|
||||
// Defines macros used within libuwind project.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
||||
#ifndef LIBUNWIND_CONFIG_H
|
||||
#define LIBUNWIND_CONFIG_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Define static_assert() unless already defined by compiler.
|
||||
#ifndef __has_feature
|
||||
#define __has_feature(__x) 0
|
||||
#endif
|
||||
#if !(__has_feature(cxx_static_assert)) && !defined(static_assert)
|
||||
#define static_assert(__b, __m) \
|
||||
extern int compile_time_assert_failed[ ( __b ) ? 1 : -1 ] \
|
||||
__attribute__( ( unused ) );
|
||||
#endif
|
||||
|
||||
// Platform specific configuration defines.
|
||||
#ifdef __APPLE__
|
||||
#include <Availability.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void __assert_rtn(const char *, const char *, int, const char *)
|
||||
__attribute__((noreturn));
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#define _LIBUNWIND_BUILD_ZERO_COST_APIS (defined(__i386__) || \
|
||||
defined(__x86_64__) || \
|
||||
defined(__arm64__))
|
||||
#define _LIBUNWIND_BUILD_SJLJ_APIS defined(__arm__)
|
||||
#define _LIBUNWIND_SUPPORT_FRAME_APIS (defined(__i386__) || \
|
||||
defined(__x86_64__))
|
||||
#define _LIBUNWIND_EXPORT __attribute__((visibility("default")))
|
||||
#define _LIBUNWIND_HIDDEN __attribute__((visibility("hidden")))
|
||||
#define _LIBUNWIND_LOG(msg, ...) fprintf(stderr, "libuwind: " msg, __VA_ARGS__)
|
||||
#define _LIBUNWIND_ABORT(msg) __assert_rtn(__func__, __FILE__, __LINE__, msg)
|
||||
|
||||
#if defined(FOR_DYLD)
|
||||
#define _LIBUNWIND_SUPPORT_COMPACT_UNWIND 1
|
||||
#define _LIBUNWIND_SUPPORT_DWARF_UNWIND 0
|
||||
#define _LIBUNWIND_SUPPORT_DWARF_INDEX 0
|
||||
#else
|
||||
#define _LIBUNWIND_SUPPORT_COMPACT_UNWIND 1
|
||||
#define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1
|
||||
#define _LIBUNWIND_SUPPORT_DWARF_INDEX 0
|
||||
#endif
|
||||
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
|
||||
static inline void assert_rtn(const char* func, const char* file, int line, const char* msg) __attribute__ ((noreturn));
|
||||
static inline void assert_rtn(const char* func, const char* file, int line, const char* msg) {
|
||||
fprintf(stderr, "libunwind: %s %s:%d - %s\n", func, file, line, msg);
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
|
||||
#define _LIBUNWIND_BUILD_ZERO_COST_APIS (defined(__i386__) || \
|
||||
defined(__x86_64__) || \
|
||||
defined(__arm__) || \
|
||||
defined(__aarch64__))
|
||||
#define _LIBUNWIND_BUILD_SJLJ_APIS 0
|
||||
#define _LIBUNWIND_SUPPORT_FRAME_APIS (defined(__i386__) || \
|
||||
defined(__x86_64__))
|
||||
#define _LIBUNWIND_EXPORT __attribute__((visibility("default")))
|
||||
#define _LIBUNWIND_HIDDEN __attribute__((visibility("hidden")))
|
||||
#define _LIBUNWIND_LOG(msg, ...) fprintf(stderr, "libuwind: " msg, __VA_ARGS__)
|
||||
#define _LIBUNWIND_ABORT(msg) assert_rtn(__func__, __FILE__, __LINE__, msg)
|
||||
|
||||
#define _LIBUNWIND_SUPPORT_COMPACT_UNWIND 0
|
||||
#define _LIBUNWIND_SUPPORT_DWARF_UNWIND !defined(__arm__) || \
|
||||
defined(__ARM_DWARF_EH__)
|
||||
#define _LIBUNWIND_SUPPORT_DWARF_INDEX _LIBUNWIND_SUPPORT_DWARF_UNWIND
|
||||
#endif
|
||||
|
||||
|
||||
// Macros that define away in non-Debug builds
|
||||
#ifdef NDEBUG
|
||||
#define _LIBUNWIND_DEBUG_LOG(msg, ...)
|
||||
#define _LIBUNWIND_TRACE_API(msg, ...)
|
||||
#define _LIBUNWIND_TRACING_UNWINDING 0
|
||||
#define _LIBUNWIND_TRACE_UNWINDING(msg, ...)
|
||||
#define _LIBUNWIND_LOG_NON_ZERO(x) x
|
||||
#else
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern bool logAPIs();
|
||||
extern bool logUnwinding();
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#define _LIBUNWIND_DEBUG_LOG(msg, ...) _LIBUNWIND_LOG(msg, __VA_ARGS__)
|
||||
#define _LIBUNWIND_LOG_NON_ZERO(x) \
|
||||
do { \
|
||||
int _err = x; \
|
||||
if ( _err != 0 ) \
|
||||
_LIBUNWIND_LOG("" #x "=%d in %s", _err, __FUNCTION__); \
|
||||
} while (0)
|
||||
#define _LIBUNWIND_TRACE_API(msg, ...) \
|
||||
do { \
|
||||
if ( logAPIs() ) _LIBUNWIND_LOG(msg, __VA_ARGS__); \
|
||||
} while(0)
|
||||
#define _LIBUNWIND_TRACE_UNWINDING(msg, ...) \
|
||||
do { \
|
||||
if ( logUnwinding() ) _LIBUNWIND_LOG(msg, __VA_ARGS__); \
|
||||
} while(0)
|
||||
#define _LIBUNWIND_TRACING_UNWINDING logUnwinding()
|
||||
#endif
|
||||
|
||||
|
||||
#endif // LIBUNWIND_CONFIG_H
|
237
src/dwarf2.h
Normal file
237
src/dwarf2.h
Normal file
@ -0,0 +1,237 @@
|
||||
//===------------------------------- dwarf2.h -----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
||||
/*
|
||||
These constants were taken from version 3 of the DWARF standard,
|
||||
which is Copyright (c) 2005 Free Standards Group, and
|
||||
Copyright (c) 1992, 1993 UNIX International, Inc.
|
||||
*/
|
||||
|
||||
#ifndef __DWARF2__
|
||||
#define __DWARF2__
|
||||
|
||||
// DWARF unwind instructions
|
||||
enum {
|
||||
DW_CFA_nop = 0x0,
|
||||
DW_CFA_set_loc = 0x1,
|
||||
DW_CFA_advance_loc1 = 0x2,
|
||||
DW_CFA_advance_loc2 = 0x3,
|
||||
DW_CFA_advance_loc4 = 0x4,
|
||||
DW_CFA_offset_extended = 0x5,
|
||||
DW_CFA_restore_extended = 0x6,
|
||||
DW_CFA_undefined = 0x7,
|
||||
DW_CFA_same_value = 0x8,
|
||||
DW_CFA_register = 0x9,
|
||||
DW_CFA_remember_state = 0xA,
|
||||
DW_CFA_restore_state = 0xB,
|
||||
DW_CFA_def_cfa = 0xC,
|
||||
DW_CFA_def_cfa_register = 0xD,
|
||||
DW_CFA_def_cfa_offset = 0xE,
|
||||
DW_CFA_def_cfa_expression = 0xF,
|
||||
DW_CFA_expression = 0x10,
|
||||
DW_CFA_offset_extended_sf = 0x11,
|
||||
DW_CFA_def_cfa_sf = 0x12,
|
||||
DW_CFA_def_cfa_offset_sf = 0x13,
|
||||
DW_CFA_val_offset = 0x14,
|
||||
DW_CFA_val_offset_sf = 0x15,
|
||||
DW_CFA_val_expression = 0x16,
|
||||
DW_CFA_advance_loc = 0x40, // high 2 bits are 0x1, lower 6 bits are delta
|
||||
DW_CFA_offset = 0x80, // high 2 bits are 0x2, lower 6 bits are register
|
||||
DW_CFA_restore = 0xC0, // high 2 bits are 0x3, lower 6 bits are register
|
||||
|
||||
// GNU extensions
|
||||
DW_CFA_GNU_window_save = 0x2D,
|
||||
DW_CFA_GNU_args_size = 0x2E,
|
||||
DW_CFA_GNU_negative_offset_extended = 0x2F
|
||||
};
|
||||
|
||||
|
||||
// FSF exception handling Pointer-Encoding constants
|
||||
// Used in CFI augmentation by GCC
|
||||
enum {
|
||||
DW_EH_PE_ptr = 0x00,
|
||||
DW_EH_PE_uleb128 = 0x01,
|
||||
DW_EH_PE_udata2 = 0x02,
|
||||
DW_EH_PE_udata4 = 0x03,
|
||||
DW_EH_PE_udata8 = 0x04,
|
||||
DW_EH_PE_signed = 0x08,
|
||||
DW_EH_PE_sleb128 = 0x09,
|
||||
DW_EH_PE_sdata2 = 0x0A,
|
||||
DW_EH_PE_sdata4 = 0x0B,
|
||||
DW_EH_PE_sdata8 = 0x0C,
|
||||
DW_EH_PE_absptr = 0x00,
|
||||
DW_EH_PE_pcrel = 0x10,
|
||||
DW_EH_PE_textrel = 0x20,
|
||||
DW_EH_PE_datarel = 0x30,
|
||||
DW_EH_PE_funcrel = 0x40,
|
||||
DW_EH_PE_aligned = 0x50,
|
||||
DW_EH_PE_indirect = 0x80,
|
||||
DW_EH_PE_omit = 0xFF
|
||||
};
|
||||
|
||||
|
||||
// DWARF expressions
|
||||
enum {
|
||||
DW_OP_addr = 0x03, // constant address (size target specific)
|
||||
DW_OP_deref = 0x06,
|
||||
DW_OP_const1u = 0x08, // 1-byte constant
|
||||
DW_OP_const1s = 0x09, // 1-byte constant
|
||||
DW_OP_const2u = 0x0A, // 2-byte constant
|
||||
DW_OP_const2s = 0x0B, // 2-byte constant
|
||||
DW_OP_const4u = 0x0C, // 4-byte constant
|
||||
DW_OP_const4s = 0x0D, // 4-byte constant
|
||||
DW_OP_const8u = 0x0E, // 8-byte constant
|
||||
DW_OP_const8s = 0x0F, // 8-byte constant
|
||||
DW_OP_constu = 0x10, // ULEB128 constant
|
||||
DW_OP_consts = 0x11, // SLEB128 constant
|
||||
DW_OP_dup = 0x12,
|
||||
DW_OP_drop = 0x13,
|
||||
DW_OP_over = 0x14,
|
||||
DW_OP_pick = 0x15, // 1-byte stack index
|
||||
DW_OP_swap = 0x16,
|
||||
DW_OP_rot = 0x17,
|
||||
DW_OP_xderef = 0x18,
|
||||
DW_OP_abs = 0x19,
|
||||
DW_OP_and = 0x1A,
|
||||
DW_OP_div = 0x1B,
|
||||
DW_OP_minus = 0x1C,
|
||||
DW_OP_mod = 0x1D,
|
||||
DW_OP_mul = 0x1E,
|
||||
DW_OP_neg = 0x1F,
|
||||
DW_OP_not = 0x20,
|
||||
DW_OP_or = 0x21,
|
||||
DW_OP_plus = 0x22,
|
||||
DW_OP_plus_uconst = 0x23, // ULEB128 addend
|
||||
DW_OP_shl = 0x24,
|
||||
DW_OP_shr = 0x25,
|
||||
DW_OP_shra = 0x26,
|
||||
DW_OP_xor = 0x27,
|
||||
DW_OP_skip = 0x2F, // signed 2-byte constant
|
||||
DW_OP_bra = 0x28, // signed 2-byte constant
|
||||
DW_OP_eq = 0x29,
|
||||
DW_OP_ge = 0x2A,
|
||||
DW_OP_gt = 0x2B,
|
||||
DW_OP_le = 0x2C,
|
||||
DW_OP_lt = 0x2D,
|
||||
DW_OP_ne = 0x2E,
|
||||
DW_OP_lit0 = 0x30, // Literal 0
|
||||
DW_OP_lit1 = 0x31, // Literal 1
|
||||
DW_OP_lit2 = 0x32, // Literal 2
|
||||
DW_OP_lit3 = 0x33, // Literal 3
|
||||
DW_OP_lit4 = 0x34, // Literal 4
|
||||
DW_OP_lit5 = 0x35, // Literal 5
|
||||
DW_OP_lit6 = 0x36, // Literal 6
|
||||
DW_OP_lit7 = 0x37, // Literal 7
|
||||
DW_OP_lit8 = 0x38, // Literal 8
|
||||
DW_OP_lit9 = 0x39, // Literal 9
|
||||
DW_OP_lit10 = 0x3A, // Literal 10
|
||||
DW_OP_lit11 = 0x3B, // Literal 11
|
||||
DW_OP_lit12 = 0x3C, // Literal 12
|
||||
DW_OP_lit13 = 0x3D, // Literal 13
|
||||
DW_OP_lit14 = 0x3E, // Literal 14
|
||||
DW_OP_lit15 = 0x3F, // Literal 15
|
||||
DW_OP_lit16 = 0x40, // Literal 16
|
||||
DW_OP_lit17 = 0x41, // Literal 17
|
||||
DW_OP_lit18 = 0x42, // Literal 18
|
||||
DW_OP_lit19 = 0x43, // Literal 19
|
||||
DW_OP_lit20 = 0x44, // Literal 20
|
||||
DW_OP_lit21 = 0x45, // Literal 21
|
||||
DW_OP_lit22 = 0x46, // Literal 22
|
||||
DW_OP_lit23 = 0x47, // Literal 23
|
||||
DW_OP_lit24 = 0x48, // Literal 24
|
||||
DW_OP_lit25 = 0x49, // Literal 25
|
||||
DW_OP_lit26 = 0x4A, // Literal 26
|
||||
DW_OP_lit27 = 0x4B, // Literal 27
|
||||
DW_OP_lit28 = 0x4C, // Literal 28
|
||||
DW_OP_lit29 = 0x4D, // Literal 29
|
||||
DW_OP_lit30 = 0x4E, // Literal 30
|
||||
DW_OP_lit31 = 0x4F, // Literal 31
|
||||
DW_OP_reg0 = 0x50, // Contents of reg0
|
||||
DW_OP_reg1 = 0x51, // Contents of reg1
|
||||
DW_OP_reg2 = 0x52, // Contents of reg2
|
||||
DW_OP_reg3 = 0x53, // Contents of reg3
|
||||
DW_OP_reg4 = 0x54, // Contents of reg4
|
||||
DW_OP_reg5 = 0x55, // Contents of reg5
|
||||
DW_OP_reg6 = 0x56, // Contents of reg6
|
||||
DW_OP_reg7 = 0x57, // Contents of reg7
|
||||
DW_OP_reg8 = 0x58, // Contents of reg8
|
||||
DW_OP_reg9 = 0x59, // Contents of reg9
|
||||
DW_OP_reg10 = 0x5A, // Contents of reg10
|
||||
DW_OP_reg11 = 0x5B, // Contents of reg11
|
||||
DW_OP_reg12 = 0x5C, // Contents of reg12
|
||||
DW_OP_reg13 = 0x5D, // Contents of reg13
|
||||
DW_OP_reg14 = 0x5E, // Contents of reg14
|
||||
DW_OP_reg15 = 0x5F, // Contents of reg15
|
||||
DW_OP_reg16 = 0x60, // Contents of reg16
|
||||
DW_OP_reg17 = 0x61, // Contents of reg17
|
||||
DW_OP_reg18 = 0x62, // Contents of reg18
|
||||
DW_OP_reg19 = 0x63, // Contents of reg19
|
||||
DW_OP_reg20 = 0x64, // Contents of reg20
|
||||
DW_OP_reg21 = 0x65, // Contents of reg21
|
||||
DW_OP_reg22 = 0x66, // Contents of reg22
|
||||
DW_OP_reg23 = 0x67, // Contents of reg23
|
||||
DW_OP_reg24 = 0x68, // Contents of reg24
|
||||
DW_OP_reg25 = 0x69, // Contents of reg25
|
||||
DW_OP_reg26 = 0x6A, // Contents of reg26
|
||||
DW_OP_reg27 = 0x6B, // Contents of reg27
|
||||
DW_OP_reg28 = 0x6C, // Contents of reg28
|
||||
DW_OP_reg29 = 0x6D, // Contents of reg29
|
||||
DW_OP_reg30 = 0x6E, // Contents of reg30
|
||||
DW_OP_reg31 = 0x6F, // Contents of reg31
|
||||
DW_OP_breg0 = 0x70, // base register 0 + SLEB128 offset
|
||||
DW_OP_breg1 = 0x71, // base register 1 + SLEB128 offset
|
||||
DW_OP_breg2 = 0x72, // base register 2 + SLEB128 offset
|
||||
DW_OP_breg3 = 0x73, // base register 3 + SLEB128 offset
|
||||
DW_OP_breg4 = 0x74, // base register 4 + SLEB128 offset
|
||||
DW_OP_breg5 = 0x75, // base register 5 + SLEB128 offset
|
||||
DW_OP_breg6 = 0x76, // base register 6 + SLEB128 offset
|
||||
DW_OP_breg7 = 0x77, // base register 7 + SLEB128 offset
|
||||
DW_OP_breg8 = 0x78, // base register 8 + SLEB128 offset
|
||||
DW_OP_breg9 = 0x79, // base register 9 + SLEB128 offset
|
||||
DW_OP_breg10 = 0x7A, // base register 10 + SLEB128 offset
|
||||
DW_OP_breg11 = 0x7B, // base register 11 + SLEB128 offset
|
||||
DW_OP_breg12 = 0x7C, // base register 12 + SLEB128 offset
|
||||
DW_OP_breg13 = 0x7D, // base register 13 + SLEB128 offset
|
||||
DW_OP_breg14 = 0x7E, // base register 14 + SLEB128 offset
|
||||
DW_OP_breg15 = 0x7F, // base register 15 + SLEB128 offset
|
||||
DW_OP_breg16 = 0x80, // base register 16 + SLEB128 offset
|
||||
DW_OP_breg17 = 0x81, // base register 17 + SLEB128 offset
|
||||
DW_OP_breg18 = 0x82, // base register 18 + SLEB128 offset
|
||||
DW_OP_breg19 = 0x83, // base register 19 + SLEB128 offset
|
||||
DW_OP_breg20 = 0x84, // base register 20 + SLEB128 offset
|
||||
DW_OP_breg21 = 0x85, // base register 21 + SLEB128 offset
|
||||
DW_OP_breg22 = 0x86, // base register 22 + SLEB128 offset
|
||||
DW_OP_breg23 = 0x87, // base register 23 + SLEB128 offset
|
||||
DW_OP_breg24 = 0x88, // base register 24 + SLEB128 offset
|
||||
DW_OP_breg25 = 0x89, // base register 25 + SLEB128 offset
|
||||
DW_OP_breg26 = 0x8A, // base register 26 + SLEB128 offset
|
||||
DW_OP_breg27 = 0x8B, // base register 27 + SLEB128 offset
|
||||
DW_OP_breg28 = 0x8C, // base register 28 + SLEB128 offset
|
||||
DW_OP_breg29 = 0x8D, // base register 29 + SLEB128 offset
|
||||
DW_OP_breg30 = 0x8E, // base register 30 + SLEB128 offset
|
||||
DW_OP_breg31 = 0x8F, // base register 31 + SLEB128 offset
|
||||
DW_OP_regx = 0x90, // ULEB128 register
|
||||
DW_OP_fbreg = 0x91, // SLEB128 offset
|
||||
DW_OP_bregx = 0x92, // ULEB128 register followed by SLEB128 offset
|
||||
DW_OP_piece = 0x93, // ULEB128 size of piece addressed
|
||||
DW_OP_deref_size = 0x94, // 1-byte size of data retrieved
|
||||
DW_OP_xderef_size = 0x95, // 1-byte size of data retrieved
|
||||
DW_OP_nop = 0x96,
|
||||
DW_OP_push_object_addres = 0x97,
|
||||
DW_OP_call2 = 0x98, // 2-byte offset of DIE
|
||||
DW_OP_call4 = 0x99, // 4-byte offset of DIE
|
||||
DW_OP_call_ref = 0x9A, // 4- or 8-byte offset of DIE
|
||||
DW_OP_lo_user = 0xE0,
|
||||
DW_OP_APPLE_uninit = 0xF0,
|
||||
DW_OP_hi_user = 0xFF
|
||||
};
|
||||
|
||||
|
||||
#endif
|
376
src/libunwind.cpp
Normal file
376
src/libunwind.cpp
Normal file
@ -0,0 +1,376 @@
|
||||
//===--------------------------- libuwind.cpp -----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//
|
||||
// Implements unw_* functions from <libunwind.h>
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <libunwind.h>
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include <cstdlib> // getenv
|
||||
#endif
|
||||
#include <new>
|
||||
#include <algorithm>
|
||||
|
||||
#include "libunwind_ext.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#include "UnwindCursor.hpp"
|
||||
|
||||
using namespace libunwind;
|
||||
|
||||
/// internal object to represent this processes address space
|
||||
LocalAddressSpace LocalAddressSpace::sThisAddressSpace;
|
||||
|
||||
_LIBUNWIND_EXPORT unw_addr_space_t unw_local_addr_space =
|
||||
(unw_addr_space_t)&LocalAddressSpace::sThisAddressSpace;
|
||||
|
||||
/// record the registers and stack position of the caller
|
||||
extern int unw_getcontext(unw_context_t *);
|
||||
// note: unw_getcontext() implemented in assembly
|
||||
|
||||
/// Create a cursor of a thread in this process given 'context' recorded by
|
||||
/// unw_getcontext().
|
||||
_LIBUNWIND_EXPORT int unw_init_local(unw_cursor_t *cursor,
|
||||
unw_context_t *context) {
|
||||
_LIBUNWIND_TRACE_API("unw_init_local(cursor=%p, context=%p)\n",
|
||||
static_cast<void *>(cursor),
|
||||
static_cast<void *>(context));
|
||||
// Use "placement new" to allocate UnwindCursor in the cursor buffer.
|
||||
#if defined(__i386__)
|
||||
new ((void *)cursor) UnwindCursor<LocalAddressSpace, Registers_x86>(
|
||||
context, LocalAddressSpace::sThisAddressSpace);
|
||||
#elif defined(__x86_64__)
|
||||
new ((void *)cursor) UnwindCursor<LocalAddressSpace, Registers_x86_64>(
|
||||
context, LocalAddressSpace::sThisAddressSpace);
|
||||
#elif defined(__ppc__)
|
||||
new ((void *)cursor) UnwindCursor<LocalAddressSpace, Registers_ppc>(
|
||||
context, LocalAddressSpace::sThisAddressSpace);
|
||||
#elif defined(__arm64__) || defined(__aarch64__)
|
||||
new ((void *)cursor) UnwindCursor<LocalAddressSpace, Registers_arm64>(
|
||||
context, LocalAddressSpace::sThisAddressSpace);
|
||||
#elif _LIBUNWIND_ARM_EHABI
|
||||
new ((void *)cursor) UnwindCursor<LocalAddressSpace, Registers_arm>(
|
||||
context, LocalAddressSpace::sThisAddressSpace);
|
||||
#elif defined(__or1k__)
|
||||
new ((void *)cursor) UnwindCursor<LocalAddressSpace, Registers_or1k>(
|
||||
context, LocalAddressSpace::sThisAddressSpace);
|
||||
#else
|
||||
#error Architecture not supported
|
||||
#endif
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
co->setInfoBasedOnIPRegister();
|
||||
|
||||
return UNW_ESUCCESS;
|
||||
}
|
||||
|
||||
#ifdef UNW_REMOTE
|
||||
/// Create a cursor into a thread in another process.
|
||||
_LIBUNWIND_EXPORT int unw_init_remote_thread(unw_cursor_t *cursor,
|
||||
unw_addr_space_t as,
|
||||
void *arg) {
|
||||
// special case: unw_init_remote(xx, unw_local_addr_space, xx)
|
||||
if (as == (unw_addr_space_t)&LocalAddressSpace::sThisAddressSpace)
|
||||
return unw_init_local(cursor, NULL); //FIXME
|
||||
|
||||
// use "placement new" to allocate UnwindCursor in the cursor buffer
|
||||
switch (as->cpuType) {
|
||||
case CPU_TYPE_I386:
|
||||
new ((void *)cursor)
|
||||
UnwindCursor<OtherAddressSpace<Pointer32<LittleEndian> >,
|
||||
Registers_x86>(((unw_addr_space_i386 *)as)->oas, arg);
|
||||
break;
|
||||
case CPU_TYPE_X86_64:
|
||||
new ((void *)cursor) UnwindCursor<
|
||||
OtherAddressSpace<Pointer64<LittleEndian> >, Registers_x86_64>(
|
||||
((unw_addr_space_x86_64 *)as)->oas, arg);
|
||||
break;
|
||||
case CPU_TYPE_POWERPC:
|
||||
new ((void *)cursor)
|
||||
UnwindCursor<OtherAddressSpace<Pointer32<BigEndian> >, Registers_ppc>(
|
||||
((unw_addr_space_ppc *)as)->oas, arg);
|
||||
break;
|
||||
default:
|
||||
return UNW_EUNSPEC;
|
||||
}
|
||||
return UNW_ESUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static bool is64bit(task_t task) {
|
||||
return false; // FIXME
|
||||
}
|
||||
|
||||
/// Create an address_space object for use in examining another task.
|
||||
_LIBUNWIND_EXPORT unw_addr_space_t unw_create_addr_space_for_task(task_t task) {
|
||||
#if __i386__
|
||||
if (is64bit(task)) {
|
||||
unw_addr_space_x86_64 *as = new unw_addr_space_x86_64(task);
|
||||
as->taskPort = task;
|
||||
as->cpuType = CPU_TYPE_X86_64;
|
||||
//as->oas
|
||||
} else {
|
||||
unw_addr_space_i386 *as = new unw_addr_space_i386(task);
|
||||
as->taskPort = task;
|
||||
as->cpuType = CPU_TYPE_I386;
|
||||
//as->oas
|
||||
}
|
||||
#else
|
||||
// FIXME
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// Delete an address_space object.
|
||||
_LIBUNWIND_EXPORT void unw_destroy_addr_space(unw_addr_space_t asp) {
|
||||
switch (asp->cpuType) {
|
||||
#if __i386__ || __x86_64__
|
||||
case CPU_TYPE_I386: {
|
||||
unw_addr_space_i386 *as = (unw_addr_space_i386 *)asp;
|
||||
delete as;
|
||||
}
|
||||
break;
|
||||
case CPU_TYPE_X86_64: {
|
||||
unw_addr_space_x86_64 *as = (unw_addr_space_x86_64 *)asp;
|
||||
delete as;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case CPU_TYPE_POWERPC: {
|
||||
unw_addr_space_ppc *as = (unw_addr_space_ppc *)asp;
|
||||
delete as;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif // UNW_REMOTE
|
||||
|
||||
|
||||
/// Get value of specified register at cursor position in stack frame.
|
||||
_LIBUNWIND_EXPORT int unw_get_reg(unw_cursor_t *cursor, unw_regnum_t regNum,
|
||||
unw_word_t *value) {
|
||||
_LIBUNWIND_TRACE_API("unw_get_reg(cursor=%p, regNum=%d, &value=%p)\n",
|
||||
static_cast<void *>(cursor), regNum,
|
||||
static_cast<void *>(value));
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
if (co->validReg(regNum)) {
|
||||
*value = co->getReg(regNum);
|
||||
return UNW_ESUCCESS;
|
||||
}
|
||||
return UNW_EBADREG;
|
||||
}
|
||||
|
||||
|
||||
/// Set value of specified register at cursor position in stack frame.
|
||||
_LIBUNWIND_EXPORT int unw_set_reg(unw_cursor_t *cursor, unw_regnum_t regNum,
|
||||
unw_word_t value) {
|
||||
_LIBUNWIND_TRACE_API("unw_set_reg(cursor=%p, regNum=%d, value=0x%llX)\n",
|
||||
static_cast<void *>(cursor), regNum, (long long)value);
|
||||
typedef LocalAddressSpace::pint_t pint_t;
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
if (co->validReg(regNum)) {
|
||||
co->setReg(regNum, (pint_t)value);
|
||||
// specical case altering IP to re-find info (being called by personality
|
||||
// function)
|
||||
if (regNum == UNW_REG_IP)
|
||||
co->setInfoBasedOnIPRegister(false);
|
||||
return UNW_ESUCCESS;
|
||||
}
|
||||
return UNW_EBADREG;
|
||||
}
|
||||
|
||||
|
||||
/// Get value of specified float register at cursor position in stack frame.
|
||||
_LIBUNWIND_EXPORT int unw_get_fpreg(unw_cursor_t *cursor, unw_regnum_t regNum,
|
||||
unw_fpreg_t *value) {
|
||||
_LIBUNWIND_TRACE_API("unw_get_fpreg(cursor=%p, regNum=%d, &value=%p)\n",
|
||||
static_cast<void *>(cursor), regNum,
|
||||
static_cast<void *>(value));
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
if (co->validFloatReg(regNum)) {
|
||||
*value = co->getFloatReg(regNum);
|
||||
return UNW_ESUCCESS;
|
||||
}
|
||||
return UNW_EBADREG;
|
||||
}
|
||||
|
||||
|
||||
/// Set value of specified float register at cursor position in stack frame.
|
||||
_LIBUNWIND_EXPORT int unw_set_fpreg(unw_cursor_t *cursor, unw_regnum_t regNum,
|
||||
unw_fpreg_t value) {
|
||||
#if _LIBUNWIND_ARM_EHABI
|
||||
_LIBUNWIND_TRACE_API("unw_set_fpreg(cursor=%p, regNum=%d, value=%llX)\n",
|
||||
static_cast<void *>(cursor), regNum, value);
|
||||
#else
|
||||
_LIBUNWIND_TRACE_API("unw_set_fpreg(cursor=%p, regNum=%d, value=%g)\n",
|
||||
static_cast<void *>(cursor), regNum, value);
|
||||
#endif
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
if (co->validFloatReg(regNum)) {
|
||||
co->setFloatReg(regNum, value);
|
||||
return UNW_ESUCCESS;
|
||||
}
|
||||
return UNW_EBADREG;
|
||||
}
|
||||
|
||||
|
||||
/// Move cursor to next frame.
|
||||
_LIBUNWIND_EXPORT int unw_step(unw_cursor_t *cursor) {
|
||||
_LIBUNWIND_TRACE_API("unw_step(cursor=%p)\n", static_cast<void *>(cursor));
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
return co->step();
|
||||
}
|
||||
|
||||
|
||||
/// Get unwind info at cursor position in stack frame.
|
||||
_LIBUNWIND_EXPORT int unw_get_proc_info(unw_cursor_t *cursor,
|
||||
unw_proc_info_t *info) {
|
||||
_LIBUNWIND_TRACE_API("unw_get_proc_info(cursor=%p, &info=%p)\n",
|
||||
static_cast<void *>(cursor), static_cast<void *>(info));
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
co->getInfo(info);
|
||||
if (info->end_ip == 0)
|
||||
return UNW_ENOINFO;
|
||||
else
|
||||
return UNW_ESUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/// Resume execution at cursor position (aka longjump).
|
||||
_LIBUNWIND_EXPORT int unw_resume(unw_cursor_t *cursor) {
|
||||
_LIBUNWIND_TRACE_API("unw_resume(cursor=%p)\n", static_cast<void *>(cursor));
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
co->jumpto();
|
||||
return UNW_EUNSPEC;
|
||||
}
|
||||
|
||||
|
||||
/// Get name of function at cursor position in stack frame.
|
||||
_LIBUNWIND_EXPORT int unw_get_proc_name(unw_cursor_t *cursor, char *buf,
|
||||
size_t bufLen, unw_word_t *offset) {
|
||||
_LIBUNWIND_TRACE_API("unw_get_proc_name(cursor=%p, &buf=%p, bufLen=%lu)\n",
|
||||
static_cast<void *>(cursor), static_cast<void *>(buf),
|
||||
static_cast<unsigned long>(bufLen));
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
if (co->getFunctionName(buf, bufLen, offset))
|
||||
return UNW_ESUCCESS;
|
||||
else
|
||||
return UNW_EUNSPEC;
|
||||
}
|
||||
|
||||
|
||||
/// Checks if a register is a floating-point register.
|
||||
_LIBUNWIND_EXPORT int unw_is_fpreg(unw_cursor_t *cursor, unw_regnum_t regNum) {
|
||||
_LIBUNWIND_TRACE_API("unw_is_fpreg(cursor=%p, regNum=%d)\n",
|
||||
static_cast<void *>(cursor), regNum);
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
return co->validFloatReg(regNum);
|
||||
}
|
||||
|
||||
|
||||
/// Checks if a register is a floating-point register.
|
||||
_LIBUNWIND_EXPORT const char *unw_regname(unw_cursor_t *cursor,
|
||||
unw_regnum_t regNum) {
|
||||
_LIBUNWIND_TRACE_API("unw_regname(cursor=%p, regNum=%d)\n",
|
||||
static_cast<void *>(cursor), regNum);
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
return co->getRegisterName(regNum);
|
||||
}
|
||||
|
||||
|
||||
/// Checks if current frame is signal trampoline.
|
||||
_LIBUNWIND_EXPORT int unw_is_signal_frame(unw_cursor_t *cursor) {
|
||||
_LIBUNWIND_TRACE_API("unw_is_signal_frame(cursor=%p)\n",
|
||||
static_cast<void *>(cursor));
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
return co->isSignalFrame();
|
||||
}
|
||||
|
||||
#ifdef __arm__
|
||||
// Save VFP registers d0-d15 using FSTMIADX instead of FSTMIADD
|
||||
_LIBUNWIND_EXPORT void unw_save_vfp_as_X(unw_cursor_t *cursor) {
|
||||
_LIBUNWIND_TRACE_API("unw_fpreg_save_vfp_as_X(cursor=%p)\n",
|
||||
static_cast<void *>(cursor));
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
return co->saveVFPAsX();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if _LIBUNWIND_SUPPORT_DWARF_UNWIND
|
||||
/// SPI: walks cached dwarf entries
|
||||
_LIBUNWIND_EXPORT void unw_iterate_dwarf_unwind_cache(void (*func)(
|
||||
unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)) {
|
||||
_LIBUNWIND_TRACE_API("unw_iterate_dwarf_unwind_cache(func=%p)\n",
|
||||
reinterpret_cast<void *>(func));
|
||||
DwarfFDECache<LocalAddressSpace>::iterateCacheEntries(func);
|
||||
}
|
||||
|
||||
|
||||
/// IPI: for __register_frame()
|
||||
void _unw_add_dynamic_fde(unw_word_t fde) {
|
||||
CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo;
|
||||
CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo;
|
||||
const char *message = CFI_Parser<LocalAddressSpace>::decodeFDE(
|
||||
LocalAddressSpace::sThisAddressSpace,
|
||||
(LocalAddressSpace::pint_t) fde, &fdeInfo, &cieInfo);
|
||||
if (message == NULL) {
|
||||
// dynamically registered FDEs don't have a mach_header group they are in.
|
||||
// Use fde as mh_group
|
||||
unw_word_t mh_group = fdeInfo.fdeStart;
|
||||
DwarfFDECache<LocalAddressSpace>::add((LocalAddressSpace::pint_t)mh_group,
|
||||
fdeInfo.pcStart, fdeInfo.pcEnd,
|
||||
fdeInfo.fdeStart);
|
||||
} else {
|
||||
_LIBUNWIND_DEBUG_LOG("_unw_add_dynamic_fde: bad fde: %s", message);
|
||||
}
|
||||
}
|
||||
|
||||
/// IPI: for __deregister_frame()
|
||||
void _unw_remove_dynamic_fde(unw_word_t fde) {
|
||||
// fde is own mh_group
|
||||
DwarfFDECache<LocalAddressSpace>::removeAllIn((LocalAddressSpace::pint_t)fde);
|
||||
}
|
||||
#endif // _LIBUNWIND_SUPPORT_DWARF_UNWIND
|
||||
|
||||
|
||||
|
||||
// Add logging hooks in Debug builds only
|
||||
#ifndef NDEBUG
|
||||
#include <stdlib.h>
|
||||
|
||||
_LIBUNWIND_HIDDEN
|
||||
bool logAPIs() {
|
||||
// do manual lock to avoid use of _cxa_guard_acquire or initializers
|
||||
static bool checked = false;
|
||||
static bool log = false;
|
||||
if (!checked) {
|
||||
log = (getenv("LIBUNWIND_PRINT_APIS") != NULL);
|
||||
checked = true;
|
||||
}
|
||||
return log;
|
||||
}
|
||||
|
||||
_LIBUNWIND_HIDDEN
|
||||
bool logUnwinding() {
|
||||
// do manual lock to avoid use of _cxa_guard_acquire or initializers
|
||||
static bool checked = false;
|
||||
static bool log = false;
|
||||
if (!checked) {
|
||||
log = (getenv("LIBUNWIND_PRINT_UNWINDING") != NULL);
|
||||
checked = true;
|
||||
}
|
||||
return log;
|
||||
}
|
||||
|
||||
#endif // NDEBUG
|
||||
|
47
src/libunwind_ext.h
Normal file
47
src/libunwind_ext.h
Normal file
@ -0,0 +1,47 @@
|
||||
//===------------------------ libunwind_ext.h -----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//
|
||||
// Extensions to libunwind API.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __LIBUNWIND_EXT__
|
||||
#define __LIBUNWIND_EXT__
|
||||
|
||||
#include "config.h"
|
||||
#include <libunwind.h>
|
||||
#include <unwind.h>
|
||||
|
||||
#define UNW_STEP_SUCCESS 1
|
||||
#define UNW_STEP_END 0
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
// SPI
|
||||
extern void unw_iterate_dwarf_unwind_cache(void (*func)(unw_word_t ip_start,
|
||||
unw_word_t ip_end,
|
||||
unw_word_t fde,
|
||||
unw_word_t mh));
|
||||
|
||||
// IPI
|
||||
extern void _unw_add_dynamic_fde(unw_word_t fde);
|
||||
extern void _unw_remove_dynamic_fde(unw_word_t fde);
|
||||
|
||||
#if _LIBUNWIND_ARM_EHABI
|
||||
extern const uint32_t* decode_eht_entry(const uint32_t*, size_t*, size_t*);
|
||||
extern _Unwind_Reason_Code _Unwind_VRS_Interpret(_Unwind_Context *context,
|
||||
const uint32_t *data,
|
||||
size_t offset, size_t len);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __LIBUNWIND_EXT__
|
37
src/unwind_ext.h
Normal file
37
src/unwind_ext.h
Normal file
@ -0,0 +1,37 @@
|
||||
//===-------------------------- unwind_ext.h ------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//
|
||||
// Extensions to unwind API.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __UNWIND_EXT__
|
||||
#define __UNWIND_EXT__
|
||||
|
||||
#include "unwind.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// These platform specific functions to get and set the top context are
|
||||
// implemented elsewhere.
|
||||
|
||||
extern struct _Unwind_FunctionContext *
|
||||
__Unwind_SjLj_GetTopOfFunctionStack();
|
||||
|
||||
extern void
|
||||
__Unwind_SjLj_SetTopOfFunctionStack(struct _Unwind_FunctionContext *fc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __UNWIND_EXT__
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user